summaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers/net
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/3c527.c52
-rw-r--r--drivers/net/3c59x.c2
-rw-r--r--drivers/net/Config.in12
-rw-r--r--drivers/net/aironet4500.h2
-rw-r--r--drivers/net/appletalk/Config.in31
-rw-r--r--drivers/net/bsd_comp.c42
-rw-r--r--drivers/net/dgrs.c5
-rw-r--r--drivers/net/dmfe.c586
-rw-r--r--drivers/net/eexpress.c1
-rw-r--r--drivers/net/epic100.c2
-rw-r--r--drivers/net/eql.c4
-rw-r--r--drivers/net/ewrk3.c4
-rw-r--r--drivers/net/hamradio/baycom_epp.c8
-rw-r--r--drivers/net/hamradio/baycom_par.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c2
-rw-r--r--drivers/net/hamradio/baycom_ser_hdx.c2
-rw-r--r--drivers/net/hamradio/bpqether.c2
-rw-r--r--drivers/net/hamradio/hdlcdrv.c6
-rw-r--r--drivers/net/hamradio/pi2.c4
-rw-r--r--drivers/net/hamradio/pt.c4
-rw-r--r--drivers/net/hamradio/scc.c12
-rw-r--r--drivers/net/hamradio/soundmodem/sm.c2
-rw-r--r--drivers/net/hamradio/soundmodem/sm_sbc.c4
-rw-r--r--drivers/net/hamradio/soundmodem/sm_wss.c2
-rw-r--r--drivers/net/hamradio/yam.c6
-rw-r--r--drivers/net/lance.c46
-rw-r--r--drivers/net/pcmcia/3c574_cs.c12
-rw-r--r--drivers/net/pcmcia/Config.in1
-rw-r--r--drivers/net/pcmcia/Makefile2
-rw-r--r--drivers/net/pcmcia/ray_cs.c2
-rw-r--r--drivers/net/pcmcia/tulip_cb.c3150
-rw-r--r--drivers/net/pcnet32.c2
-rw-r--r--drivers/net/plip.c59
-rw-r--r--drivers/net/ppp_async.c627
-rw-r--r--drivers/net/ppp_deflate.c49
-rw-r--r--drivers/net/ppp_generic.c1510
-rw-r--r--drivers/net/ppp_synctty.c2
-rw-r--r--drivers/net/rcpci45.c15
-rw-r--r--drivers/net/rrunner.c8
-rw-r--r--drivers/net/sb1000.c4
-rw-r--r--drivers/net/setup.c5
-rw-r--r--drivers/net/shaper.c11
-rw-r--r--drivers/net/sis900.c2
-rw-r--r--drivers/net/skfp/drvfbi.c2
-rw-r--r--drivers/net/skfp/skfddi.c2
-rw-r--r--drivers/net/sunhme.c4
-rw-r--r--drivers/net/tokenring/Config.in1
-rw-r--r--drivers/net/tokenring/Makefile1
-rw-r--r--drivers/net/tokenring/lanstreamer.c1776
-rw-r--r--drivers/net/tokenring/lanstreamer.h319
-rw-r--r--drivers/net/via-rhine.c2
-rw-r--r--drivers/net/wan/Config.in19
-rw-r--r--drivers/net/wan/Makefile56
-rw-r--r--drivers/net/wan/comx-hw-comx.c1426
-rw-r--r--drivers/net/wan/comx-hw-locomx.c496
-rw-r--r--drivers/net/wan/comx-hw-mixcom.c948
-rw-r--r--drivers/net/wan/comx-proto-fr.c1006
-rw-r--r--drivers/net/wan/comx-proto-lapb.c548
-rw-r--r--drivers/net/wan/comx-proto-ppp.c269
-rw-r--r--drivers/net/wan/comx.c1238
-rw-r--r--drivers/net/wan/comx.h240
-rw-r--r--drivers/net/wan/comxhw.h113
-rw-r--r--drivers/net/wan/cosa.c10
-rw-r--r--drivers/net/wan/hscx.h103
-rw-r--r--drivers/net/wan/mixcom.h35
-rw-r--r--drivers/net/wan/sbni.c6
-rw-r--r--drivers/net/wan/sdla.c2
-rw-r--r--drivers/net/wan/syncppp.c142
-rw-r--r--drivers/net/wan/syncppp.h4
-rw-r--r--drivers/net/wan/z85230.c94
-rw-r--r--drivers/net/wavelan.c6
-rw-r--r--drivers/net/wavelan.h6
-rw-r--r--drivers/net/yellowfin.c1
73 files changed, 10796 insertions, 4385 deletions
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index dab3f99b9..a44153e31 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -2,6 +2,7 @@
*
* (c) Copyright 1998 Red Hat Software Inc
* Written by Alan Cox.
+ * Further debugging by Carl Drougge.
*
* Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
* (for the MCA stuff) written by Wim Dumon.
@@ -15,7 +16,7 @@
*/
static const char *version =
- "3c527.c:v0.07 2000/01/18 Alan Cox (alan@redhat.com)\n";
+ "3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
/**
* DOC: Traps for the unwary
@@ -122,6 +123,7 @@ struct mc32_local
u32 base;
u16 rx_halted;
u16 tx_halted;
+ u16 rx_pending;
u16 exec_pending;
u16 mc_reload_wait; /* a multicast load request is pending */
atomic_t tx_count; /* buffers left */
@@ -451,6 +453,9 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
dev->name, lp->rx_len, lp->tx_len, lp->base);
+
+ if(lp->tx_len > TX_RING_MAX)
+ lp->tx_len = TX_RING_MAX;
dev->open = mc32_open;
dev->stop = mc32_close;
@@ -462,6 +467,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
lp->rx_halted = 1;
lp->tx_halted = 1;
+ lp->rx_pending = 0;
/* Fill in the fields of the device structure with ethernet values. */
ether_setup(dev);
@@ -652,6 +658,7 @@ static void mc32_rx_begin(struct net_device *dev)
mc32_ring_poll(dev);
lp->rx_halted=0;
+ lp->rx_pending=0;
}
/**
@@ -944,6 +951,7 @@ static void mc32_timeout(struct net_device *dev)
static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
+ int ioaddr = dev->base_addr;
unsigned long flags;
u16 tx_head;
@@ -967,7 +975,16 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
lp->tx_skb[lp->tx_skb_end] = skb;
lp->tx_skb_end++;
lp->tx_skb_end&=(TX_RING_MAX-1);
+
+ /* TX suspend - shouldnt be needed but apparently is.
+ This is a research item ... */
+
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ lp->tx_box->mbox=0;
+ outb(3, ioaddr+HOST_CMD);
+ /* Transmit now stopped */
+
/* P is the last sending/sent buffer as a pointer */
p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
@@ -990,7 +1007,9 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
p->status = 0;
p->control &= ~(1<<6);
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->tx_box->mbox=0;
+ outb(5, ioaddr+HOST_CMD); /* Restart TX */
restore_flags(flags);
netif_wake_queue(dev);
@@ -1096,11 +1115,16 @@ static void mc32_rx_ring(struct net_device *dev)
base = p->next;
}
while(x++<48);
+
+ /*
+ * Restart ring processing
+ */
while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
lp->rx_box->mbox=0;
lp->rx_box->data[0] = top;
outb(1<<3, ioaddr+HOST_CMD);
+ lp->rx_halted=0;
}
@@ -1123,7 +1147,6 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
struct net_device *dev = dev_id;
struct mc32_local *lp;
int ioaddr, status, boguscount = 0;
- int rx_event = 0;
if (dev == NULL) {
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
@@ -1182,7 +1205,15 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
case 0:
break;
case 2: /* RX */
- rx_event=1;
+ lp->rx_pending=1;
+ if(!lp->rx_halted)
+ {
+ /*
+ * Halt ring receive
+ */
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ outb(3<<3, ioaddr+HOST_CMD);
+ }
break;
case 3:
case 4:
@@ -1195,9 +1226,10 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
break;
case 6:
/* Out of RX buffers stat */
- /* Must restart */
lp->net_stats.rx_dropped++;
- rx_event = 1; /* To restart */
+ lp->rx_pending=1;
+ /* Must restart */
+ lp->rx_halted=1;
break;
default:
printk("%s: strange rx ack %d\n",
@@ -1231,11 +1263,17 @@ static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
/*
- * Process and restart the receive ring.
+ * Process and restart the receive ring. This has some state
+ * as we must halt the ring to process it and halting the ring
+ * might not occur in the same IRQ handling loop as we issue
+ * the halt.
*/
- if(rx_event)
+ if(lp->rx_pending && lp->rx_halted)
+ {
mc32_rx_ring(dev);
+ lp->rx_pending = 0;
+ }
return;
}
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 839c15ebc..31e8c79b8 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1914,7 +1914,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
EL3WINDOW(4);
mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index b264def30..b62986f83 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -199,11 +199,13 @@ if [ "$CONFIG_FDDI" = "y" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
- if [ "$CONFIG_HIPPI" = "y" ]; then
- tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
- if [ "$CONFIG_ROADRUNNER" != "n" ]; then
- bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ if [ "$CONFIG_INET" = "y" ]; then
+ bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+ if [ "$CONFIG_HIPPI" = "y" ]; then
+ tristate ' Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER
+ if [ "$CONFIG_ROADRUNNER" != "n" ]; then
+ bool ' Use large TX/RX rings' CONFIG_ROADRUNNER_LARGE_RINGS
+ fi
fi
fi
fi
diff --git a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h
index 45e3bc044..844f0ba03 100644
--- a/drivers/net/aironet4500.h
+++ b/drivers/net/aironet4500.h
@@ -454,7 +454,7 @@ struct awc_fid_queue {
};
-extern void
+extern __inline__ void
awc_fid_queue_init(struct awc_fid_queue * queue){
unsigned long flags;
diff --git a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in
index 951cf498d..e42231cbf 100644
--- a/drivers/net/appletalk/Config.in
+++ b/drivers/net/appletalk/Config.in
@@ -3,18 +3,21 @@
#
if [ "$CONFIG_ATALK" != "n" ]; then
- mainmenu_option next_comment
- comment 'Appletalk devices'
- dep_tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_ATALK
- dep_tristate 'COPS LocalTalk PC support' CONFIG_COPS $CONFIG_ATALK
- if [ "$CONFIG_COPS" != "n" ]; then
- bool ' Dayna firmware support' CONFIG_COPS_DAYNA
- bool ' Tangent firmware support' CONFIG_COPS_TANGENT
- fi
- dep_tristate 'Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
- if [ "$CONFIG_IPDDP" != "n" ]; then
- bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
- bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
- fi
- endmenu
+ mainmenu_option next_comment
+ comment 'Appletalk devices'
+ bool 'Appletalk interfaces support' CONFIG_APPLETALK
+ if [ "$CONFIG_APPLETALK" != "n" ]; then
+ dep_tristate ' Apple/Farallon LocalTalk PC support' CONFIG_LTPC $CONFIG_APPLETALK
+ dep_tristate ' COPS LocalTalk PC support' CONFIG_COPS $CONFIG_APPLETALK
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool ' Dayna firmware support' CONFIG_COPS_DAYNA
+ bool ' Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
+ dep_tristate ' Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_APPLETALK
+ if [ "$CONFIG_IPDDP" != "n" ]; then
+ bool ' IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+ bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
+ fi
+ fi
+ endmenu
fi
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index 073a7392a..493c2f6c5 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -39,7 +39,7 @@
/*
* This version is for use with contiguous buffers on Linux-derived systems.
*
- * ==FILEVERSION 970607==
+ * ==FILEVERSION 20000226==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the number above to the
@@ -58,32 +58,9 @@
#endif
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
+#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/byteorder.h>
-
-#include <linux/if.h>
-
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
#include <linux/ppp_defs.h>
@@ -1177,17 +1154,18 @@ static struct compressor ppp_bsd_compress = {
* Module support routines
*************************************************************/
-int
-init_module(void)
+int bsdcomp_init(void)
{
- int answer = ppp_register_compressor (&ppp_bsd_compress);
+ int answer = ppp_register_compressor(&ppp_bsd_compress);
if (answer == 0)
- printk (KERN_INFO "PPP BSD Compression module registered\n");
+ printk(KERN_INFO "PPP BSD Compression module registered\n");
return answer;
}
-void
-cleanup_module(void)
+void bsdcomp_cleanup(void)
{
- ppp_unregister_compressor (&ppp_bsd_compress);
+ ppp_unregister_compressor(&ppp_bsd_compress);
}
+
+module_init(bsdcomp_init);
+module_exit(bsdcomp_cleanup);
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index b42b8d390..5a5f3cc62 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -852,6 +852,8 @@ static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd)
return -EFAULT;
return (0);
case DGRS_SETFILTER:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (ioc.port > privN->bcomm->bc_nports)
return -EINVAL;
if (ioc.filter >= NFILTERS)
@@ -1188,8 +1190,11 @@ dgrs_probe1(struct net_device *dev)
priv->intrcnt = 0;
for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); )
+ {
+ barrier(); /* gcc 2.95 needs this */
if (priv->intrcnt >= 2)
break;
+ }
if (priv->intrcnt < 2)
{
printk("%s: Not interrupting on IRQ %d (%d)\n",
diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c
index 01a32afd5..87e5ccd7f 100644
--- a/drivers/net/dmfe.c
+++ b/drivers/net/dmfe.c
@@ -1,15 +1,26 @@
/*
- dmfe.c: Version 1.26
+ dmfe.c: Version 1.28 01/18/2000
- A Davicom DM9102 fast ethernet driver for Linux.
+ A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
+ Copyright (C) 1997 Sten Wang
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 1.
Compiler command:
"gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall
- -Wstrict-prototypes -O6 -c dmfe.c"
+ -Wstrict-prototypes -O6 -c dmfe.c"
+ OR
+ "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net -Wall
+ -Wstrict-prototypes -O6 -c dmfe.c"
The following steps teach you how to active DM9102 board:
1. Used the upper compiler command to compile dmfe.c
@@ -25,7 +36,7 @@
"route add -net 172.22.3.0 eth0"
5. Well done. Your DM9102 adapter actived now.
- Author: Sten Wang, E-mail: sten_wang@davicom.com.tw
+ Author: Sten Wang, 886-3-5798797-8517, E-mail: sten_wang@davicom.com.tw
Date: 10/28,1998
@@ -44,6 +55,9 @@
Check and fix on 64bit and big endian boxes.
Sort out the PCI latency.
+ (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+
+ Cleaned up for kernel merge by Alan Cox (alan@redhat.com)
*/
#include <linux/module.h>
@@ -60,38 +74,41 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/version.h>
-
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
#include <linux/delay.h>
+
#include <asm/processor.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
#define DMFE_SUCC 0
#define DM9102_IO_SIZE 0x80
-#define TX_FREE_DESC_CNT 0x1 /* Tx packet count */
+#define DM9102A_IO_SIZE 0x100
+#define TX_FREE_DESC_CNT 0xc /* Tx packet count */
+#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
#define DM910X_RESET 1
-#define CR6_DEFAULT 0x002c0000 /* SF, MII, HD */
+#define CR6_DEFAULT 0x00280000 /* SF, HD */
#define CR7_DEFAULT 0x1a2cd
#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
#define MAX_PACKET_SIZE 1514
#define DMFE_MAX_MULTICAST 14
-#define RX_MAX_TRAFFIC 0x5000
+#define RX_MAX_TRAFFIC 0x14000
#define MAX_CHECK_PACKET 0x8000
#define DMFE_10MHF 0
@@ -100,8 +117,8 @@
#define DMFE_100MFD 5
#define DMFE_AUTO 8
-#define DMFE_TIMER_WUT jiffies+HZ*1 /* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT HZ*2 /* tx packet time-out time */
+#define DMFE_TIMER_WUT jiffies+(HZ*2)/2 /* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT HZ*1.5 /* tx packet time-out time 1.5 s" */
#define DMFE_DBUG(dbug_now, msg, vaule) if (dmfe_debug || dbug_now) printk("DBUG: %s %x\n", msg, vaule)
@@ -109,7 +126,7 @@
#define DELAY_1US udelay(1) /* udelay scale 1 usec */
-#define SHOW_MEDIA_TYPE(mode) printk("\n<WARN> Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_WARNING "dmfe: Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
/* CR9 definition: SROM/MII */
@@ -125,6 +142,9 @@
#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;
+#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+
+
/* Structure/enum declaration ------------------------------- */
struct tx_desc {
u32 tdes0, tdes1, tdes2, tdes3;
@@ -149,13 +169,14 @@ struct dmfe_board_info {
struct pci_dev *net_dev; /* PCI device */
- u32 ioaddr; /* I/O base address */
+ unsigned long ioaddr; /* I/O base address */
+ u32 cr0_data;
u32 cr5_data;
u32 cr6_data;
u32 cr7_data;
u32 cr15_data;
-
-/* descriptor pointer */
+
+ /* descriptor pointer */
unsigned char *buf_pool_ptr; /* Tx buffer pool memory */
unsigned char *buf_pool_start; /* Tx buffer pool align dword */
unsigned char *desc_pool_ptr; /* descriptor pool memory */
@@ -166,9 +187,12 @@ struct dmfe_board_info {
struct rx_desc *rx_insert_ptr;
struct rx_desc *rx_ready_ptr; /* packet come pointer */
u32 tx_packet_cnt; /* transmitted packet count */
+ u32 tx_queue_cnt; /* wait to send packet count */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
+ u16 phy_id2; /* Phyxcer ID2 */
+
u8 media_mode; /* user specify media mode */
u8 op_mode; /* real work media mode */
u8 phy_addr;
@@ -190,14 +214,14 @@ enum dmfe_offsets {
enum dmfe_CR6_bits {
CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, CR6_FDM = 0x200,
- CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000
+ CR6_TXSC = 0x2000, CR6_STI = 0x100000, CR6_SFT = 0x200000, CR6_RXA = 0x40000000,
+ CR6_NO_PURGE = 0x20000000
};
/* Global variable declaration ----------------------------- */
-
static int dmfe_debug = 0;
static unsigned char dmfe_media_mode = 8;
-static struct net_device *dmfe_root_dev = NULL; /* First device */
+static struct net_device *dmfe_root_dev = NULL; /* First device */
static u32 dmfe_cr6_user_set = 0;
/* For module input parameter */
@@ -206,7 +230,7 @@ static u32 cr6set = 0;
static unsigned char mode = 8;
static u8 chkmode = 1;
-unsigned long CrcTable[256] =
+static unsigned long CrcTable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
@@ -275,7 +299,7 @@ unsigned long CrcTable[256] =
};
/* function declaration ------------------------------------- */
-static int dmfe_reg_board(void);
+static int dmfe_probe(void);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static int dmfe_stop(struct net_device *);
@@ -288,11 +312,11 @@ static void dmfe_descriptor_init(struct dmfe_board_info *, u32);
static void allocated_rx_buffer(struct dmfe_board_info *);
static void update_cr6(u32, u32);
static void send_filter_frame(struct net_device *, int);
-static u16 phy_read(u32, u8, u8);
-static void phy_write(u32, u8, u8, u16);
+static void dm9132_id_table(struct net_device *, int);
+static u16 phy_read(u32, u8, u8, u32);
+static void phy_write(u32, u8, u8, u16, u32);
static void phy_write_1bit(u32, u32);
static u16 phy_read_1bit(u32);
-static void parser_ctrl_info(struct dmfe_board_info *);
static void dmfe_sense_speed(struct dmfe_board_info *);
static void dmfe_process_mode(struct dmfe_board_info *);
static void dmfe_timer(unsigned long);
@@ -301,7 +325,7 @@ static void dmfe_reused_skb(struct dmfe_board_info *, struct sk_buff *);
static void dmfe_dynamic_reset(struct net_device *);
static void dmfe_free_rxbuffer(struct dmfe_board_info *);
static void dmfe_init_dm910x(struct net_device *);
-static unsigned long cal_CRC(unsigned char *, unsigned int);
+static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
/* DM910X network board routine ---------------------------- */
@@ -309,9 +333,9 @@ static unsigned long cal_CRC(unsigned char *, unsigned int);
* Search DM910X board, allocate space and register it
*/
-static int __init dmfe_reg_board(void)
+static int __init dmfe_probe(void)
{
- u32 pci_iobase;
+ unsigned long pci_iobase;
u16 dm9102_count = 0;
u8 pci_irqline;
static int index = 0; /* For multiple call */
@@ -320,7 +344,7 @@ static int __init dmfe_reg_board(void)
struct pci_dev *net_dev = NULL;
struct net_device *dev;
- DMFE_DBUG(0, "dmfe_reg_board()", 0);
+ DMFE_DBUG(0, "dmfe_probe()", 0);
if (!pci_present())
return -ENODEV;
@@ -329,20 +353,18 @@ static int __init dmfe_reg_board(void)
while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev)))
{
u32 pci_id;
+ u32 dev_rev;
index++;
if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC)
continue;
- if (pci_id != PCI_DM9102_ID)
+ if ((pci_id != PCI_DM9102_ID) && (pci_id != PCI_DM9132_ID))
continue;
pci_iobase = net_dev->resource[0].start;
pci_irqline = net_dev->irq;
- if (check_region(pci_iobase, DM9102_IO_SIZE)) /* IO range check */
- continue;
-
/* Enable Master/IO access, Disable memory access */
pci_enable_device (net_dev);
@@ -355,7 +377,19 @@ static int __init dmfe_reg_board(void)
pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80);
- /* IO range and interrupt check */
+ /* Read Chip revesion */
+ pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev);
+
+ /* IO range check */
+ if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) {
+ printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", pci_iobase, CHK_IO_SIZE(pci_id, dev_rev));
+ continue;
+ }
+ /* Interrupt check */
+ if (pci_irqline == 0) {
+ printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline);
+ continue;
+ }
/* Found DM9102 card and PCI resource allocated OK */
dm9102_count++; /* Found a DM9102 card */
@@ -373,7 +407,7 @@ static int __init dmfe_reg_board(void)
db->chip_id = pci_id; /* keep Chip vandor/Device ID */
db->ioaddr = pci_iobase;
- pci_read_config_dword(net_dev, 8, &db->chip_revesion);
+ db->chip_revesion = dev_rev;
db->net_dev = net_dev;
@@ -386,7 +420,7 @@ static int __init dmfe_reg_board(void)
dev->set_multicast_list = &dmfe_set_filter_mode;
dev->do_ioctl = &dmfe_do_ioctl;
- request_region(pci_iobase, DM9102_IO_SIZE, dev->name);
+ request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name);
/* read 64 word srom data */
for (i = 0; i < 64; i++)
@@ -422,20 +456,17 @@ static int dmfe_open(struct net_device *dev)
db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA);
if (db->desc_pool_ptr == NULL)
return -ENOMEM;
-
if ((u32) db->desc_pool_ptr & 0x1f)
db->first_tx_desc = (struct tx_desc *) (((u32) db->desc_pool_ptr & ~0x1f) + 0x20);
else
db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr;
/* Allocated Tx buffer memory */
-
db->buf_pool_ptr = kmalloc(TX_BUF_ALLOC * TX_DESC_CNT + 4, GFP_KERNEL | GFP_DMA);
if (db->buf_pool_ptr == NULL) {
kfree(db->desc_pool_ptr);
return -ENOMEM;
}
-
if ((u32) db->buf_pool_ptr & 0x3)
db->buf_pool_start = (char *) (((u32) db->buf_pool_ptr & ~0x3) + 0x4);
else
@@ -444,16 +475,21 @@ static int dmfe_open(struct net_device *dev)
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
db->in_reset_state = 0;
db->rx_error_cnt = 0;
- if (chkmode && (db->chip_revesion < 0x02000030)) {
- db->dm910x_chk_mode = 1; /* Enter the check mode */
- } else {
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) {
+ //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */
+ //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */
+ db->cr0_data = 0xc00000; /* TX/RX desc burst mode */
db->dm910x_chk_mode = 4; /* Enter the normal mode */
+ } else {
+ db->cr0_data = 0;
+ db->dm910x_chk_mode = 1; /* Enter the check mode */
}
/* Initilize DM910X board */
@@ -474,14 +510,12 @@ static int dmfe_open(struct net_device *dev)
return 0;
}
-/*
- * Initialize DM910X board
- * Reset DM910X board
- * Initialize TX/Rx descriptor chain structure
- * Send the set-up frame
- * Enable Tx/Rx machine
+/* Initilize DM910X board
+ Reset DM910X board
+ Initilize TX/Rx descriptor chain structure
+ Send the set-up frame
+ Enable Tx/Rx machine
*/
-
static void dmfe_init_dm910x(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -490,16 +524,18 @@ static void dmfe_init_dm910x(struct net_device *dev)
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x board : need 32 PCI clock to complete */
- outl(DM910X_RESET, ioaddr + DCR0);
+ outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
DELAY_5US;
- outl(0, ioaddr + DCR0);
+ outl(db->cr0_data, ioaddr + DCR0);
outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
- outl(0x80, ioaddr + DCR12); /* Reset DM9102 phyxcer */
+ outl(0x80, ioaddr + DCR12); /* RESET DM9102 phyxcer */
outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
- /* Parser control information: Phy addr */
- parser_ctrl_info(db);
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Media Mode Check */
db->media_mode = dmfe_media_mode;
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
@@ -514,7 +550,10 @@ static void dmfe_init_dm910x(struct net_device *dev)
update_cr6(db->cr6_data, ioaddr);
/* Send setup frame */
- send_filter_frame(dev, 0);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
/* Init CR5/CR7, interrupt active bit */
outl(0xffffffff, ioaddr + DCR5); /* clear all CR5 status */
@@ -533,10 +572,9 @@ static void dmfe_init_dm910x(struct net_device *dev)
/*
- * Hardware start transmission.
- * Send a packet to media from the upper layer.
+ Hardware start transmission.
+ Send a packet to media from the upper layer.
*/
-
static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -561,25 +599,24 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
txptr->tdes1 = 0xe1000000 | skb->len;
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
/* Point to next transmit free descriptor */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
- /* transmit counter increase 1 */
- db->tx_packet_cnt++;
- db->stats.tx_packets++;
-
- /* issue Tx polling command */
- outl(0x1, dev->base_addr + DCR1);
+ /* Transmit Packet Process */
+ if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ } else {
+ db->tx_queue_cnt++; /* queue the tx packet */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling comand */
+ }
/* Tx resource check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
- /* Set transmit time stamp */
- dev->trans_start = jiffies; /* saved the time stamp */
-
/* free this SKB */
dev_kfree_skb(skb);
return 0;
@@ -622,8 +659,8 @@ static int dmfe_stop(struct net_device *dev)
}
/*
- * DM9102 insterrupt handler
- * receive the packet to upper layer, free the transmitted packet
+ DM9102 insterrupt handler
+ receive the packet to upper layer, free the transmitted packet
*/
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -637,7 +674,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
return;
}
-
/* A real interrupt coming */
db = (struct dmfe_board_info *) dev->priv;
ioaddr = dev->base_addr;
@@ -667,15 +703,35 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* printk("tdes0=%x\n", txptr->tdes0); */
if (txptr->tdes0 & 0x80000000)
break;
+ db->stats.tx_packets++;
+
if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
/* printk("tdes0=%x\n", txptr->tdes0); */
db->stats.tx_errors++;
}
+ /* Transmit statistic counter */
+ if (txptr->tdes0 != 0x7fffffff) {
+ /* printk("tdes0=%x\n", txptr->tdes0); */
+ db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
+ db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
+ if (txptr->tdes0 & TDES0_ERR_MASK)
+ db->stats.tx_errors++;
+ }
txptr = (struct tx_desc *) txptr->next_tx_desc;
db->tx_packet_cnt--;
}
+ /* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
+ /* Send the Tx packet in queue */
+ if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ db->tx_packet_cnt++; /* Ready to send count */
+ outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
+ dev->trans_start = jiffies; /* saved the time stamp */
+ db->tx_queue_cnt--;
+ }
+ /* Resource available check */
if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
netif_wake_queue(dev);
@@ -695,7 +751,6 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/* Restore CR7 to enable interrupt mask */
-
if (db->interval_rx_cnt > RX_MAX_TRAFFIC)
db->cr7_data = 0x1a28d;
else
@@ -704,9 +759,8 @@ static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/*
- * Receive the come packet and pass to upper layer
+ Receive the come packet and pass to upper layer
*/
-
static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
{
struct rx_desc *rxptr;
@@ -727,11 +781,11 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
/* reused this SKB */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
- db->rx_error_cnt++;
+ /* db->rx_error_cnt++; */
} else {
+ /* A packet with First/Last flag */
rxlen = ((rxptr->rdes0 >> 16) & 0x3fff) - 4; /* skip CRC */
- /* A packet with First/Last flag */
if (rxptr->rdes0 & 0x8000) { /* error summary bit check */
/* This is a error packet */
/* printk("rdes0 error : %x \n", rxptr->rdes0); */
@@ -748,7 +802,7 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb = (struct sk_buff *) rxptr->rx_skb_ptr;
/* Received Packet CRC check need or not */
- if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen) != (*(unsigned long *) (skb->tail + rxlen)))) {
+ if ((db->dm910x_chk_mode & 1) && (cal_CRC(skb->tail, rxlen, 1) != (*(unsigned long *) (skb->tail + rxlen)))) {
/* Found a error received packet */
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
db->dm910x_chk_mode = 3;
@@ -758,11 +812,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
skb_put(skb, rxlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* Send to upper layer */
- /* skb->ip_summed = CHECKSUM_UNNECESSARY; */
dev->last_rx = jiffies;
db->stats.rx_packets++;
+ db->stats.rx_bytes += rxlen;
}
} else {
+ /* Reuse SKB buffer when the packet is error */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
}
@@ -772,12 +827,12 @@ static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db)
}
db->rx_ready_ptr = rxptr;
+
}
/*
- * Get statistics from driver.
+ Get statistics from driver.
*/
-
static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
{
struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
@@ -787,9 +842,8 @@ static struct enet_statistics *dmfe_get_stats(struct net_device *dev)
}
/*
- * Set DM910X multicast address
+ Set DM910X multicast address
*/
-
static void dmfe_set_filter_mode(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -809,13 +863,15 @@ static void dmfe_set_filter_mode(struct net_device *dev)
return;
}
DMFE_DBUG(0, "Set multicast address", dev->mc_count);
- send_filter_frame(dev, dev->mc_count);
+ if (db->chip_id == PCI_DM9132_ID)
+ dm9132_id_table(dev, dev->mc_count); /* DM9132 */
+ else
+ send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
}
/*
- * Process the upper socket ioctl command
+ Process the upper socket ioctl command
*/
-
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -823,10 +879,9 @@ static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
/*
- * A periodic timer routine
- * Dynamic media sense, allocated Rx buffer...
+ A periodic timer routine
+ Dynamic media sense, allocated Rx buffer...
*/
-
static void dmfe_timer(unsigned long data)
{
u32 tmp_cr8;
@@ -861,18 +916,26 @@ static void dmfe_timer(unsigned long data)
if (db->wait_reset | (db->tx_packet_cnt &&
((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
- /* printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start); */
+ /*
+ printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
+ */
DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
dmfe_dynamic_reset(dev);
db->timer.expires = DMFE_TIMER_WUT;
add_timer(&db->timer);
return;
}
- db->rx_error_cnt = 0; /* Clear previous counter */
+ db->rx_error_cnt = 0; /* Clear previos counter */
/* Link status check, Dynamic media type change */
- tmp_cr12 = inb(db->ioaddr + DCR12);
- if (db->chip_revesion == 0x02000030) {
+ if (db->chip_id == PCI_DM9132_ID)
+ tmp_cr12 = inb(db->ioaddr + DCR9 + 3); /* DM9132 */
+ else
+ tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */
+
+ if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) ||
+ ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) {
+ /* DM9102A Chip */
if (tmp_cr12 & 2)
tmp_cr12 = 0x0; /* Link failed */
else
@@ -882,10 +945,26 @@ static void dmfe_timer(unsigned long data)
/* Link Failed */
DMFE_DBUG(0, "Link Failed", tmp_cr12);
db->link_failed = 1;
- phy_write(db->ioaddr, db->phy_addr, 0, 0x8000); /* reset Phy controller */
+ phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); /* reset Phy */
+
+ /* 10/100M link failed, used 1M Home-Net */
+ db->cr6_data |= 0x00040000; /* CR6 bit18 = 1, select Home-Net */
+ db->cr6_data &= ~0x00000200; /* CR6 bit9 =0, half duplex mode */
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* For DM9801 : PHY ID1 0181h, PHY ID2 B900h */
+ db->phy_id2 = phy_read(db->ioaddr, db->phy_addr, 3, db->chip_id);
+ if (db->phy_id2 == 0xb900)
+ phy_write(db->ioaddr, db->phy_addr, 25, 0x7e08, db->chip_id);
} else if ((tmp_cr12 & 0x3) && db->link_failed) {
DMFE_DBUG(0, "Link link OK", tmp_cr12);
db->link_failed = 0;
+
+ /* CR6 bit18=0, select 10/100M */
+ db->cr6_data &= ~0x00040000;
+ update_cr6(db->cr6_data, db->ioaddr);
+
+ /* Auto Sense Speed */
if (db->media_mode & DMFE_AUTO)
dmfe_sense_speed(db);
dmfe_process_mode(db);
@@ -902,13 +981,12 @@ static void dmfe_timer(unsigned long data)
}
/*
- * Dynamic reset the DM910X board
- * Stop DM910X board
- * Free Tx/Rx allocated memory
- * Reset DM910X board
- * Re-initilize DM910X board
+ Dynamic reset the DM910X board
+ Stop DM910X board
+ Free Tx/Rx allocated memory
+ Reset DM910X board
+ Re-initilize DM910X board
*/
-
static void dmfe_dynamic_reset(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
@@ -929,6 +1007,7 @@ static void dmfe_dynamic_reset(struct net_device *dev)
/* system variable init */
db->tx_packet_cnt = 0;
+ db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
@@ -945,9 +1024,8 @@ static void dmfe_dynamic_reset(struct net_device *dev)
}
/*
- * Free all allocated rx buffer
+ free all allocated rx buffer
*/
-
static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
{
DMFE_DBUG(0, "dmfe_free_rxbuffer()", 0);
@@ -961,9 +1039,8 @@ static void dmfe_free_rxbuffer(struct dmfe_board_info *db)
}
/*
- * Reused the SK buffer
+ Reused the SK buffer
*/
-
static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
{
struct rx_desc *rxptr = db->rx_insert_ptr;
@@ -979,10 +1056,9 @@ static void dmfe_reused_skb(struct dmfe_board_info *db, struct sk_buff *skb)
}
/*
- * Initialize transmit/Receive descriptor
- * Using Chain structure, and allocated Tx/Rx buffer
+ Initialize transmit/Receive descriptor
+ Using Chain structure, and allocated Tx/Rx buffer
*/
-
static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
{
struct tx_desc *tmp_tx;
@@ -1033,10 +1109,9 @@ static void dmfe_descriptor_init(struct dmfe_board_info *db, u32 ioaddr)
}
/*
- * Update CR6 vaule
- * Firstly stop DM910X , then written value and start
+ Update CR6 vaule
+ Firstly stop DM910X , then written value and start
*/
-
static void update_cr6(u32 cr6_data, u32 ioaddr)
{
u32 cr6_tmp;
@@ -1049,11 +1124,50 @@ static void update_cr6(u32 cr6_data, u32 ioaddr)
/* printk("CR6 update %x ", cr6_tmp); */
}
-/*
- * Send a setup frame
- * This setup frame initilize DM910X addres filter mode
+/* Send a setup frame for DM9132
+ This setup frame initilize DM910X addres filter mode
+ */
+static void dm9132_id_table(struct net_device *dev, int mc_cnt)
+{
+ struct dev_mc_list *mcptr;
+ u16 *addrptr;
+ u32 ioaddr = dev->base_addr + 0xc0; /* ID Table */
+ u32 hash_val;
+ u16 i, hash_table[4];
+
+ DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+ /* Node address */
+ addrptr = (u16 *) dev->dev_addr;
+ outw(addrptr[0], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[1], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[2], ioaddr);
+ ioaddr += 4;
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ /* the multicast address in Hash Table : 64 bits */
+ for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+ }
+
+ /* Write the hash table to MAC MD table */
+ for (i = 0; i < 4; i++, ioaddr += 4) {
+ outw(hash_table[i], ioaddr);
+ }
+}
+
+/* Send a setup frame for DM9102/DM9102A
+ This setup frame initilize DM910X addres filter mode
*/
-
static void send_filter_frame(struct net_device *dev, int mc_cnt)
{
struct dmfe_board_info *db = dev->priv;
@@ -1068,17 +1182,17 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
txptr = db->tx_insert_ptr;
suptr = (u32 *) txptr->tx_buf_ptr;
- /* broadcast address */
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
- *suptr++ = 0xffff;
-
/* Node address */
addrptr = (u16 *) dev->dev_addr;
*suptr++ = addrptr[0];
*suptr++ = addrptr[1];
*suptr++ = addrptr[2];
+ /* broadcast address */
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+ *suptr++ = 0xffff;
+
/* fit the multicast address */
for (mcptr = dev->mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
addrptr = (u16 *) mcptr->dmi_addr;
@@ -1092,19 +1206,21 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt)
*suptr++ = 0xffff;
*suptr++ = 0xffff;
}
-
/* prepare the setup frame */
- db->tx_packet_cnt++;
- netif_stop_queue(dev);
- txptr->tdes1 = 0x890000c0;
- txptr->tdes0 = 0x80000000;
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
-
- update_cr6(db->cr6_data | 0x2000, dev->base_addr);
- outl(0x1, dev->base_addr + DCR1);
- update_cr6(db->cr6_data, dev->base_addr);
- dev->trans_start = jiffies;
-
+ txptr->tdes1 = 0x890000c0;
+ /* Resource Check and Send the setup packet */
+ if (!db->tx_packet_cnt) {
+ /* Resource Empty */
+ db->tx_packet_cnt++;
+ txptr->tdes0 = 0x80000000;
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ update_cr6(db->cr6_data, dev->base_addr);
+ } else {
+ /* Put into TX queue */
+ db->tx_queue_cnt++;
+ }
}
/*
@@ -1132,9 +1248,8 @@ static void allocated_rx_buffer(struct dmfe_board_info *db)
}
/*
- * Read one word data from the serial ROM
+ Read one word data from the serial ROM
*/
-
static u16 read_srom_word(long ioaddr, int offset)
{
int i;
@@ -1170,35 +1285,6 @@ static u16 read_srom_word(long ioaddr, int offset)
}
/*
- * Parser Control media block to get Phy address
- */
-
-static void parser_ctrl_info(struct dmfe_board_info *db)
-{
- int i;
- char *sdata = db->srom;
- unsigned char count;
-
- /* point to info leaf0 */
- count = *(sdata + 33);
-
- /* Point to First media block */
- sdata += 34;
- for (i = 0; i < count; i++) {
- if (*(sdata + 1) == 1) {
- db->phy_addr = *(sdata + 2);
- break;
- }
- sdata += ((unsigned char) *(sdata) & 0x7f) + 1;
- }
-
- if (i >= count) {
- printk("Can't found Control Block\n");
- db->phy_addr = 1;
- }
-}
-
-/*
* Auto sense the media mode
*/
@@ -1209,13 +1295,16 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
for (i = 1000; i; i--) {
DELAY_5US;
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 1);
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 1, db->chip_id);
if ((phy_mode & 0x24) == 0x24)
break;
}
if (i) {
- phy_mode = phy_read(db->ioaddr, db->phy_addr, 17) & 0xf000;
+ if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000;
+ else /* DM9102/DM9102A */
+ phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000;
/* printk("Phy_mode %x ",phy_mode); */
switch (phy_mode) {
case 0x1000:
@@ -1231,23 +1320,22 @@ static void dmfe_sense_speed(struct dmfe_board_info *db)
db->op_mode = DMFE_100MFD;
break;
default:
- db->op_mode = DMFE_100MHF;
- DMFE_DBUG(1, "Media Type error, phy reg17", phy_mode);
+ db->op_mode = DMFE_10MHF;
+ DMFE_DBUG(0, "Media Type error, phy reg17", phy_mode);
break;
}
} else {
- db->op_mode = DMFE_100MHF;
+ db->op_mode = DMFE_10MHF;
DMFE_DBUG(0, "Link Failed :", phy_mode);
}
}
/*
- * Process op-mode
- * AUTO mode : PHY controller in Auto-negotiation Mode
- * Force mode: PHY controller in force mode with HUB
- * N-way force capability with SWITCH
+ Process op-mode
+ AUTO mode : PHY controller in Auto-negotiation Mode
+ Force mode: PHY controller in force mode with HUB
+ N-way force capability with SWITCH
*/
-
static void dmfe_process_mode(struct dmfe_board_info *db)
{
u16 phy_reg;
@@ -1259,11 +1347,11 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
if (!(db->media_mode & DMFE_AUTO)) { /* Force Mode Check */
/* User force the media type */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 5);
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 5, db->chip_id);
/* printk("Nway phy_reg5 %x ",phy_reg); */
if (phy_reg & 0x1) {
/* parter own the N-Way capability */
- phy_reg = phy_read(db->ioaddr, db->phy_addr, 4) & ~0x1e0;
+ phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x1e0;
switch (db->op_mode) {
case DMFE_10MHF:
phy_reg |= 0x20;
@@ -1278,7 +1366,7 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg |= 0x100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 4, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id);
} else {
/* parter without the N-Way capability */
switch (db->op_mode) {
@@ -1295,95 +1383,109 @@ static void dmfe_process_mode(struct dmfe_board_info *db)
phy_reg = 0x2100;
break;
}
- phy_write(db->ioaddr, db->phy_addr, 0, phy_reg);
+ phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id);
}
}
}
/*
- * Write a word to Phy register
+ Write a word to Phy register
*/
-
-static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data)
+static void phy_write(u32 iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id)
{
u16 i;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ ioaddr = iobase + 0x80 + offset * 4;
+ outw(phy_data, ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send write command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* written trasnition */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Write a word data to PHY controller */
- for (i = 0x8000; i > 0; i >>= 1)
- phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Write a word data to PHY controller */
+ for (i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0);
+ }
}
/*
- * Read a word data from phy register
+ Read a word data from phy register
*/
-
-static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset)
+static u16 phy_read(u32 iobase, u8 phy_addr, u8 offset, u32 chip_id)
{
int i;
u16 phy_data;
- u32 ioaddr = iobase + DCR9;
+ u32 ioaddr;
- /* Send 33 synchronization clock to Phy controller */
- for (i = 0; i < 35; i++)
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ if (chip_id == PCI_DM9132_ID) {
+ /* DM9132 Chip */
+ ioaddr = iobase + 0x80 + offset * 4;
+ phy_data = inw(ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+
+ ioaddr = iobase + DCR9;
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send start command(01) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_0);
- phy_write_1bit(ioaddr, PHY_DATA_1);
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
- /* Send read command(10) to Phy */
- phy_write_1bit(ioaddr, PHY_DATA_1);
- phy_write_1bit(ioaddr, PHY_DATA_0);
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
- /* Send Phy addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Send register addres */
- for (i = 0x10; i > 0; i = i >> 1)
- phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
- /* Skip transition state */
- phy_read_1bit(ioaddr);
+ /* Skip transition state */
+ phy_read_1bit(ioaddr);
- /* read 16bit data */
- for (phy_data = 0, i = 0; i < 16; i++) {
- phy_data <<= 1;
- phy_data |= phy_read_1bit(ioaddr);
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr);
+ }
}
return phy_data;
}
/*
- * Write one bit data to Phy Controller
+ Write one bit data to Phy Controller
*/
-
static void phy_write_1bit(u32 ioaddr, u32 phy_data)
{
outl(phy_data, ioaddr); /* MII Clock Low */
@@ -1395,9 +1497,8 @@ static void phy_write_1bit(u32 ioaddr, u32 phy_data)
}
/*
- * Read one bit phy data from PHY controller
+ Read one bit phy data from PHY controller
*/
-
static u16 phy_read_1bit(u32 ioaddr)
{
u16 phy_data;
@@ -1412,10 +1513,11 @@ static u16 phy_read_1bit(u32 ioaddr)
}
/*
- * Calculate the CRC valude of the Rx packet
+ Calculate the CRC valude of the Rx packet
+ flag = 1 : return the reverse CRC (for the received packet CRC)
+ 0 : return the normal CRC (for Hash Table index)
*/
-
-static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
+unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag)
{
unsigned long Crc = 0xffffffff;
@@ -1423,8 +1525,10 @@ static unsigned long cal_CRC(unsigned char *Data, unsigned int Len)
Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
}
- return ~Crc;
-
+ if (flag)
+ return ~Crc;
+ else
+ return Crc;
}
@@ -1461,7 +1565,7 @@ static int __init dmfe_init_module(void)
break;
}
- return dmfe_reg_board(); /* search board and register */
+ return dmfe_probe(); /* search board and register */
}
/*
@@ -1473,14 +1577,16 @@ static int __init dmfe_init_module(void)
static void __exit dmfe_cleanup_module(void)
{
struct net_device *next_dev;
+ struct dmfe_board_info *db;
DMFE_DBUG(0, "clean_module()", 0);
while (dmfe_root_dev) {
- next_dev = ((struct dmfe_board_info *) dmfe_root_dev->priv)->next_dev;
+ db = dmfe_root_dev->priv;
+ next_dev = db->next_dev;
unregister_netdev(dmfe_root_dev);
- release_region(dmfe_root_dev->base_addr, DM9102_IO_SIZE);
- kfree(dmfe_root_dev->priv); /* free board information */
+ release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion));
+ kfree(db); /* free board information */
kfree(dmfe_root_dev); /* free device structure */
dmfe_root_dev = next_dev;
}
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 03c69417a..d5230ddda 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -712,6 +712,7 @@ static unsigned short eexp_start_irq(struct net_device *dev,
ack_cmd |= SCB_RUstart;
scb_wrrfa(dev, lp->rx_buf_start);
lp->rx_ptr = lp->rx_buf_start;
+ lp->started |= STARTED_RU;
}
ack_cmd |= SCB_CUstart | 0x2000;
}
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 1c546b4d1..f855d1978 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -1071,7 +1071,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (! netif_running(dev)) {
outl(0x0200, ioaddr + GENCTL);
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
index 820bc5d4f..e9f0254a7 100644
--- a/drivers/net/eql.c
+++ b/drivers/net/eql.c
@@ -42,7 +42,7 @@ static const char *version =
* reformatted.
*
* Revision 3.12 1995/03/22 21:07:51 anarchy
- * Added suser() checks on configuration.
+ * Added capable() checks on configuration.
* Moved header file.
*
* Revision 3.11 1995/01/19 23:14:31 guru
@@ -1030,6 +1030,8 @@ int init_module(void)
void cleanup_module(void)
{
+ kfree(((equalizer_t *)dev_eql.priv)->stats );
+ kfree(dev_eql.priv);
unregister_netdev(&dev_eql);
}
#endif /* MODULE */
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 90c82b341..ae92696c9 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -1836,7 +1836,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
status = -EFAULT;
break;
case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 1;
} else {
status = -EPERM;
@@ -1844,7 +1844,7 @@ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
break;
case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
- if (suser()) {
+ if (capable(CAP_NET_ADMIN)) {
lp->txc = 0;
} else {
status = -EPERM;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 98d228b91..e770baacf 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -1254,7 +1254,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
bc->ch_params.tx_delay = hi.data.cp.tx_delay;
bc->ch_params.tx_tail = hi.data.cp.tx_tail;
@@ -1275,7 +1275,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = hi.data.mp.iobase;
dev->irq = /*hi.data.mp.irq*/0;
@@ -1299,6 +1299,8 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
return 0;
@@ -1314,7 +1316,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODE:
- if (!suser() || netif_running(dev))
+ if (!capable(CAP_NET_ADMIN) || netif_running(dev))
return -EACCES;
hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
return baycom_setmode(bc, hi.data.modename);
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
index b68063b07..63dc93cfb 100644
--- a/drivers/net/hamradio/baycom_par.c
+++ b/drivers/net/hamradio/baycom_par.c
@@ -445,7 +445,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index cdcab2621..a90eb0f8c 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -555,7 +555,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
index 64b27286c..111373ca9 100644
--- a/drivers/net/hamradio/baycom_ser_hdx.c
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -598,7 +598,7 @@ static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return baycom_setmode(bc, hi->data.modename);
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 0cc9aa7c1..a48b3f6c6 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -366,7 +366,7 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct bpqdev *bpq = dev->priv;
struct bpq_req req;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (bpq == NULL) /* woops! */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index a3560c207..eac33513d 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -635,7 +635,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETCHANNELPAR:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
s->ch_params.tx_delay = bi.data.cp.tx_delay;
s->ch_params.tx_tail = bi.data.cp.tx_tail;
@@ -656,7 +656,7 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_SETMODEMPAR:
- if ((!suser()) || netif_running(dev))
+ if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev))
return -EACCES;
dev->base_addr = bi.data.mp.iobase;
dev->irq = bi.data.mp.irq;
@@ -684,6 +684,8 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case HDLCDRVCTL_CALIBRATE:
+ if(!capable(CAP_SYS_RAWIO))
+ return -EPERM;
s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
return 0;
diff --git a/drivers/net/hamradio/pi2.c b/drivers/net/hamradio/pi2.c
index b52a254c8..220c4d920 100644
--- a/drivers/net/hamradio/pi2.c
+++ b/drivers/net/hamradio/pi2.c
@@ -1580,7 +1580,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1597,7 +1597,7 @@ static int pi_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & 2) { /* if A channel */
diff --git a/drivers/net/hamradio/pt.c b/drivers/net/hamradio/pt.c
index 59762edd7..8baff63ff 100644
--- a/drivers/net/hamradio/pt.c
+++ b/drivers/net/hamradio/pt.c
@@ -998,7 +998,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (rq.cmd) {
case SIOCSPIPARAM:
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
save_flags(flags);
cli();
@@ -1015,7 +1015,7 @@ static int pt_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCSPIDMA:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = 0;
if (dev->base_addr & CHANA) { /* if A channel */
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index 09bb422f1..c7f27414f 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1791,7 +1791,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int found = 1;
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg) return -EFAULT;
if (Nchips >= SCC_MAXCHIPS)
@@ -1887,7 +1887,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (cmd == SIOCSCCINI)
{
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
if (Nchips == 0)
@@ -1904,7 +1904,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
if (cmd == SIOCSCCCHANINI)
{
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg) return -EINVAL;
scc->stat.bufsize = SCC_BUFSIZE;
@@ -1957,7 +1957,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -ENOIOCTLCMD;
case SIOCSCCSMEM:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
return -EINVAL;
scc->stat.bufsize = memcfg.bufsize;
@@ -1977,13 +1977,13 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return 0;
case SIOCSCCSKISS:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
return -EINVAL;
return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
case SIOCSCCCAL:
- if (!suser()) return -EPERM;
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
return -EINVAL;
diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c
index 4e5e3e9b9..af04a2c87 100644
--- a/drivers/net/hamradio/soundmodem/sm.c
+++ b/drivers/net/hamradio/soundmodem/sm.c
@@ -509,7 +509,7 @@ static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
return 0;
case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !suser())
+ if (netif_running(dev) || !capable(CAP_NET_ADMIN))
return -EACCES;
hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
return sethw(dev, sm, hi->data.modename);
diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c
index efc2a3295..772940049 100644
--- a/drivers/net/hamradio/soundmodem/sm_sbc.c
+++ b/drivers/net/hamradio/soundmodem/sm_sbc.c
@@ -1,4 +1,4 @@
-/*****************************************************************************/
+
/*
* sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver
@@ -576,7 +576,7 @@ static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
switch (SCSTATE->revhi) {
case 2:
diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c
index f1dc3744e..23abd970a 100644
--- a/drivers/net/hamradio/soundmodem/sm_wss.c
+++ b/drivers/net/hamradio/soundmodem/sm_wss.c
@@ -637,7 +637,7 @@ static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *
return i;
case SMCTL_SETMIXER:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
!SCSTATE->crystal) &&
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 400facbf8..27929e78b 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -962,7 +962,7 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (yp == NULL || yp->magic != YAM_MAGIC)
return -EINVAL;
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd != SIOCDEVPRIVATE)
@@ -977,6 +977,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (netif_running(dev))
return -EINVAL; /* Cannot change this parameter when up */
ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC);
+ if(ym==NULL)
+ return -ENOBUFS;
ym->bitrate = 9600;
if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs)))
return -EFAULT;
@@ -987,6 +989,8 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCYAMSCFG:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
return -EFAULT;
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index 689ffbd22..ce94c3139 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -14,22 +14,8 @@
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- Fixing alignment problem with 1.3.* kernel and some minor changes
- by Andrey V. Savochkin, 1996.
-
- Problems or questions may be send to Donald Becker (see above) or to
- Andrey Savochkin -- saw@shade.msu.ru or
- Laboratory of Computation Methods,
- Department of Mathematics and Mechanics,
- Moscow State University,
- Leninskye Gory, Moscow 119899
-
- But I should to inform you that I'm not an expert in the LANCE card
- and it may occurs that you will receive no answer on your mail
- to Donald Becker. I didn't receive any answer on all my letters
- to him. Who knows why... But may be you are more lucky? ;->
- SAW
-
+ Andrey V. Savochkin:
+ - alignment problem with 1.3.* kernel and some minor changes.
Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
- added support for Linux/Alpha, but removed most of it, because
it worked only for the PCI chip.
@@ -785,11 +771,19 @@ lance_open(struct net_device *dev)
*/
static void
-lance_purge_tx_ring(struct net_device *dev)
+lance_purge_ring(struct net_device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int i;
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = lp->rx_skbuff[i];
+ lp->rx_skbuff[i] = 0;
+ lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
+ if (skb)
+ dev_kfree_skb(skb);
+ }
for (i = 0; i < TX_RING_SIZE; i++) {
if (lp->tx_skbuff[i]) {
dev_kfree_skb(lp->tx_skbuff[i]);
@@ -850,7 +844,7 @@ lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
- lance_purge_tx_ring(dev);
+ lance_purge_ring(dev);
lance_init_ring(dev, GFP_ATOMIC);
}
outw(0x0000, dev->base_addr + LANCE_ADDR);
@@ -869,7 +863,7 @@ static void lance_tx_timeout (struct net_device *dev)
outw (0x0004, ioaddr + LANCE_DATA);
lp->stats.tx_errors++;
#ifndef final_version
- {
+ if (lance_debug > 3) {
int i;
printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
@@ -1192,19 +1186,7 @@ lance_close(struct net_device *dev)
}
free_irq(dev->irq, dev);
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = lp->rx_skbuff[i];
- lp->rx_skbuff[i] = 0;
- lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
- if (skb)
- dev_kfree_skb(skb);
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i])
- dev_kfree_skb(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = 0;
- }
+ lance_purge_ring(dev);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 2b5624b29..53f841af2 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -920,8 +920,6 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
"status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS));
- netif_stop_queue (dev);
-
outw(skb->len, ioaddr + TX_FIFO);
outw(0, ioaddr + TX_FIFO);
outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
@@ -929,13 +927,13 @@ static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
/* TxFree appears only in Window 1, not offset 0x1c. */
- if (inw(ioaddr + TxFree) > 1536) {
- netif_start_queue (dev);
- } else
+ if (inw(ioaddr + TxFree) <= 1536) {
+ netif_stop_queue (dev);
/* Interrupt us when the FIFO has room for max-sized packet.
The threshold is in units of dwords. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-
+ }
+
dev_kfree_skb (skb);
pop_tx_status(dev);
@@ -976,8 +974,6 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue (dev);
- } else {
- netif_stop_queue (dev);
}
if (status & TxComplete)
diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in
index 606d5a606..534a4bdbd 100644
--- a/drivers/net/pcmcia/Config.in
+++ b/drivers/net/pcmcia/Config.in
@@ -18,7 +18,6 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
if [ "$CONFIG_CARDBUS" = "y" ]; then
dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m
- dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP m
fi
bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile
index 11c583dac..5d0d36f4a 100644
--- a/drivers/net/pcmcia/Makefile
+++ b/drivers/net/pcmcia/Makefile
@@ -20,7 +20,6 @@ obj- :=
export-objs := ray_cs.o
CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE
-CFLAGS_tulip_cb.o = -DCARDBUS -DMODULE
# 16-bit client drivers
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
@@ -40,7 +39,6 @@ obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o
# Cardbus client drivers
obj-$(CONFIG_PCMCIA_3C575) += 3c575_cb.o
-obj-$(CONFIG_PCMCIA_TULIP) += tulip_cb.o
O_OBJS := $(filter-out $(export-objs), $(obj-y))
OX_OBJS := $(filter $(export-objs), $(obj-y))
diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c
index 7c763cf8e..3c088df36 100644
--- a/drivers/net/pcmcia/ray_cs.c
+++ b/drivers/net/pcmcia/ray_cs.c
@@ -1417,7 +1417,7 @@ static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
#define SIOCGIPFRAMING SIOCDEVPRIVATE + 1 /* Get framing mode */
#define SIOCGIPCOUNTRY SIOCDEVPRIVATE + 3 /* Get country code */
case SIOCSIPFRAMING:
- if(!suser()) /* For private IOCTLs, we need to check permissions */
+ if(!capable(CAP_NET_ADMIN)) /* For private IOCTLs, we need to check permissions */
{
err = -EPERM;
break;
diff --git a/drivers/net/pcmcia/tulip_cb.c b/drivers/net/pcmcia/tulip_cb.c
deleted file mode 100644
index 38dcdc0c1..000000000
--- a/drivers/net/pcmcia/tulip_cb.c
+++ /dev/null
@@ -1,3150 +0,0 @@
-/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
-/*
- Written/copyright 1994-1999 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU Public License, incorporated herein by reference.
-
- This driver is for the Digital "Tulip" Ethernet adapter interface.
- It should work with most DEC 21*4*-based chips/ethercards, as well as
- with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
-
- The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- Center of Excellence in Space Data and Information Sciences
- Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-
- Support and updates available at
- http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
-*/
-
-#define SMP_CHECK
-static const char version[] = "tulip.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
-
-/* A few user-configurable values. */
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 25;
-
-#define MAX_UNITS 8
-/* Used to pass the full-duplex flag, etc. */
-static int full_duplex[MAX_UNITS] = {0, };
-static int options[MAX_UNITS] = {0, };
-static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */
-
-/* The possible media types that can be set in options[] are: */
-static const char * const medianame[] = {
- "10baseT", "10base2", "AUI", "100baseTx",
- "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
- "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
- "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-
-/* Keep the ring sizes a power of two for efficiency.
- Making the Tx ring too large decreases the effectiveness of channel
- bonding and packet priority.
- There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 32
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#ifdef __alpha__
-static int rx_copybreak = 1518;
-#else
-static int rx_copybreak = 100;
-#endif
-
-/*
- Set the bus performance register.
- Typical: Set 16 longword cache alignment, no burst limit.
- Cache alignment bits 15:14 Burst length 13:8
- 0000 No alignment 0x00000000 unlimited 0800 8 longwords
- 4000 8 longwords 0100 1 longword 1000 16 longwords
- 8000 16 longwords 0200 2 longwords 2000 32 longwords
- C000 32 longwords 0400 4 longwords
- Warning: many older 486 systems are broken and require setting 0x00A04800
- 8 longword cache alignment, 8 longword burst.
- ToDo: Non-Intel setting could be better.
-*/
-
-#if defined(__alpha__)
-static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__powerpc__)
-static int csr0 = 0x01B00000 | 0x8000;
-#elif defined(__sparc__)
-static int csr0 = 0x01B00080 | 0x8000;
-#elif defined(__i386__)
-static int csr0 = 0x01A00000 | 0x8000;
-#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
-#endif
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (4*HZ)
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
-/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
- to support a pre-NWay full-duplex signaling mechanism using short frames.
- No one knows what it should be, but if left at its default value some
- 10base2(!) packets trigger a full-duplex-request interrupt. */
-#define FULL_DUPLEX_MAGIC 0x6969
-
-#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
-#warning You must compile this file with the correct options!
-#warning See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <asm/processor.h> /* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/unaligned.h>
-
-/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
- This is only in the support-all-kernels source code. */
-
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(reverse_probe, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(csr0, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define tulip_debug debug
-#ifdef TULIP_DEBUG
-static int tulip_debug = TULIP_DEBUG;
-#else
-static int tulip_debug = 1;
-#endif
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the DECchip "Tulip", Digital's
-single-chip ethernet controllers for PCI. Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike
-chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
-supported.
-
-These chips are used on at least 140 unique PCI board designs. The great
-number of chips and board designs supported is the reason for the
-driver size and complexity. Almost of the increasing complexity is in the
-board configuration and media selection code. There is very little
-increasing in the operational critical path length.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS preferably should assign the
-PCI INTA signal to an otherwise unused system IRQ line.
-
-Some boards have EEPROMs tables with default media entry. The factory default
-is usually "autoselect". This should only be overridden when using
-transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!)
-for forcing full-duplex when used with old link partners that do not do
-autonegotiation.
-
-III. Driver operation
-
-IIIa. Ring buffers
-
-The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-This driver uses statically allocated rings of Rx and Tx descriptors, set at
-compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs
-for the Rx ring buffers at open() time and passes the skb->data field to the
-Tulip as receive data buffers. When an incoming frame is less than
-RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
-copied to the new skbuff. When the incoming frame is larger, the skbuff is
-passed directly up the protocol stack and replaced by a newly allocated
-skbuff.
-
-The RX_COPYBREAK value is chosen to trade-off the memory wasted by
-using a full-sized skbuff for small frames vs. the copying costs of larger
-frames. For small frames the copying cost is negligible (esp. considering
-that we are pre-loading the cache with immediately useful header
-information). For large frames the copying cost is non-trivial, and the
-larger copy might flush the cache of useful data. A subtle aspect of this
-choice is that the Tulip only receives into longword aligned buffers, thus
-the IP header at offset 14 isn't longword aligned for further processing.
-Copied frames are put into the new skbuff at an offset of "+2", thus copying
-has the beneficial effect of aligning the IP header and preloading the
-cache.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'tp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board.
-Greg LaPolla at Linksys provided PNIC and other Linksys boards.
-Znyx provided a four-port card for testing.
-
-IVb. References
-
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
-http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM")
-http://www.national.com/pf/DP/DP83840A.html
-http://www.asix.com.tw/pmac.htm
-http://www.admtek.com.tw/
-
-IVc. Errata
-
-The old DEC databooks were light on details.
-The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written. Hmmm, now how is that possible?
-
-The DEC SROM format is very badly designed not precisely defined, leading to
-part of the media selection junkheap below. Some boards do not have EEPROM
-media tables and need to be patched up. Worse, other boards use the DEC
-design kit media table when it isn't correct for their board.
-
-We cannot use MII interrupts because there is no defined GPIO pin to attach
-them. The MII transceiver status is polled using an kernel timer.
-
-*/
-
-/* This table use during operation for capabilities and media timer. */
-
-static void tulip_timer(unsigned long data);
-static void t21142_timer(unsigned long data);
-static void mxic_timer(unsigned long data);
-static void pnic_timer(unsigned long data);
-static void comet_timer(unsigned long data);
-
-enum tbl_flag {
- HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
- HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
- HAS_NWAY143=0x40, /* Uses 21143-like internal NWay. */
-};
-static struct tulip_chip_table {
- char *chip_name;
- int io_size;
- int valid_intrs; /* CSR7 interrupt enable settings */
- int flags;
- void (*media_timer)(unsigned long data);
-} tulip_tbl[] = {
- { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
- { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
- { "Digital DS21140 Tulip", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
- { "Digital DS21143 Tulip", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer },
- { "Lite-On 82c168 PNIC", 256, 0x0001ebef,
- HAS_MII, pnic_timer },
- { "Macronix 98713 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Macronix 98715 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "Macronix 98725 PMAC", 256, 0x0001ebef,
- HAS_MEDIA_TABLE, mxic_timer },
- { "ASIX AX88140", 128, 0x0001fbff,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer },
- { "Lite-On PNIC-II", 256, 0x0001ebef,
- HAS_MII | HAS_NWAY143, pnic_timer },
- { "ADMtek Comet", 256, 0x0001abef,
- MC_HASH_ONLY, comet_timer },
- { "Compex 9881 PMAC", 128, 0x0001ebef,
- HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
- { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
- HAS_MII | HAS_ACPI, tulip_timer },
- {0},
-};
-/* This matches the table above. Note 21142 == 21143. */
-enum chips {
- DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
- LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881,
- X3201_3,
-};
-
-/* A full-duplex map for media types. */
-enum MediaIs {
- MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
- MediaIs100=16};
-static const char media_cap[] =
-{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 };
-static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0};
-/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
-static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
-static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
-static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
-static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
-static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-
-/* Offsets to the Command and Status Registers, "CSRs". All accesses
- must be longword instructions and quadword aligned. */
-enum tulip_offsets {
- CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
- CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
- CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
-
-/* The bits in the CSR5 status registers, mostly interrupt sources. */
-enum status_bits {
- TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
- NormalIntr=0x10000, AbnormalIntr=0x8000,
- RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
- TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
-};
-
-/* The Tulip Rx and Tx buffer descriptors. */
-struct tulip_rx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2;
-};
-
-struct tulip_tx_desc {
- s32 status;
- s32 length;
- u32 buffer1, buffer2; /* We use only buffer 1. */
-};
-
-enum desc_status_bits {
- DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
-};
-
-/* Ring-wrap flag in length field, use for last ring entry.
- 0x01000000 means chain on buffer2 address,
- 0x02000000 means use the ring start address in CSR2/3.
- Note: Some work-alike chips do not function correctly in chained mode.
- The ASIX chip works only in chained mode.
- Thus we indicates ring mode, but always write the 'next' field for
- chained mode as well.
-*/
-#define DESC_RING_WRAP 0x02000000
-
-#ifdef CARDBUS
-#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6)
-#else
-#define EEPROM_ADDRLEN 6
-#endif
-#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
-
-struct medialeaf {
- u8 type;
- u8 media;
- unsigned char *leafdata;
-};
-
-struct mediatable {
- u16 defaultmedia;
- u8 leafcount, csr12dir; /* General purpose pin directions. */
- unsigned has_mii:1, has_nonmii:1, has_reset:6;
- u32 csr15dir, csr15val; /* 21143 NWay setting. */
- struct medialeaf mleaf[0];
-};
-
-struct mediainfo {
- struct mediainfo *next;
- int info_type;
- int index;
- unsigned char *info;
-};
-
-struct tulip_private {
- char devname[8]; /* Used only for kernel debugging. */
- const char *product_name;
- struct tulip_rx_desc rx_ring[RX_RING_SIZE];
- struct tulip_tx_desc tx_ring[TX_RING_SIZE];
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
- /* The X3201-3 requires double word aligned tx bufs */
- struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
- /* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- char *rx_buffs; /* Address of temporary Rx buffers. */
- u8 setup_buf[96*sizeof(u16) + 7];
- u16 *setup_frame; /* Pseudo-Tx frame to init address table. */
- int chip_id;
- int revision;
- struct net_device_stats stats;
- struct timer_list timer; /* Media selection timer. */
- int interrupt; /* In-interrupt flag. */
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int full_duplex_lock:1;
- unsigned int fake_addr:1; /* Multiport board faked address. */
- unsigned int default_port:4; /* Last dev->if_port value. */
- unsigned int media2:4; /* Secondary monitored media port. */
- unsigned int medialock:1; /* Don't sense media type. */
- unsigned int mediasense:1; /* Media sensing in progress. */
- unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */
- unsigned int open:1;
- unsigned int csr0; /* CSR0 setting. */
- unsigned int csr6; /* Current CSR6 control settings. */
- unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
- u16 to_advertise; /* NWay capabilities advertised. */
- u16 lpar; /* 21143 Link partner ability. */
- u16 advertising[4];
- signed char phys[4], mii_cnt; /* MII device addresses. */
- struct mediatable *mtable;
- int cur_index; /* Current media index. */
- int saved_if_port;
- struct pci_dev *pdev;
- spinlock_t lock;
- int pad0, pad1; /* Used for 8-byte alignment */
-};
-
-static void parse_eeprom(struct net_device *dev);
-static int read_eeprom(long ioaddr, int location, int addr_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static void select_media(struct net_device *dev, int startup);
-static void tulip_up(struct net_device *dev);
-static void tulip_down(struct net_device *dev);
-static int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
-static void t21142_start_nway(struct net_device *dev);
-static void tulip_tx_timeout(struct net_device *dev);
-static void tulip_init_ring(struct net_device *dev);
-static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int tulip_close(struct net_device *dev);
-static struct net_device_stats *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
-static void set_rx_mode(struct net_device *dev);
-
-/* The Xircom cards are picky about when certain bits in CSR6 can be
- manipulated. Keith Owens <kaos@ocs.com.au>. */
-
-static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
-{
- const int strict_bits = 0x0060e202;
- int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
- long flags;
- save_flags(flags);
- cli();
- if (chip_idx != X3201_3) {
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
- return;
- }
- newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
- /* read 0 on the Xircom cards */
- newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
- currcsr6 = inl(ioaddr + CSR6);
- if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
- ((currcsr6 & ~0x2002) == 0)) {
- outl(newcsr6, ioaddr + CSR6); /* safe */
- restore_flags(flags);
- return;
- }
- /* make sure the transmitter and receiver are stopped first */
- currcsr6 &= ~0x2002;
- while (1) {
- csr5 = inl(ioaddr + CSR5);
- if (csr5 == 0xffffffff)
- break; /* cannot read csr5, card removed? */
- csr5_22_20 = csr5 & 0x700000;
- csr5_19_17 = csr5 & 0x0e0000;
- if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
- (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
- break; /* both are stopped or suspended */
- if (!--attempts) {
- printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
- "csr5=0x%08x\n", csr5);
- outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */
- restore_flags(flags);
- return;
- }
- outl(currcsr6, ioaddr + CSR6);
- udelay(1);
- }
- /* now it is safe to change csr6 */
- outl(newcsr6, ioaddr + CSR6);
- restore_flags(flags);
-}
-
-static struct net_device *tulip_probe1(struct pci_dev *pdev,
- struct net_device *dev, long ioaddr, int irq,
- int chip_idx, int board_idx)
-{
- static int did_version = 0; /* Already printed version info. */
- struct tulip_private *tp;
- /* See note below on the multiport cards. */
- static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
- static int last_irq = 0;
- static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */
- u8 chip_rev;
- int i;
- unsigned short sum;
-
- if (tulip_debug > 0 && did_version++ == 0)
- printk(KERN_INFO "%s", version);
-
- dev = init_etherdev(dev, 0);
-
- pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
- /* Bring the 21143 out of sleep mode.
- Caution: Snooze mode does not work with some boards! */
- if (tulip_tbl[chip_idx].flags & HAS_ACPI)
- pci_write_config_dword(pdev, 0x40, 0x00000000);
-
- printk(KERN_INFO "%s: %s rev %d at %#3lx,",
- dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
-
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
- /* Clear the missed-packet counter. */
- (volatile int)inl(ioaddr + CSR8);
-
- if (chip_idx == DC21041) {
- if (inl(ioaddr + CSR9) & 0x8000) {
- printk(" 21040 compatible mode,");
- chip_idx = DC21040;
- } else {
- printk(" 21041 mode,");
- }
- }
-
- /* The station address ROM is read byte serially. The register must
- be polled, waiting for the value to be read bit serially from the
- EEPROM.
- */
- sum = 0;
- if (chip_idx == DC21040) {
- outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
- for (i = 0; i < 6; i++) {
- int value, boguscnt = 100000;
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- dev->dev_addr[i] = value;
- sum += value & 0xff;
- }
- } else if (chip_idx == LC82C168) {
- for (i = 0; i < 3; i++) {
- int value, boguscnt = 100000;
- outl(0x600 | i, ioaddr + 0x98);
- do
- value = inl(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
- put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i);
- sum += value & 0xffff;
- }
- } else if (chip_idx == COMET) {
- /* No need to read the EEPROM. */
- put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr);
- put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4));
- for (i = 0; i < 6; i ++)
- sum += dev->dev_addr[i];
- } else if (chip_idx == X3201_3) {
- /* Xircom has its address stored in the CIS
- * we access it through the boot rom interface for now
- * this might not work, as the CIS is not parsed but I
- * (danilo) use the offset I found on my card's CIS !!!
- *
- * Doug Ledford: I changed this routine around so that it
- * walks the CIS memory space, parsing the config items, and
- * finds the proper lan_node_id tuple and uses the data
- * stored there.
- */
- unsigned char j, tuple, link, data_id, data_count;
- outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
- for (i = 0x100; i < 0x1f7; i += link+2) {
- outl(i, ioaddr + CSR10);
- tuple = inl(ioaddr + CSR9) & 0xff;
- outl(i + 1, ioaddr + CSR10);
- link = inl(ioaddr + CSR9) & 0xff;
- outl(i + 2, ioaddr + CSR10);
- data_id = inl(ioaddr + CSR9) & 0xff;
- outl(i + 3, ioaddr + CSR10);
- data_count = inl(ioaddr + CSR9) & 0xff;
- if ( (tuple == 0x22) &&
- (data_id == 0x04) && (data_count == 0x06) ) {
- /*
- * This is it. We have the data we want.
- */
- for (j = 0; j < 6; j++) {
- outl(i + j + 4, ioaddr + CSR10);
- dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
- }
- break;
- } else if (link == 0) {
- break;
- }
- }
- sum = 1; // to make check below fail!
- } else { /* Must be a new chip, with a serial EEPROM interface. */
- /* We read the whole EEPROM, and sort it out later. DEC has a
- specification _Digital Semiconductor 21X4 Serial ROM Format_
- but early vendor boards just put the address in the first six
- EEPROM locations. */
- unsigned char ee_data[EEPROM_SIZE];
- int sa_offset = 0;
-
- for (i = 0; i < sizeof(ee_data)/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect the simple EEPROM format by the duplicated station addr. */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- sa_offset = 20;
- if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) {
- sa_offset = 2; /* Grrr, damn Matrox boards. */
- multiport_cnt = 4;
- }
- for (i = 0; i < 6; i ++) {
- dev->dev_addr[i] = ee_data[i + sa_offset];
- sum += ee_data[i + sa_offset];
- }
- }
- /* Lite-On boards have the address byte-swapped. */
- if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0)
- && dev->dev_addr[1] == 0x00)
- for (i = 0; i < 6; i+=2) {
- char tmp = dev->dev_addr[i];
- dev->dev_addr[i] = dev->dev_addr[i+1];
- dev->dev_addr[i+1] = tmp;
- }
- /* On the Zynx 315 Etherarray and other multiport boards only the
- first Tulip has an EEPROM.
- The addresses of the subsequent ports are derived from the first.
- Many PCI BIOSes also incorrectly report the IRQ line, so we correct
- that here as well. */
- if (sum == 0 || sum == 6*0xff) {
- printk(" EEPROM not present,");
- for (i = 0; i < 5; i++)
- dev->dev_addr[i] = last_phys_addr[i];
- dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386__) /* Patch up x86 BIOS bug. */
- if (last_irq)
- irq = last_irq;
-#endif
- }
-
- for (i = 0; i < 6; i++)
- printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
- printk(", IRQ %d.\n", irq);
- last_irq = irq;
-
- /* We do a request_region() only to register /proc/ioports info. */
- /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
- request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name);
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
-
- /* Make certain the data structures are quadword aligned. */
- tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
- memset(tp, 0, sizeof(*tp));
- dev->priv = tp;
-
- tp->lock = SPIN_LOCK_UNLOCKED;
- tp->pdev = pdev;
- tp->chip_id = chip_idx;
- tp->revision = chip_rev;
- tp->csr0 = csr0;
- tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
-
- /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
- And the ASIX must have a burst limit or horrible things happen. */
- if ( (chip_idx == DC21143 && chip_rev == 65) ||
- (chip_idx == X3201_3) )
- tp->csr0 &= ~0x01000000;
- else if (chip_idx == AX88140)
- tp->csr0 |= 0x2000;
-
-#ifdef TULIP_FULL_DUPLEX
- tp->full_duplex = 1;
- tp->full_duplex_lock = 1;
-#endif
-#ifdef TULIP_DEFAULT_MEDIA
- tp->default_port = TULIP_DEFAULT_MEDIA;
-#endif
-#ifdef TULIP_NO_MEDIA_SWITCH
- tp->medialock = 1;
-#endif
-
- /* The lower four bits are the media type. */
- if (board_idx >= 0 && board_idx < MAX_UNITS) {
- tp->default_port = options[board_idx] & 15;
- if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
- tp->full_duplex = 1;
- if (mtu[board_idx] > 0)
- dev->mtu = mtu[board_idx];
- }
- if (dev->mem_start)
- tp->default_port = dev->mem_start;
- if (tp->default_port) {
- tp->medialock = 1;
- if (media_cap[tp->default_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- }
- if (tp->full_duplex)
- tp->full_duplex_lock = 1;
-
- /* This is logically part of probe1(), but too complex to write inline. */
- if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE)
- parse_eeprom(dev);
-
- if (media_cap[tp->default_port] & MediaIsMII) {
- u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
- tp->to_advertise = media2advert[tp->default_port - 9];
- } else
- tp->to_advertise = 0x03e1;
-
- if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) ||
- (tp->mtable && tp->mtable->has_mii) ||
- ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) {
- int phy, phy_idx;
- if (tp->mtable && tp->mtable->has_mii) {
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == 11) {
- tp->cur_index = i;
- tp->saved_if_port = dev->if_port;
- select_media(dev, 1);
- dev->if_port = tp->saved_if_port;
- break;
- }
- }
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later,
- but takes much time. */
- for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
- phy++) {
- int mii_status = mdio_read(dev, phy, 1);
- if ((mii_status & 0x8301) == 0x8001 ||
- ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
- int mii_reg0 = mdio_read(dev, phy, 0);
- int mii_advert = mdio_read(dev, phy, 4);
- int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
- tp->phys[phy_idx] = phy;
- tp->advertising[phy_idx++] = reg4;
- printk(KERN_INFO "%s: MII transceiver #%d "
- "config %4.4x status %4.4x advertising %4.4x.\n",
- dev->name, phy, mii_reg0, mii_status, mii_advert);
- /* Fixup for DLink with miswired PHY. */
- if (mii_advert != reg4) {
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d,"
- " previously advertising %4.4x.\n",
- dev->name, reg4, phy, mii_advert);
- mdio_write(dev, phy, 4, reg4);
- }
- /* Enable autonegotiation: some boards default to off. */
- mdio_write(dev, phy, 0, mii_reg0 |
- (tp->full_duplex ? 0x1100 : 0x1000) |
- (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
- }
- }
- tp->mii_cnt = phy_idx;
- if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
- printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
- dev->name);
- tp->phys[0] = 1;
- }
- }
-
- /* The Tulip-specific entries in the device structure. */
- dev->open = &tulip_open;
- dev->hard_start_xmit = &tulip_start_xmit;
- dev->stop = &tulip_close;
- dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
- dev->do_ioctl = &private_ioctl;
-#endif
-#ifdef HAVE_MULTICAST
- dev->set_multicast_list = &set_rx_mode;
-#endif
- dev->tx_timeout = tulip_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- /* Reset the xcvr interface and turn on heartbeat. */
- switch (chip_idx) {
- case DC21041:
- outl(0x00000000, ioaddr + CSR13);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx);
- outl(0x0000EF05, ioaddr + CSR13);
- break;
- case DC21040:
- outl(0x00000000, ioaddr + CSR13);
- outl(0x00000004, ioaddr + CSR13);
- break;
- case DC21140: default:
- if (tp->mtable)
- outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
- break;
- case DC21142:
- case PNIC2:
- if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) {
- outl_CSR6(0x82020000, ioaddr, chip_idx);
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl_CSR6(0x820E0000, ioaddr, chip_idx);
- } else {
- outl_CSR6(0x82420200, ioaddr, chip_idx);
- outl(0x0001, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- outl(0x0001, ioaddr + CSR13);
- outl(0x1301, ioaddr + CSR12); /* Start NWay. */
- }
- break;
- case X3201_3:
- outl(0x0008, ioaddr + CSR15);
- udelay(5); /* The delays are Xircom recommended to give the
- * chipset time to reset the actual hardware
- * on the PCMCIA card
- */
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- outl_CSR6(0x32000200, ioaddr, chip_idx);
- break;
- case LC82C168:
- if ( ! tp->mii_cnt) {
- outl_CSR6(0x00420000, ioaddr, chip_idx);
- outl(0x30, ioaddr + CSR12);
- outl(0x0001F078, ioaddr + 0xB8);
- outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
- }
- break;
- case MX98713: case COMPEX9881:
- outl_CSR6(0x00000000, ioaddr, chip_idx);
- outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
- outl(0x00000001, ioaddr + CSR13);
- break;
- case MX98715: case MX98725:
- outl_CSR6(0x01a80000, ioaddr, chip_idx);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00001000, ioaddr + CSR12);
- break;
- case COMET:
- /* No initialization necessary. */
- break;
- }
-
- return dev;
-}
-
-/* Serial EEPROM section. */
-/* The main routine to parse the very complicated SROM structure.
- Search www.digital.com for "21X4 SROM" to get details.
- This code is very complex, and will require changes to support
- additional cards, so I'll be verbose about what is going on.
- */
-
-/* Known cards that have old-style EEPROMs. */
-static struct fixups {
- char *name;
- unsigned char addr0, addr1, addr2;
- u16 newtable[32]; /* Max length below. */
-} eeprom_fixups[] = {
- {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
- 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
- {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
- 0x0000, 0x009E, /* 10baseT */
- 0x0903, 0x006D, /* 100baseTx */ }},
- {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
- 0x0107, 0x8021, /* 100baseFx */
- 0x0108, 0x8021, /* 100baseFx-FD */
- 0x0103, 0x006D, /* 100baseTx */ }},
- {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
- 0x1001, 0x009E, /* 10base2, CSR12 0x10*/
- 0x0000, 0x009E, /* 10baseT */
- 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
- {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
- 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
- 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
- 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
- }},
- {0, 0, 0, 0, {}}};
-
-static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
-
-#if defined(__i386__) /* AKA get_unaligned() */
-#define get_u16(ptr) (*(u16 *)(ptr))
-#else
-#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
-#endif
-
-static void parse_eeprom(struct net_device *dev)
-{
- /* The last media info list parsed, for multiport boards. */
- static struct mediatable *last_mediatable = NULL;
- static unsigned char *last_ee_data = NULL;
- static int controller_index = 0;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- unsigned char *ee_data = tp->eeprom;
- int i;
-#ifdef CARDBUS
- int chip_rev = tp->revision;
-#endif
-
- tp->mtable = 0;
- for (i = 0; i < EEPROM_SIZE/2; i++)
- ((u16 *)ee_data)[i] =
- le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
-
- /* Detect an old-style (SA only) EEPROM layout:
- memcmp(eedata, eedata+16, 8). */
- for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- break;
- if (i >= 8) {
- if (ee_data[0] == 0xff) {
- if (last_mediatable) {
- controller_index++;
- printk(KERN_INFO "%s: Controller %d of multiport board.\n",
- dev->name, controller_index);
- tp->mtable = last_mediatable;
- ee_data = last_ee_data;
- goto subsequent_board;
- } else
- printk(KERN_INFO "%s: Missing EEPROM, this interface may "
- "not work correctly!\n",
- dev->name);
- return;
- }
- /* Do a fix-up based on the vendor half of the station address prefix. */
- for (i = 0; eeprom_fixups[i].name; i++) {
- if (dev->dev_addr[0] == eeprom_fixups[i].addr0
- && dev->dev_addr[1] == eeprom_fixups[i].addr1
- && dev->dev_addr[2] == eeprom_fixups[i].addr2) {
- if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
- i++; /* An Accton EN1207, not an outlaw Maxtech. */
- memcpy(ee_data + 26, eeprom_fixups[i].newtable,
- sizeof(eeprom_fixups[i].newtable));
- printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using"
- " substitute media control info.\n",
- dev->name, eeprom_fixups[i].name);
- break;
- }
- }
- if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
- printk(KERN_INFO "%s: Old style EEPROM with no media selection "
- "information.\n",
- dev->name);
- return;
- }
- }
-
- controller_index = 0;
- if (ee_data[19] > 1) { /* Multiport board. */
- last_ee_data = ee_data;
- }
-subsequent_board:
-
- if (ee_data[27] == 0) { /* No valid media table. */
- } else if (tp->chip_id == DC21041) {
- unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
- short media;
- int count;
-
- media = get_u16(p);
- p += 2;
- count = *p++;
-
- printk(KERN_INFO "%s:21041 Media information at %d, default media "
- "%4.4x (%s).\n", dev->name, ee_data[27], media,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- unsigned char media_code = *p++;
- u16 csrvals[3];
- int idx;
- for (idx = 0; idx < 3; idx++) {
- csrvals[idx] = get_u16(p);
- p += 2;
- }
- if (media_code & 0x40) {
- printk(KERN_INFO "%s: 21041 media %2.2x (%s),"
- " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
- dev->name, media_code & 15, medianame[media_code & 15],
- csrvals[0], csrvals[1], csrvals[2]);
- } else
- printk(KERN_INFO "%s: 21041 media #%d, %s.\n",
- dev->name, media_code & 15, medianame[media_code & 15]);
- }
- } else {
- unsigned char *p = (void *)ee_data + ee_data[27];
- unsigned char csr12dir = 0;
- int count;
- struct mediatable *mtable;
- u16 media = get_u16(p);
-
- p += 2;
- if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
- csr12dir = *p++;
- count = *p++;
- mtable = (struct mediatable *)
- kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
- GFP_KERNEL);
- if (mtable == NULL)
- return; /* Horrible, impossible failure. */
- last_mediatable = tp->mtable = mtable;
- mtable->defaultmedia = media;
- mtable->leafcount = count;
- mtable->csr12dir = csr12dir;
- mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
- mtable->csr15dir = mtable->csr15val = 0;
-
- printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name,
- media & 0x0800 ? "Autosense" : medianame[media & 15]);
- for (i = 0; i < count; i++) {
- struct medialeaf *leaf = &mtable->mleaf[i];
-
- if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
- leaf->type = 0;
- leaf->media = p[0] & 0x3f;
- leaf->leafdata = p;
- if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
- mtable->has_mii = 1;
- p += 4;
- } else {
- leaf->type = p[1];
- if (p[1] == 0x05) {
- mtable->has_reset = i;
- leaf->media = p[2] & 0x0f;
- } else if (p[1] & 1) {
- mtable->has_mii = 1;
- leaf->media = 11;
- } else {
- mtable->has_nonmii = 1;
- leaf->media = p[2] & 0x0f;
- if (p[1] == 2) {
- if (leaf->media == 0) {
- mtable->csr15dir = get_unaligned((u16*)&p[3])<<16;
- mtable->csr15val = get_unaligned((u16*)&p[5])<<16;
- } else if (leaf->media == 0x40) {
- u32 base15 = get_unaligned((u16*)&p[7]);
- mtable->csr15dir =
- (get_unaligned((u16*)&p[9])<<16) + base15;
- mtable->csr15val =
- (get_unaligned((u16*)&p[11])<<16) + base15;
- }
- }
- }
- leaf->leafdata = p + 2;
- p += (p[0] & 0x3f) + 1;
- }
- if (tulip_debug > 1 && leaf->media == 11) {
- unsigned char *bp = leaf->leafdata;
- printk(KERN_INFO "%s: MII interface PHY %d, setup/reset "
- "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
- dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
- bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
- }
- printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described "
- "by a %s (%d) block.\n",
- dev->name, i, medianame[leaf->media], leaf->media,
- block_name[leaf->type], leaf->type);
- }
- }
-}
-/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
-#define EE_CS 0x01 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x01
-#define EE_WRITE_1 0x05
-#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
-#define EE_ENB (0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- Even at 33Mhz current PCI implementations don't overrun the EEPROM clock.
- We add a bus turn-around to insure that this remains true. */
-#define eeprom_delay() inl(ee_addr)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5 << addr_len)
-#define EE_READ_CMD (6 << addr_len)
-#define EE_ERASE_CMD (7 << addr_len)
-
-static int read_eeprom(long ioaddr, int location, int addr_len)
-{
- int i;
- unsigned short retval = 0;
- long ee_addr = ioaddr + CSR9;
- int read_cmd = location | EE_READ_CMD;
-
- outl(EE_ENB & ~EE_CS, ee_addr);
- outl(EE_ENB, ee_addr);
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- outl(EE_ENB | dataval, ee_addr);
- eeprom_delay();
- outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- }
- outl(EE_ENB, ee_addr);
-
- for (i = 16; i > 0; i--) {
- outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
- eeprom_delay();
- retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
- outl(EE_ENB, ee_addr);
- eeprom_delay();
- }
-
- /* Terminate the EEPROM access. */
- outl(EE_ENB & ~EE_CS, ee_addr);
- return retval;
-}
-
-/* MII transceiver control section.
- Read and write the MII registers using software-generated serial
- MDIO protocol. See the MII specifications or DP83840A data sheet
- for details. */
-
-/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues or future 66Mhz PCI. */
-#define mdio_delay() inl(mdio_addr)
-
-/* Read and write the MII registers using software-generated serial
- MDIO protocol. It is just different enough from the EEPROM protocol
- to not share code. The maxium data clock rate is 2.5 Mhz. */
-#define MDIO_SHIFT_CLK 0x10000
-#define MDIO_DATA_WRITE0 0x00000
-#define MDIO_DATA_WRITE1 0x20000
-#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
-#define MDIO_ENB_IN 0x40000
-#define MDIO_DATA_READ 0x80000
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int retval = 0;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- inl(ioaddr + 0xA0);
- while (--i > 0)
- if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
- return retval & 0xffff;
- return 0xffff;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id == 1) {
- if (location < 7)
- return inl(ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- return inl(ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- return inl(ioaddr + 0xD4 + ((location-29)<<2));
- }
- return 0xffff;
- }
-
- /* Establish sync by sending at least 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
-
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
- int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
- long ioaddr = dev->base_addr;
- long mdio_addr = ioaddr + CSR9;
-
- if (tp->chip_id == LC82C168) {
- int i = 1000;
- outl(cmd, ioaddr + 0xA0);
- do
- if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
- break;
- while (--i > 0);
- return;
- }
-
- if (tp->chip_id == COMET) {
- if (phy_id != 1)
- return;
- if (location < 7)
- outl(value, ioaddr + 0xB4 + (location<<2));
- else if (location == 17)
- outl(value, ioaddr + 0xD0);
- else if (location >= 29 && location <= 31)
- outl(value, ioaddr + 0xD4 + ((location-29)<<2));
- return;
- }
-
- /* Establish sync by sending 32 logic ones. */
- for (i = 32; i >= 0; i--) {
- outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
- outl(MDIO_ENB | dataval, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- outl(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return;
-}
-
-static void
-tulip_up(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int i;
-
- /* On some chip revs we must set the MII/SYM port before the reset!? */
- if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- outl_CSR6(0x00040000, ioaddr, tp->chip_id);
-
- /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
- outl(0x00000001, ioaddr + CSR0);
-
- /* Deassert reset. */
- outl(tp->csr0, ioaddr + CSR0);
- udelay(2);
-
- if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
- pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
-
- /* Clear the tx ring */
- for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- }
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
-
- if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
- u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
- if (tp->chip_id == AX88140) {
- outl(0, ioaddr + CSR13);
- outl(addr_low, ioaddr + CSR14);
- outl(1, ioaddr + CSR13);
- outl(addr_high, ioaddr + CSR14);
- } else if (tp->chip_id == COMET) {
- outl(addr_low, ioaddr + 0xA4);
- outl(addr_high, ioaddr + 0xA8);
- outl(0, ioaddr + 0xAC);
- outl(0, ioaddr + 0xB0);
- }
- } else if (tp->chip_id != X3201_3) {
- /* This is set_rx_mode(), but without starting the transmitter. */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[15*6];
-
- /* 21140 bug: you must add the broadcast address. */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* Fill the final entry of the table with our physical address. */
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- /* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[0].status = DescOwned;
-
- tp->cur_tx++;
- } else { /* X3201_3 */
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = &tp->setup_frame[0*6];
-
- /* fill the table with the broadcast address */
- memset(tp->setup_frame, 0xff, 96*sizeof(u16));
- /* re-fill the first 14 table entries with our address */
- for(i=0; i<14; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
-
- /* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- /* Lie about the address of our setup frame to make the */
- /* chip happy */
- tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- tp->tx_ring[0].status = DescOwned;
-
- tp->cur_tx++;
- }
- outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
- outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
-
- tp->saved_if_port = dev->if_port;
- if (dev->if_port == 0)
- dev->if_port = tp->default_port;
- if (tp->chip_id == DC21041 && dev->if_port > 4)
- /* Invalid: Select initial TP, autosense, autonegotiate. */
- dev->if_port = 4;
-
- /* Allow selecting a default media. */
- i = 0;
- if (tp->mtable == NULL)
- goto media_picked;
- if (dev->if_port) {
- int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
- (dev->if_port == 12 ? 0 : dev->if_port);
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using user-specified media %s.\n",
- dev->name, medianame[dev->if_port]);
- goto media_picked;
- }
- }
- if ((tp->mtable->defaultmedia & 0x0800) == 0) {
- int looking_for = tp->mtable->defaultmedia & 15;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == looking_for) {
- printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
- dev->name, medianame[looking_for]);
- goto media_picked;
- }
- }
- /* Start sensing first non-full-duplex media. */
- for (i = tp->mtable->leafcount - 1;
- (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
- ;
-media_picked:
-
- tp->csr6 = 0;
- tp->cur_index = i;
- if (dev->if_port == 0 && tp->chip_id == DC21142) {
- if (tp->mii_cnt) {
- select_media(dev, 1);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Using MII transceiver %d, status "
- "%4.4x.\n",
- dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
- outl_CSR6(0x82020000, ioaddr, tp->chip_id);
- tp->csr6 = 0x820E0000;
- dev->if_port = 11;
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- } else
- t21142_start_nway(dev);
- } else if ((tp->chip_id == LC82C168 || tp->chip_id == PNIC2)
- && tp->mii_cnt && ! tp->medialock) {
- dev->if_port = 11;
- tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0001, ioaddr + CSR15);
- } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
- && ! tp->medialock) {
- dev->if_port = 0;
- tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) {
- /* Provided by BOLO, Macronix - 12/10/1998. */
- dev->if_port = 0;
- tp->csr6 = 0x01880200;
- outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
- outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
- } else if (tp->chip_id == DC21143 &&
- media_cap[dev->if_port] & MediaIsMII) {
- /* We must reset the media CSRs when we force-select MII mode. */
- outl(0x0000, ioaddr + CSR13);
- outl(0x0000, ioaddr + CSR14);
- outl(0x0008, ioaddr + CSR15);
- } else if (tp->chip_id == X3201_3) {
- outl(0x0008, ioaddr + CSR15);
- udelay(5);
- outl(0xa8050000, ioaddr + CSR15);
- udelay(5);
- outl(0xa00f0000, ioaddr + CSR15);
- udelay(5);
- tp->csr6 = 0x32400000;
- } else if (tp->chip_id == COMET) {
- dev->if_port = 0;
- tp->csr6 = 0x00040000;
- } else
- select_media(dev, 1);
-
- /* Start the chip's Tx to process setup frame. */
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
-
- /* Enable interrupts by setting the interrupt mask. */
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- outl(0, ioaddr + CSR2); /* Rx poll demand */
-
- netif_start_queue (dev);
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
- dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
- inl(ioaddr + CSR6));
- }
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = RUN_AT(5*HZ);
- tp->timer.data = (unsigned long)dev;
- tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
- add_timer(&tp->timer);
-}
-
-static int
-tulip_open(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
- return -EAGAIN;
-
- tulip_init_ring(dev);
-
- tulip_up(dev);
- tp->open = 1;
- MOD_INC_USE_COUNT;
-
- return 0;
-}
-
-/* Set up the transceiver control registers for the selected media type. */
-static void select_media(struct net_device *dev, int startup)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- struct mediatable *mtable = tp->mtable;
- u32 new_csr6;
- int i;
-
- if (mtable) {
- struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
- unsigned char *p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: /* 21140 non-MII xcvr. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver"
- " with control setting %2.2x.\n",
- dev->name, p[1]);
- dev->if_port = p[0];
- if (startup)
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- outl(p[1], ioaddr + CSR12);
- new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
- break;
- case 2: case 4: {
- u16 setup[5];
- u32 csr13val, csr14val, csr15dir, csr15val;
- for (i = 0; i < 5; i++)
- setup[i] = get_u16(&p[i*2 + 1]);
-
- dev->if_port = p[0] & 15;
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
-
- if (startup && mtable->has_reset) {
- struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
- unsigned char *rst = rleaf->leafdata;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Resetting the transceiver.\n",
- dev->name);
- for (i = 0; i < rst[0]; i++)
- outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control "
- "%4.4x/%4.4x.\n",
- dev->name, medianame[dev->if_port], setup[0], setup[1]);
- if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
- csr13val = setup[0];
- csr14val = setup[1];
- csr15dir = (setup[3]<<16) | setup[2];
- csr15val = (setup[4]<<16) | setup[2];
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- outl(csr13val, ioaddr + CSR13);
- } else {
- csr13val = 1;
- csr14val = 0x0003FF7F;
- csr15dir = (setup[0]<<16) | 0x0008;
- csr15val = (setup[1]<<16) | 0x0008;
- if (dev->if_port <= 4)
- csr14val = t21142_csr14[dev->if_port];
- if (startup) {
- outl(0, ioaddr + CSR13);
- outl(csr14val, ioaddr + CSR14);
- }
- outl(csr15dir, ioaddr + CSR15); /* Direction */
- outl(csr15val, ioaddr + CSR15); /* Data */
- if (startup) outl(csr13val, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n",
- dev->name, csr15dir, csr15val);
- if (mleaf->type == 4)
- new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
- else
- new_csr6 = 0x82420000;
- break;
- }
- case 1: case 3: {
- int phy_num = p[0];
- int init_length = p[1];
- u16 *misc_info;
- u16 to_advertise;
-
- dev->if_port = 11;
- new_csr6 = 0x020E0000;
- if (mleaf->type == 3) { /* 21142 */
- u16 *init_sequence = (u16*)(p+2);
- u16 *reset_sequence = &((u16*)(p+3))[init_length];
- int reset_length = p[2 + init_length*2];
- misc_info = reset_sequence + reset_length;
- if (startup)
- for (i = 0; i < reset_length; i++)
- outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
- for (i = 0; i < init_length; i++)
- outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
- } else {
- u8 *init_sequence = p + 2;
- u8 *reset_sequence = p + 3 + init_length;
- int reset_length = p[2 + init_length];
- misc_info = (u16*)(reset_sequence + reset_length);
- if (startup) {
- outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
- for (i = 0; i < reset_length; i++)
- outl(reset_sequence[i], ioaddr + CSR12);
- }
- for (i = 0; i < init_length; i++)
- outl(init_sequence[i], ioaddr + CSR12);
- }
- to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1;
- tp->advertising[phy_num] = to_advertise;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n",
- dev->name, to_advertise, phy_num, tp->phys[phy_num]);
- /* Bogus: put in by a committee? */
- mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
- break;
- }
- default:
- printk(KERN_DEBUG "%s: Invalid media table selection %d.\n",
- dev->name, mleaf->type);
- new_csr6 = 0x020E0000;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12) & 0xff);
- } else if (tp->chip_id == DC21041) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
- dev->name, medianame[dev->if_port & 15],
- inl(ioaddr + CSR12) & 0xffff);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- new_csr6 = 0x80020000;
- } else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) {
- if (startup && ! tp->medialock)
- dev->if_port = tp->mii_cnt ? 11 : 0;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
- " media %s.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
- medianame[dev->if_port]);
- if (tp->mii_cnt) {
- new_csr6 = 0x810C0000;
- outl(0x0001, ioaddr + CSR15);
- outl(0x0201B07A, ioaddr + 0xB8);
- } else if (startup) {
- /* Start with 10mbps to do autonegotiation. */
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x0001B078, ioaddr + 0xB8);
- outl(0x0201B078, ioaddr + 0xB8);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- if (startup)
- outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
- else
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- } else if (tp->chip_id == DC21040) { /* 21040 */
- /* Turn on the xcvr interface. */
- int csr12 = inl(ioaddr + CSR12);
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
- dev->name, medianame[dev->if_port], csr12);
- if (media_cap[dev->if_port] & MediaAlwaysFD)
- tp->full_duplex = 1;
- new_csr6 = 0x20000;
- /* Set the full duplux match frame. */
- outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
- outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
- if (t21040_csr13[dev->if_port] & 8) {
- outl(0x0705, ioaddr + CSR14);
- outl(0x0006, ioaddr + CSR15);
- } else {
- outl(0xffff, ioaddr + CSR14);
- outl(0x0000, ioaddr + CSR15);
- }
- outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13);
- } else if (tp->chip_id == X3201_3) { /* Xircom */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
-/* Someone is on crack, the Xircom only does MII, no Fx */
-/* if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;*/
- new_csr6 = 0x324c0000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Xircom CardBus Adapter: "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- } else { /* Unknown chip type with no media table. */
- if (tp->default_port == 0)
- dev->if_port = tp->mii_cnt ? 11 : 3;
- if (media_cap[dev->if_port] & MediaIsMII) {
- new_csr6 = 0x020E0000;
- } else if (media_cap[dev->if_port] & MediaIsFx) {
- new_csr6 = 0x028600000;
- } else
- new_csr6 = 0x038600000;
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No media description table, assuming "
- "%s transceiver, CSR12 %2.2x.\n",
- dev->name, medianame[dev->if_port],
- inl(ioaddr + CSR12));
- }
-
- tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
- return;
-}
-
-/*
- Check the MII negotiated duplex, and change the CSR6 setting if
- required.
- Return 0 if everything is OK.
- Return < 0 if the transceiver is missing or has no link beat.
- */
-static int check_duplex(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int mii_reg1, mii_reg5, negotiated, duplex;
-
- if (tp->full_duplex_lock)
- return 0;
- mii_reg1 = mdio_read(dev, tp->phys[0], 1);
- mii_reg5 = mdio_read(dev, tp->phys[0], 5);
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
- "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
- if (mii_reg1 == 0xffff)
- return -2;
- if ((mii_reg1 & 0x0004) == 0) {
- int new_reg1 = mdio_read(dev, tp->phys[0], 1);
- if ((new_reg1 & 0x0004) == 0) {
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: No link beat on the MII interface,"
- " status %4.4x.\n", dev->name, new_reg1);
- return -1;
- }
- }
- negotiated = mii_reg5 & tp->advertising[0];
- duplex = ((negotiated & 0x0300) == 0x0100
- || (negotiated & 0x00C0) == 0x0040);
- /* 100baseTx-FD or 10T-FD, but not 100-HD */
- if (tp->full_duplex != duplex) {
- tp->full_duplex = duplex;
- if (tp->full_duplex) tp->csr6 |= 0x0200;
- else tp->csr6 &= ~0x0200;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- if (tulip_debug > 0)
- printk(KERN_INFO "%s: Setting %s-duplex based on MII"
- "#%d link partner capability of %4.4x.\n",
- dev->name, tp->full_duplex ? "full" : "half",
- tp->phys[0], mii_reg5);
- }
- return 0;
-}
-
-static void tulip_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u32 csr12 = inl(ioaddr + CSR12);
- int next_tick = 2*HZ;
-
- if (tulip_debug > 2) {
- printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
- "SIA %8.8x %8.8x %8.8x %8.8x.\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
- csr12, inl(ioaddr + CSR13),
- inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- }
- switch (tp->chip_id) {
- case DC21040:
- if (!tp->medialock && csr12 & 0x0002) { /* Network error */
- printk(KERN_INFO "%s: No link beat found.\n",
- dev->name);
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- select_media(dev, 0);
- dev->trans_start = jiffies;
- }
- break;
- case DC21041:
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n",
- dev->name, csr12);
- switch (dev->if_port) {
- case 0: case 3: case 4:
- if (csr12 & 0x0004) { /*LnkFail */
- /* 10baseT is dead. Check for activity on alternate port. */
- tp->mediasense = 1;
- if (csr12 & 0x0200)
- dev->if_port = 2;
- else
- dev->if_port = 1;
- printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
- dev->name, medianame[dev->if_port]);
- outl(0, ioaddr + CSR13); /* Reset */
- outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
- outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
- next_tick = 10*HZ; /* 2.4 sec. */
- } else
- next_tick = 30*HZ;
- break;
- case 1: /* 10base2 */
- case 2: /* AUI */
- if (csr12 & 0x0100) {
- next_tick = (30*HZ); /* 30 sec. */
- tp->mediasense = 0;
- } else if ((csr12 & 0x0004) == 0) {
- printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n",
- dev->name);
- dev->if_port = 0;
- select_media(dev, 0);
- next_tick = (24*HZ)/10; /* 2.4 sec. */
- } else if (tp->mediasense || (csr12 & 0x0002)) {
- dev->if_port = 3 - dev->if_port; /* Swap ports. */
- select_media(dev, 0);
- next_tick = 20*HZ;
- } else {
- next_tick = 20*HZ;
- }
- break;
- }
- break;
- case DC21140: case DC21142: case MX98713: case COMPEX9881: default: {
- struct medialeaf *mleaf;
- unsigned char *p;
- if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */
- /* Not much that can be done.
- Assume this a generic MII or SYM transceiver. */
- next_tick = 60*HZ;
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
- "CSR12 0x%2.2x.\n",
- dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
- break;
- }
- mleaf = &tp->mtable->mleaf[tp->cur_index];
- p = mleaf->leafdata;
- switch (mleaf->type) {
- case 0: case 4: {
- /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */
- int offset = mleaf->type == 4 ? 5 : 2;
- s8 bitnum = p[offset];
- if (p[offset+1] & 0x80) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG"%s: Transceiver monitor tick "
- "CSR12=%#2.2x, no media sense.\n",
- dev->name, csr12);
- if (mleaf->type == 4) {
- if (mleaf->media == 3 && (csr12 & 0x02))
- goto select_next_media;
- }
- break;
- }
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x"
- " bit %d is %d, expecting %d.\n",
- dev->name, csr12, (bitnum >> 1) & 7,
- (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
- (bitnum >= 0));
- /* Check that the specified bit has the proper value. */
- if ((bitnum < 0) !=
- ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
- medianame[mleaf->media]);
- if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */
- goto actually_mii;
- break;
- }
- if (tp->medialock)
- break;
- select_next_media:
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
- if (media_cap[dev->if_port] & MediaIsFD)
- goto select_next_media; /* Skip FD entries. */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: No link beat on media %s,"
- " trying transceiver type %s.\n",
- dev->name, medianame[mleaf->media & 15],
- medianame[tp->mtable->mleaf[tp->cur_index].media]);
- select_media(dev, 0);
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- next_tick = (24*HZ)/10;
- break;
- }
- case 1: case 3: /* 21140, 21142 MII */
- actually_mii:
- check_duplex(dev);
- next_tick = 60*HZ;
- break;
- case 2: /* 21142 serial block has no link beat. */
- default:
- break;
- }
- }
- break;
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
- of available transceivers. */
-static void t21142_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = 0;
-
- if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII))
- printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n",
- dev->name, csr12, medianame[dev->if_port]);
- if (media_cap[dev->if_port] & MediaIsMII) {
- check_duplex(dev);
- next_tick = 60*HZ;
- } else if (tp->nwayset) {
- /* Don't screw up a negotiated session! */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n",
- dev->name, medianame[dev->if_port], csr12);
- } else if (tp->medialock) {
- ;
- } else if (dev->if_port == 3) {
- if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, "
- "trying NWay.\n", dev->name, csr12);
- t21142_start_nway(dev);
- next_tick = 3*HZ;
- }
- } else if (((csr12 & 0x7000) != 0x5000)
- && tp->chip_id != X3201_3) {
- /* Negotiation failed. Search media types. */
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n",
- dev->name, csr12);
- if (!(csr12 & 4)) { /* 10mbps link beat good. */
- new_csr6 = 0x82420000;
- dev->if_port = 0;
- outl(0, ioaddr + CSR13);
- outl(0x0003FFFF, ioaddr + CSR14);
- outw(t21142_csr15[dev->if_port], ioaddr + CSR15);
- outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
- } else {
- /* Select 100mbps port to check for link beat. */
- new_csr6 = 0x83860000;
- dev->if_port = 3;
- outl(0, ioaddr + CSR13);
- outl(0x0003FF7F, ioaddr + CSR14);
- outw(8, ioaddr + CSR15);
- outl(1, ioaddr + CSR13);
- }
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: Testing new 21143 media %s.\n",
- dev->name, medianame[dev->if_port]);
- if (new_csr6 != (tp->csr6 & ~0x00D5)) {
- tp->csr6 &= 0x00D5;
- tp->csr6 |= new_csr6;
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- next_tick = 3*HZ;
- }
- if (tp->cur_tx - tp->dirty_tx > 0 &&
- jiffies - dev->trans_start > TX_TIMEOUT) {
- printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- tulip_tx_timeout(dev);
- }
-
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void t21142_start_nway(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr14 = ((tp->to_advertise & 0x0180) << 9) |
- ((tp->to_advertise&0x0020)<<1) | 0xffbf;
-
- dev->if_port = 0;
- tp->nway = tp->mediasense = 1;
- tp->nwayset = tp->lpar = 0;
- if (debug > 1)
- printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n",
- dev->name, csr14);
- outl(0x0001, ioaddr + CSR13);
- outl(csr14, ioaddr + CSR14);
- tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
- outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
- if (tp->mtable && tp->mtable->csr15dir) {
- outl(tp->mtable->csr15dir, ioaddr + CSR15);
- outl(tp->mtable->csr15val, ioaddr + CSR15);
- } else
- outw(0x0008, ioaddr + CSR15);
- outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */
-}
-
-static void t21142_lnk_change(struct net_device *dev, int csr5)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
-
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
- "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
-
- /* If NWay finished and we have a negotiated partner capability. */
- if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) {
- int setup_done = 0;
- tp->lpar = csr12 >> 16;
- tp->nwayset = 1;
- if (csr12 & 0x01000000) dev->if_port = 5;
- else if (csr12 & 0x00800000) dev->if_port = 3;
- else if (csr12 & 0x00400000) dev->if_port = 4;
- else if (csr12 & 0x00200000) dev->if_port = 0;
- else {
- tp->nwayset = 0;
- if ( ! (csr12 & 2)) dev->if_port = 3;
- else if ( ! (csr12 & 4)) dev->if_port = 0;
- }
- tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0;
-
- if (tulip_debug > 1) {
- if (tp->nwayset)
- printk(KERN_INFO "%s: Switching to %s based on link partner "
- "advertisement %4.4x.\n",
- dev->name, medianame[dev->if_port], tp->lpar);
- else
- printk(KERN_INFO "%s: Switching to %s based on link beat "
- "status of %4.4x.\n",
- dev->name, medianame[dev->if_port], csr12);
- }
-
- if (tp->mtable) {
- int i;
- for (i = 0; i < tp->mtable->leafcount; i++)
- if (tp->mtable->mleaf[i].media == dev->if_port) {
- tp->cur_index = i;
- select_media(dev, 0);
- setup_done = 1;
- break;
- }
- }
- if ( ! setup_done) {
- tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000;
- if (tp->full_duplex)
- tp->csr6 |= 0x0200;
- outw(0x0000, ioaddr + CSR13);
- outw(0x0000, ioaddr + CSR14);
- }
- outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id);
- if (debug > 2)
- printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
- dev->name, inl(ioaddr + CSR5));
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- } else if ((tp->nwayset && (csr5 & 0x08000000)
- && (dev->if_port == 3 || dev->if_port == 5)
- && (csr12 & 2) == 2) ||
- (tp->nway && (csr5 & (TPLnkFail)))) {
- /* Link blew? Maybe restart NWay. */
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- } else if (dev->if_port == 3 || dev->if_port == 5) {
- if (tulip_debug > 1)
- printk(KERN_INFO"%s: 21143 %s link beat %s.\n",
- dev->name, medianame[dev->if_port],
- (csr12 & 2) ? "failed" : "good");
- if ((csr12 & 2) && ! tp->medialock) {
- del_timer(&tp->timer);
- t21142_start_nway(dev);
- tp->timer.expires = RUN_AT(3*HZ);
- add_timer(&tp->timer);
- }
- } else if (dev->if_port == 0 || dev->if_port == 4) {
- if ((csr12 & 4) == 0)
- printk(KERN_INFO"%s: 21143 10baseT link beat good.\n",
- dev->name);
- } else if (!(csr12 & 4)) { /* 10mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 10mbps sensed media.\n",
- dev->name);
- dev->if_port = 0;
- } else if (tp->nwayset) {
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n",
- dev->name, medianame[dev->if_port], tp->csr6);
- } else { /* 100mbps link beat good. */
- if (tulip_debug)
- printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n",
- dev->name);
- dev->if_port = 3;
- tp->csr6 = 0x83860000;
- outl(0x0003FF7F, ioaddr + CSR14);
- outl(0x0301, ioaddr + CSR12);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
-}
-
-static void mxic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 3) {
- printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name,
- inl(ioaddr + CSR12));
- }
- if (next_tick) {
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
- }
-}
-
-static void pnic_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr12 = inl(ioaddr + CSR12);
- int next_tick = 60*HZ;
- int new_csr6 = tp->csr6 & ~0x40C40200;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, "
- "CSR5 %8.8x.\n",
- dev->name, negotiated, inl(ioaddr + CSR5));
-
- if (negotiated & 0x0380) /* 10 vs 100mbps */
- new_csr6 |= 0x810E0000;
- else
- new_csr6 |= 0x814E0000;
- if (((negotiated & 0x0300) == 0x0100) /* Duplex */
- || (negotiated & 0x00C0) == 0x0040
- || tp->full_duplex_lock) {
- tp->full_duplex = 1;
- new_csr6 |= 0x0200;
- }
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link "
- "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
- dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
- tp->csr6, inl(ioaddr + CSR6));
- } else {
- int phy_reg = inl(ioaddr + 0xB8);
- int csr5 = inl(ioaddr + CSR5);
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n",
- dev->name, phy_reg, csr5);
-
- if (phy_reg & 0x04000000) { /* Remote link fault */
- /*outl(0x0201F078, ioaddr + 0xB8);*/
- next_tick = 3*HZ;
- }
- if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
- "CSR5 %8.8x, PHY %3.3x.\n",
- dev->name, medianame[dev->if_port], csr12,
- inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
- if (tp->medialock) {
- } else if (dev->if_port == 0) {
- dev->if_port = 3;
- outl(0x33, ioaddr + CSR12);
- new_csr6 = 0x01860000;
- outl(0x1F868, ioaddr + 0xB8);
- } else {
- dev->if_port = 0;
- outl(0x32, ioaddr + CSR12);
- new_csr6 = 0x00420000;
- outl(0x1F078, ioaddr + 0xB8);
- }
- new_csr6 |= (tp->csr6 & 0xfdff);
- next_tick = 3*HZ;
- } else
- new_csr6 = tp->csr6;
- if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) {
- tp->full_duplex = 1;
- new_csr6 |= 0x00000200;
- }
- }
- if (tp->csr6 != new_csr6) {
- tp->csr6 = new_csr6;
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); /* Restart Tx */
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- dev->trans_start = jiffies;
- if (tulip_debug > 1)
- printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
- "CSR6 %8.8x.\n",
- dev->name, tp->full_duplex ? "full" : "half", new_csr6);
- }
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void comet_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int next_tick = 60*HZ;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability "
- "%4.4x.\n",
- dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8));
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
-}
-
-static void tulip_tx_timeout(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (media_cap[dev->if_port] & MediaIsMII) {
- /* Do nothing -- the media monitor should handle this. */
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
- dev->name);
- } else if (tp->chip_id == DC21040) {
- if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) {
- dev->if_port = (dev->if_port == 2 ? 0 : 2);
- printk(KERN_INFO "%s: transmit timed out, switching to "
- "%s.\n",
- dev->name, medianame[dev->if_port]);
- select_media(dev, 0);
- }
- dev->trans_start = jiffies;
- return;
- } else if (tp->chip_id == DC21041) {
- int csr12 = inl(ioaddr + CSR12);
-
- printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, "
- "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), csr12,
- inl(ioaddr + CSR13), inl(ioaddr + CSR14));
- tp->mediasense = 1;
- if ( ! tp->medialock) {
- if (dev->if_port == 1 || dev->if_port == 2)
- if (csr12 & 0x0004) {
- dev->if_port = 2 - dev->if_port;
- } else
- dev->if_port = 0;
- else
- dev->if_port = 1;
- select_media(dev, 0);
- }
- } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
- || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
- printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
- "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
- inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
- if ( ! tp->medialock && tp->mtable) {
- do
- --tp->cur_index;
- while (tp->cur_index >= 0
- && (media_cap[tp->mtable->mleaf[tp->cur_index].media]
- & MediaIsFD));
- if (--tp->cur_index < 0) {
- /* We start again, but should instead look for default. */
- tp->cur_index = tp->mtable->leafcount - 1;
- }
- select_media(dev, 0);
- printk(KERN_WARNING "%s: transmit timed out, switching to %s "
- "media.\n", dev->name, medianame[dev->if_port]);
- }
- } else {
- printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 "
- "%8.8x, resetting...\n",
- dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
- dev->if_port = 0;
- }
-
-#if defined(way_too_many_messages)
- if (tulip_debug > 3) {
- int i;
- for (i = 0; i < RX_RING_SIZE; i++) {
- u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
- int j;
- printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x "
- "%2.2x %2.2x %2.2x.\n",
- i, (unsigned int)tp->rx_ring[i].status,
- (unsigned int)tp->rx_ring[i].length,
- (unsigned int)tp->rx_ring[i].buffer1,
- (unsigned int)tp->rx_ring[i].buffer2,
- buf[0], buf[1], buf[2]);
- for (j = 0; buf[j] != 0xee && j < 1600; j++)
- if (j < 100) printk(" %2.2x", buf[j]);
- printk(" j=%d.\n", j);
- }
- printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
- printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
- printk("\n");
- }
-#endif
-
- /* Stop and restart the chip's Tx processes . */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
-
- dev->trans_start = jiffies;
- netif_wake_queue (dev);
- tp->stats.tx_errors++;
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void tulip_init_ring(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- tp->tx_full = 0;
- tp->cur_rx = tp->cur_tx = 0;
- tp->dirty_rx = tp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- tp->rx_ring[i].status = 0x00000000;
- tp->rx_ring[i].length = PKT_BUF_SZ;
- tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
- tp->rx_skbuff[i] = NULL;
- }
- /* Mark the last entry as wrapping the ring. */
- tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
- tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- /* Note the receive buffer must be longword aligned.
- dev_alloc_skb() provides 16 byte alignment. But do *not*
- use skb_reserve() to align the IP header! */
- struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
- tp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */
- tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
- }
- tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-
- /* The Tx buffer descriptor is filled in as needed, but we
- do need to clear the ownership bit. */
- for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_ring[i].status = 0x00000000;
- tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3)
- tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif CARDBUS
- }
- tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
-}
-
-static int
-tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry;
- u32 flag;
-
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- /* Calculate the next Tx descriptor entry. */
- entry = tp->cur_tx % TX_RING_SIZE;
-
- tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
- if (tp->chip_id == X3201_3) {
- memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
- } else
-#endif
- tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
-
- if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
- flag = 0x60000000; /* No interrupt */
- } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
- flag = 0xe0000000; /* Tx-done intr. */
- } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
- flag = 0x60000000; /* No Tx-done intr. */
- } else {
- /* Leave room for set_rx_mode() to fill entries. */
- flag = 0xe0000000; /* Tx-done intr. */
- tp->tx_full = 1;
- }
- if (entry == TX_RING_SIZE-1)
- flag |= 0xe0000000 | DESC_RING_WRAP;
-
- tp->tx_ring[entry].length = skb->len | flag;
- tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */
- tp->cur_tx++;
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- /* Trigger an immediate transmit demand. */
- outl(0, dev->base_addr + CSR1);
-
- dev->trans_start = jiffies;
-
- return 0;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_instance;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int csr5, work_budget = max_interrupt_work;
-
- spin_lock (&tp->lock);
-
- do {
- csr5 = inl(ioaddr + CSR5);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outl(csr5 & 0x0001ffff, ioaddr + CSR5);
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
- dev->name, csr5, inl(dev->base_addr + CSR5));
-
- if (csr5 == 0xffffffff)
- break; /* all bits set, assume PCMCIA card removed */
-
- if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
- break;
-
- if (csr5 & (RxIntr | RxNoBuf))
- work_budget -= tulip_rx(dev);
-
- if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
- unsigned int dirty_tx;
-
- for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
- dirty_tx++) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = tp->tx_ring[entry].status;
-
- if (status < 0)
- break; /* It still hasn't been Txed */
- /* Check for Rx filter setup frames. */
- if (tp->tx_skbuff[entry] == NULL)
- continue;
-
- if (status & 0x8000) {
- /* There was an major error, log it. */
-#ifndef final_version
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
- dev->name, status);
-#endif
- tp->stats.tx_errors++;
- if (status & 0x4104) tp->stats.tx_aborted_errors++;
- if (status & 0x0C00) tp->stats.tx_carrier_errors++;
- if (status & 0x0200) tp->stats.tx_window_errors++;
- if (status & 0x0002) tp->stats.tx_fifo_errors++;
- if ((status & 0x0080) && tp->full_duplex == 0)
- tp->stats.tx_heartbeat_errors++;
-#ifdef ETHER_STATS
- if (status & 0x0100) tp->stats.collisions16++;
-#endif
- } else {
-#ifdef ETHER_STATS
- if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
- tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
- tp->stats.collisions += (status >> 3) & 15;
- tp->stats.tx_packets++;
- }
-
- /* Free the original skb. */
- dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
- }
-
-#ifndef final_version
- if (tp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
- dev->name, dirty_tx, tp->cur_tx, tp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- if (tp->tx_full &&
- tp->cur_tx - dirty_tx < TX_RING_SIZE - 2)
- /* The ring is no longer full */
- tp->tx_full = 0;
-
- if (tp->tx_full)
- netif_stop_queue (dev);
- else
- netif_wake_queue (dev);
-
- tp->dirty_tx = dirty_tx;
- if (csr5 & TxDied) {
- if (tulip_debug > 2)
- printk(KERN_WARNING "%s: The transmitter stopped."
- " CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
- dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- }
-
- /* Log errors. */
- if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */
- if (csr5 == 0xffffffff)
- break;
- if (csr5 & TxJabber) tp->stats.tx_errors++;
- if (csr5 & TxFIFOUnderflow) {
- if ((tp->csr6 & 0xC000) != 0xC000)
- tp->csr6 += 0x4000; /* Bump up the Tx threshold */
- else
- tp->csr6 |= 0x00200000; /* Store-n-forward. */
- /* Restart the transmit process. */
- outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & RxDied) { /* Missed a Rx frame. */
- tp->stats.rx_errors++;
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
- }
- if (csr5 & TimerInt) {
- if (tulip_debug > 2)
- printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
- dev->name, csr5);
- outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- }
- if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
- if ( tp->chip_id == DC21142)
- t21142_lnk_change(dev, csr5);
- }
- /* Clear all error sources, included undocumented ones! */
- outl(0x0800f7ba, ioaddr + CSR5);
- }
- if (--work_budget < 0) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Too much work during an interrupt, "
- "csr5=0x%8.8x.\n", dev->name, csr5);
- /* Acknowledge all interrupt sources. */
- outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
- /* Clear all but standard interrupt sources. */
- outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
- break;
- }
- } while (1);
-
- if (tulip_debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- spin_unlock (&tp->lock);
-}
-
-static int
-tulip_rx(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int entry = tp->cur_rx % RX_RING_SIZE;
- int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
- int work_done = 0;
-
- if (tulip_debug > 4)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- /* If we own the next entry, it's a new packet. Send it up. */
- while (tp->rx_ring[entry].status >= 0) {
- s32 status = tp->rx_ring[entry].status;
-
- if (tulip_debug > 5)
- printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
- tp->rx_ring[entry].status);
- if (--rx_work_limit < 0)
- break;
- if ((status & 0x38008300) != 0x0300) {
- if ((status & 0x38000300) != 0x0300) {
- /* Ingore earlier buffers. */
- if ((status & 0xffff) != 0x7fff) {
- if (tulip_debug > 1)
- printk(KERN_WARNING "%s: Oversized Ethernet frame "
- "spanned multiple buffers, status %8.8x!\n",
- dev->name, status);
- tp->stats.rx_length_errors++;
- }
- } else if (status & RxDescFatalErr) {
- /* There was a fatal error. */
- if (tulip_debug > 2)
- printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
- dev->name, status);
- tp->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x0890) tp->stats.rx_length_errors++;
- if (status & 0x0004) tp->stats.rx_frame_errors++;
- if (status & 0x0002) tp->stats.rx_crc_errors++;
- if (status & 0x0001) tp->stats.rx_fifo_errors++;
- }
- } else {
- /* Omit the four octet CRC from the length. */
- short pkt_len = ((status >> 16) & 0x7ff) - 4;
- struct sk_buff *skb;
-
-#ifndef final_version
- if (pkt_len > 1518) {
- printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
- dev->name, pkt_len, pkt_len);
- pkt_len = 1518;
- tp->stats.rx_length_errors++;
- }
-#endif
- /* Check if the packet is long enough to accept without copying
- to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb->dev = dev;
- skb_reserve(skb, 2); /* 16 byte align the IP header */
-#if ! defined(__alpha__)
- eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
- pkt_len, 0);
- skb_put(skb, pkt_len);
-#else
- memcpy(skb_put(skb, pkt_len),
- bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
-#endif
- work_done++;
- } else { /* Pass up the skb already on the Rx ring. */
- char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
- tp->rx_skbuff[entry] = NULL;
-#ifndef final_version
- if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
- printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
- "do not match in tulip_rx: %p vs. %p / %p.\n",
- dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
- skb->head, temp);
-#endif
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->last_rx = jiffies;
- tp->stats.rx_packets++;
- tp->stats.rx_bytes += pkt_len;
- }
- entry = (++tp->cur_rx) % RX_RING_SIZE;
- }
-
- /* Refill the Rx ring buffers. */
- for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
- entry = tp->dirty_rx % RX_RING_SIZE;
- if (tp->rx_skbuff[entry] == NULL) {
- struct sk_buff *skb;
- skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
- if (skb == NULL)
- break;
- skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
- work_done++;
- }
- tp->rx_ring[entry].status = DescOwned;
- }
-
- return work_done;
-}
-
-static void
-tulip_down(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- /* Disable interrupts by clearing the interrupt mask. */
- outl(0x00000000, ioaddr + CSR7);
- /* Stop the chip's Tx and Rx processes. */
- outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
- /* 21040 -- Leave the card in 10baseT state. */
- if (tp->chip_id == DC21040)
- outl(0x00000004, ioaddr + CSR13);
-
- if (inl(ioaddr + CSR6) != 0xffffffff)
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- dev->if_port = tp->saved_if_port;
-}
-
-static int
-tulip_close(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
-
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inl(ioaddr + CSR5));
-
- netif_stop_queue(dev);
-
- if (netif_device_present(dev))
- tulip_down(dev);
-
- del_timer(&tp->timer);
-
- free_irq(dev->irq, dev);
-
- /* Free all the skbuffs in the Rx queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
- tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
- tp->rx_ring[i].length = 0;
- tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
- if (skb) {
- dev_kfree_skb(skb);
- }
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (tp->tx_skbuff[i])
- dev_kfree_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
- }
-
- MOD_DEC_USE_COUNT;
- tp->open = 0;
- return 0;
-}
-
-static struct net_device_stats *tulip_get_stats(struct net_device *dev)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
-
- if (netif_device_present(dev))
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-
- return &tp->stats;
-}
-
-#ifdef HAVE_PRIVATE_IOCTL
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u16 *data = (u16 *)&rq->ifr_data;
- int phy = tp->phys[0] & 0x1f;
- long flags;
-
- switch(cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- if (tp->mii_cnt)
- data[0] = phy;
- else if (tp->chip_id == DC21142) /* 21142 pseudo-MII */
- data[0] = 32;
- else if (tp->chip_id == PNIC2)
- data[0] = 32;
- else if (tp->chip_id == COMET)
- data[0] = 1;
- else
- return -ENODEV;
- return 0;
- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- if (data[0] == 32 &&
- (tp->chip_id == DC21142 || tp->chip_id == PNIC2)) {
- int csr12 = inl(ioaddr + CSR12);
- int csr14 = inl(ioaddr + CSR14);
- switch (data[1]) {
- case 0: {
- data[3] = (csr14<<5) & 0x1000;
- break; }
- case 1:
- data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0)
- + (csr12&0x06 ? 0x04 : 0);
- break;
- case 4: {
- data[3] = ((csr14>>9)&0x0380) +
- ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1;
- break;
- }
- case 5: data[3] = csr12 >> 16; break;
- default: data[3] = 0; break;
- }
- } else {
- save_flags(flags);
- cli();
- data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
- restore_flags(flags);
- }
- return 0;
- case SIOCDEVPRIVATE+2: /* Write the specified MII register */
-#if defined(CAP_NET_ADMIN)
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-#else
- if (!suser())
- return -EPERM;
-#endif
- if (data[0] == 32 && tp->chip_id == DC21142) {
- if (data[1] == 5)
- tp->to_advertise = data[2];
- } else {
- save_flags(flags);
- cli();
- mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
- restore_flags(flags);
- }
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-
- return -EOPNOTSUPP;
-}
-#endif /* HAVE_PRIVATE_IOCTL */
-
-/* Set or clear the multicast filter for this adaptor.
- Note that we only use exclusion around actually queueing the
- new frame, not around filling tp->setup_frame. This is non-deterministic
- when re-entered but still correct. */
-
-/* The little-endian AUTODIN32 ethernet CRC calculation.
- N.B. Do not use for bulk data, use a table-based routine instead.
- This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline u32 ether_crc_le(int length, unsigned char *data)
-{
- u32 crc = 0xffffffff; /* Initial value. */
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 8; --bit >= 0; current_octet >>= 1) {
- if ((crc ^ current_octet) & 1) {
- crc >>= 1;
- crc ^= ethernet_polynomial_le;
- } else
- crc >>= 1;
- }
- }
- return crc;
-}
-static unsigned const ethernet_polynomial = 0x04c11db7U;
-static inline u32 ether_crc(int length, unsigned char *data)
-{
- int crc = -1;
-
- while(--length >= 0) {
- unsigned char current_octet = *data++;
- int bit;
- for (bit = 0; bit < 8; bit++, current_octet >>= 1)
- crc = (crc << 1) ^
- ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
- }
- return crc;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- tp->csr6 &= ~0x00D5;
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- tp->csr6 |= 0x00C0;
- csr6 |= 0x00C0;
- /* Unconditionally log net taps. */
- printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
- } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter well -- accept all multicasts. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) {
- /* Some work-alikes have only a 64-entry hash filter table. */
- /* Should verify correctness on big-endian/__powerpc__ */
- struct dev_mc_list *mclist;
- int i;
- u32 mc_filter[2]; /* Multicast hash filter */
- if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */
- tp->csr6 |= 0x0080;
- csr6 |= 0x0080;
- } else {
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
- if (tp->chip_id == AX88140) {
- outl(2, ioaddr + CSR13);
- outl(mc_filter[0], ioaddr + CSR14);
- outl(3, ioaddr + CSR13);
- outl(mc_filter[1], ioaddr + CSR14);
- } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */
- outl(mc_filter[0], ioaddr + 0xAC);
- outl(mc_filter[1], ioaddr + 0xB0);
- }
- }
- } else {
- u16 *eaddrs, *setup_frm = tp->setup_frame;
- struct dev_mc_list *mclist;
- u32 tx_flags = 0x08000000 | 192;
- int i;
-
- /* Note that only the low-address shortword of setup_frame is valid!
- The values are doubled for big-endian architectures. */
- if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */
- u16 hash_table[32];
- tx_flags = 0x08400000 | 192; /* Use hash filter. */
- memset(hash_table, 0, sizeof(hash_table));
- set_bit(255, hash_table); /* Broadcast entry */
- /* This should work on big-endian machines as well. */
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
- hash_table);
- for (i = 0; i < 32; i++) {
- *setup_frm++ = hash_table[i];
- *setup_frm++ = hash_table[i];
- }
- setup_frm = &tp->setup_frame[13*6];
- } else if(tp->chip_id != X3201_3) {
- /* We have <= 14 addresses so we can use the wonderful
- 16 address perfect filtering of the Tulip. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- } else {
- /* fill the first two table entries with our address */
- eaddrs = (u16 *)dev->dev_addr;
- for(i=0; i<2; i++) {
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- /* Double fill each entry to accomodate chips that */
- /* don't like to parse these correctly */
- for (i=0, mclist=dev->mc_list; i<dev->mc_count;
- i++, mclist=mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- }
- i=((i+1)*2);
- /* Fill the unused entries with the broadcast address. */
- memset(setup_frm, 0xff, (15-i)*12);
- setup_frm = &tp->setup_frame[15*6];
- }
-
- /* Fill the final entry with our physical address. */
- eaddrs = (u16 *)dev->dev_addr;
- *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
- /* Now add this frame to the Tx list. */
- if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
- /* Same setup recently queued, we need not add it. */
- } else {
- unsigned long flags;
- unsigned int entry, dummy = -1;
-
- save_flags(flags); cli();
- entry = tp->cur_tx++ % TX_RING_SIZE;
-
- if (entry != 0) {
- /* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
- tp->tx_ring[entry].length =
- (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
- tp->tx_ring[entry].buffer1 = 0;
- /* race with chip, set DescOwned later */
- dummy = entry;
- entry = tp->cur_tx++ % TX_RING_SIZE;
- }
-
- tp->tx_skbuff[entry] = 0;
- /* Put the setup frame on the Tx list. */
- if (entry == TX_RING_SIZE-1)
- tx_flags |= DESC_RING_WRAP; /* Wrap ring. */
- tp->tx_ring[entry].length = tx_flags;
- if(tp->chip_id == X3201_3)
- tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4);
- else
- tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[entry].status = DescOwned;
- if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
- tp->tx_full = 1;
- netif_stop_queue (dev);
- }
- if (dummy >= 0)
- tp->tx_ring[dummy].status = DescOwned;
- restore_flags(flags);
- /* Trigger an immediate transmit demand. */
- outl(0, ioaddr + CSR1);
- }
- }
- outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
-}
-
-static const struct pci_device_id tulip_pci_table[] __devinitdata = {
- { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 },
- { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 },
- { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
- { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 },
- { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 },
- { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
- { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 },
- { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 },
- { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 },
- { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
- { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
- { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
- {0},
-};
-
-MODULE_DEVICE_TABLE(pci, tulip_pci_table);
-
-static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net_device *dev;
- static int board_idx = 0;
-
- printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
-
- pci_enable_device (pdev);
- pci_set_master (pdev);
- dev = tulip_probe1(pdev, NULL,
- pci_resource_start (pdev, 0), pdev->irq,
- id->driver_data, board_idx++);
- if (dev) {
- pdev->driver_data = dev;
- return 0;
- }
- return -ENODEV;
-}
-
-static void tulip_suspend(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
- if (tp->open) tulip_down(dev);
-}
-
-static void tulip_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
- if (tp->open) tulip_up(dev);
-}
-
-static void __devexit tulip_remove(struct pci_dev *pdev)
-{
- struct net_device *dev = pdev->driver_data;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
- printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
- unregister_netdev(dev);
- kfree(dev);
- kfree(tp);
-}
-
-static struct pci_driver tulip_ops = {
- name: "tulip_cb",
- id_table: tulip_pci_table,
- probe: tulip_pci_probe,
- remove: tulip_remove,
- suspend: tulip_suspend,
- resume: tulip_resume
-};
-
-static int __init tulip_init(void)
-{
- pci_register_driver(&tulip_ops);
- return 0;
-}
-
-static __exit void tulip_exit(void)
-{
- pci_unregister_driver(&tulip_ops);
-}
-
-module_init(tulip_init)
-module_exit(tulip_exit)
-
-
-/*
- * Local variables:
- * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 7374eac66..167d3cb12 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1470,7 +1470,7 @@ static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
lp->a.write_bcr (ioaddr, 33, phyaddr);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
lp->a.write_bcr (ioaddr, 34, data[2]);
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index d86ce1d85..64fafa17f 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -583,6 +583,61 @@ plip_receive(unsigned short nibble_timeout, struct net_device *dev,
return OK;
}
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ *
+ * PLIP is ethernet ish but the daddr might not be valid if unicast.
+ * PLIP fortunately has no bus architecture (its Point-to-point).
+ *
+ * We can't fix the daddr thing as that quirk (more bug) is embedded
+ * in far too many old systems not all even running Linux.
+ */
+
+static unsigned short plip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ */
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+
/* PLIP_RECEIVE_PACKET --- receive a packet */
static int
plip_receive_packet(struct net_device *dev, struct net_local *nl,
@@ -669,7 +724,7 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
case PLIP_PK_DONE:
/* Inform the upper layer for the arrival of a packet. */
- rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+ rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
netif_rx(rcv->skb);
nl->enet_stats.rx_bytes += rcv->length.h;
nl->enet_stats.rx_packets++;
@@ -1227,6 +1282,8 @@ plip_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
pc->nibble = nl->nibble;
break;
case PLIP_SET_TIMEOUT:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
nl->trigger = pc->trigger;
nl->nibble = pc->nibble;
break;
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 43b5508ae..3cbf0e5e4 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -17,11 +17,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990806==
+ * ==FILEVERSION 20000227==
*/
-/* $Id: ppp_async.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
@@ -31,9 +29,18 @@
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
-#define PPP_VERSION "2.4.0"
+#ifndef spin_trylock_bh
+#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \
+ __r = spin_trylock(lock); \
+ if (!__r) local_bh_enable(); \
+ __r; })
+#endif
+
+#define PPP_VERSION "2.4.1"
#define OBUFSIZE 256
@@ -44,7 +51,9 @@ struct asyncppp {
unsigned int state;
unsigned int rbits;
int mru;
- unsigned long busy;
+ spinlock_t xmit_lock;
+ spinlock_t recv_lock;
+ unsigned long xmit_flags;
u32 xaccm[8];
u32 raccm;
unsigned int bytes_sent;
@@ -55,24 +64,18 @@ struct asyncppp {
u16 tfcs;
unsigned char *optr;
unsigned char *olim;
- struct sk_buff_head xq;
unsigned long last_xmit;
struct sk_buff *rpkt;
- struct sk_buff_head rq;
- wait_queue_head_t rwait;
+ int lcp_fcs;
struct ppp_channel chan; /* interface to generic ppp layer */
- int connected;
- int index;
unsigned char obuf[OBUFSIZE];
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
-#define XMIT_FULL 3
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP 0
+#define XMIT_FULL 1
/* State bits */
#define SC_TOSS 0x20000000
@@ -81,8 +84,6 @@ struct asyncppp {
/* Bits in rbits */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
-#define PPPASYNC_MAX_RQLEN 32 /* arbitrary */
-
static int flag_time = HZ;
MODULE_PARM(flag_time, "i");
@@ -95,57 +96,17 @@ static int ppp_async_push(struct asyncppp *ap);
static void ppp_async_flush_output(struct asyncppp *ap);
static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
char *flags, int count);
+static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound);
struct ppp_channel_ops async_ops = {
- ppp_async_send
+ ppp_async_send,
+ ppp_async_ioctl
};
/*
- * Routines for locking and unlocking the transmit and receive paths.
- */
-static inline void
-lock_path(struct asyncppp *ap, int bit)
-{
- do {
- while (test_bit(bit, &ap->busy))
- mb();
- } while (test_and_set_bit(bit, &ap->busy));
- mb();
-}
-
-static inline int
-trylock_path(struct asyncppp *ap, int bit)
-{
- if (test_and_set_bit(bit, &ap->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct asyncppp *ap, int bit)
-{
- mb();
- clear_bit(bit, &ap->busy);
-}
-
-#define lock_xmit_path(ap) lock_path(ap, XMIT_BUSY)
-#define trylock_xmit_path(ap) trylock_path(ap, XMIT_BUSY)
-#define unlock_xmit_path(ap) unlock_path(ap, XMIT_BUSY)
-#define lock_recv_path(ap) lock_path(ap, RECV_BUSY)
-#define trylock_recv_path(ap) trylock_path(ap, RECV_BUSY)
-#define unlock_recv_path(ap) unlock_path(ap, RECV_BUSY)
-
-static inline void
-flush_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(q)) != 0)
- kfree_skb(skb);
-}
-
-/*
* Routines implementing the PPP line discipline.
*/
@@ -153,256 +114,113 @@ flush_skb_queue(struct sk_buff_head *q)
* Called when a tty is put into PPP line discipline.
*/
static int
-ppp_async_open(struct tty_struct *tty)
+ppp_asynctty_open(struct tty_struct *tty)
{
struct asyncppp *ap;
+ int err;
ap = kmalloc(sizeof(*ap), GFP_KERNEL);
if (ap == 0)
return -ENOMEM;
- MOD_INC_USE_COUNT;
-
/* initialize the asyncppp structure */
memset(ap, 0, sizeof(*ap));
ap->tty = tty;
ap->mru = PPP_MRU;
+ spin_lock_init(&ap->xmit_lock);
+ spin_lock_init(&ap->recv_lock);
ap->xaccm[0] = ~0U;
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
ap->optr = ap->obuf;
ap->olim = ap->obuf;
- skb_queue_head_init(&ap->xq);
- skb_queue_head_init(&ap->rq);
- init_waitqueue_head(&ap->rwait);
+ ap->lcp_fcs = -1;
+
+ ap->chan.private = ap;
+ ap->chan.ops = &async_ops;
+ ap->chan.mtu = PPP_MRU;
+ err = ppp_register_channel(&ap->chan);
+ if (err) {
+ kfree(ap);
+ return err;
+ }
tty->disc_data = ap;
+ MOD_INC_USE_COUNT;
return 0;
}
/*
* Called when the tty is put into another line discipline
- * (or it hangs up).
+ * or it hangs up.
+ * We assume that while we are in this routine, the tty layer
+ * won't call any of the other line discipline entries for the
+ * same tty.
*/
static void
-ppp_async_close(struct tty_struct *tty)
+ppp_asynctty_close(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
tty->disc_data = 0;
- lock_xmit_path(ap);
- lock_recv_path(ap);
+ ppp_unregister_channel(&ap->chan);
if (ap->rpkt != 0)
kfree_skb(ap->rpkt);
- flush_skb_queue(&ap->rq);
if (ap->tpkt != 0)
kfree_skb(ap->tpkt);
- flush_skb_queue(&ap->xq);
- if (ap->connected)
- ppp_unregister_channel(&ap->chan);
kfree(ap);
MOD_DEC_USE_COUNT;
}
/*
- * Read a PPP frame. pppd can use this to negotiate over the
- * channel before it joins it to a bundle.
+ * Read does nothing.
*/
static ssize_t
-ppp_async_read(struct tty_struct *tty, struct file *file,
- unsigned char *buf, size_t count)
+ppp_asynctty_read(struct tty_struct *tty, struct file *file,
+ unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- struct sk_buff *skb = 0;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- add_wait_queue(&ap->rwait, &wait);
- current->state = TASK_INTERRUPTIBLE;
- for (;;) {
- ret = -EAGAIN;
- skb = skb_dequeue(&ap->rq);
- if (skb)
- break;
- if (file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&ap->rwait, &wait);
-
- if (skb == 0)
- goto out;
-
- ret = -EOVERFLOW;
- if (skb->len > count)
- goto outf;
- ret = -EFAULT;
- if (copy_to_user(buf, skb->data, skb->len))
- goto outf;
- ret = skb->len;
-
- outf:
- kfree_skb(skb);
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_read(&ap->chan, file, buf, count);
}
/*
- * Write a ppp frame. pppd can use this to send frames over
- * this particular channel.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
*/
static ssize_t
-ppp_async_write(struct tty_struct *tty, struct file *file,
- const unsigned char *buf, size_t count)
+ppp_asynctty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t count)
{
+ /* For now, do the same as the old 2.3.x code useta */
struct asyncppp *ap = tty->disc_data;
- struct sk_buff *skb;
- ssize_t ret;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- ret = -ENOMEM;
- skb = alloc_skb(count + 2, GFP_KERNEL);
- if (skb == 0)
- goto out;
- skb_reserve(skb, 2);
- ret = -EFAULT;
- if (copy_from_user(skb_put(skb, count), buf, count)) {
- kfree_skb(skb);
- goto out;
- }
-
- skb_queue_tail(&ap->xq, skb);
- ppp_async_push(ap);
-
- ret = count;
-
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_write(&ap->chan, buf, count);
}
static int
-ppp_async_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct asyncppp *ap = tty->disc_data;
int err, val;
- u32 accm[8];
- struct sk_buff *skb;
-
- err = -ENXIO;
- if (ap == 0)
- goto out; /* should never happen */
- err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- goto out;
err = -EFAULT;
switch (cmd) {
- case PPPIOCGFLAGS:
- val = ap->flags | ap->rbits;
- if (put_user(val, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSFLAGS:
- if (get_user(val, (int *) arg))
- break;
- ap->flags = val & ~SC_RCV_BITS;
- ap->rbits = val & SC_RCV_BITS;
- err = 0;
- break;
-
- case PPPIOCGASYNCMAP:
- if (put_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSASYNCMAP:
- if (get_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGRASYNCMAP:
- if (put_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSRASYNCMAP:
- if (get_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGXASYNCMAP:
- if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
- break;
- err = 0;
- break;
- case PPPIOCSXASYNCMAP:
- if (copy_from_user(accm, (void *) arg, sizeof(accm)))
- break;
- accm[2] &= ~0x40000000U; /* can't escape 0x5e */
- accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
- memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
- err = 0;
- break;
-
- case PPPIOCGMRU:
- if (put_user(ap->mru, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSMRU:
- if (get_user(val, (int *) arg))
- break;
- if (val < PPP_MRU)
- val = PPP_MRU;
- ap->mru = val;
- err = 0;
- break;
-
- case PPPIOCATTACH:
- if (get_user(val, (int *) arg))
- break;
- err = -EALREADY;
- if (ap->connected)
- break;
- ap->chan.private = ap;
- ap->chan.ops = &async_ops;
- err = ppp_register_channel(&ap->chan, val);
- if (err != 0)
- break;
- ap->connected = 1;
- ap->index = val;
- break;
- case PPPIOCDETACH:
- err = -ENXIO;
- if (!ap->connected)
- break;
- ppp_unregister_channel(&ap->chan);
- ap->connected = 0;
- err = 0;
- break;
case PPPIOCGUNIT:
err = -ENXIO;
- if (!ap->connected)
+ if (ap == 0)
break;
- if (put_user(ap->index, (int *) arg))
+ err = -EFAULT;
+ if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
break;
err = 0;
break;
@@ -414,8 +232,6 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case TCFLSH:
/* flush our buffers and the serial port's buffer */
- if (arg == TCIFLUSH || arg == TCIOFLUSH)
- flush_skb_queue(&ap->rq);
if (arg == TCIOFLUSH || arg == TCOFLUSH)
ppp_async_flush_output(ap);
err = n_tty_ioctl(tty, file, cmd, arg);
@@ -423,68 +239,86 @@ ppp_async_ioctl(struct tty_struct *tty, struct file *file,
case FIONREAD:
val = 0;
- if ((skb = skb_peek(&ap->rq)) != 0)
- val = skb->len;
if (put_user(val, (int *) arg))
break;
err = 0;
break;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ case PPPIOCGFLAGS:
+ case PPPIOCSFLAGS:
+ case PPPIOCGASYNCMAP:
+ case PPPIOCSASYNCMAP:
+ case PPPIOCGRASYNCMAP:
+ case PPPIOCSRASYNCMAP:
+ case PPPIOCGXASYNCMAP:
+ case PPPIOCSXASYNCMAP:
+ case PPPIOCGMRU:
+ case PPPIOCSMRU:
+ err = ppp_async_ioctl(&ap->chan, cmd, arg);
+ break;
+
+ case PPPIOCATTACH:
+ err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+ break;
+
default:
err = -ENOIOCTLCMD;
}
- out:
+
return err;
}
static unsigned int
-ppp_async_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
{
- struct asyncppp *ap = tty->disc_data;
unsigned int mask;
+ struct asyncppp *ap = tty->disc_data;
- if (ap == 0)
- return 0; /* should never happen */
- poll_wait(file, &ap->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ap->rq))
- mask |= POLLIN | POLLRDNORM;
+/*
+ * For now, do the same as the old 2.3 driver useta
+ */
+ if (ap != 0)
+ mask |= ppp_channel_poll(&ap->chan, file, wait);
if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
mask |= POLLHUP;
return mask;
}
static int
-ppp_async_room(struct tty_struct *tty)
+ppp_asynctty_room(struct tty_struct *tty)
{
return 65535;
}
static void
-ppp_async_receive(struct tty_struct *tty, const unsigned char *buf,
+ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count)
{
struct asyncppp *ap = tty->disc_data;
if (ap == 0)
return;
- trylock_recv_path(ap);
+ spin_lock_bh(&ap->recv_lock);
ppp_async_input(ap, buf, flags, count);
- unlock_recv_path(ap);
+ spin_unlock_bh(&ap->recv_lock);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver.unthrottle)
tty->driver.unthrottle(tty);
}
static void
-ppp_async_wakeup(struct tty_struct *tty)
+ppp_asynctty_wakeup(struct tty_struct *tty)
{
struct asyncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0)
return;
- if (ppp_async_push(ap) && ap->connected)
+ if (ppp_async_push(ap))
ppp_output_wakeup(&ap->chan);
}
@@ -492,15 +326,15 @@ ppp_async_wakeup(struct tty_struct *tty)
static struct tty_ldisc ppp_ldisc = {
magic: TTY_LDISC_MAGIC,
name: "ppp",
- open: ppp_async_open,
- close: ppp_async_close,
- read: ppp_async_read,
- write: ppp_async_write,
- ioctl: ppp_async_ioctl,
- poll: ppp_async_poll,
- receive_room: ppp_async_room,
- receive_buf: ppp_async_receive,
- write_wakeup: ppp_async_wakeup,
+ open: ppp_asynctty_open,
+ close: ppp_asynctty_close,
+ read: ppp_asynctty_read,
+ write: ppp_asynctty_write,
+ ioctl: ppp_asynctty_ioctl,
+ poll: ppp_asynctty_poll,
+ receive_room: ppp_asynctty_room,
+ receive_buf: ppp_asynctty_receive,
+ write_wakeup: ppp_asynctty_wakeup,
};
int
@@ -516,6 +350,91 @@ ppp_async_init(void)
}
/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+ struct asyncppp *ap = chan->private;
+ int err, val;
+ u32 accm[8];
+
+ err = -EFAULT;
+ switch (cmd) {
+ case PPPIOCGFLAGS:
+ val = ap->flags | ap->rbits;
+ if (put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if (get_user(val, (int *) arg))
+ break;
+ ap->flags = val & ~SC_RCV_BITS;
+ spin_lock_bh(&ap->recv_lock);
+ ap->rbits = val & SC_RCV_BITS;
+ spin_unlock_bh(&ap->recv_lock);
+ err = 0;
+ break;
+
+ case PPPIOCGASYNCMAP:
+ if (put_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSASYNCMAP:
+ if (get_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGRASYNCMAP:
+ if (put_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSRASYNCMAP:
+ if (get_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGXASYNCMAP:
+ if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSXASYNCMAP:
+ if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+ break;
+ accm[2] &= ~0x40000000U; /* can't escape 0x5e */
+ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
+ memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+ err = 0;
+ break;
+
+ case PPPIOCGMRU:
+ if (put_user(ap->mru, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSMRU:
+ if (get_user(val, (int *) arg))
+ break;
+ if (val < PPP_MRU)
+ val = PPP_MRU;
+ ap->mru = val;
+ err = 0;
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+
+ return err;
+}
+
+/*
* Procedures for encapsulation and framing.
*/
@@ -597,6 +516,9 @@ ppp_async_encode(struct asyncppp *ap)
islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
if (i == 0) {
+ if (islcp)
+ async_lcp_peek(ap, data, count, 0);
+
/*
* Start of a new packet - insert the leading FLAG
* character if necessary.
@@ -675,7 +597,7 @@ ppp_async_send(struct ppp_channel *chan, struct sk_buff *skb)
ppp_async_push(ap);
- if (test_and_set_bit(XMIT_FULL, &ap->busy))
+ if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
return 0; /* already full */
ap->tpkt = skb;
ap->tpkt_pos = 0;
@@ -694,12 +616,11 @@ ppp_async_push(struct asyncppp *ap)
struct tty_struct *tty = ap->tty;
int tty_stuffed = 0;
- if (!trylock_xmit_path(ap)) {
- set_bit(XMIT_WAKEUP, &ap->busy);
+ set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+ if (!spin_trylock_bh(&ap->xmit_lock))
return 0;
- }
for (;;) {
- if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy))
+ if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
tty_stuffed = 0;
if (!tty_stuffed && ap->optr < ap->olim) {
avail = ap->olim - ap->optr;
@@ -715,22 +636,17 @@ ppp_async_push(struct asyncppp *ap)
if (ap->optr == ap->olim && ap->tpkt != 0) {
if (ppp_async_encode(ap)) {
/* finished processing ap->tpkt */
- struct sk_buff *skb = skb_dequeue(&ap->xq);
- if (skb != 0) {
- ap->tpkt = skb;
- } else {
- clear_bit(XMIT_FULL, &ap->busy);
- done = 1;
- }
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
+ done = 1;
}
continue;
}
/* haven't made any progress */
- unlock_xmit_path(ap);
- if (!(test_bit(XMIT_WAKEUP, &ap->busy)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
|| (!tty_stuffed && ap->tpkt != 0)))
break;
- if (!trylock_xmit_path(ap))
+ if (!spin_trylock_bh(&ap->xmit_lock))
break;
}
return done;
@@ -739,11 +655,11 @@ flush:
if (ap->tpkt != 0) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
ap->optr = ap->olim;
- unlock_xmit_path(ap);
+ spin_unlock_bh(&ap->xmit_lock);
return done;
}
@@ -756,17 +672,16 @@ ppp_async_flush_output(struct asyncppp *ap)
{
int done = 0;
- flush_skb_queue(&ap->xq);
- lock_xmit_path(ap);
+ spin_lock_bh(&ap->xmit_lock);
ap->optr = ap->olim;
if (ap->tpkt != NULL) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
- unlock_xmit_path(ap);
- if (done && ap->connected)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (done)
ppp_output_wakeup(&ap->chan);
}
@@ -795,7 +710,7 @@ process_input_packet(struct asyncppp *ap)
{
struct sk_buff *skb;
unsigned char *p;
- unsigned int len, fcs;
+ unsigned int len, fcs, proto;
int code = 0;
skb = ap->rpkt;
@@ -827,37 +742,32 @@ process_input_packet(struct asyncppp *ap)
goto err;
p = skb_pull(skb, 2);
}
- if (p[0] & 1) {
+ proto = p[0];
+ if (proto & 1) {
/* protocol is compressed */
skb_push(skb, 1)[0] = 0;
- } else if (skb->len < 2)
- goto err;
-
- /* all OK, give it to the generic layer or queue it */
- if (ap->connected) {
- ppp_input(&ap->chan, skb);
} else {
- skb_queue_tail(&ap->rq, skb);
- /* drop old frames if queue too long */
- while (ap->rq.qlen > PPPASYNC_MAX_RQLEN
- && (skb = skb_dequeue(&ap->rq)) != 0)
- kfree(skb);
- wake_up_interruptible(&ap->rwait);
+ if (skb->len < 2)
+ goto err;
+ proto = (proto << 8) + p[1];
+ if (proto == PPP_LCP)
+ async_lcp_peek(ap, p, skb->len, 1);
}
+
+ /* all OK, give it to the generic layer */
+ ppp_input(&ap->chan, skb);
return;
err:
kfree_skb(skb);
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
static inline void
input_error(struct asyncppp *ap, int code)
{
ap->state |= SC_TOSS;
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
/* called when the tty driver has data for us. */
@@ -950,17 +860,96 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
input_error(ap, 0);
}
-#ifdef MODULE
-int
-init_module(void)
+/*
+ * We look at LCP frames going past so that we can notice
+ * and react to the LCP configure-ack from the peer.
+ * In the situation where the peer has been sent a configure-ack
+ * already, LCP is up once it has sent its configure-ack
+ * so the immediately following packet can be sent with the
+ * configured LCP options. This allows us to process the following
+ * packet correctly without pppd needing to respond quickly.
+ *
+ * We only respond to the received configure-ack if we have just
+ * sent a configure-request, and the configure-ack contains the
+ * same data (this is checked using a 16-bit crc of the data).
+ */
+#define CONFREQ 1 /* LCP code field values */
+#define CONFACK 2
+#define LCP_MRU 1 /* LCP option numbers */
+#define LCP_ASYNCMAP 2
+
+static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
+ int len, int inbound)
{
- return ppp_async_init();
+ int dlen, fcs, i, code;
+ u32 val;
+
+ data += 2; /* skip protocol bytes */
+ len -= 2;
+ if (len < 4) /* 4 = code, ID, length */
+ return;
+ code = data[0];
+ if (code != CONFACK && code != CONFREQ)
+ return;
+ dlen = (data[2] << 8) + data[3];
+ if (len < dlen)
+ return; /* packet got truncated or length is bogus */
+
+ if (code == (inbound? CONFACK: CONFREQ)) {
+ /*
+ * sent confreq or received confack:
+ * calculate the crc of the data from the ID field on.
+ */
+ fcs = PPP_INITFCS;
+ for (i = 1; i < dlen; ++i)
+ fcs = PPP_FCS(fcs, data[i]);
+
+ if (!inbound) {
+ /* outbound confreq - remember the crc for later */
+ ap->lcp_fcs = fcs;
+ return;
+ }
+
+ /* received confack, check the crc */
+ fcs ^= ap->lcp_fcs;
+ ap->lcp_fcs = -1;
+ if (fcs != 0)
+ return;
+ } else if (inbound)
+ return; /* not interested in received confreq */
+
+ /* process the options in the confack */
+ data += 4;
+ dlen -= 4;
+ /* data[0] is code, data[1] is length */
+ while (dlen >= 2 && dlen >= data[1]) {
+ switch (data[0]) {
+ case LCP_MRU:
+ val = (data[2] << 8) + data[3];
+ if (inbound)
+ ap->mru = val;
+ else
+ ap->chan.mtu = val;
+ break;
+ case LCP_ASYNCMAP:
+ val = (data[2] << 24) + (data[3] << 16)
+ + (data[4] << 8) + data[5];
+ if (inbound)
+ ap->raccm = val;
+ else
+ ap->xaccm[0] = val;
+ break;
+ }
+ dlen -= data[1];
+ data += data[1];
+ }
}
-void
-cleanup_module(void)
+void __exit ppp_async_cleanup(void)
{
if (tty_register_ldisc(N_PPP, NULL) != 0)
printk(KERN_ERR "failed to unregister PPP line discipline\n");
}
-#endif /* MODULE */
+
+module_init(ppp_async_init);
+module_exit(ppp_async_cleanup);
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 3ef379ab2..761d8705c 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -32,26 +32,9 @@
*/
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/vmalloc.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-
-#include <asm/system.h>
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
+#include <linux/init.h>
#include <linux/ppp_defs.h>
#include <linux/ppp-comp.h>
@@ -656,31 +639,21 @@ struct compressor ppp_deflate_draft = {
z_comp_stats, /* decomp_stat */
};
-#ifdef MODULE
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-int
-init_module(void)
+int deflate_init(void)
{
- int answer = ppp_register_compressor (&ppp_deflate);
+ int answer = ppp_register_compressor(&ppp_deflate);
if (answer == 0)
- printk (KERN_INFO
- "PPP Deflate Compression module registered\n");
+ printk(KERN_INFO
+ "PPP Deflate Compression module registered\n");
ppp_register_compressor(&ppp_deflate_draft);
return answer;
}
-void
-cleanup_module(void)
+void deflate_cleanup(void)
{
- if (MOD_IN_USE)
- printk (KERN_INFO
- "Deflate Compression module busy, remove delayed\n");
- else {
- ppp_unregister_compressor (&ppp_deflate);
- ppp_unregister_compressor (&ppp_deflate_draft);
- }
+ ppp_unregister_compressor(&ppp_deflate);
+ ppp_unregister_compressor(&ppp_deflate_draft);
}
-#endif
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 0b3aeff4b..01a4e2f81 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1,7 +1,7 @@
/*
* Generic PPP layer for Linux.
*
- * Copyright 1999 Paul Mackerras.
+ * Copyright 1999-2000 Paul Mackerras.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,11 +19,9 @@
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 990915==
+ * ==FILEVERSION 20000313==
*/
-/* $Id: ppp_generic.c,v 1.5 1999/09/15 11:21:48 paulus Exp $ */
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -44,16 +42,9 @@
#include <linux/tcp.h>
#include <linux/spinlock.h>
#include <net/slhc_vj.h>
+#include <asm/atomic.h>
-#define PPP_VERSION "2.4.0"
-
-EXPORT_SYMBOL(ppp_register_channel);
-EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_input);
-EXPORT_SYMBOL(ppp_input_error);
-EXPORT_SYMBOL(ppp_output_wakeup);
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
+#define PPP_VERSION "2.4.1"
/*
* Network protocols we support.
@@ -64,32 +55,56 @@ EXPORT_SYMBOL(ppp_unregister_compressor);
#define NP_AT 3 /* Appletalk protocol */
#define NUM_NP 4 /* Number of NPs. */
+#define MPHDRLEN 4 /* multilink protocol header length */
+#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */
+#define MIN_FRAG_SIZE 64
+
+/*
+ * An instance of /dev/ppp can be associated with either a ppp
+ * interface unit or a ppp channel. In both cases, file->private_data
+ * points to one of these.
+ */
+struct ppp_file {
+ enum {
+ INTERFACE=1, CHANNEL
+ } kind;
+ struct sk_buff_head xq; /* pppd transmit queue */
+ struct sk_buff_head rq; /* receive queue for pppd */
+ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
+ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */
+ int hdrlen; /* space to leave for headers */
+ struct list_head list; /* link in all_* list */
+ int index; /* interface unit / channel number */
+};
+
+#define PF_TO_X(pf, X) ((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file)))
+
+#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
+#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
+
+#define ROUNDUP(n, x) (((n) + (x) - 1) / (x))
+
/*
* Data structure describing one ppp unit.
* A ppp unit corresponds to a ppp network interface device
* and represents a multilink bundle.
- * It may have 0 or more ppp channels connected to it.
+ * It can have 0 or more ppp channels connected to it.
*/
struct ppp {
- struct list_head list; /* link in list of ppp units */
- int index; /* interface unit number */
+ struct ppp_file file; /* stuff for read/write/poll */
char name[16]; /* unit name */
- int refcnt; /* # open /dev/ppp attached */
- unsigned long busy; /* lock and other bits */
struct list_head channels; /* list of attached channels */
int n_channels; /* how many channels are attached */
+ spinlock_t rlock; /* lock for receive side */
+ spinlock_t wlock; /* lock for transmit side */
int mru; /* max receive unit */
unsigned int flags; /* control bits */
unsigned int xstate; /* transmit state bits */
unsigned int rstate; /* receive state bits */
int debug; /* debug flags */
struct slcompress *vj; /* state for VJ header compression */
- struct sk_buff_head xq; /* pppd transmit queue */
- struct sk_buff_head rq; /* receive queue for pppd */
- wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
enum NPmode npmode[NUM_NP]; /* what to do with each net proto */
struct sk_buff *xmit_pending; /* a packet ready to go out */
- struct sk_buff_head recv_pending;/* pending input packets */
struct compressor *xcomp; /* transmit packet compressor */
void *xc_state; /* its internal state */
struct compressor *rcomp; /* receive decompressor */
@@ -97,57 +112,121 @@ struct ppp {
unsigned long last_xmit; /* jiffies when last pkt sent */
unsigned long last_recv; /* jiffies when last pkt rcvd */
struct net_device *dev; /* network interface device */
+#ifdef CONFIG_PPP_MULTILINK
+ int nxchan; /* next channel to send something on */
+ u32 nxseq; /* next sequence number to send */
+ int mrru; /* MP: max reconst. receive unit */
+ u32 nextseq; /* MP: seq no of next packet */
+ u32 minseq; /* MP: min of most recent seqnos */
+ struct sk_buff_head mrq; /* MP: receive reconstruction queue */
+#endif /* CONFIG_PPP_MULTILINK */
struct net_device_stats stats; /* statistics */
};
-static LIST_HEAD(all_ppp_units);
-static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+/*
+ * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
+ * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ.
+ * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
+ * Bits in xstate: SC_COMP_RUN
+ */
+#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC \
+ |SC_MULTILINK|SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ)
/*
* Private data structure for each channel.
- * Ultimately this will have multilink stuff etc. in it.
+ * This includes the data structure used for multilink.
*/
struct channel {
- struct list_head list; /* link in list of channels per unit */
+ struct ppp_file file; /* stuff for read/write/poll */
struct ppp_channel *chan; /* public channel data structure */
- int blocked; /* if channel refused last packet */
+ spinlock_t downl; /* protects `chan', file.xq dequeue */
struct ppp *ppp; /* ppp unit we're connected to */
+ struct list_head clist; /* link in list of channels per unit */
+ rwlock_t upl; /* protects `ppp' and `ulist' */
+#ifdef CONFIG_PPP_MULTILINK
+ u8 avail; /* flag used in multilink stuff */
+ u8 had_frag; /* >= 1 fragments have been sent */
+ u32 lastseq; /* MP: last sequence # received */
+#endif /* CONFIG_PPP_MULTILINK */
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
+/*
+ * SMP locking issues:
+ * Both the ppp.rlock and ppp.wlock locks protect the ppp.channels
+ * list and the ppp.n_channels field, you need to take both locks
+ * before you modify them.
+ * The lock ordering is: channel.upl -> ppp.wlock -> ppp.rlock ->
+ * channel.downl.
+ */
/*
- * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC.
- * Bits in rstate: SC_DECOMP_RUN, SC_DC_ERROR, SC_DC_FERROR.
- * Bits in xstate: SC_COMP_RUN
+ * all_ppp_lock protects the all_ppp_units.
+ * It also ensures that finding a ppp unit in the all_ppp_units list
+ * and updating its file.refcnt field is atomic.
+ */
+static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_ppp_units);
+
+/*
+ * all_channels_lock protects all_channels and last_channel_index,
+ * and the atomicity of find a channel and updating its file.refcnt
+ * field.
*/
-#define SC_FLAG_BITS (SC_NO_TCP_CCID|SC_CCP_OPEN|SC_CCP_UP|SC_LOOP_TRAFFIC)
+static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(all_channels);
+static int last_channel_index;
/* Get the PPP protocol number from a skb */
#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
-/* We limit the length of ppp->rq to this (arbitrary) value */
+/* We limit the length of ppp->file.rq to this (arbitrary) value */
#define PPP_MAX_RQLEN 32
+/* Multilink header bits. */
+#define B 0x80 /* this fragment begins a packet */
+#define E 0x40 /* this fragment ends a packet */
+
+/* Compare multilink sequence numbers (assumed to be 32 bits wide) */
+#define seq_before(a, b) ((s32)((a) - (b)) < 0)
+#define seq_after(a, b) ((s32)((a) - (b)) > 0)
+
/* Prototypes. */
-static void ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh);
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count);
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count);
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static void ppp_xmit_process(struct ppp *ppp, int wakeup);
static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
static void ppp_push(struct ppp *ppp);
-static void ppp_recv_unlock(struct ppp *ppp);
-static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_channel_push(struct channel *pch);
+static void ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb);
static struct sk_buff *ppp_decompress_frame(struct ppp *ppp,
struct sk_buff *skb);
+#ifdef CONFIG_PPP_MULTILINK
+static void ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb,
+ struct channel *pch);
+static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
+#endif /* CONFIG_PPP_MULTILINK */
static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
static void ppp_ccp_closed(struct ppp *ppp);
static struct compressor *find_compressor(int type);
static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_unit(int unit, int *retp);
-static void ppp_release_unit(struct ppp *ppp);
+static struct ppp *ppp_create_interface(int unit, int *retp);
+static void init_ppp_file(struct ppp_file *pf, int kind);
+static void ppp_destroy_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(int unit);
+static struct channel *ppp_find_channel(int unit);
+static int ppp_connect_channel(struct channel *pch, int unit);
+static int ppp_disconnect_channel(struct channel *pch);
+static void ppp_destroy_channel(struct channel *pch);
/* Translates a PPP protocol number to a NP index (NP == network protocol) */
static inline int proto_to_npindex(int proto)
@@ -199,69 +278,24 @@ static const int npindex_to_ethertype[NUM_NP] = {
};
/*
- * Routines for locking and unlocking the transmit and receive paths
- * of each unit.
- *
- * On the transmit side, we have threads of control coming into the
- * driver from (at least) three places: the core net code, write calls
- * on /dev/ppp from pppd, and wakeup calls from channels. There is
- * possible concurrency even on UP systems (between mainline and
- * BH processing). The XMIT_BUSY bit in ppp->busy serializes the
- * transmit-side processing for each ppp unit.
+ * Locking shorthand.
*/
-static inline void
-lock_path(struct ppp *ppp, int bit)
-{
- int timeout = 1000000;
-
- do {
- while (test_bit(bit, &ppp->busy)) {
- mb();
- if (--timeout == 0) {
- printk(KERN_ERR "lock_path timeout ppp=%p bit=%x\n", ppp, bit);
- return;
- }
- }
- } while (test_and_set_bit(bit, &ppp->busy));
- mb();
-}
+#define ppp_xmit_lock(ppp) spin_lock_bh(&(ppp)->wlock)
+#define ppp_xmit_unlock(ppp) spin_unlock_bh(&(ppp)->wlock)
+#define ppp_recv_lock(ppp) spin_lock_bh(&(ppp)->rlock)
+#define ppp_recv_unlock(ppp) spin_unlock_bh(&(ppp)->rlock)
+#define ppp_lock(ppp) do { ppp_xmit_lock(ppp); \
+ ppp_recv_lock(ppp); } while (0)
+#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \
+ ppp_xmit_unlock(ppp); } while (0)
-static inline int
-trylock_path(struct ppp *ppp, int bit)
-{
- if (test_and_set_bit(bit, &ppp->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct ppp *ppp, int bit)
-{
- mb();
- clear_bit(bit, &ppp->busy);
-}
-
-#define lock_xmit_path(ppp) lock_path(ppp, XMIT_BUSY)
-#define trylock_xmit_path(ppp) trylock_path(ppp, XMIT_BUSY)
-#define unlock_xmit_path(ppp) unlock_path(ppp, XMIT_BUSY)
-#define lock_recv_path(ppp) lock_path(ppp, RECV_BUSY)
-#define trylock_recv_path(ppp) trylock_path(ppp, RECV_BUSY)
-#define unlock_recv_path(ppp) unlock_path(ppp, RECV_BUSY)
-
-static inline void
-free_skbs(struct sk_buff_head *head)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(head)) != 0)
- kfree_skb(skb);
-}
/*
* /dev/ppp device routines.
* The /dev/ppp device is used by pppd to control the ppp unit.
* It supports the read, write, ioctl and poll functions.
+ * Open instances of /dev/ppp can be in one of three states:
+ * unattached, attached to a ppp unit, or attached to a ppp channel.
*/
static int ppp_open(struct inode *inode, struct file *file)
{
@@ -276,11 +310,20 @@ static int ppp_open(struct inode *inode, struct file *file)
static int ppp_release(struct inode *inode, struct file *file)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
- if (ppp != 0) {
+ if (pf != 0) {
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt)) {
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_destroy_interface(PF_TO_PPP(pf));
+ break;
+ case CHANNEL:
+ ppp_destroy_channel(PF_TO_CHANNEL(pf));
+ break;
+ }
+ }
}
MOD_DEC_USE_COUNT;
return 0;
@@ -289,20 +332,27 @@ static int ppp_release(struct inode *inode, struct file *file)
static ssize_t ppp_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_read(pf, file, buf, count);
+}
+
+static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
+ char *buf, size_t count)
+{
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
struct sk_buff *skb = 0;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out; /* not currently attached */
- add_wait_queue(&ppp->rwait, &wait);
+ add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
ret = -EAGAIN;
- skb = skb_dequeue(&ppp->rq);
+ skb = skb_dequeue(&pf->rq);
if (skb)
break;
if (file->f_flags & O_NONBLOCK)
@@ -313,7 +363,7 @@ static ssize_t ppp_read(struct file *file, char *buf,
schedule();
}
current->state = TASK_RUNNING;
- remove_wait_queue(&ppp->rwait, &wait);
+ remove_wait_queue(&pf->rwait, &wait);
if (skb == 0)
goto out;
@@ -335,32 +385,42 @@ static ssize_t ppp_read(struct file *file, char *buf,
static ssize_t ppp_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+
+ return ppp_file_write(pf, buf, count);
+}
+
+static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
+ size_t count)
+{
struct sk_buff *skb;
ssize_t ret;
- int extra;
ret = -ENXIO;
- if (ppp == 0)
+ if (pf == 0)
goto out;
ret = -ENOMEM;
- extra = PPP_HDRLEN - 2;
- if (ppp->dev && ppp->dev->hard_header_len > PPP_HDRLEN)
- extra = ppp->dev->hard_header_len - 2;
- skb = alloc_skb(count + extra, GFP_KERNEL);
+ skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
if (skb == 0)
goto out;
- skb_reserve(skb, extra);
+ skb_reserve(skb, pf->hdrlen);
ret = -EFAULT;
if (copy_from_user(skb_put(skb, count), buf, count)) {
kfree_skb(skb);
goto out;
}
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ skb_queue_tail(&pf->xq, skb);
+
+ switch (pf->kind) {
+ case INTERFACE:
+ ppp_xmit_process(PF_TO_PPP(pf), 0);
+ break;
+ case CHANNEL:
+ ppp_channel_push(PF_TO_CHANNEL(pf));
+ break;
+ }
ret = count;
@@ -370,68 +430,82 @@ static ssize_t ppp_write(struct file *file, const char *buf,
static unsigned int ppp_poll(struct file *file, poll_table *wait)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
unsigned int mask;
- if (ppp == 0)
+ if (pf == 0)
return 0;
- poll_wait(file, &ppp->rwait, wait);
+ poll_wait(file, &pf->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ppp->rq) != 0)
+ if (skb_peek(&pf->rq) != 0)
mask |= POLLIN | POLLRDNORM;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ if (pch->chan == 0)
+ mask |= POLLHUP;
+ }
return mask;
}
static int ppp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct ppp *ppp = (struct ppp *) file->private_data;
- int err, val, val2, i;
+ struct ppp_file *pf = (struct ppp_file *) file->private_data;
+ struct ppp *ppp;
+ int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
struct npioctl npi;
+ int unit;
+ struct slcompress *vj;
- if (cmd == PPPIOCNEWUNIT) {
- /* Create a new ppp unit */
- int unit, ret;
+ if (pf == 0)
+ return ppp_unattached_ioctl(pf, file, cmd, arg);
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- ppp = ppp_create_unit(unit, &ret);
- if (ppp == 0)
- return ret;
- file->private_data = ppp;
- if (put_user(ppp->index, (int *) arg))
- return -EFAULT;
- return 0;
+ if (pf->kind == CHANNEL) {
+ struct channel *pch = PF_TO_CHANNEL(pf);
+ struct ppp_channel *chan;
+
+ switch (cmd) {
+ case PPPIOCCONNECT:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+
+ case PPPIOCDISCONN:
+ err = ppp_disconnect_channel(pch);
+ break;
+
+ case PPPIOCDETACH:
+ file->private_data = 0;
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_channel(pch);
+ err = 0;
+ break;
+
+ default:
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ err = -ENOTTY;
+ if (chan->ops->ioctl)
+ err = chan->ops->ioctl(chan, cmd, arg);
+ spin_unlock_bh(&pch->downl);
+ }
+ return err;
}
- if (cmd == PPPIOCATTACH) {
- /* Attach to an existing ppp unit */
- int unit;
- if (ppp != 0)
- return -EINVAL;
- if (get_user(unit, (int *) arg))
- return -EFAULT;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp != 0)
- ++ppp->refcnt;
- spin_unlock(&all_ppp_lock);
- if (ppp == 0)
- return -ENXIO;
- file->private_data = ppp;
- return 0;
+ if (pf->kind != INTERFACE) {
+ /* can't happen */
+ printk(KERN_ERR "PPP: not interface or channel??\n");
+ return -EINVAL;
}
- if (ppp == 0)
- return -ENXIO;
- err = -EFAULT;
+ ppp = PF_TO_PPP(pf);
switch (cmd) {
case PPPIOCDETACH:
file->private_data = 0;
- ppp_release_unit(ppp);
+ if (atomic_dec_and_test(&pf->refcnt))
+ ppp_destroy_interface(ppp);
err = 0;
break;
@@ -445,9 +519,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
case PPPIOCSFLAGS:
if (get_user(val, (int *) arg))
break;
+ ppp_lock(ppp);
if (ppp->flags & ~val & SC_CCP_OPEN)
ppp_ccp_closed(ppp);
ppp->flags = val & SC_FLAG_BITS;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -463,7 +539,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
break;
case PPPIOCGUNIT:
- if (put_user(ppp->index, (int *) arg))
+ if (put_user(ppp->file.index, (int *) arg))
break;
err = 0;
break;
@@ -497,18 +573,17 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
val2 = val >> 16;
val &= 0xffff;
}
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
- if (ppp->vj != 0)
- slhc_free(ppp->vj);
- ppp->vj = slhc_init(val2+1, val+1);
- ppp_recv_unlock(ppp);
- ppp_xmit_unlock(ppp, 1);
- err = -ENOMEM;
- if (ppp->vj == 0) {
+ vj = slhc_init(val2+1, val+1);
+ if (vj == 0) {
printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+ err = -ENOMEM;
break;
}
+ ppp_lock(ppp);
+ if (ppp->vj != 0)
+ slhc_free(ppp->vj);
+ ppp->vj = vj;
+ ppp_unlock(ppp);
err = 0;
break;
@@ -533,6 +608,76 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
err = 0;
break;
+#ifdef CONFIG_PPP_MULTILINK
+ case PPPIOCSMRRU:
+ if (get_user(val, (int *) arg))
+ break;
+ ppp_recv_lock(ppp);
+ ppp->mrru = val;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ break;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ default:
+ err = -ENOTTY;
+ }
+ return err;
+}
+
+static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit, err = -EFAULT;
+ struct ppp *ppp;
+ struct channel *chan;
+
+ switch (cmd) {
+ case PPPIOCNEWUNIT:
+ /* Create a new ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ ppp = ppp_create_interface(unit, &err);
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = -EFAULT;
+ if (put_user(ppp->file.index, (int *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCATTACH:
+ /* Attach to an existing ppp unit */
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp != 0)
+ atomic_inc(&ppp->file.refcnt);
+ spin_unlock(&all_ppp_lock);
+ err = -ENXIO;
+ if (ppp == 0)
+ break;
+ file->private_data = &ppp->file;
+ err = 0;
+ break;
+
+ case PPPIOCATTCHAN:
+ if (get_user(unit, (int *) arg))
+ break;
+ spin_lock_bh(&all_channels_lock);
+ chan = ppp_find_channel(unit);
+ if (chan != 0)
+ atomic_inc(&chan->file.refcnt);
+ spin_unlock_bh(&all_channels_lock);
+ err = -ENXIO;
+ if (chan == 0)
+ break;
+ file->private_data = &chan->file;
+ err = 0;
+ break;
+
default:
err = -ENOTTY;
}
@@ -557,37 +702,17 @@ static devfs_handle_t devfs_handle = NULL;
int __init ppp_init(void)
{
int err;
-#ifndef MODULE
-#ifdef CONFIG_PPP_DEFLATE
- extern struct compressor ppp_deflate, ppp_deflate_draft;
-#endif
- extern int ppp_async_init(void);
- extern int ppp_sync_init(void);
-#endif
printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
if (err)
printk(KERN_ERR "failed to register PPP device (%d)\n", err);
- devfs_handle = devfs_register (NULL, "ppp", 0, DEVFS_FL_NONE,
- PPP_MAJOR, 0,
- S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
- &ppp_device_fops, NULL);
-#ifndef MODULE
-#ifdef CONFIG_PPP_ASYNC
- ppp_async_init();
-#endif
-#ifdef CONFIG_PPP_SYNC_TTY
- ppp_sync_init();
-#endif
-#ifdef CONFIG_PPP_DEFLATE
- if (ppp_register_compressor(&ppp_deflate) == 0)
- printk(KERN_INFO "PPP Deflate compression module registered\n");
- ppp_register_compressor(&ppp_deflate_draft);
-#endif
-#endif /* MODULE */
+ devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE,
+ PPP_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR, 0, 0,
+ &ppp_device_fops, NULL);
- return -ENODEV;
+ return 0;
}
/*
@@ -636,9 +761,8 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
pp[1] = proto;
netif_stop_queue(dev);
- skb_queue_tail(&ppp->xq, skb);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 0);
+ skb_queue_tail(&ppp->file.xq, skb);
+ ppp_xmit_process(ppp, 0);
return 0;
outf:
@@ -719,38 +843,26 @@ ppp_net_init(struct net_device *dev)
*/
/*
- * Called to unlock the transmit side of the ppp unit,
- * making sure that any work queued up gets done.
+ * Called to do any work queued up on the transmit side
+ * that can now be done.
*/
static void
-ppp_xmit_unlock(struct ppp *ppp, int do_mark_bh)
+ppp_xmit_process(struct ppp *ppp, int wakeup)
{
struct sk_buff *skb;
- for (;;) {
- /* Do whatever work is waiting to be done. */
- if (test_and_clear_bit(XMIT_WAKEUP, &ppp->busy))
- ppp_push(ppp);
- /* If there's no work left to do, tell the core net
- code that we can accept some more. */
- while (ppp->xmit_pending == 0
- && (skb = skb_dequeue(&ppp->xq)) != 0)
- ppp_send_frame(ppp, skb);
- if (ppp->xmit_pending == 0 && skb_peek(&ppp->xq) == 0)
- netif_wake_queue(ppp->dev);
-
- /* Now unlock the transmit path, let others in. */
- unlock_xmit_path(ppp);
- /* Check whether any work was queued up
- between our last check and the unlock. */
- if (!(test_bit(XMIT_WAKEUP, &ppp->busy)
- || (ppp->xmit_pending == 0 && skb_peek(&ppp->xq))))
- break;
- /* If so, lock again and do the work. If we can't get
- the lock, someone else has it and they'll do the work. */
- if (!trylock_xmit_path(ppp))
- break;
- }
+ ppp_xmit_lock(ppp);
+ if (wakeup)
+ ppp_push(ppp);
+ while (ppp->xmit_pending == 0
+ && (skb = skb_dequeue(&ppp->file.xq)) != 0)
+ ppp_send_frame(ppp, skb);
+ /* If there's no work left to do, tell the core net
+ code that we can accept some more. */
+ if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0
+ && ppp->dev != 0)
+ netif_wake_queue(ppp->dev);
+ ppp_xmit_unlock(ppp);
}
/*
@@ -847,10 +959,10 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
* queue it up for pppd to receive.
*/
if (ppp->flags & SC_LOOP_TRAFFIC) {
- if (ppp->rq.qlen > PPP_MAX_RQLEN)
+ if (ppp->file.rq.qlen > PPP_MAX_RQLEN)
goto drop;
- skb_queue_tail(&ppp->rq, skb);
- wake_up_interruptible(&ppp->rwait);
+ skb_queue_tail(&ppp->file.rq, skb);
+ wake_up_interruptible(&ppp->file.rwait);
return;
}
@@ -871,7 +983,7 @@ static void
ppp_push(struct ppp *ppp)
{
struct list_head *list;
- struct channel *chan;
+ struct channel *pch;
struct sk_buff *skb = ppp->xmit_pending;
if (skb == 0)
@@ -885,40 +997,222 @@ ppp_push(struct ppp *ppp)
return;
}
- /* If we are doing multilink, decide which channel gets the
- packet, and/or fragment the packet over several links. */
- /* XXX for now, just take the first channel */
- list = list->next;
- chan = list_entry(list, struct channel, list);
+ if ((ppp->flags & SC_MULTILINK) == 0) {
+ /* not doing multilink: send it down the first channel */
+ list = list->next;
+ pch = list_entry(list, struct channel, clist);
- if (chan->chan->ops->start_xmit(chan->chan, skb)) {
- ppp->xmit_pending = 0;
- chan->blocked = 0;
- } else
- chan->blocked = 1;
+ spin_lock_bh(&pch->downl);
+ if (skb_queue_len(&pch->file.xq) == 0
+ && pch->chan->ops->start_xmit(pch->chan, skb))
+ ppp->xmit_pending = 0;
+ spin_unlock_bh(&pch->downl);
+ return;
+ }
+
+#ifdef CONFIG_PPP_MULTILINK
+ /* Multilink: fragment the packet over as many links
+ as can take the packet at the moment. */
+ if (!ppp_mp_explode(ppp, skb))
+ return;
+#endif /* CONFIG_PPP_MULTILINK */
+
+ ppp->xmit_pending = 0;
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Divide a packet to be transmitted into fragments and
+ * send them out the individual links.
+ */
+static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
+{
+ int nch, len, fragsize;
+ int i, bits, hdrlen;
+ unsigned char *p, *q;
+ struct list_head *list;
+ struct channel *pch;
+ struct sk_buff *frag;
+ struct ppp_channel *chan;
+
+ nch = 0;
+ list = &ppp->channels;
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0);
+ /*
+ * If a channel hasn't had a fragment yet, it has to get
+ * one before we send any fragments on later channels.
+ * If it can't take a fragment now, don't give any
+ * to subsequent channels.
+ */
+ if (!pch->had_frag && !pch->avail) {
+ while ((list = list->next) != &ppp->channels) {
+ pch = list_entry(list, struct channel, clist);
+ pch->avail = 0;
+ }
+ break;
+ }
+ }
+ if (nch == 0)
+ return 0; /* can't take now, leave it in xmit_pending */
+
+ /* Do protocol field compression (XXX this should be optional) */
+ p = skb->data;
+ len = skb->len;
+ if (*p == 0) {
+ ++p;
+ --len;
+ }
+
+ /* decide on fragment size */
+ fragsize = len;
+ if (nch > 1) {
+ int maxch = ROUNDUP(len, MIN_FRAG_SIZE);
+ if (nch > maxch)
+ nch = maxch;
+ fragsize = ROUNDUP(fragsize, nch);
+ }
+
+ /* skip to the channel after the one we last used
+ and start at that one */
+ for (i = 0; i < ppp->nxchan; ++i) {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ break;
+ }
+ }
+
+ /* create a fragment for each channel */
+ bits = B;
+ hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
+ /* XXX gotta do A/C and prot compression here */
+ do {
+ list = list->next;
+ if (list == &ppp->channels) {
+ i = 0;
+ continue;
+ }
+ pch = list_entry(list, struct channel, clist);
+ ++i;
+ if (!pch->avail)
+ continue;
+ if (fragsize >= len) {
+ fragsize = len;
+ bits |= E;
+ }
+ frag = alloc_skb(fragsize + hdrlen, GFP_ATOMIC);
+ if (frag != 0) {
+ q = skb_put(frag, fragsize + hdrlen);
+ /* make the MP header */
+ if (ppp->flags & SC_MP_XSHORTSEQ) {
+ q[0] = bits + ((ppp->nxseq >> 8) & 0xf);
+ q[1] = ppp->nxseq;
+ } else {
+ q[0] = bits;
+ q[1] = ppp->nxseq >> 16;
+ q[2] = ppp->nxseq >> 8;
+ q[3] = ppp->nxseq;
+ }
+
+ /* copy the data in */
+ memcpy(q + hdrlen, p, fragsize);
+
+ /* try to send it down the channel */
+ spin_lock_bh(&pch->downl);
+ chan = pch->chan;
+ if (chan != 0) {
+ if (!chan->ops->start_xmit(chan, frag))
+ skb_queue_tail(&pch->file.xq, frag);
+ } else {
+ /* channel got unregistered, too bad */
+ kfree_skb(skb);
+ }
+ spin_unlock_bh(&pch->downl);
+ }
+ p += fragsize;
+ len -= fragsize;
+ ++ppp->nxseq;
+ bits = 0;
+ } while (len > 0);
+ ppp->nxchan = i;
+
+ return 1;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
+/*
+ * Try to send data out on a channel.
+ */
+static void
+ppp_channel_push(struct channel *pch)
+{
+ struct sk_buff *skb;
+
+ spin_lock_bh(&pch->downl);
+ if (pch->chan != 0) {
+ while (skb_queue_len(&pch->file.xq) > 0) {
+ skb = skb_dequeue(&pch->file.xq);
+ if (!pch->chan->ops->start_xmit(pch->chan, skb)) {
+ /* put the packet back and try again later */
+ skb_queue_head(&pch->file.xq, skb);
+ break;
+ }
+ }
+ } else {
+ /* channel got deregistered */
+ skb_queue_purge(&pch->file.xq);
+ }
+ spin_unlock_bh(&pch->downl);
}
/*
* Receive-side routines.
*/
+
+/* misuse a few fields of the skb for MP reconstruction */
+#define sequence priority
+#define BEbits cb[0]
+
static inline void
-ppp_do_recv(struct ppp *ppp, struct sk_buff *skb)
+ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- skb_queue_tail(&ppp->recv_pending, skb);
- if (trylock_recv_path(ppp))
- ppp_recv_unlock(ppp);
+ ppp_recv_lock(ppp);
+ /* ppp->dev == 0 means interface is closing down */
+ if (ppp->dev != 0)
+ ppp_receive_frame(ppp, skb, pch);
+ else
+ kfree_skb(skb);
+ ppp_recv_unlock(ppp);
}
void
ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
{
struct channel *pch = chan->ppp;
+ int proto;
if (pch == 0 || skb->len == 0) {
kfree_skb(skb);
return;
}
- ppp_do_recv(pch->ppp, skb);
+
+ proto = PPP_PROTO(skb);
+ read_lock_bh(&pch->upl);
+ if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+ /* put it on the channel queue */
+ skb_queue_tail(&pch->file.rq, skb);
+ /* drop old frames if queue too long */
+ while (pch->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&pch->file.rq)) != 0)
+ kfree_skb(skb);
+ wake_up_interruptible(&pch->file.rwait);
+ } else {
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ read_unlock_bh(&pch->upl);
}
/* Put a 0-length skb in the receive queue as an error indication */
@@ -930,47 +1224,64 @@ ppp_input_error(struct ppp_channel *chan, int code)
if (pch == 0)
return;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (skb == 0)
- return;
- skb->len = 0; /* probably unnecessary */
- skb->cb[0] = code;
- ppp_do_recv(pch->ppp, skb);
+
+ read_lock_bh(&pch->upl);
+ if (pch->ppp != 0) {
+ skb = alloc_skb(0, GFP_ATOMIC);
+ if (skb != 0) {
+ skb->len = 0; /* probably unnecessary */
+ skb->cb[0] = code;
+ ppp_do_recv(pch->ppp, skb, pch);
+ }
+ }
+ read_unlock_bh(&pch->upl);
}
+/*
+ * We come in here to process a received frame.
+ * The receive side of the ppp unit is locked.
+ */
static void
-ppp_recv_unlock(struct ppp *ppp)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
- struct sk_buff *skb;
-
- for (;;) {
- while ((skb = skb_dequeue(&ppp->recv_pending)) != 0)
- ppp_receive_frame(ppp, skb);
- unlock_recv_path(ppp);
- if (skb_peek(&ppp->recv_pending) == 0)
- break;
- if (!trylock_recv_path(ppp))
- break;
+ if (skb->len >= 2) {
+#ifdef CONFIG_PPP_MULTILINK
+ /* XXX do channel-level decompression here */
+ if (PPP_PROTO(skb) == PPP_MP)
+ ppp_receive_mp_frame(ppp, skb, pch);
+ else
+#endif /* CONFIG_PPP_MULTILINK */
+ ppp_receive_nonmp_frame(ppp, skb);
+ return;
}
+
+ if (skb->len > 0)
+ /* note: a 0-length skb is used as an error indication */
+ ++ppp->stats.rx_length_errors;
+
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static void
-ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
+ppp_receive_error(struct ppp *ppp)
+{
+ ++ppp->stats.rx_errors;
+ if (ppp->vj != 0)
+ slhc_toss(ppp->vj);
+}
+
+static void
+ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
{
struct sk_buff *ns;
int proto, len, npi;
- if (skb->len == 0) {
- /* XXX should do something with code in skb->cb[0] */
- goto err; /* error indication */
- }
-
- if (skb->len < 2) {
- ++ppp->stats.rx_length_errors;
- goto err;
- }
-
- /* Decompress the frame, if compressed. */
+ /*
+ * Decompress the frame, if compressed.
+ * Note that some decompressors need to see uncompressed frames
+ * that come in as well as compressed frames.
+ */
if (ppp->rc_state != 0 && (ppp->rstate & SC_DECOMP_RUN)
&& (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
skb = ppp_decompress_frame(ppp, skb);
@@ -995,7 +1306,12 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
}
len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
if (len <= 0) {
- printk(KERN_ERR "PPP: VJ decompression error\n");
+ int i;
+ printk(KERN_DEBUG "PPP: VJ decompression error\n");
+ printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
+ for (i = 0; i < 16 && i < skb->len; ++i)
+ printk(" %.2x", skb->data[i]);
+ printk("\n");
goto err;
}
len += 2;
@@ -1027,15 +1343,13 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
npi = proto_to_npindex(proto);
if (npi < 0) {
/* control or unknown frame - pass it to pppd */
- skb_queue_tail(&ppp->rq, skb);
+ skb_queue_tail(&ppp->file.rq, skb);
/* limit queue length by dropping old frames */
- while (ppp->rq.qlen > PPP_MAX_RQLEN) {
- skb = skb_dequeue(&ppp->rq);
- if (skb)
- kfree_skb(skb);
- }
+ while (ppp->file.rq.qlen > PPP_MAX_RQLEN
+ && (skb = skb_dequeue(&ppp->file.rq)) != 0)
+ kfree_skb(skb);
/* wake up any process polling or blocking on read */
- wake_up_interruptible(&ppp->rwait);
+ wake_up_interruptible(&ppp->file.rwait);
} else {
/* network protocol frame - give it to the kernel */
@@ -1054,10 +1368,8 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
return;
err:
- ++ppp->stats.rx_errors;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
kfree_skb(skb);
+ ppp_receive_error(ppp);
}
static struct sk_buff *
@@ -1070,7 +1382,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
if (proto == PPP_COMP) {
ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);
if (ns == 0) {
- printk(KERN_ERR "ppp_receive: no memory\n");
+ printk(KERN_ERR "ppp_decompress_frame: no memory\n");
goto err;
}
/* the decompressor still expects the A/C bytes in the hdr */
@@ -1101,70 +1413,288 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
err:
ppp->rstate |= SC_DC_ERROR;
- if (ppp->vj != 0)
- slhc_toss(ppp->vj);
- ++ppp->stats.rx_errors;
+ ppp_receive_error(ppp);
return skb;
}
+#ifdef CONFIG_PPP_MULTILINK
+/*
+ * Receive a multilink frame.
+ * We put it on the reconstruction queue and then pull off
+ * as many completed frames as we can.
+ */
+static void
+ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
+{
+ u32 mask, seq, minseq;
+ struct list_head *l;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+
+ if (skb->len < mphdrlen + 3)
+ goto err; /* no good, throw it away */
+
+ /* Decode sequence number and begin/end bits */
+ if (ppp->flags & SC_MP_SHORTSEQ) {
+ seq = ((skb->data[2] & 0x0f) << 8) | skb->data[3];
+ mask = 0xfff;
+ } else {
+ seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
+ mask = 0xffffff;
+ }
+ skb->BEbits = skb->data[2];
+ skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/
+
+ /* Expand sequence number to 32 bits */
+ seq |= pch->lastseq & ~mask;
+ if (seq_before(seq, pch->lastseq)) {
+ if (seq_after(seq, pch->lastseq - 100)) {
+ printk(KERN_DEBUG "PPP: MP fragments out of order"
+ " (%u, %u)\n", pch->lastseq, seq);
+ goto err;
+ }
+ seq += mask + 1;
+ }
+ skb->sequence = seq;
+ pch->lastseq = seq;
+
+ /*
+ * Reevaluate minseq, the minimum over all channels of the
+ * last sequence number received on each channel. Because of
+ * the increasing sequence number rule, we know that any fragment
+ * before `minseq' which hasn't arrived is never going to arrive.
+ * The list of channels can't change because we have the receive
+ * side of the ppp unit locked.
+ */
+ minseq = seq;
+ for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {
+ struct channel *ch = list_entry(l, struct channel, clist);
+ if (seq_before(ch->lastseq, seq))
+ seq = ch->lastseq;
+ }
+ ppp->minseq = minseq;
+
+ /* Put the fragment on the reconstruction queue */
+ ppp_mp_insert(ppp, skb);
+
+ /* Pull completed packets off the queue and receive them. */
+ while ((skb = ppp_mp_reconstruct(ppp)) != 0)
+ ppp_receive_nonmp_frame(ppp, skb);
+
+ return;
+
+ err:
+ kfree_skb(skb);
+ ppp_receive_error(ppp);
+}
+
+/*
+ * Insert a fragment on the MP reconstruction queue.
+ * The queue is ordered by increasing sequence number.
+ */
+static void
+ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
+{
+ struct sk_buff *p;
+ struct sk_buff_head *list = &ppp->mrq;
+ u32 seq = skb->sequence;
+
+ /* N.B. we don't need to lock the list lock because we have the
+ ppp unit receive-side lock. */
+ for (p = list->next; p != (struct sk_buff *)list; p = p->next)
+ if (seq_before(seq, p->sequence))
+ break;
+ __skb_insert(skb, p->prev, p, list);
+}
+
+/*
+ * Reconstruct a packet from the MP fragment queue.
+ * We go through increasing sequence numbers until we find a
+ * complete packet, or we get to the sequence number for a fragment
+ * which hasn't arrived but might still do so.
+ */
+struct sk_buff *
+ppp_mp_reconstruct(struct ppp *ppp)
+{
+ u32 seq = ppp->nextseq;
+ u32 minseq = ppp->minseq;
+ struct sk_buff_head *list = &ppp->mrq;
+ struct sk_buff *p, *next;
+ struct sk_buff *head, *tail;
+ struct sk_buff *skb = NULL;
+ int lost = 0, len = 0;
+
+ head = list->next;
+ tail = NULL;
+ for (p = head; p != (struct sk_buff *) list; p = next) {
+ next = p->next;
+ if (seq_before(p->sequence, seq)) {
+ /* this can't happen, anyway toss the skb */
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+ p->sequence, seq);
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ continue;
+ }
+ if (p->sequence != seq) {
+ /* Fragment `seq' is missing. If it is after
+ minseq, it might arrive later, so stop here. */
+ if (seq_after(seq, minseq))
+ break;
+ /* Fragment `seq' is lost, keep going. */
+ lost = 1;
+ seq = seq_before(p->sequence, minseq)?
+ p->sequence: minseq;
+ next = p;
+ continue;
+ }
+
+ /*
+ * At this point we know that all the fragments from
+ * ppp->nextseq to seq are either present or lost.
+ * Also, there are no complete packets in the queue
+ * that have no missing fragments and end before this
+ * fragment.
+ */
+
+ /* B bit set indicates this fragment starts a packet */
+ if (p->BEbits & B) {
+ head = p;
+ lost = 0;
+ /* reset len, allow for protocol ID compression */
+ len = p->data[0] & 1;
+ }
+
+ len += p->len;
+
+ /* Got a complete packet yet? */
+ if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+ if (len > ppp->mrru) {
+ ++ppp->stats.rx_length_errors;
+ } else if ((skb = dev_alloc_skb(len)) == NULL) {
+ ++ppp->stats.rx_missed_errors;
+ } else {
+ tail = p;
+ break;
+ }
+ }
+
+ /*
+ * If this is the ending fragment of a packet,
+ * and we haven't found a complete valid packet yet,
+ * we can discard up to and including this fragment.
+ */
+ if (p->BEbits & E)
+ head = next;
+
+ ++seq;
+ }
+
+ /* If we have a complete packet, copy it all into one skb. */
+ if (tail != NULL) {
+ /* If we have discarded any fragments,
+ signal a receive error. */
+ if (head->sequence != ppp->nextseq)
+ ppp_receive_error(ppp);
+
+ /* uncompress protocol ID */
+ if (head->data[0] & 1)
+ *skb_put(skb, 1) = 0;
+ p = head;
+ for (;;) {
+ memcpy(skb_put(skb, p->len), p->data, p->len);
+ if (p == tail)
+ break;
+ p = p->next;
+ }
+ ppp->nextseq = tail->sequence + 1;
+ head = tail->next;
+ }
+
+ /* Discard all the skbuffs that we have copied the data out of
+ or that we can't use. */
+ while ((p = list->next) != head) {
+ __skb_unlink(p, list);
+ kfree_skb(p);
+ }
+
+ return skb;
+}
+#endif /* CONFIG_PPP_MULTILINK */
+
/*
* Channel interface.
*/
/*
- * Connect a channel to a given PPP unit.
- * The channel MUST NOT be connected to a PPP unit already.
+ * Create a new, unattached ppp channel.
*/
int
-ppp_register_channel(struct ppp_channel *chan, int unit)
+ppp_register_channel(struct ppp_channel *chan)
{
- struct ppp *ppp;
struct channel *pch;
- int ret = -ENXIO;
- spin_lock(&all_ppp_lock);
- ppp = ppp_find_unit(unit);
- if (ppp == 0)
- goto out;
pch = kmalloc(sizeof(struct channel), GFP_ATOMIC);
- ret = -ENOMEM;
if (pch == 0)
- goto out;
+ return -ENOMEM;
memset(pch, 0, sizeof(struct channel));
- pch->ppp = ppp;
+ pch->ppp = NULL;
pch->chan = chan;
- list_add(&pch->list, &ppp->channels);
chan->ppp = pch;
- ++ppp->n_channels;
- if (ppp->dev && chan->hdrlen + PPP_HDRLEN > ppp->dev->hard_header_len)
- ppp->dev->hard_header_len = chan->hdrlen + PPP_HDRLEN;
- ret = 0;
- out:
- spin_unlock(&all_ppp_lock);
- return ret;
+ init_ppp_file(&pch->file, CHANNEL);
+ pch->file.hdrlen = chan->hdrlen;
+ spin_lock_init(&pch->downl);
+ pch->upl = RW_LOCK_UNLOCKED;
+ spin_lock_bh(&all_channels_lock);
+ pch->file.index = ++last_channel_index;
+ list_add(&pch->file.list, &all_channels);
+ spin_unlock_bh(&all_channels_lock);
+ MOD_INC_USE_COUNT;
+ return 0;
}
/*
- * Disconnect a channel from its PPP unit.
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ return pch->file.index;
+}
+
+/*
+ * Disconnect a channel from the generic layer.
+ * This can be called from mainline or BH/softirq level.
*/
void
ppp_unregister_channel(struct ppp_channel *chan)
{
- struct channel *pch;
+ struct channel *pch = chan->ppp;
- spin_lock(&all_ppp_lock);
- if ((pch = chan->ppp) != 0) {
- chan->ppp = 0;
- list_del(&pch->list);
- --pch->ppp->n_channels;
- kfree(pch);
- }
- spin_unlock(&all_ppp_lock);
+ if (pch == 0)
+ return; /* should never happen */
+ chan->ppp = 0;
+
+ /*
+ * This ensures that we have returned from any calls into the
+ * the channel's start_xmit or ioctl routine before we proceed.
+ */
+ spin_lock_bh(&pch->downl);
+ pch->chan = 0;
+ spin_unlock_bh(&pch->downl);
+ ppp_disconnect_channel(pch);
+ wake_up_interruptible(&pch->file.rwait);
+ spin_lock_bh(&all_channels_lock);
+ list_del(&pch->file.list);
+ spin_unlock_bh(&all_channels_lock);
+ if (atomic_dec_and_test(&pch->file.refcnt))
+ ppp_destroy_channel(pch);
+ MOD_DEC_USE_COUNT;
}
/*
* Callback from a channel when it can accept more to transmit.
- * This should ideally be called at BH level, not interrupt level.
+ * This should be called at BH/softirq level, not interrupt level.
*/
void
ppp_output_wakeup(struct ppp_channel *chan)
@@ -1174,11 +1704,75 @@ ppp_output_wakeup(struct ppp_channel *chan)
if (pch == 0)
return;
- ppp = pch->ppp;
- pch->blocked = 0;
- set_bit(XMIT_WAKEUP, &ppp->busy);
- if (trylock_xmit_path(ppp))
- ppp_xmit_unlock(ppp, 1);
+ ppp_channel_push(pch);
+ if (skb_queue_len(&pch->file.xq) == 0) {
+ read_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0)
+ ppp_xmit_process(ppp, 1);
+ read_unlock_bh(&pch->upl);
+ }
+}
+
+/*
+ * This is basically temporary compatibility stuff.
+ */
+ssize_t
+ppp_channel_read(struct ppp_channel *chan, struct file *file,
+ char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_read(&pch->file, file, buf, count);
+}
+
+ssize_t
+ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
+{
+ struct channel *pch = chan->ppp;
+
+ if (pch == 0)
+ return -ENXIO;
+ return ppp_file_write(&pch->file, buf, count);
+}
+
+unsigned int
+ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ struct channel *pch = chan->ppp;
+
+ mask = POLLOUT | POLLWRNORM;
+ if (pch != 0) {
+ poll_wait(file, &pch->file.rwait, wait);
+ if (skb_peek(&pch->file.rq) != 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ return mask;
+}
+
+int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg)
+{
+ struct channel *pch = chan->ppp;
+ int err = -ENOTTY;
+ int unit;
+
+ if (pch == 0)
+ return -EINVAL;
+ switch (cmd) {
+ case PPPIOCATTACH:
+ if (get_user(unit, (int *) arg))
+ break;
+ err = ppp_connect_channel(pch, unit);
+ break;
+ case PPPIOCDETACH:
+ err = ppp_disconnect_channel(pch);
+ break;
+ }
+ return err;
}
/*
@@ -1192,6 +1786,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
int err;
struct compressor *cp;
struct ppp_option_data data;
+ void *state;
unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
#ifdef CONFIG_KMOD
char modname[32];
@@ -1220,34 +1815,41 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
err = -ENOBUFS;
if (data.transmit) {
- lock_xmit_path(ppp);
+ ppp_xmit_lock(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state != 0) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
-
- ppp->xcomp = cp;
- ppp->xc_state = cp->comp_alloc(ccp_option, data.length);
- ppp_xmit_unlock(ppp, 1);
- if (ppp->xc_state == 0)
- goto out;
+ ppp_xmit_unlock(ppp);
+
+ state = cp->comp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_xmit_lock(ppp);
+ ppp->xcomp = cp;
+ ppp->xc_state = state;
+ ppp_xmit_unlock(ppp);
+ err = 0;
+ }
} else {
- lock_recv_path(ppp);
+ ppp_recv_lock(ppp);
ppp->rstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state != 0) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
-
- ppp->rcomp = cp;
- ppp->rc_state = cp->decomp_alloc(ccp_option, data.length);
ppp_recv_unlock(ppp);
- if (ppp->rc_state == 0)
- goto out;
+
+ state = cp->decomp_alloc(ccp_option, data.length);
+ if (state != 0) {
+ ppp_recv_lock(ppp);
+ ppp->rcomp = cp;
+ ppp->rc_state = state;
+ ppp_recv_unlock(ppp);
+ err = 0;
+ }
}
- err = 0;
out:
return err;
@@ -1292,7 +1894,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->rc_state == 0)
break;
if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
- ppp->index, 0, ppp->mru, ppp->debug)) {
+ ppp->file.index, 0, ppp->mru, ppp->debug)) {
ppp->rstate |= SC_DECOMP_RUN;
ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
}
@@ -1301,7 +1903,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (ppp->xc_state == 0)
break;
if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
- ppp->index, 0, ppp->debug))
+ ppp->file.index, 0, ppp->debug))
ppp->xstate |= SC_COMP_RUN;
}
break;
@@ -1329,21 +1931,17 @@ ppp_ccp_closed(struct ppp *ppp)
{
ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
- lock_xmit_path(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
- ppp_xmit_unlock(ppp, 1);
- lock_recv_path(ppp);
ppp->xstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
- ppp_recv_unlock(ppp);
}
/* List of compressors. */
@@ -1451,16 +2049,17 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
}
/*
- * Stuff for handling the list of ppp units and for initialization.
+ * Stuff for handling the lists of ppp units and channels
+ * and for initialization.
*/
/*
- * Create a new ppp unit. Fails if it can't allocate memory or
- * if there is already a unit with the requested number.
+ * Create a new ppp interface unit. Fails if it can't allocate memory
+ * or if there is already a unit with the requested number.
* unit == -1 means allocate a new number.
*/
static struct ppp *
-ppp_create_unit(int unit, int *retp)
+ppp_create_interface(int unit, int *retp)
{
struct ppp *ppp;
struct net_device *dev;
@@ -1472,13 +2071,13 @@ ppp_create_unit(int unit, int *retp)
spin_lock(&all_ppp_lock);
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if ((unit < 0 && ppp->index > last_unit + 1)
- || (unit >= 0 && unit < ppp->index))
+ ppp = list_entry(list, struct ppp, file.list);
+ if ((unit < 0 && ppp->file.index > last_unit + 1)
+ || (unit >= 0 && unit < ppp->file.index))
break;
- if (unit == ppp->index)
+ if (unit == ppp->file.index)
goto out; /* unit already exists */
- last_unit = ppp->index;
+ last_unit = ppp->file.index;
}
if (unit < 0)
unit = last_unit + 1;
@@ -1496,17 +2095,15 @@ ppp_create_unit(int unit, int *retp)
}
memset(dev, 0, sizeof(struct net_device));
- ppp->index = unit;
+ ppp->file.index = unit;
sprintf(ppp->name, "ppp%d", unit);
ppp->mru = PPP_MRU;
- skb_queue_head_init(&ppp->xq);
- skb_queue_head_init(&ppp->rq);
- init_waitqueue_head(&ppp->rwait);
- ppp->refcnt = 1;
+ init_ppp_file(&ppp->file, INTERFACE);
for (i = 0; i < NUM_NP; ++i)
ppp->npmode[i] = NPMODE_PASS;
INIT_LIST_HEAD(&ppp->channels);
- skb_queue_head_init(&ppp->recv_pending);
+ spin_lock_init(&ppp->rlock);
+ spin_lock_init(&ppp->wlock);
ppp->dev = dev;
dev->init = ppp_net_init;
@@ -1524,7 +2121,7 @@ ppp_create_unit(int unit, int *retp)
goto out;
}
- list_add(&ppp->list, list->prev);
+ list_add(&ppp->file.list, list->prev);
out:
spin_unlock(&all_ppp_lock);
*retp = ret;
@@ -1534,56 +2131,59 @@ ppp_create_unit(int unit, int *retp)
}
/*
- * Remove a reference to a ppp unit, and destroy it if
- * the reference count goes to 0.
+ * Initialize a ppp_file structure.
*/
-static void ppp_release_unit(struct ppp *ppp)
+static void
+init_ppp_file(struct ppp_file *pf, int kind)
{
- struct list_head *list, *next;
- int ref;
+ pf->kind = kind;
+ skb_queue_head_init(&pf->xq);
+ skb_queue_head_init(&pf->rq);
+ atomic_set(&pf->refcnt, 1);
+ init_waitqueue_head(&pf->rwait);
+}
+
+/*
+ * Free up all the resources used by a ppp interface unit.
+ */
+static void ppp_destroy_interface(struct ppp *ppp)
+{
+ struct net_device *dev;
spin_lock(&all_ppp_lock);
- ref = --ppp->refcnt;
- if (ref == 0)
- list_del(&ppp->list);
- spin_unlock(&all_ppp_lock);
- if (ref != 0)
- return;
+ list_del(&ppp->file.list);
/* Last fd open to this ppp unit is being closed or detached:
mark the interface down, free the ppp unit */
- if (ppp->dev) {
- rtnl_lock();
- dev_close(ppp->dev);
- rtnl_unlock();
- }
- for (list = ppp->channels.next; list != &ppp->channels; list = next) {
- /* forcibly detach this channel */
- struct channel *chan;
- chan = list_entry(list, struct channel, list);
- chan->chan->ppp = 0;
- next = list->next;
- kfree(chan);
- }
-
- /* Free up resources. */
+ ppp_lock(ppp);
ppp_ccp_closed(ppp);
- lock_xmit_path(ppp);
- lock_recv_path(ppp);
if (ppp->vj) {
slhc_free(ppp->vj);
ppp->vj = 0;
}
- free_skbs(&ppp->xq);
- free_skbs(&ppp->rq);
- free_skbs(&ppp->recv_pending);
- if (ppp->dev) {
+ skb_queue_purge(&ppp->file.xq);
+ skb_queue_purge(&ppp->file.rq);
+ dev = ppp->dev;
+ ppp->dev = 0;
+ ppp_unlock(ppp);
+
+ if (dev) {
rtnl_lock();
- unregister_netdevice(ppp->dev);
- ppp->dev = 0;
+ dev_close(dev);
+ unregister_netdevice(dev);
rtnl_unlock();
}
- kfree(ppp);
+
+ /*
+ * We can't acquire any new channels (since we have the
+ * all_ppp_lock) so if n_channels is 0, we can free the
+ * ppp structure. Otherwise we leave it around until the
+ * last channel disconnects from it.
+ */
+ if (ppp->n_channels == 0)
+ kfree(ppp);
+
+ spin_unlock(&all_ppp_lock);
}
/*
@@ -1598,32 +2198,136 @@ ppp_find_unit(int unit)
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
- ppp = list_entry(list, struct ppp, list);
- if (ppp->index == unit)
+ ppp = list_entry(list, struct ppp, file.list);
+ if (ppp->file.index == unit)
return ppp;
}
return 0;
}
/*
- * Module stuff.
+ * Locate an existing ppp channel.
+ * The caller should have locked the all_channels_lock.
*/
-#ifdef MODULE
-int
-init_module(void)
+static struct channel *
+ppp_find_channel(int unit)
{
- ppp_init();
+ struct channel *pch;
+ struct list_head *list;
+
+ list = &all_channels;
+ while ((list = list->next) != &all_channels) {
+ pch = list_entry(list, struct channel, file.list);
+ if (pch->file.index == unit)
+ return pch;
+ }
return 0;
}
-void
-cleanup_module(void)
+/*
+ * Connect a PPP channel to a PPP interface unit.
+ */
+static int
+ppp_connect_channel(struct channel *pch, int unit)
+{
+ struct ppp *ppp;
+ int ret = -ENXIO;
+ int hdrlen;
+
+ spin_lock(&all_ppp_lock);
+ ppp = ppp_find_unit(unit);
+ if (ppp == 0)
+ goto out;
+ write_lock_bh(&pch->upl);
+ ret = -EINVAL;
+ if (pch->ppp != 0)
+ goto outw;
+ ppp_lock(ppp);
+ spin_lock_bh(&pch->downl);
+ if (pch->chan == 0) /* need to check this?? */
+ goto outr;
+
+ hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
+ if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+ ppp->dev->hard_header_len = hdrlen;
+ list_add(&pch->clist, &ppp->channels);
+ ++ppp->n_channels;
+ pch->ppp = ppp;
+ ret = 0;
+
+ outr:
+ spin_unlock_bh(&pch->downl);
+ ppp_unlock(ppp);
+ outw:
+ write_unlock_bh(&pch->upl);
+ out:
+ spin_unlock(&all_ppp_lock);
+ return ret;
+}
+
+/*
+ * Disconnect a channel from its ppp unit.
+ */
+static int
+ppp_disconnect_channel(struct channel *pch)
+{
+ struct ppp *ppp;
+ int err = -EINVAL;
+
+ write_lock_bh(&pch->upl);
+ ppp = pch->ppp;
+ if (ppp != 0) {
+ /* remove it from the ppp unit's list */
+ pch->ppp = NULL;
+ ppp_lock(ppp);
+ list_del(&pch->clist);
+ --ppp->n_channels;
+ if (ppp->dev == 0 && ppp->n_channels == 0)
+ /* Last disconnect from a ppp unit
+ that is already dead: free it. */
+ kfree(ppp);
+ else
+ ppp_unlock(ppp);
+ err = 0;
+ }
+ write_unlock_bh(&pch->upl);
+ return err;
+}
+
+/*
+ * Free up the resources used by a ppp channel.
+ */
+static void ppp_destroy_channel(struct channel *pch)
+{
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ kfree(pch);
+}
+
+void __exit ppp_cleanup(void)
{
/* should never happen */
- if (!list_empty(&all_ppp_units))
+ if (!list_empty(&all_ppp_units) || !list_empty(&all_channels))
printk(KERN_ERR "PPP: removing module but units remain!\n");
if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
printk(KERN_ERR "PPP: failed to unregister PPP device\n");
- devfs_unregister (devfs_handle);
+ devfs_unregister(devfs_handle);
}
-#endif /* MODULE */
+
+module_init(ppp_init);
+module_exit(ppp_cleanup);
+
+EXPORT_SYMBOL(ppp_register_channel);
+EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_input);
+EXPORT_SYMBOL(ppp_input_error);
+EXPORT_SYMBOL(ppp_output_wakeup);
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
+EXPORT_SYMBOL(ppp_channel_read);
+EXPORT_SYMBOL(ppp_channel_write);
+EXPORT_SYMBOL(ppp_channel_poll);
+EXPORT_SYMBOL(ppp_channel_ioctl);
+EXPORT_SYMBOL(all_ppp_units); /* for debugging */
+EXPORT_SYMBOL(all_channels); /* for debugging */
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index 1a69f6ede..8bae76e1c 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -439,7 +439,7 @@ ppp_sync_ioctl(struct tty_struct *tty, struct file *file,
break;
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
- err = ppp_register_channel(&ap->chan, val);
+ err = ppp_register_channel(&ap->chan);
if (err != 0)
break;
ap->connected = 1;
diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c
index 27b0c6474..e19f1e0fd 100644
--- a/drivers/net/rcpci45.c
+++ b/drivers/net/rcpci45.c
@@ -1034,6 +1034,9 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
printk("RCioctl: cmd = 0x%x\n", cmd);
#endif
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
switch (cmd) {
case RCU_PROTOCOL_REV:
@@ -1157,14 +1160,14 @@ static int RCioctl(struct net_device *dev, struct ifreq *rq, int cmd)
RCUD_DEFAULT -> rc = 0x11223344;
break;
}
- copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser));
+ if(copy_to_user(rq->ifr_data, &RCuser, sizeof(RCuser)))
+ return -EFAULT;
break;
- } /* RCU_COMMAND */
+ } /* RCU_COMMAND */
- default:
- printk("RC default\n");
- rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
- break;
+ default:
+ rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+ return -EINVAL;
}
return 0;
}
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index a3a0018cb..8e8236e92 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -76,8 +76,8 @@ static inline void netif_start_queue(struct net_device *dev)
#else
#define NET_BH 0
#define rr_mark_net_bh(foo) {do{} while(0);}
-#define rr_if_busy(dev) test_bit(LINK_STATE_XOFF, &dev->state)
-#define rr_if_running(dev) test_bit(LINK_STATE_START, &dev->state)
+#define rr_if_busy(dev) netif_queue_stopped(dev)
+#define rr_if_running(dev) netif_running(dev)
#define rr_if_down(dev) {do{} while(0);}
#endif
@@ -1550,7 +1550,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
switch(cmd){
case SIOCRRGFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
@@ -1582,7 +1582,7 @@ static int rr_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
kfree(image);
break;
case SIOCRRPFW:
- if (!suser()){
+ if (!capable(CAP_SYS_RAWIO)){
error = -EPERM;
goto out;
}
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index 9b17cc79d..bcf6e7e5b 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -1052,7 +1052,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMFREQUENCY: /* set frequency */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(get_user(frequency, (int*) ifr->ifr_data))
return -EFAULT;
@@ -1068,7 +1068,7 @@ static int sb1000_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSCMPIDS: /* set PIDs */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if(copy_from_user(PID, ifr->ifr_data, sizeof(PID)))
return -EFAULT;
diff --git a/drivers/net/setup.c b/drivers/net/setup.c
index d9019b046..f8f59b9fb 100644
--- a/drivers/net/setup.c
+++ b/drivers/net/setup.c
@@ -28,6 +28,7 @@ extern int dlci_setup(void);
extern int lapbeth_init(void);
extern int sdla_setup(void);
extern int sdla_c_setup(void);
+extern int comx_init(void);
extern int abyss_probe(void);
extern int madgemc_probe(void);
@@ -75,7 +76,9 @@ struct net_probe pci_probes[] __initdata = {
#if defined(CONFIG_8xx)
{cpm_enet_init, 0},
#endif
- /*
+#if defined(CONFIG_COMX)
+ {comx_init(), 0},
+#endif /*
* SLHC if present needs attaching so other people see it
* even if not opened.
*/
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index 89cbb0e9e..9190b5a87 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -480,6 +480,8 @@ static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev,
}
#endif
+#ifdef CONFIG_INET
+
static int shaper_neigh_setup(struct neighbour *n)
{
if (n->nud_state == NUD_NONE) {
@@ -499,6 +501,15 @@ static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
return 0;
}
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+ return 0;
+}
+
+#endif
+
static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
{
sh->dev = dev;
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index f8af277ea..2653270c3 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1127,7 +1127,7 @@ static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c
index e1e48bc91..93533c5ef 100644
--- a/drivers/net/skfp/drvfbi.c
+++ b/drivers/net/skfp/drvfbi.c
@@ -129,7 +129,7 @@ extern int AIX_vpdReadByte() ;
/*
* FDDI card reset
*/
-void card_start(smc)
+static void card_start(smc)
struct s_smc *smc ;
{
int i ;
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index 6aceec4d5..b6b112318 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1249,7 +1249,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len);
break;
case SKFP_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
memset(&lp->MacStat, 0, sizeof(lp->MacStat));
} else {
status = -EPERM;
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 344e682f8..059d4a4ea 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.92 2000/02/18 13:49:22 davem Exp $
+/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -254,7 +254,7 @@ do { (__txd)->tx_addr = (__addr); \
#define hme_read_desc32(__hp, __p) (*(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
-#define hme_dma_unmap(__hp, __addr, __size) \
+#define hme_dma_unmap(__hp, __addr, __size, __dir) \
sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
#define hme_dma_sync(__hp, __addr, __size, __dir) \
sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir))
diff --git a/drivers/net/tokenring/Config.in b/drivers/net/tokenring/Config.in
index 31688f34d..316b638c2 100644
--- a/drivers/net/tokenring/Config.in
+++ b/drivers/net/tokenring/Config.in
@@ -9,6 +9,7 @@ bool 'Token Ring driver support' CONFIG_TR
if [ "$CONFIG_TR" != "n" ]; then
dep_tristate ' IBM Tropic chipset based adapter support' CONFIG_IBMTR $CONFIG_TR
dep_tristate ' IBM Olympic chipset PCI adapter support' CONFIG_IBMOL $CONFIG_TR
+ dep_tristate ' IBM Lanstreamer chipset PCI adapter support' CONFIG_IBMLS $CONFIG_TR
dep_tristate ' Generic TMS380 Token Ring ISA/PCI adapter support' CONFIG_TMS380TR $CONFIG_TR
if [ "$CONFIG_TMS380TR" != "n" ]; then
dep_tristate ' Generic TMS380 PCI support' CONFIG_TMSPCI $CONFIG_TMS380TR
diff --git a/drivers/net/tokenring/Makefile b/drivers/net/tokenring/Makefile
index f90055b45..1fa4547c4 100644
--- a/drivers/net/tokenring/Makefile
+++ b/drivers/net/tokenring/Makefile
@@ -18,6 +18,7 @@ export-objs := tms380tr.o
obj-$(CONFIG_IBMTR) += ibmtr.o
obj-$(CONFIG_IBMOL) += olympic.o
+obj-$(CONFIG_IBMLS) += lanstreamer.o
obj-$(CONFIG_TMS380TR) += tms380tr.o
obj-$(CONFIG_ABYSS) += abyss.o
obj-$(CONFIG_MADGEMC) += madgemc.o
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
new file mode 100644
index 000000000..e0a339bfa
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -0,0 +1,1776 @@
+/*
+ * lanstreamer.c -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ * 03/03/00 - Merged to kernel, indented -kr -i8 -bri0, fixed some missing
+ * malloc free checks, reviewed code. <alan@redhat.com>
+ *
+ * To Do:
+ *
+ * 1) Test Network Monitor Mode
+ * 2) Add auto reset logic on adapter errors
+ * 3) Test with varying options
+ *
+ * If Problems do Occur
+ * Most problems can be rectified by either closing and opening the interface
+ * (ifconfig down and up) or rmmod and insmod'ing the driver (a bit difficult
+ * if compiled into the kernel).
+ */
+
+/* Change STREAMER_DEBUG to 1 to get verbose, and I mean really verbose, messages */
+
+#define STREAMER_DEBUG 0
+#define STREAMER_DEBUG_PACKETS 0
+
+/* Change STREAMER_NETWORK_MONITOR to receive mac frames through the arb channel.
+ * Will also create a /proc/net/streamer_tr entry if proc_fs is compiled into the
+ * kernel.
+ * Intended to be used to create a ring-error reporting network module
+ * i.e. it will give you the source address of beaconers on the ring
+ */
+
+#define STREAMER_NETWORK_MONITOR 0
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/ptrace.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <net/checksum.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include "lanstreamer.h"
+
+/* I've got to put some intelligence into the version number so that Peter and I know
+ * which version of the code somebody has got.
+ * Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author.
+ * So 0.0.1.pds = Peter, 0.0.1.mlp = Mike
+ *
+ * Official releases will only have an a.b.c version number format.
+ */
+
+static char *version = "LanStreamer.c v0.1.0 12/10/99 - Mike Sullivan";
+
+static char *open_maj_error[] = {
+ "No error", "Lobe Media Test", "Physical Insertion",
+ "Address Verification", "Neighbor Notification (Ring Poll)",
+ "Request Parameters", "FDX Registration Request",
+ "FDX Lobe Media Test", "FDX Duplicate Address Check",
+ "Unknown stage"
+};
+
+static char *open_min_error[] = {
+ "No error", "Function Failure", "Signal Lost", "Wire Fault",
+ "Ring Speed Mismatch", "Timeout", "Ring Failure", "Ring Beaconing",
+ "Duplicate Node Address", "Request Parameters", "Remove Received",
+ "Reserved", "Reserved", "No Monitor Detected for RPL",
+ "Monitor Contention failer for RPL", "FDX Protocol Error"
+};
+
+/* Module paramters */
+
+/* Ring Speed 0,4,16
+ * 0 = Autosense
+ * 4,16 = Selected speed only, no autosense
+ * This allows the card to be the first on the ring
+ * and become the active monitor.
+ *
+ * WARNING: Some hubs will allow you to insert
+ * at the wrong speed
+ */
+
+static int ringspeed[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(ringspeed, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Packet buffer size */
+
+static int pkt_buf_sz[STREAMER_MAX_ADAPTERS] = { 0, };
+
+MODULE_PARM(pkt_buf_sz, "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+/* Message Level */
+
+static int message_level[STREAMER_MAX_ADAPTERS] = { 1, };
+
+MODULE_PARM(message_level,
+ "1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");
+
+static int streamer_scan(struct net_device *dev);
+static int streamer_init(struct net_device *dev);
+static int streamer_open(struct net_device *dev);
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev);
+static int streamer_close(struct net_device *dev);
+static void streamer_set_rx_mode(struct net_device *dev);
+static void streamer_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs);
+static struct net_device_stats *streamer_get_stats(struct net_device *dev);
+static int streamer_set_mac_address(struct net_device *dev, void *addr);
+static void streamer_arb_cmd(struct net_device *dev);
+static int streamer_change_mtu(struct net_device *dev, int mtu);
+static void streamer_srb_bh(struct net_device *dev);
+static void streamer_asb_bh(struct net_device *dev);
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int sprintf_info(char *buffer, struct net_device *dev);
+#endif
+#endif
+
+int __init streamer_probe(struct net_device *dev)
+{
+ int cards_found;
+
+ cards_found = streamer_scan(dev);
+ return cards_found ? 0 : -ENODEV;
+}
+
+static int __init streamer_scan(struct net_device *dev)
+{
+ struct pci_dev *pci_device = NULL;
+ struct streamer_private *streamer_priv;
+ int card_no = 0;
+ if (pci_present())
+ {
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+ pci_set_master(pci_device);
+
+ /* Check to see if io has been allocated, if so, we've already done this card,
+ so continue on the card discovery loop */
+
+ if (check_region(pci_device->resource[0].start, STREAMER_IO_SPACE))
+ {
+ card_no++;
+ continue;
+ }
+
+ streamer_priv = kmalloc(sizeof(struct streamer_private), GFP_KERNEL);
+ if(streamer_priv==NULL)
+ {
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+ memset(streamer_priv, 0, sizeof(struct streamer_private));
+#ifndef MODULE
+ dev = init_trdev(dev, 0);
+ if(dev==NULL)
+ {
+ kfree(streamer_priv);
+ printk(KERN_ERR "lanstreamer: out of memory.\n");
+ break;
+ }
+#endif
+ dev->priv = (void *) streamer_priv;
+#if STREAMER_DEBUG
+ printk("pci_device: %p, dev:%p, dev->priv: %p\n",
+ pci_device, dev, dev->priv);
+#endif
+ dev->irq = pci_device->irq;
+ dev->base_addr = pci_device->resource[0].start;
+ dev->init = &streamer_init;
+ streamer_priv->streamer_mmio = ioremap(pci_device->resource[1].start, 256);
+ init_waitqueue_head(&streamer_priv->srb_wait);
+ init_waitqueue_head(&streamer_priv->trb_wait);
+ if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000))
+ streamer_priv->pkt_buf_sz = PKT_BUF_SZ;
+ else
+ streamer_priv->pkt_buf_sz = pkt_buf_sz[card_no];
+
+ streamer_priv->streamer_ring_speed = ringspeed[card_no];
+ streamer_priv->streamer_message_level = message_level[card_no];
+ streamer_priv->streamer_multicast_set = 0;
+
+ if (streamer_init(dev) == -1) {
+ unregister_netdevice(dev);
+ kfree(dev->priv);
+ return 0;
+ }
+
+ dev->open = &streamer_open;
+ dev->hard_start_xmit = &streamer_xmit;
+ dev->change_mtu = &streamer_change_mtu;
+
+ dev->stop = &streamer_close;
+ dev->do_ioctl = NULL;
+ dev->set_multicast_list = &streamer_set_rx_mode;
+ dev->get_stats = &streamer_get_stats;
+ dev->set_mac_address = &streamer_set_mac_address;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int __init streamer_init(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ __u8 *streamer_mmio;
+ unsigned long t;
+ unsigned int uaa_addr;
+ struct sk_buff *skb = 0;
+ __u16 misr;
+
+ streamer_priv = (struct streamer_private *) dev->priv;
+ streamer_mmio = streamer_priv->streamer_mmio;
+
+ printk("%s \n", version);
+ printk(KERN_INFO "%s: IBM PCI tokenring card. I/O at %hx, MMIO at %p, using irq %d\n",
+ dev->name, (unsigned int) dev->base_addr,
+ streamer_priv->streamer_mmio, dev->irq);
+
+ request_region(dev->base_addr, STREAMER_IO_SPACE, "streamer");
+ writew(readw(streamer_mmio + BCTL) | BCTL_SOFTRESET, streamer_mmio + BCTL);
+ t = jiffies;
+ /* Hold soft reset bit for a while */
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ);
+
+ writew(readw(streamer_mmio + BCTL) & ~BCTL_SOFTRESET,
+ streamer_mmio + BCTL);
+
+#if STREAMER_DEBUG
+ printk("BCTL: %x\n", readw(streamer_mmio + BCTL));
+ printk("GPR: %x\n", readw(streamer_mmio + GPR));
+ printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+ if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */
+ writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE,
+ streamer_mmio + GPR);
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Ringspeed autosense mode on\n",
+ dev->name);
+ } else if (streamer_priv->streamer_ring_speed == 16) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 16 Mbps as requested\n",
+ dev->name);
+ writew(GPR_16MBPS, streamer_mmio + GPR);
+ } else if (streamer_priv->streamer_ring_speed == 4) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Trying to open at 4 Mbps as requested\n",
+ dev->name);
+ writew(0, streamer_mmio + GPR);
+ }
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (!skb) {
+ printk(KERN_INFO "%s: skb allocation for diagnostics failed...proceeding\n",
+ dev->name);
+ } else {
+ streamer_priv->streamer_rx_ring[0].forward = 0;
+ streamer_priv->streamer_rx_ring[0].status = 0;
+ streamer_priv->streamer_rx_ring[0].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[0].framelen_buflen = 512; /* streamer_priv->pkt_buf_sz; */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ }
+
+#if STREAMER_DEBUG
+ printk("GPR = %x\n", readw(streamer_mmio + GPR));
+#endif
+ /* start solo init */
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+
+ while (!((readw(streamer_mmio + SISR)) & SISR_SRB_REPLY)) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+ if (jiffies - t > 40 * HZ) {
+ printk(KERN_ERR
+ "IBM PCI tokenring card not responding\n");
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+ }
+ writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (skb)
+ dev_kfree_skb(skb); /* release skb used for diagnostics */
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x LAPE: %x\n",
+ readw(streamer_mmio + LAPWWO), readw(streamer_mmio + LAPA),
+ readw(streamer_mmio + LAPE));
+#endif
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("initialization response srb dump: ");
+ for (i = 0; i < 10; i++)
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ printk("\n");
+ }
+#endif
+
+ writew(readw(streamer_mmio + LAPWWO) + 6, streamer_mmio + LAPA);
+ if (readw(streamer_mmio + LAPD)) {
+ printk(KERN_INFO "tokenring card intialization failed. errorcode : %x\n",
+ readw(streamer_mmio + LAPD));
+ release_region(dev->base_addr, STREAMER_IO_SPACE);
+ return -1;
+ }
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+ uaa_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* skip over Level.Addr field */
+ streamer_priv->streamer_addr_table_addr = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->streamer_parms_addr = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ printk("UAA resides at %x\n", uaa_addr);
+#endif
+
+ /* setup uaa area for access with LAPD */
+ writew(uaa_addr, streamer_mmio + LAPA);
+
+ /* setup uaa area for access with LAPD */
+ {
+ int i;
+ __u16 addr;
+ writew(uaa_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 6; i += 2) {
+ addr = readw(streamer_mmio + LAPDINC);
+ dev->dev_addr[i] = addr & 0xff;
+ dev->dev_addr[i + 1] = (addr >> 8) & 0xff;
+ }
+#if STREAMER_DEBUG
+ printk("Adapter address: ");
+ for (i = 0; i < 6; i++) {
+ printk("%02x:", dev->dev_addr[i]);
+ }
+ printk("\n");
+#endif
+ }
+ return 0;
+}
+
+static int streamer_open(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ char open_error[255];
+ int i, open_finished = 1;
+ __u16 srb_word;
+ __u16 srb_open;
+
+
+ if (request_irq(dev->irq, &streamer_interrupt, SA_SHIRQ, "streamer", dev)) {
+ return -EAGAIN;
+ }
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("pending ints: %x\n", readw(streamer_mmio + SISR));
+#endif
+
+ writew(SISR_MI | SISR_SRB_REPLY, streamer_mmio + SISR_MASK); /* more ints later, doesn't stop arb cmd interrupt */
+ writew(LISR_LIE, streamer_mmio + LISR); /* more ints later */
+
+ /* adapter is closed, so SRB is pointed to by LAPWWO */
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+
+#if STREAMER_DEBUG
+ printk("LAPWWO: %x, LAPA: %x\n", readw(streamer_mmio + LAPWWO),
+ readw(streamer_mmio + LAPA));
+ printk("LAPE: %x\n", readw(streamer_mmio + LAPE));
+ printk("SISR Mask = %04x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+ do {
+ int i;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < SRB_COMMAND_SIZE; i += 2) {
+ writew(0, streamer_mmio + LAPDINC);
+ }
+
+ writew(readw(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ writew(SRB_OPEN_ADAPTER, streamer_mmio + LAPDINC); /* open */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ writew(readw(streamer_mmio + LAPWWO) + 8, streamer_mmio + LAPA);
+#if STREAMER_NETWORK_MONITOR
+ /* If Network Monitor, instruct card to copy MAC frames through the ARB */
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON), streamer_mmio + LAPDINC); /* offset 8 word contains open options */
+#else
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX), streamer_mmio + LAPDINC); /* Offset 8 word contains Open.Options */
+#endif
+
+ if (streamer_priv->streamer_laa[0]) {
+ writew(readw(streamer_mmio + LAPWWO) + 12, streamer_mmio + LAPA);
+ writew(((__u16 *) (streamer_priv->streamer_laa))[0], streamer_mmio + LAPDINC); /* offset 12 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[2], streamer_mmio + LAPDINC); /* offset 14 word */
+ writew(((__u16 *) (streamer_priv->streamer_laa))[4], streamer_mmio + LAPDINC); /* offset 16 word */
+ memcpy(dev->dev_addr, streamer_priv->streamer_laa, dev->addr_len);
+ }
+
+ /* save off srb open offset */
+ srb_open = readw(streamer_mmio + LAPWWO);
+#if STREAMER_DEBUG
+ writew(readw(streamer_mmio + LAPWWO),
+ streamer_mmio + LAPA);
+ printk("srb open request: \n");
+ for (i = 0; i < 16; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+
+ streamer_priv->srb_queued = 1;
+
+ /* signal solo that SRB command has been issued */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued) {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ);
+ if (signal_pending(current)) {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x, LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+ restore_flags(flags);
+
+#if STREAMER_DEBUG
+ printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+ printk("srb open response:\n");
+ writew(srb_open, streamer_mmio + LAPA);
+ for (i = 0; i < 10; i++) {
+ printk("%x:",
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+#endif
+
+ /* If we get the same return response as we set, the interrupt wasn't raised and the open
+ * timed out.
+ */
+ writew(srb_open + 2, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word == STREAMER_CLEAR_RET_CODE) {
+ printk(KERN_WARNING "%s: Adapter Open time out or error.\n",
+ dev->name);
+ return -EIO;
+ }
+
+ if (srb_word != 0) {
+ if (srb_word == 0x07) {
+ if (!streamer_priv->streamer_ring_speed && open_finished) { /* Autosense , first time around */
+ printk(KERN_WARNING "%s: Retrying at different ring speed \n",
+ dev->name);
+ open_finished = 0;
+ } else {
+ __u16 error_code;
+
+ writew(srb_open + 6, streamer_mmio + LAPA);
+ error_code = ntohs(readw(streamer_mmio + LAPD));
+ strcpy(open_error, open_maj_error[(error_code & 0xf0) >> 4]);
+ strcat(open_error, " - ");
+ strcat(open_error, open_min_error[(error_code & 0x0f)]);
+
+ if (!streamer_priv->streamer_ring_speed
+ && ((error_code & 0x0f) == 0x0d))
+ {
+ printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name);
+ printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ printk(KERN_WARNING "%s: %s\n",
+ dev->name, open_error);
+ free_irq(dev->irq, dev);
+ return -EIO;
+
+ } /* if autosense && open_finished */
+ } else {
+ printk(KERN_WARNING "%s: Bad OPEN response: %x\n",
+ dev->name, srb_word);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+ } else
+ open_finished = 1;
+ } while (!(open_finished)); /* Will only loop if ring speed mismatch re-open attempted && autosense is on */
+
+ writew(srb_open + 18, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPD) & 0xFF;
+ if (srb_word & (1 << 3))
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in FDX Mode\n", dev->name);
+
+ if (srb_word & 1)
+ streamer_priv->streamer_ring_speed = 16;
+ else
+ streamer_priv->streamer_ring_speed = 4;
+
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Opened in %d Mbps mode\n",
+ dev->name,
+ streamer_priv->streamer_ring_speed);
+
+ writew(srb_open + 8, streamer_mmio + LAPA);
+ streamer_priv->asb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->srb = ntohs(readw(streamer_mmio + LAPDINC));
+ streamer_priv->arb = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* offset 14 word is rsvd */
+ streamer_priv->trb = ntohs(readw(streamer_mmio + LAPDINC));
+
+ streamer_priv->streamer_receive_options = 0x00;
+ streamer_priv->streamer_copy_all_options = 0;
+
+ /* setup rx ring */
+ /* enable rx channel */
+ writew(~BMCTL_RX_DIS, streamer_mmio + BMCTL_RUM);
+
+ /* setup rx descriptors */
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ if (skb == NULL)
+ break;
+
+ skb->dev = dev;
+
+ streamer_priv->streamer_rx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_rx_ring[i + 1]);
+ streamer_priv->streamer_rx_ring[i].status = 0;
+ streamer_priv->streamer_rx_ring[i].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_rx_ring[i].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->rx_ring_skb[i] = skb;
+ }
+ streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_rx_ring[0]);
+
+ if (i == 0) {
+ printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled\n", dev->name);
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+
+ streamer_priv->rx_ring_last_received = STREAMER_RX_RING_SIZE - 1; /* last processed rx status */
+
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[0]), streamer_mmio + RXBDA);
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[STREAMER_RX_RING_SIZE - 1]), streamer_mmio + RXLBDA);
+
+ /* set bus master interrupt event mask */
+ writew(MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+
+
+ /* setup tx ring */
+ writew(~BMCTL_TX2_DIS, streamer_mmio + BMCTL_RUM); /* Enables TX channel 2 */
+ for (i = 0; i < STREAMER_TX_RING_SIZE; i++) {
+ streamer_priv->streamer_tx_ring[i].forward = virt_to_bus(&streamer_priv->streamer_tx_ring[i + 1]);
+ streamer_priv->streamer_tx_ring[i].status = 0;
+ streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[i].buffer = 0;
+ streamer_priv->streamer_tx_ring[i].buflen = 0;
+ }
+ streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward =
+ virt_to_bus(&streamer_priv->streamer_tx_ring[0]);;
+
+ streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE;
+ streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */
+ streamer_priv->tx_ring_last_status = STREAMER_TX_RING_SIZE - 1;
+
+ /* set Busmaster interrupt event mask (handle receives on interrupt only */
+ writew(MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF, streamer_mmio + MISR_MASK);
+ /* set system event interrupt mask */
+ writew(SISR_ADAPTER_CHECK | SISR_ARB_CMD | SISR_TRB_REPLY | SISR_ASB_FREE, streamer_mmio + SISR_MASK_SUM);
+
+#if STREAMER_DEBUG
+ printk("BMCTL: %x\n", readw(streamer_mmio + BMCTL_SUM));
+ printk("SISR MASK: %x\n", readw(streamer_mmio + SISR_MASK));
+#endif
+
+#if STREAMER_NETWORK_MONITOR
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ printk("%s: Node Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+ readw(streamer_mmio + LAPDINC);
+ readw(streamer_mmio + LAPDINC);
+ printk("%s: Functional Address: %04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+
+ writew(streamer_priv->streamer_parms_addr + 4,
+ streamer_mmio + LAPA);
+ printk("%s: NAUN Address: %04x:%04x:%04x\n", dev->name,
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)),
+ ntohs(readw(streamer_mmio + LAPDINC)));
+#endif
+
+ netif_start_queue(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * When we enter the rx routine we do not know how many frames have been
+ * queued on the rx channel. Therefore we start at the next rx status
+ * position and travel around the receive ring until we have completed
+ * all the frames.
+ *
+ * This means that we may process the frame before we receive the end
+ * of frame interrupt. This is why we always test the status instead
+ * of blindly processing the next frame.
+ *
+ */
+static void streamer_rx(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_rx_desc *rx_desc;
+ int rx_ring_last_received, length, frame_length, buffer_cnt = 0;
+ struct sk_buff *skb, *skb2;
+
+ /* setup the next rx descriptor to be received */
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ while (rx_desc->status & 0x01000000) { /* While processed descriptors are available */
+ if (rx_ring_last_received != streamer_priv->rx_ring_last_received)
+ {
+ printk(KERN_WARNING "RX Error 1 rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received, streamer_priv->rx_ring_last_received);
+ }
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ frame_length = (rx_desc->framelen_buflen >> 16) & 0xffff;
+
+ if (rx_desc->status & 0x7E830000) { /* errors */
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_WARNING "%s: Rx Error %x \n",
+ dev->name, rx_desc->status);
+ }
+ } else { /* received without errors */
+ if (rx_desc->status & 0x80000000) { /* frame complete */
+ buffer_cnt = 1;
+ skb = dev_alloc_skb(streamer_priv->pkt_buf_sz);
+ } else {
+ skb = dev_alloc_skb(frame_length);
+ }
+
+ if (skb == NULL)
+ {
+ printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name);
+ streamer_priv->streamer_stats.rx_dropped++;
+ } else { /* we allocated an skb OK */
+ skb->dev = dev;
+
+ if (buffer_cnt == 1) {
+ skb2 = streamer_priv->rx_ring_skb[rx_ring_last_received];
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_rx packet print: skb->data2 %p skb->head %p\n", skb2->data, skb2->head);
+ for (i = 0; i < frame_length; i++)
+ {
+ printk("%x:", skb2->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+ skb_put(skb2, length);
+ skb2->protocol = tr_type_trans(skb2, dev);
+ /* recycle this descriptor */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ streamer_priv-> rx_ring_skb[rx_ring_last_received] = skb;
+ /* place recycled descriptor back on the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]),streamer_mmio + RXLBDA);
+ /* pass the received skb up to the protocol */
+ netif_rx(skb2);
+ } else {
+ do { /* Walk the buffers */
+ memcpy(skb_put(skb, length),bus_to_virt(rx_desc->buffer), length); /* copy this fragment */
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].status = 0;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].framelen_buflen = streamer_priv->pkt_buf_sz;
+ streamer_priv->streamer_rx_ring[rx_ring_last_received].buffer = virt_to_bus(skb->data);
+ /* give descriptor back to the adapter */
+ writel(virt_to_bus(&streamer_priv->streamer_rx_ring[rx_ring_last_received]), streamer_mmio + RXLBDA);
+
+ if (rx_desc->status & 0x80000000)
+ break; /* this descriptor completes the frame */
+
+ /* else get the next pending descriptor */
+ if (rx_ring_last_received!= streamer_priv->rx_ring_last_received)
+ {
+ printk("RX Error rx_ring_last_received not the same %x %x\n",
+ rx_ring_last_received,
+ streamer_priv->rx_ring_last_received);
+ }
+ rx_desc = &streamer_priv->streamer_rx_ring[(streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE-1)];
+
+ length = rx_desc->framelen_buflen & 0xffff; /* buffer length */
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received+1) & (STREAMER_RX_RING_SIZE - 1);
+ rx_ring_last_received = streamer_priv->rx_ring_last_received;
+ } while (1);
+
+ skb->protocol = tr_type_trans(skb, dev);
+ /* send up to the protocol */
+ netif_rx(skb);
+ }
+ streamer_priv->streamer_stats.rx_packets++;
+ streamer_priv->streamer_stats.rx_bytes += length;
+ } /* if skb == null */
+ } /* end received without errors */
+
+ /* try the next one */
+ rx_desc = &streamer_priv->streamer_rx_ring[(rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1)];
+ } /* end for all completed rx descriptors */
+}
+
+static void streamer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 sisr;
+ __u16 misr;
+ __u16 sisrmask;
+
+ sisrmask = SISR_MI;
+ writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
+ sisr = readw(streamer_mmio + SISR);
+ writew(~sisr, streamer_mmio + SISR_RUM);
+ misr = readw(streamer_mmio + MISR_RUM);
+ writew(~misr, streamer_mmio + MISR_RUM);
+
+ if (!sisr) { /* Interrupt isn't for us */
+ return;
+ }
+
+ if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
+ || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
+ if (sisr & SISR_SRB_REPLY) {
+ if (streamer_priv->srb_queued == 1) {
+ wake_up_interruptible(&streamer_priv->srb_wait);
+ } else if (streamer_priv->srb_queued == 2) {
+ streamer_srb_bh(dev);
+ }
+ streamer_priv->srb_queued = 0;
+ }
+ /* SISR_SRB_REPLY */
+ if (misr & MISR_TX2_EOF) {
+ while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status)
+ {
+ streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1);
+ streamer_priv->free_tx_ring_entries++;
+ streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len;
+ streamer_priv->streamer_stats.tx_packets++;
+ dev_kfree_skb_irq(streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]);
+ streamer_priv-> streamer_tx_ring[streamer_priv->tx_ring_last_status].buffer = 0xdeadbeef;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0;
+ }
+ netif_wake_queue(dev);
+ }
+
+ if (misr & MISR_RX_EOF) {
+ streamer_rx(dev);
+ }
+ /* MISR_RX_EOF */
+ if (sisr & SISR_ADAPTER_CHECK) {
+ printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name);
+ writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
+ printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
+ dev->name, readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC),
+ readw(streamer_mmio + LAPDINC));
+ free_irq(dev->irq, dev);
+ }
+
+ /* SISR_ADAPTER_CHECK */
+ if (sisr & SISR_ASB_FREE) {
+ /* Wake up anything that is waiting for the asb response */
+ if (streamer_priv->asb_queued) {
+ streamer_asb_bh(dev);
+ }
+ }
+ /* SISR_ASB_FREE */
+ if (sisr & SISR_ARB_CMD) {
+ streamer_arb_cmd(dev);
+ }
+ /* SISR_ARB_CMD */
+ if (sisr & SISR_TRB_REPLY) {
+ /* Wake up anything that is waiting for the trb response */
+ if (streamer_priv->trb_queued) {
+ wake_up_interruptible(&streamer_priv->
+ trb_wait);
+ }
+ streamer_priv->trb_queued = 0;
+ }
+ /* SISR_TRB_REPLY */
+ if (misr & MISR_RX_NOBUF) {
+ /* According to the documentation, we don't have to do anything, but trapping it keeps it out of
+ /var/log/messages. */
+ } /* SISR_RX_NOBUF */
+ } else {
+ printk(KERN_WARNING "%s: Unexpected interrupt: %x\n",
+ dev->name, sisr);
+ printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name,
+ readw(streamer_mmio + SISR_MASK));
+ } /* One if the interrupts we want */
+
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+}
+
+
+static int streamer_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ netif_stop_queue(dev);
+
+ if (streamer_priv->free_tx_ring_entries) {
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len;
+ streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb;
+ streamer_priv->free_tx_ring_entries--;
+#if STREAMER_DEBUG_PACKETS
+ {
+ int i;
+ printk("streamer_xmit packet print:\n");
+ for (i = 0; i < skb->len; i++) {
+ printk("%x:", skb->data[i]);
+ if (((i + 1) % 16) == 0)
+ printk("\n");
+ }
+ printk("\n");
+ }
+#endif
+
+ writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
+
+ streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
+ netif_start_queue(dev);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+static int streamer_close(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ unsigned long flags;
+ int i;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_CLOSE_ADAPTER, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+
+ save_flags(flags);
+ cli();
+
+ streamer_priv->srb_queued = 1;
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ while (streamer_priv->srb_queued)
+ {
+ interruptible_sleep_on_timeout(&streamer_priv->srb_wait,
+ jiffies + 60 * HZ);
+ if (signal_pending(current))
+ {
+ printk(KERN_WARNING "%s: SRB timed out.\n", dev->name);
+ printk(KERN_WARNING "SISR=%x MISR=%x LISR=%x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR));
+ streamer_priv->srb_queued = 0;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+
+ for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
+ dev_kfree_skb(streamer_priv->rx_ring_skb[streamer_priv->rx_ring_last_received]);
+ streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);
+ }
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* TBD. Add graceful way to reset the LLC channel without doing a soft reset.
+ writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL);
+ */
+
+#if STREAMER_DEBUG
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ printk("srb): ");
+ for (i = 0; i < 2; i++) {
+ printk("%x ", htons(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+#endif
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void streamer_set_rx_mode(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 options = 0, set_mc_list = 0;
+ __u16 ata1, ata2;
+ struct dev_mc_list *dmi;
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+ options = streamer_priv->streamer_copy_all_options;
+
+ if (dev->flags & IFF_PROMISC)
+ options |= (3 << 5); /* All LLC and MAC frames, all through the main rx channel */
+ else
+ options &= ~(3 << 5);
+
+ if (dev->mc_count) {
+ set_mc_list = 1;
+ }
+
+ /* Only issue the srb if there is a change in options */
+
+ if ((options ^ streamer_priv->streamer_copy_all_options))
+ {
+ /* Now to issue the srb command to alter the copy.all.options */
+
+ writew(SRB_MODIFY_RECEIVE_OPTIONS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(streamer_priv->streamer_receive_options | (options << 8), streamer_mmio + LAPDINC);
+ writew(0x414a, streamer_mmio + LAPDINC);
+ writew(0x454d, streamer_mmio + LAPDINC);
+ writew(0x2053, streamer_mmio + LAPDINC);
+ writew(0x2020, streamer_mmio + LAPDINC);
+
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_copy_all_options = options;
+ return;
+ }
+
+ if (set_mc_list ^ streamer_priv->streamer_multicast_set)
+ { /* Multicast options have changed */
+ dmi = dev->mc_list;
+
+ writel(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ ata1 = readw(streamer_mmio + LAPDINC);
+ ata2 = readw(streamer_mmio + LAPD);
+
+ writel(streamer_priv->srb, streamer_mmio + LAPA);
+
+ if (set_mc_list)
+ {
+ /* Turn multicast on */
+
+ /* RFC 1469 Says we must support using the functional address C0 00 00 04 00 00
+ * We do this with a set functional address mask.
+ */
+
+ if (!(ata1 & 0x0400)) { /* need to set functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 | 0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 1;
+ }
+
+ } else { /* Turn multicast off */
+
+ if ((ata1 & 0x0400)) { /* Hmmm, need to reset the functional mask */
+ writew(SRB_SET_FUNC_ADDRESS, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ata1 & ~0x0400, streamer_mmio + LAPDINC);
+ writew(ata2, streamer_mmio + LAPD);
+
+ streamer_priv->srb_queued = 2;
+ writel(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ streamer_priv->streamer_multicast_set = 0;
+ }
+ }
+
+ }
+}
+
+static void streamer_srb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u16 srb_word;
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+
+ switch (srb_word) {
+
+ /* SRB_MODIFY_RECEIVE_OPTIONS i.e. set_multicast_list options (promiscuous)
+ * At some point we should do something if we get an error, such as
+ * resetting the IFF_PROMISC flag in dev
+ */
+
+ case SRB_MODIFY_RECEIVE_OPTIONS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command\n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_WARNING "%s: Receive Options Modified to %x,%x\n",
+ dev->name,
+ streamer_priv->streamer_copy_all_options,
+ streamer_priv->streamer_receive_options);
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_GROUP_ADDRESS - Multicast group setting
+ */
+ case SRB_SET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 1;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n",dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x3c:
+ printk(KERN_WARNING "%s: Group/Functional address indicator bits not set correctly\n", dev->name);
+ break;
+ case 0x3e: /* If we ever implement individual multicast addresses, will need to deal with this */
+ printk(KERN_WARNING "%s: Group address registers full\n", dev->name);
+ break;
+ case 0x55:
+ printk(KERN_INFO "%s: Group Address already set.\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_RESET_GROUP_ADDRESS - Remove a multicast address from group list
+ */
+ case SRB_RESET_GROUP_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ streamer_priv->streamer_multicast_set = 0;
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ case 0x39: /* Must deal with this if individual multicast addresses used */
+ printk(KERN_INFO "%s: Group address not found \n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+
+ /* SRB_SET_FUNC_ADDRESS - Called by the set_rx_mode
+ */
+
+ case SRB_SET_FUNC_ADDRESS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Functional Address Mask Set \n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_LOG - Read and reset the adapter error counters
+ */
+
+ case SRB_READ_LOG:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ {
+ int i;
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Log command complete\n", dev->name);
+ printk("Read Log statistics: ");
+ writew(streamer_priv->srb + 6,
+ streamer_mmio + LAPA);
+ for (i = 0; i < 5; i++) {
+ printk("%x:", ntohs(readw(streamer_mmio + LAPDINC)));
+ }
+ printk("\n");
+ }
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+
+ } /* switch srb[2] */
+ break;
+
+ /* SRB_READ_SR_COUNTERS - Read and reset the source routing bridge related counters */
+
+ case SRB_READ_SR_COUNTERS:
+ srb_word = readw(streamer_mmio + LAPDINC) & 0xFF;
+ switch (srb_word) {
+ case 0x00:
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Read Source Routing Counters issued\n", dev->name);
+ break;
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized srb command \n", dev->name);
+ break;
+ case 0x04:
+ printk(KERN_WARNING "%s: Adapter must be open for this operation, doh!!\n", dev->name);
+ break;
+ default:
+ break;
+ } /* switch srb[2] */
+ break;
+
+ default:
+ printk(KERN_WARNING "%s: Unrecognized srb bh return value.\n", dev->name);
+ break;
+ } /* switch srb[0] */
+}
+
+static struct net_device_stats *streamer_get_stats(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv;
+ streamer_priv = (struct streamer_private *) dev->priv;
+ return (struct net_device_stats *) &streamer_priv->streamer_stats;
+}
+
+static int streamer_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *saddr = addr;
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+
+ if (netif_running(dev)) {
+ printk(KERN_WARNING "%s: Cannot set mac/laa address while card is open\n", dev->name);
+ return -EBUSY;
+ }
+
+ memcpy(streamer_priv->streamer_laa, saddr->sa_data, dev->addr_len);
+
+ if (streamer_priv->streamer_message_level) {
+ printk(KERN_INFO "%s: MAC/LAA Set to = %x.%x.%x.%x.%x.%x\n",
+ dev->name, streamer_priv->streamer_laa[0],
+ streamer_priv->streamer_laa[1],
+ streamer_priv->streamer_laa[2],
+ streamer_priv->streamer_laa[3],
+ streamer_priv->streamer_laa[4],
+ streamer_priv->streamer_laa[5]);
+ }
+ return 0;
+}
+
+static void streamer_arb_cmd(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ __u8 header_len;
+ __u16 frame_len, buffer_len;
+ struct sk_buff *mac_frame;
+ __u8 frame_data[256];
+ __u16 buff_off;
+ __u16 lan_status = 0, lan_status_diff; /* Initialize to stop compiler warning */
+ __u8 fdx_prot_error;
+ __u16 next_ptr;
+ __u16 arb_word;
+
+#if STREAMER_NETWORK_MONITOR
+ struct trh_hdr *mac_hdr;
+#endif
+
+ writew(streamer_priv->arb, streamer_mmio + LAPA);
+ arb_word = readw(streamer_mmio + LAPD) & 0xFF;
+
+ if (arb_word == ARB_RECEIVE_DATA) { /* Receive.data, MAC frames */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ streamer_priv->mac_rx_buffer = buff_off = ntohs(readw(streamer_mmio + LAPDINC));
+ header_len = readw(streamer_mmio + LAPDINC) & 0xff; /* 802.5 Token-Ring Header Length */
+ frame_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+#if STREAMER_DEBUG
+ {
+ int i;
+ __u16 next;
+ __u8 status;
+ __u16 len;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /*setup window to frame data */
+ next = ntohs(readw(streamer_mmio + LAPDINC));
+ status =
+ ntohs(readw(streamer_mmio + LAPDINC)) & 0xff;
+ len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ /* print out 1st 14 bytes of frame data */
+ for (i = 0; i < 7; i++) {
+ printk("Loc %d = %04x\n", i,
+ ntohs(readw
+ (streamer_mmio + LAPDINC)));
+ }
+
+ printk("next %04x, fs %02x, len %04x \n", next,
+ status, len);
+ }
+#endif
+ mac_frame = dev_alloc_skb(frame_len);
+
+ /* Walk the buffer chain, creating the frame */
+
+ do {
+ int i;
+ __u16 rx_word;
+
+ writew(ntohs(buff_off), streamer_mmio + LAPA); /* setup window to frame data */
+ next_ptr = ntohs(readw(streamer_mmio + LAPDINC));
+ readw(streamer_mmio + LAPDINC); /* read thru status word */
+ buffer_len = ntohs(readw(streamer_mmio + LAPDINC));
+
+ if (buffer_len > 256)
+ break;
+
+ i = 0;
+ while (i < buffer_len) {
+ rx_word = readw(streamer_mmio + LAPDINC);
+ frame_data[i] = rx_word & 0xff;
+ frame_data[i + 1] = (rx_word >> 8) & 0xff;
+ i += 2;
+ }
+
+ memcpy_fromio(skb_put(mac_frame, buffer_len),
+ frame_data, buffer_len);
+ } while (next_ptr && (buff_off = next_ptr));
+
+#if STREAMER_NETWORK_MONITOR
+ printk(KERN_WARNING "%s: Received MAC Frame, details: \n",
+ dev->name);
+ mac_hdr = (struct trh_hdr *) mac_frame->data;
+ printk(KERN_WARNING
+ "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1],
+ mac_hdr->daddr[2], mac_hdr->daddr[3],
+ mac_hdr->daddr[4], mac_hdr->daddr[5]);
+ printk(KERN_WARNING
+ "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n",
+ dev->name, mac_hdr->saddr[0], mac_hdr->saddr[1],
+ mac_hdr->saddr[2], mac_hdr->saddr[3],
+ mac_hdr->saddr[4], mac_hdr->saddr[5]);
+#endif
+ mac_frame->dev = dev;
+ mac_frame->protocol = tr_type_trans(mac_frame, dev);
+ netif_rx(mac_frame);
+
+ /* Now tell the card we have dealt with the received frame */
+
+ /* Set LISR Bit 1 */
+ writel(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ /* Is the ASB free ? */
+
+ if (!(readl(streamer_priv->streamer_mmio + SISR) & SISR_ASB_FREE))
+ {
+ streamer_priv->asb_queued = 1;
+ writel(LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ return;
+ /* Drop out and wait for the bottom half to be run */
+ }
+
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+
+ streamer_priv->asb_queued = 2;
+ return;
+
+ } else if (arb_word == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */
+ writew(streamer_priv->arb + 6, streamer_mmio + LAPA);
+ lan_status = ntohs(readw(streamer_mmio + LAPDINC));
+ fdx_prot_error = readw(streamer_mmio + LAPD) & 0xFF;
+
+ /* Issue ARB Free */
+ writew(LISR_ARB_FREE, streamer_priv->streamer_mmio + LISR_SUM);
+
+ lan_status_diff = streamer_priv->streamer_lan_status ^ lan_status;
+
+ if (lan_status_diff & (LSC_LWF | LSC_ARW | LSC_FPE | LSC_RR))
+ {
+ if (lan_status_diff & LSC_LWF)
+ printk(KERN_WARNING "%s: Short circuit detected on the lobe\n", dev->name);
+ if (lan_status_diff & LSC_ARW)
+ printk(KERN_WARNING "%s: Auto removal error\n", dev->name);
+ if (lan_status_diff & LSC_FPE)
+ printk(KERN_WARNING "%s: FDX Protocol Error\n", dev->name);
+ if (lan_status_diff & LSC_RR)
+ printk(KERN_WARNING "%s: Force remove MAC frame received\n", dev->name);
+
+ /* Adapter has been closed by the hardware */
+
+ /* reset tx/rx fifo's and busmaster logic */
+
+ /* @TBD. no llc reset on autostreamer writel(readl(streamer_mmio+BCTL)|(3<<13),streamer_mmio+BCTL);
+ udelay(1);
+ writel(readl(streamer_mmio+BCTL)&~(3<<13),streamer_mmio+BCTL); */
+ netif_stop_queue(dev);
+ free_irq(dev->irq, dev);
+
+ printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name);
+
+ }
+ /* If serious error */
+ if (streamer_priv->streamer_message_level) {
+ if (lan_status_diff & LSC_SIG_LOSS)
+ printk(KERN_WARNING "%s: No receive signal detected \n", dev->name);
+ if (lan_status_diff & LSC_HARD_ERR)
+ printk(KERN_INFO "%s: Beaconing \n", dev->name);
+ if (lan_status_diff & LSC_SOFT_ERR)
+ printk(KERN_WARNING "%s: Adapter transmitted Soft Error Report Mac Frame \n", dev->name);
+ if (lan_status_diff & LSC_TRAN_BCN)
+ printk(KERN_INFO "%s: We are tranmitting the beacon, aaah\n", dev->name);
+ if (lan_status_diff & LSC_SS)
+ printk(KERN_INFO "%s: Single Station on the ring \n", dev->name);
+ if (lan_status_diff & LSC_RING_REC)
+ printk(KERN_INFO "%s: Ring recovery ongoing\n", dev->name);
+ if (lan_status_diff & LSC_FDX_MODE)
+ printk(KERN_INFO "%s: Operating in FDX mode\n", dev->name);
+ }
+
+ if (lan_status_diff & LSC_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Counter Overflow \n", dev->name);
+
+ /* Issue READ.LOG command */
+
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_LOG, streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC);
+ writew(0, streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+ }
+
+ if (lan_status_diff & LSC_SR_CO) {
+ if (streamer_priv->streamer_message_level)
+ printk(KERN_INFO "%s: Source routing counters overflow\n", dev->name);
+
+ /* Issue a READ.SR.COUNTERS */
+ writew(streamer_priv->srb, streamer_mmio + LAPA);
+ writew(SRB_READ_SR_COUNTERS,
+ streamer_mmio + LAPDINC);
+ writew(STREAMER_CLEAR_RET_CODE,
+ streamer_mmio + LAPDINC);
+ streamer_priv->srb_queued = 2; /* Can't sleep, use srb_bh */
+ writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+
+ }
+ streamer_priv->streamer_lan_status = lan_status;
+ } /* Lan.change.status */
+ else
+ printk(KERN_WARNING "%s: Unknown arb command \n", dev->name);
+}
+
+static void streamer_asb_bh(struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ if (streamer_priv->asb_queued == 1)
+ {
+ /* Dropped through the first time */
+
+ writew(streamer_priv->asb, streamer_mmio + LAPA);
+ writew(ASB_RECEIVE_DATA, streamer_mmio + LAPDINC); /* Receive data */
+ writew(STREAMER_CLEAR_RET_CODE, streamer_mmio + LAPDINC); /* Necessary ?? */
+ writew(0, streamer_mmio + LAPDINC);
+ writew(ntohs(streamer_priv->mac_rx_buffer), streamer_mmio + LAPD);
+
+ writel(LISR_ASB_REPLY | LISR_ASB_FREE_REQ, streamer_priv->streamer_mmio + LISR_SUM);
+ streamer_priv->asb_queued = 2;
+
+ return;
+ }
+
+ if (streamer_priv->asb_queued == 2) {
+ __u8 rc;
+ writew(streamer_priv->asb + 2, streamer_mmio + LAPA);
+ rc = readw(streamer_mmio + LAPD) & 0xff;
+ switch (rc) {
+ case 0x01:
+ printk(KERN_WARNING "%s: Unrecognized command code \n", dev->name);
+ break;
+ case 0x26:
+ printk(KERN_WARNING "%s: Unrecognized buffer address \n", dev->name);
+ break;
+ case 0xFF:
+ /* Valid response, everything should be ok again */
+ break;
+ default:
+ printk(KERN_WARNING "%s: Invalid return code in asb\n", dev->name);
+ break;
+ }
+ }
+ streamer_priv->asb_queued = 0;
+}
+
+static int streamer_change_mtu(struct net_device *dev, int mtu)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u16 max_mtu;
+
+ if (streamer_priv->streamer_ring_speed == 4)
+ max_mtu = 4500;
+ else
+ max_mtu = 18000;
+
+ if (mtu > max_mtu)
+ return -EINVAL;
+ if (mtu < 100)
+ return -EINVAL;
+
+ dev->mtu = mtu;
+ streamer_priv->pkt_buf_sz = mtu + TR_HLEN;
+
+ return 0;
+}
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+static int streamer_proc_info(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ struct pci_dev *pci_device = NULL;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct net_device *dev;
+
+
+ size = sprintf(buffer, "IBM LanStreamer/MPC Chipset Token Ring Adapters\n");
+
+ pos += size;
+ len += size;
+
+ while ((pci_device = pci_find_device(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, pci_device)))
+ {
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->base_addr == (pci_device->base_address[0] & (~3)))
+ { /* Yep, a Streamer device */
+ size = sprintf_info(buffer + len, dev);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ } /* if */
+ } /* for */
+ } /* While */
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+ return len;
+}
+
+static int sprintf_info(char *buffer, struct net_device *dev)
+{
+ struct streamer_private *streamer_priv =
+ (struct streamer_private *) dev->priv;
+ __u8 *streamer_mmio = streamer_priv->streamer_mmio;
+ struct streamer_adapter_addr_table sat;
+ struct streamer_parameters_table spt;
+ int size = 0;
+ int i;
+
+ writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 14; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & sat;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+ writew(streamer_priv->streamer_parms_addr, streamer_mmio + LAPA);
+ for (i = 0; i < 68; i += 2) {
+ __u16 io_word;
+ __u8 *datap = (__u8 *) & spt;
+ io_word = readw(streamer_mmio + LAPDINC);
+ datap[size] = io_word & 0xff;
+ datap[size + 1] = (io_word >> 8) & 0xff;
+ }
+
+
+ size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4],
+ dev->dev_addr[5], sat.node_addr[0], sat.node_addr[1],
+ sat.node_addr[2], sat.node_addr[3], sat.node_addr[4],
+ sat.node_addr[5], sat.func_addr[0], sat.func_addr[1],
+ sat.func_addr[2], sat.func_addr[3]);
+
+ size += sprintf(buffer + size, "\n%6s: Token Ring Parameters Table:\n", dev->name);
+
+ size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address : Poll Address : AccPri : Auth Src : Att Code :\n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x :\n",
+ dev->name, spt.phys_addr[0], spt.phys_addr[1],
+ spt.phys_addr[2], spt.phys_addr[3],
+ spt.up_node_addr[0], spt.up_node_addr[1],
+ spt.up_node_addr[2], spt.up_node_addr[3],
+ spt.up_node_addr[4], spt.up_node_addr[4],
+ spt.poll_addr[0], spt.poll_addr[1], spt.poll_addr[2],
+ spt.poll_addr[3], spt.poll_addr[4], spt.poll_addr[5],
+ ntohs(spt.acc_priority), ntohs(spt.auth_source_class),
+ ntohs(spt.att_code));
+
+ size += sprintf(buffer + size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: %02x:%02x:%02x:%02x:%02x:%02x : %04x : %04x : %04x : %04x : %04x : %04x : \n",
+ dev->name, spt.source_addr[0], spt.source_addr[1],
+ spt.source_addr[2], spt.source_addr[3],
+ spt.source_addr[4], spt.source_addr[5],
+ ntohs(spt.beacon_type), ntohs(spt.major_vector),
+ ntohs(spt.lan_status), ntohs(spt.local_ring),
+ ntohs(spt.mon_error), ntohs(spt.frame_correl));
+
+ size += sprintf(buffer + size, "%6s: Beacon Details : Tx : Rx : NAUN Node Address : NAUN Node Phys : \n",
+ dev->name);
+
+ size += sprintf(buffer + size,
+ "%6s: : %02x : %02x : %02x:%02x:%02x:%02x:%02x:%02x : %02x:%02x:%02x:%02x : \n",
+ dev->name, ntohs(spt.beacon_transmit),
+ ntohs(spt.beacon_receive), spt.beacon_naun[0],
+ spt.beacon_naun[1], spt.beacon_naun[2],
+ spt.beacon_naun[3], spt.beacon_naun[4],
+ spt.beacon_naun[5], spt.beacon_phys[0],
+ spt.beacon_phys[1], spt.beacon_phys[2],
+ spt.beacon_phys[3]);
+ return size;
+}
+#endif
+#endif
+
+#ifdef MODULE
+
+static struct net_device *dev_streamer[STREAMER_MAX_ADAPTERS];
+
+int init_module(void)
+{
+ int i;
+
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+
+ ent = create_proc_entry("net/streamer_tr", 0, 0);
+ ent->read_proc = &streamer_proc_info;
+#endif
+#endif
+ for (i = 0; (i < STREAMER_MAX_ADAPTERS); i++)
+ {
+ dev_streamer[i] = NULL;
+ dev_streamer[i] = init_trdev(dev_streamer[i], 0);
+ if (dev_streamer[i] == NULL)
+ return -ENOMEM;
+
+ dev_streamer[i]->init = &streamer_probe;
+
+ if (register_trdev(dev_streamer[i]) != 0) {
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ if (i == 0)
+ {
+ printk(KERN_INFO "Streamer: No IBM LanStreamer PCI Token Ring cards found in system.\n");
+ return -EIO;
+ } else {
+ printk(KERN_INFO "Streamer: %d IBM LanStreamer PCI Token Ring card(s) found in system.\n", i);
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+ for (i = 0; i < STREAMER_MAX_ADAPTERS; i++)
+ if (dev_streamer[i]) {
+ unregister_trdev(dev_streamer[i]);
+ release_region(dev_streamer[i]->base_addr, STREAMER_IO_SPACE);
+ kfree_s(dev_streamer[i]->priv, sizeof(struct streamer_private));
+ kfree_s(dev_streamer[i], sizeof(struct net_device));
+ dev_streamer[i] = NULL;
+ }
+#if STREAMER_NETWORK_MONITOR
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("net/streamer_tr", NULL);
+#endif
+#endif
+}
+#endif /* MODULE */
diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h
new file mode 100644
index 000000000..7ba86dfe5
--- /dev/null
+++ b/drivers/net/tokenring/lanstreamer.h
@@ -0,0 +1,319 @@
+/*
+ * lanstreamer.h -- driver for the IBM Auto LANStreamer PCI Adapter
+ *
+ * Written By: Mike Sullivan, IBM Corporation
+ *
+ * Copyright (C) 1999 IBM Corporation
+ *
+ * Linux driver for IBM PCI tokenring cards based on the LanStreamer MPC
+ * chipset.
+ *
+ * This driver is based on the olympic driver for IBM PCI TokenRing cards (Pit/Pit-Phy/Olympic
+ * chipsets) written by:
+ * 1999 Peter De Schrijver All Rights Reserved
+ * 1999 Mike Phillips (phillim@amtrak.com)
+ *
+ * Base Driver Skeleton:
+ * Written 1993-94 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+ *
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * 12/10/99 - Alpha Release 0.1.0
+ * First release to the public
+ *
+ */
+
+#define BCTL 0x60
+#define BCTL_SOFTRESET (1<<15)
+
+#define GPR 0x4a
+#define GPR_AUTOSENSE (1<<2)
+#define GPR_16MBPS (1<<3)
+
+#define LISR 0x10
+#define LISR_SUM 0x12
+#define LISR_RUM 0x14
+
+#define LISR_LIE (1<<15)
+#define LISR_SLIM (1<<13)
+#define LISR_SLI (1<<12)
+#define LISR_BPEI (1<<9)
+#define LISR_BPE (1<<8)
+#define LISR_SRB_CMD (1<<5)
+#define LISR_ASB_REPLY (1<<4)
+#define LISR_ASB_FREE_REQ (1<<2)
+#define LISR_ARB_FREE (1<<1)
+#define LISR_TRB_FRAME (1<<0)
+
+#define SISR 0x16
+#define SISR_SUM 0x18
+#define SISR_RUM 0x1A
+#define SISR_MASK 0x54
+#define SISR_MASK_SUM 0x56
+#define SISR_MASK_RUM 0x58
+
+#define SISR_MI (1<<15)
+#define SISR_TIMER (1<<11)
+#define SISR_LAP_PAR_ERR (1<<10)
+#define SISR_LAP_ACC_ERR (1<<9)
+#define SISR_PAR_ERR (1<<8)
+#define SISR_ADAPTER_CHECK (1<<6)
+#define SISR_SRB_REPLY (1<<5)
+#define SISR_ASB_FREE (1<<4)
+#define SISR_ARB_CMD (1<<3)
+#define SISR_TRB_REPLY (1<<2)
+
+#define MISR_RUM 0x5A
+#define MISR_MASK 0x5C
+#define MISR_MASK_RUM 0x5E
+
+#define MISR_TX2_IDLE (1<<15)
+#define MISR_TX2_NO_STATUS (1<<14)
+#define MISR_TX2_HALT (1<<13)
+#define MISR_TX2_EOF (1<<12)
+#define MISR_TX1_IDLE (1<<11)
+#define MISR_TX1_NO_STATUS (1<<10)
+#define MISR_TX1_HALT (1<<9)
+#define MISR_TX1_EOF (1<<8)
+#define MISR_RX_NOBUF (1<<5)
+#define MISR_RX_EOB (1<<4)
+#define MISR_RX_NO_STATUS (1<<2)
+#define MISR_RX_HALT (1<<1)
+#define MISR_RX_EOF (1<<0)
+
+#define LAPA 0x62
+#define LAPE 0x64
+#define LAPD 0x66
+#define LAPDINC 0x68
+#define LAPWWO 0x6A
+#define LAPWWC 0x6C
+#define LAPCTL 0x6E
+
+#define TIMER 0x4E4
+
+#define BMCTL_SUM 0x50
+#define BMCTL_RUM 0x52
+#define BMCTL_TX1_DIS (1<<14)
+#define BMCTL_TX2_DIS (1<<10)
+#define BMCTL_RX_DIS (1<<6)
+
+#define RXLBDA 0x90
+#define RXBDA 0x94
+#define RXSTAT 0x98
+#define RXDBA 0x9C
+
+#define TX1LFDA 0xA0
+#define TX1FDA 0xA4
+#define TX1STAT 0xA8
+#define TX1DBA 0xAC
+#define TX2LFDA 0xB0
+#define TX2FDA 0xB4
+#define TX2STAT 0xB8
+#define TX2DBA 0xBC
+
+#define STREAMER_IO_SPACE 256
+
+#define SRB_COMMAND_SIZE 50
+
+#define STREAMER_MAX_ADAPTERS 8 /* 0x08 __MODULE_STRING can't hand 0xnn */
+
+/* Defines for LAN STATUS CHANGE reports */
+#define LSC_SIG_LOSS 0x8000
+#define LSC_HARD_ERR 0x4000
+#define LSC_SOFT_ERR 0x2000
+#define LSC_TRAN_BCN 0x1000
+#define LSC_LWF 0x0800
+#define LSC_ARW 0x0400
+#define LSC_FPE 0x0200
+#define LSC_RR 0x0100
+#define LSC_CO 0x0080
+#define LSC_SS 0x0040
+#define LSC_RING_REC 0x0020
+#define LSC_SR_CO 0x0010
+#define LSC_FDX_MODE 0x0004
+
+/* Defines for OPEN ADAPTER command */
+
+#define OPEN_ADAPTER_EXT_WRAP (1<<15)
+#define OPEN_ADAPTER_DIS_HARDEE (1<<14)
+#define OPEN_ADAPTER_DIS_SOFTERR (1<<13)
+#define OPEN_ADAPTER_PASS_ADC_MAC (1<<12)
+#define OPEN_ADAPTER_PASS_ATT_MAC (1<<11)
+#define OPEN_ADAPTER_ENABLE_EC (1<<10)
+#define OPEN_ADAPTER_CONTENDER (1<<8)
+#define OPEN_ADAPTER_PASS_BEACON (1<<7)
+#define OPEN_ADAPTER_ENABLE_FDX (1<<6)
+#define OPEN_ADAPTER_ENABLE_RPL (1<<5)
+#define OPEN_ADAPTER_INHIBIT_ETR (1<<4)
+#define OPEN_ADAPTER_INTERNAL_WRAP (1<<3)
+
+
+/* Defines for SRB Commands */
+#define SRB_CLOSE_ADAPTER 0x04
+#define SRB_CONFIGURE_BRIDGE 0x0c
+#define SRB_CONFIGURE_HP_CHANNEL 0x13
+#define SRB_MODIFY_BRIDGE_PARMS 0x15
+#define SRB_MODIFY_OPEN_OPTIONS 0x01
+#define SRB_MODIFY_RECEIVE_OPTIONS 0x17
+#define SRB_NO_OPERATION 0x00
+#define SRB_OPEN_ADAPTER 0x03
+#define SRB_READ_LOG 0x08
+#define SRB_READ_SR_COUNTERS 0x16
+#define SRB_RESET_GROUP_ADDRESS 0x02
+#define SRB_RESET_TARGET_SEGMETN 0x14
+#define SRB_SAVE_CONFIGURATION 0x1b
+#define SRB_SET_BRIDGE_PARMS 0x09
+#define SRB_SET_FUNC_ADDRESS 0x07
+#define SRB_SET_GROUP_ADDRESS 0x06
+#define SRB_SET_TARGET_SEGMENT 0x05
+
+/* Clear return code */
+#define STREAMER_CLEAR_RET_CODE 0xfe
+
+/* ARB Commands */
+#define ARB_RECEIVE_DATA 0x81
+#define ARB_LAN_CHANGE_STATUS 0x84
+
+/* ASB Response commands */
+#define ASB_RECEIVE_DATA 0x81
+
+
+/* Streamer defaults for buffers */
+
+#define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */
+#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */
+
+#define PKT_BUF_SZ 4096 /* Default packet size */
+
+/* Streamer data structures */
+
+struct streamer_tx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 bufcnt_framelen;
+ __u32 buffer;
+ __u32 buflen;
+ __u32 rsvd1;
+ __u32 rsvd2;
+ __u32 rsvd3;
+};
+
+struct streamer_rx_desc {
+ __u32 forward;
+ __u32 status;
+ __u32 buffer;
+ __u32 framelen_buflen;
+};
+
+struct mac_receive_buffer {
+ __u16 next;
+ __u8 padding;
+ __u8 frame_status;
+ __u16 buffer_length;
+ __u8 frame_data;
+};
+
+struct streamer_private {
+
+ __u16 srb;
+ __u16 trb;
+ __u16 arb;
+ __u16 asb;
+
+ __u8 *streamer_mmio;
+
+ volatile int srb_queued; /* True if an SRB is still posted */
+ wait_queue_head_t srb_wait;
+
+ volatile int asb_queued; /* True if an ASB is posted */
+
+ volatile int trb_queued; /* True if a TRB is posted */
+ wait_queue_head_t trb_wait;
+
+ struct streamer_rx_desc streamer_rx_ring[STREAMER_RX_RING_SIZE];
+ struct streamer_tx_desc streamer_tx_ring[STREAMER_TX_RING_SIZE];
+ struct sk_buff *tx_ring_skb[STREAMER_TX_RING_SIZE],
+ *rx_ring_skb[STREAMER_RX_RING_SIZE];
+ int tx_ring_free, tx_ring_last_status, rx_ring_last_received,
+ free_tx_ring_entries;
+
+ struct net_device_stats streamer_stats;
+ __u16 streamer_lan_status;
+ __u8 streamer_ring_speed;
+ __u16 pkt_buf_sz;
+ __u8 streamer_receive_options, streamer_copy_all_options,
+ streamer_message_level;
+ __u8 streamer_multicast_set;
+ __u16 streamer_addr_table_addr, streamer_parms_addr;
+ __u16 mac_rx_buffer;
+ __u8 streamer_laa[6];
+};
+
+struct streamer_adapter_addr_table {
+
+ __u8 node_addr[6];
+ __u8 reserved[4];
+ __u8 func_addr[4];
+};
+
+struct streamer_parameters_table {
+
+ __u8 phys_addr[4];
+ __u8 up_node_addr[6];
+ __u8 up_phys_addr[4];
+ __u8 poll_addr[6];
+ __u16 reserved;
+ __u16 acc_priority;
+ __u16 auth_source_class;
+ __u16 att_code;
+ __u8 source_addr[6];
+ __u16 beacon_type;
+ __u16 major_vector;
+ __u16 lan_status;
+ __u16 soft_error_time;
+ __u16 reserved1;
+ __u16 local_ring;
+ __u16 mon_error;
+ __u16 beacon_transmit;
+ __u16 beacon_receive;
+ __u16 frame_correl;
+ __u8 beacon_naun[6];
+ __u32 reserved2;
+ __u8 beacon_phys[4];
+};
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index bbca052e7..19615012f 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1105,7 +1105,7 @@ static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in
index bc4bd5331..e0a6f5e8f 100644
--- a/drivers/net/wan/Config.in
+++ b/drivers/net/wan/Config.in
@@ -16,6 +16,25 @@ if [ "$CONFIG_WAN" = "y" ]; then
dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
+ #
+ # COMX drivers
+ #
+
+ tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+ if [ "$CONFIG_COMX" != "n" ]; then
+ dep_tristate ' Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+ dep_tristate ' Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+ dep_tristate ' Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+ dep_tristate ' Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+ if [ "$CONFIG_LAPB" = "y" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+ fi
+ if [ "$CONFIG_LAPB" = "m" ]; then
+ dep_tristate ' Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+ fi
+ dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+ fi
+
# There is no way to detect a Sealevel board. Force it modular
dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 67f6bb59b..b2cd8aafb 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -49,6 +49,58 @@ else
endif
endif
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else
+ ifeq ($(CONFIG_COMX_HW_COMX),m)
+ M_OBJS += comx-hw-comx.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else
+ ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+ M_OBJS += comx-hw-locomx.o
+ CONFIG_85230_MODULE=y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else
+ ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+ M_OBJS += comx-hw-mixcom.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else
+ ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+ M_OBJS += comx-proto-ppp.o
+ CONFIG_SYNCPPP_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+ M_OBJS += comx-proto-lapb.o
+ endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else
+ ifeq ($(CONFIG_COMX_PROTO_FR),m)
+ M_OBJS += comx-proto-fr.o
+ endif
+endif
+
ifeq ($(CONFIG_COSA),y)
L_OBJS += cosa.o
CONFIG_SYNCPPP_BUILTIN = y
@@ -180,8 +232,8 @@ clean:
rm -f core *.o *.a *.s
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $(WANPIPE_OBJS)
+ $(LD) -r -o $@ $(WANPIPE_OBJS)
cyclomx.o: $(CYCLOMX_OBJS)
- ld -r -o $@ $(CYCLOMX_OBJS)
+ $(LD) -r -o $@ $(CYCLOMX_OBJS)
diff --git a/drivers/net/wan/comx-hw-comx.c b/drivers/net/wan/comx-hw-comx.c
new file mode 100644
index 000000000..7381dc8a9
--- /dev/null
+++ b/drivers/net/wan/comx-hw-comx.c
@@ -0,0 +1,1426 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-2000 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - port back to kernel, add support builtin driver
+ * - cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ * - cleaned up the board load functions, no more long reset
+ * timeouts
+ * - lower modem lines on close
+ * - some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ * - fix multiple board support
+ *
+ * Version 0.83 (99/11/30):
+ * - interrupt handling and locking fixes during initalization
+ * - really fix multiple board support
+ *
+ * Version 0.84 (99/12/02):
+ * - some workarounds for problematic hardware/firmware
+ *
+ * Version 0.85 (00/01/14):
+ * - some additional workarounds :/
+ * - printk cleanups
+ */
+
+#define VERSION "0.85"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
+ (unsigned int)(((struct comx_privdata *)\
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
+ + (unsigned int)(((struct comx_privdata *) \
+ ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+ * COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd) (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+ int len;
+ unsigned char *data;
+};
+
+struct comx_privdata {
+ struct comx_firmware *firmware;
+ u16 clock;
+ char channel; // channel no.
+ int memory_size;
+ short io_extent;
+ u_long histogram[5];
+};
+
+static struct net_device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) |
+ COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct net_device *dev)
+{
+ outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) |
+ HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct net_device *COMX_access_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct net_device *ret;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ ret = memory_used[mempos];
+
+ if(ret == dev) {
+ goto out;
+ }
+
+ memory_used[mempos] = dev;
+
+ if (!ch->twin || ret != ch->twin) {
+ if (ret) ((struct comx_channel *)ret->priv)->HW_board_off(ret);
+ ch->HW_board_on(dev);
+ }
+out:
+ restore_flags(flags);
+ return ret;
+}
+
+static void COMX_release_board(struct net_device *dev, struct net_device *savep)
+{
+ unsigned long flags;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct comx_channel *ch = dev->priv;
+
+ save_flags(flags); cli();
+
+ if (memory_used[mempos] == savep) {
+ goto out;
+ }
+
+ memory_used[mempos] = savep;
+ if (!ch->twin || ch->twin != savep) {
+ ch->HW_board_off(dev);
+ if (savep) ((struct comx_channel*)savep->priv)->HW_board_on(savep);
+ }
+out:
+ restore_flags(flags);
+}
+
+static int COMX_txe(struct net_device *dev)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ int rc = 0;
+
+ savep = ch->HW_access_board(dev);
+ if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+ rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+ }
+ ch->HW_release_board(dev,savep);
+ if(rc==0xffff) {
+ printk(KERN_ERR "%s, OFF_A_L2_TxEMPTY is %d\n",dev->name, rc);
+ }
+ return rc;
+}
+
+static int COMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct net_device *savep;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int ret = FRAME_DROPPED;
+ word tmp;
+
+ savep = ch->HW_access_board(dev);
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+ }
+
+ if (skb->len > COMX_MAX_TX_SIZE) {
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if ((ch->line_status & LINE_UP) && tmp==1) {
+ int lensave = skb->len;
+ int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+ word *data = (word *)skb->data;
+
+ if(dest==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxBUFP is %d\n", dev->name, dest);
+ ret=FRAME_DROPPED;
+ goto out;
+ }
+
+ writew((unsigned short)skb->len, dev->mem_start + dest);
+ dest += 2;
+ while (skb->len > 1) {
+ writew(*data++, dev->mem_start + dest);
+ dest += 2; skb->len -= 2;
+ }
+ if (skb->len == 1) {
+ writew(*((byte *)data), dev->mem_start + dest);
+ }
+ writew(0, dev->mem_start + (int)hw->channel *
+ COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += lensave;
+ ret = FRAME_ACCEPTED;
+ } else {
+ ch->stats.tx_dropped++;
+ printk(KERN_INFO "%s: frame dropped\n",dev->name);
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n",dev->name,tmp);
+ }
+ }
+
+out:
+ ch->HW_release_board(dev, savep);
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static inline int comx_read_buffer(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ word rbuf_offs;
+ struct sk_buff *skb;
+ word len;
+ int i=0;
+ word *writeptr;
+
+ i = 0;
+ rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+ if(rbuf_offs == 0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_RxBUFP is %d\n",dev->name,rbuf_offs);
+ return 0;
+ }
+ len = readw(dev->mem_start + rbuf_offs);
+ if(len > COMX_MAX_RX_SIZE) {
+ printk(KERN_ERR "%s: packet length is %d\n",dev->name,len);
+ return 0;
+ }
+ if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+ ch->stats.rx_dropped++;
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ return 0;
+ }
+ rbuf_offs += 2;
+ skb_reserve(skb, 16);
+ skb_put(skb, len);
+ skb->dev = dev;
+ writeptr = (word *)skb->data;
+ while (i < len) {
+ *writeptr++ = readw(dev->mem_start + rbuf_offs);
+ rbuf_offs += 2;
+ i += 2;
+ }
+ COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += len;
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+ }
+ ch->LINE_rx(dev, skb);
+ return 1;
+}
+
+static inline char comx_line_change(struct net_device *dev, char linestat)
+{
+ struct comx_channel *ch=dev->priv;
+ char idle=1;
+
+
+ if (linestat & LINE_UP) { /* Vonal fol */
+ if (ch->lineup_delay) {
+ if (!test_and_set_bit(0, &ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies +
+ HZ*ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ idle=0;
+ }
+ } else {
+ idle=0;
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+ } else { /* Vonal le */
+ idle=0;
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+ }
+ }
+ return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *interrupted;
+ unsigned long jiffs;
+ char idle = 0;
+ int count = 0;
+ word tmp;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+ return;
+ }
+
+ jiffs = jiffies;
+
+ interrupted = ch->HW_access_board(dev);
+
+ while (!idle && count < 5000) {
+ char channel = 0;
+ idle = 1;
+
+ while (channel < 2) {
+ char linestat = 0;
+ char buffers_emptied = 0;
+
+ if (channel == 1) {
+ if (ch->twin) {
+ dev = ch->twin;
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+ } else {
+ break;
+ }
+ } else {
+ COMX_WRITE(dev, OFF_A_L1_REPENA,
+ COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+ }
+ channel++;
+
+ if ((ch->init_status & (HW_OPEN | LINE_OPEN)) !=
+ (HW_OPEN | LINE_OPEN)) {
+ continue;
+ }
+
+ /* Collect stats */
+ tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+ COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_ABOREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_over_errors += tmp & 0xff;
+ }
+ tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+ COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L1_CRCREC is %d\n",dev->name,tmp);
+ break;
+ } else {
+ ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+ ch->stats.rx_missed_errors += tmp & 0xff;
+ }
+
+ if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ while (tmp==1) {
+ idle=0;
+ buffers_emptied+=comx_read_buffer(dev);
+ tmp=COMX_readw(dev, OFF_A_L2_DAV);
+ }
+ if(tmp) {
+ printk(KERN_ERR "%s: OFF_A_L2_DAV is %d\n", dev->name, tmp);
+ break;
+ }
+ }
+
+ tmp=COMX_readw(dev, OFF_A_L2_TxEMPTY);
+ if (tmp==1 && ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ if(tmp==0xffff) {
+ printk(KERN_ERR "%s: OFF_A_L2_TxEMPTY is %d\n", dev->name, tmp);
+ break;
+ }
+
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ linestat &= ~LINE_UP;
+ } else {
+ linestat |= LINE_UP;
+ }
+
+ if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+ ch->stats.tx_carrier_errors++;
+ idle &= comx_line_change(dev,linestat);
+ }
+
+ hw->histogram[(int)buffers_emptied]++;
+ }
+ count++;
+ }
+
+ if(count==5000) {
+ printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+ }
+
+ ch->HW_release_board(dev, interrupted);
+}
+
+static int COMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long jiffs;
+ int twin_open=0;
+ int retval;
+ struct net_device *savep;
+
+ if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+ return -ENODEV;
+ }
+
+ if (ch->twin && (((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN)) {
+ twin_open=1;
+ }
+
+ if (!twin_open) {
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+ if (request_irq(dev->irq, COMX_interrupt, 0, dev->name,
+ (void *)dev)) {
+ printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status |= IRQ_ALLOCATED;
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+ if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+ ch->init_status &= ~IRQ_ALLOCATED;
+ retval=-ENODEV;
+ goto error;
+ }
+ }
+
+ savep = ch->HW_access_board(dev);
+ COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+ if (ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+
+ COMX_CMD(dev, COMX_CMD_INIT);
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+ udelay(1000);
+
+ COMX_CMD(dev, COMX_CMD_OPEN);
+
+ jiffs = jiffies;
+ while (COMX_readw(dev, OFF_A_L2_LINKUP) != 3 && jiffies < jiffs + HZ) {
+ schedule_timeout(1);
+ }
+
+ if (jiffies >= jiffs + HZ) {
+ printk(KERN_ERR "%s: board timeout on OPEN command\n", dev->name);
+ ch->HW_release_board(dev, savep);
+ retval=-EIO;
+ goto error;
+ }
+
+ ch->init_status |= HW_OPEN;
+
+ /* Ez eleg ciki, de ilyen a rendszer */
+ if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+ ch->line_status &= ~LINE_UP;
+ } else {
+ ch->line_status |= LINE_UP;
+ }
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status);
+ }
+
+ ch->HW_release_board(dev, savep);
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0444;
+
+ }
+ }
+
+ return 0;
+
+error:
+ if(!twin_open) {
+ release_region(dev->base_addr, hw->io_extent);
+ free_irq(dev->irq, (void *)dev);
+ }
+ return retval;
+
+}
+
+static int COMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_channel *twin_ch;
+ struct net_device *savep;
+
+ savep = ch->HW_access_board(dev);
+
+ COMX_CMD(dev, COMX_CMD_CLOSE);
+ udelay(1000);
+ COMX_CMD(dev, COMX_CMD_EXIT);
+
+ ch->HW_release_board(dev, savep);
+
+ if (ch->init_status & IRQ_ALLOCATED) {
+ free_irq(dev->irq, (void *)dev);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+ release_region(dev->base_addr, hw->io_extent);
+
+ if (ch->twin && (twin_ch = ch->twin->priv) &&
+ (twin_ch->init_status & HW_OPEN)) {
+ /* Pass the irq to the twin */
+ if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name,
+ (void *)ch->twin) == 0) {
+ twin_ch->init_status |= IRQ_ALLOCATED;
+ }
+ }
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IRQ) == 0
+ || strcmp(procfile->name, FILENAME_IO) == 0
+ || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+ || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+ || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+ || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int COMX_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct net_device *savep;
+ int len = 0;
+
+ savep = ch->HW_access_board(dev);
+
+ len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+ "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+ "TxEMPTY: %02x, TxBUFP: %02x\n",
+ (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+ (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+ (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+ (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+ COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+ (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+ COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+ COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+ COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+ len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+ "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+ hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+ ch->HW_release_board(dev, savep);
+
+ return len;
+}
+
+static int COMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ unsigned long flags;
+ unsigned char id1, id2;
+ struct net_device *saved;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n",
+ dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+ /* 10 usec should be enough here */
+ udelay(100);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount=0;
+ while(loopcount++ < 10000 &&
+ readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ udelay(100);
+ }
+
+ if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+ printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+ dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+ retval=-ENODEV;
+ goto out;
+ }
+
+ writeb(0x55, dev->mem_start + 0x18ff);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && readb(dev->mem_start + 0x18ff) != 0) {
+ udelay(100);
+ }
+
+ if(readb(dev->mem_start + 0x18ff) != 0) {
+ printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+ dev->name);
+ retval=-ENODEV;
+ goto out;
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+ loopcount = 0;
+ while ( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int CMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 16;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ #if 0
+ unsigned char id1, id2;
+ #endif
+ struct net_device *saved;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ byte *COMX_address;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+ if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+ printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+ inb_p(dev->base_addr));
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET,
+ dev->base_addr);
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], COMX_address++);
+ }
+
+ len = 0;
+ COMX_address = (byte *)dev->mem_start;
+ while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+ len++;
+ }
+
+ outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readb(COMX_address - 1), fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ loopcount=0;
+ while( loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ udelay(100);
+ }
+
+ if (COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static int HICOMX_load_board(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ struct comx_firmware *fw = hw->firmware;
+ word board_segment = dev->mem_start >> 12;
+ int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+ struct net_device *saved;
+ unsigned char id1, id2;
+ unsigned long flags;
+ int retval;
+ int loopcount;
+ int len;
+ word *HICOMX_address;
+ char id = 1;
+
+ if (!fw || !fw->len) {
+ struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+ struct comx_privdata *twin_hw;
+
+ if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+ return -EAGAIN;
+ }
+
+ if (!(fw = twin_hw->firmware) || !fw->len) {
+ return -EAGAIN;
+ }
+ }
+
+ while (id != 4) {
+ if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+ break;
+ }
+ }
+
+ if (id != 4) {
+ printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+ dev->name, (unsigned int)dev->base_addr, id - 1,
+ inb_p(dev->base_addr + id - 1));
+ return -1;
+ }
+
+ id1 = fw->data[OFF_FW_L1_ID];
+ id2 = fw->data[OFF_FW_L1_ID + 1];
+ if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+ printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+ return -EAGAIN;
+ }
+
+ printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name,
+ (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+ id1 = fw->data[OFF_FW_L2_ID];
+ id2 = fw->data[OFF_FW_L2_ID + 1];
+ if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+ printk(KERN_INFO "with Layer 2 code %s\n",
+ (char *)(fw->data + OFF_FW_L2_ID + 2));
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ udelay(10);
+
+ save_flags(flags); cli();
+ saved=memory_used[mempos];
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_off(saved);
+ }
+ memory_used[mempos]=dev;
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+ outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (fw->len > len) {
+ writeb(fw->data[len++], HICOMX_address++);
+ }
+
+ len = 0;
+ HICOMX_address = (word *)dev->mem_start;
+ while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+ len++;
+ }
+
+ if (len != fw->len) {
+ printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+ "instead of 0x%02x\n", dev->name, len,
+ readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+ loopcount=0;
+ while(loopcount++ < 10000 && COMX_readw(dev, OFF_A_L2_LINKUP) != 1) {
+ udelay(100);
+ }
+
+ if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+ printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+ dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+ retval=-EAGAIN;
+ goto out;
+ }
+
+ ch->init_status |= FW_LOADED;
+ retval=0;
+
+out:
+ outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+ outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+
+ if(saved) {
+ ((struct comx_channel *)saved->priv)->HW_board_on(saved);
+ }
+ memory_used[mempos]=saved;
+ restore_flags(flags);
+ return retval;
+}
+
+static struct net_device *comx_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct comx_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+
+ if(!S_ISDIR(procfile->mode)) {
+ continue;
+ }
+
+ twin=procfile->data;
+ ch_twin=twin->priv;
+ hw_twin=ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+ dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+ dev->mem_start == twin->mem_start &&
+ hw->channel == (1 - hw_twin->channel) &&
+ ch->hardware == ch_twin->hardware) {
+ return twin;
+ }
+ }
+ return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ char *page;
+
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if(ch->init_status & HW_OPEN) {
+ return -EAGAIN;
+ }
+
+ if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+ } else {
+ byte *tmp;
+
+ if (!hw->firmware) {
+ if ((hw->firmware = kmalloc(sizeof(struct comx_firmware),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ hw->firmware->len = 0;
+ hw->firmware->data = NULL;
+ }
+
+ if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+ if (hw->firmware && hw->firmware->len && file->f_pos
+ && hw->firmware->len < count + file->f_pos) {
+ memcpy(tmp, hw->firmware->data, hw->firmware->len);
+ }
+ if (hw->firmware->data) {
+ kfree(hw->firmware->data);
+ }
+ copy_from_user(tmp + file->f_pos, buffer, count);
+ hw->firmware->len = entry->size = file->f_pos + count;
+ hw->firmware->data = tmp;
+ file->f_pos += count;
+ return count;
+ }
+
+ if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ hw->channel = simple_strtoul(page, NULL, 0);
+ if (hw->channel >= MAX_CHANNELNO) {
+ printk(KERN_ERR "Invalid channel number\n");
+ hw->channel = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ dev->irq = simple_strtoul(page, NULL, 0);
+ if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+ if (dev->irq < 3 || dev->irq > 15) {
+ printk(KERN_ERR "comxhw: Invalid irq number\n");
+ dev->irq = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_IO) == 0) {
+ dev->base_addr = simple_strtoul(page, NULL, 0);
+ if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300
+ || dev->base_addr > 0x3fc) {
+ printk(KERN_ERR "Invalid io value\n");
+ dev->base_addr = 0;
+ }
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+ dev->mem_start = simple_strtoul(page, NULL, 0);
+ if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+ dev->mem_start *= 16;
+ }
+ if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+ || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+ printk(KERN_ERR "Invalid memory page\n");
+ dev->mem_start = 0;
+ }
+ dev->mem_end = dev->mem_start + hw->memory_size;
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+ len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) {
+ len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+ } else {
+ len = sprintf(page, "external\n");
+ }
+ } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+ len = min(FILE_PAGESIZE, min(count,
+ hw->firmware ? (hw->firmware->len - off) : 0));
+ if (len < 0) {
+ len = 0;
+ }
+ *start = hw->firmware ? (hw->firmware->data + off) : NULL;
+ if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+ *eof = 1;
+ }
+ return len;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+ if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+ hw->memory_size = COMX_MEMORY_SIZE;
+ hw->io_extent = COMX_IO_EXTENT;
+ dev->base_addr = COMX_DEFAULT_IO;
+ dev->irq = COMX_DEFAULT_IRQ;
+ dev->mem_start = COMX_DEFAULT_MEMADDR;
+ dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+ } else if (ch->hardware == &hicomx_hw) {
+ hw->memory_size = HICOMX_MEMORY_SIZE;
+ hw->io_extent = HICOMX_IO_EXTENT;
+ dev->base_addr = HICOMX_DEFAULT_IO;
+ dev->irq = HICOMX_DEFAULT_IRQ;
+ dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+ dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 6;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+ == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 2; // Ezt tudjuk
+ new_file->nlink = 1;
+
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 9;
+ new_file->nlink = 1;
+ }
+
+ if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 8;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = NULL;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxhw_read_proc;
+ new_file->write_proc = &comxhw_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if (ch->hardware == &comx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = COMX_load_board;
+ } else if (ch->hardware == &cmx_hw) {
+ ch->HW_board_on = COMX_board_on;
+ ch->HW_board_off = COMX_board_off;
+ ch->HW_load_board = CMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else if (ch->hardware == &hicomx_hw) {
+ ch->HW_board_on = HICOMX_board_on;
+ ch->HW_board_off = HICOMX_board_off;
+ ch->HW_load_board = HICOMX_load_board;
+ ch->HW_set_clock = COMX_set_clock;
+ } else {
+ printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+ }
+
+ ch->HW_access_board = COMX_access_board;
+ ch->HW_release_board = COMX_release_board;
+ ch->HW_txe = COMX_txe;
+ ch->HW_open = COMX_open;
+ ch->HW_close = COMX_close;
+ ch->HW_send_packet = COMX_send_packet;
+ ch->HW_statistics = COMX_statistics;
+
+ if ((ch->twin = comx_twin_check(dev)) != NULL) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = dev;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_privdata *hw = ch->HW_privdata;
+
+ if (hw->firmware) {
+ if (hw->firmware->data) kfree(hw->firmware->data);
+ kfree(hw->firmware);
+ } if (ch->twin) {
+ struct comx_channel *twin_ch = ch->twin->priv;
+
+ twin_ch->twin = NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+ remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+ if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+ }
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int COMX_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+ return 0;
+}
+
+static struct comx_hardware comx_hw = {
+ "comx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware cmx_hw = {
+ "cmx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+ "hicomx",
+ VERSION,
+ COMX_init,
+ COMX_exit,
+ COMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+int __init comx_hw_comx_init(void)
+{
+ comx_register_hardware(&comx_hw);
+ comx_register_hardware(&cmx_hw);
+ comx_register_hardware(&hicomx_hw);
+ memset(memory_used, 0, sizeof(memory_used));
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("comx");
+ comx_unregister_hardware("cmx");
+ comx_unregister_hardware("hicomx");
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-locomx.c b/drivers/net/wan/comx-hw-locomx.c
new file mode 100644
index 000000000..010f02373
--- /dev/null
+++ b/drivers/net/wan/comx-hw-locomx.c
@@ -0,0 +1,496 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu>
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.10 (99/06/17):
+ * - rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ * - some printk's fixed
+ * - get rid of a memory leak (it was impossible though :))
+ *
+ * Version 0.12 (99/07/07):
+ * - check CTS for modem lines, not DCD (which is always high
+ * in case of this board)
+ * Version 0.13 (99/07/08):
+ * - Fix the transmitter status check
+ * - Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+ 11, TCRTxCP,
+ 14, DTRREQ,
+ 255
+};
+
+struct locomx_data {
+ int io_extent;
+ struct z8530_dev board;
+ struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+ struct net_device *dev=c->netdevice;
+ struct comx_channel *ch=dev->priv;
+
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, skb, "locomx_rx receiving");
+ }
+ ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+ printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+ return FRAME_DROPPED;
+ }
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+ }
+
+ if(!hw->board.chanA.tx_next_skb) {
+ return FRAME_QUEUED;
+ } else {
+ return FRAME_ACCEPTED;
+ }
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct locomx_data *hw=ch->HW_privdata;
+
+ if(!(ch->line_status & LINE_UP) &&
+ (hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status | LINE_UP);
+ }
+ if((ch->line_status & LINE_UP) &&
+ !(hw->board.chanA.status & CTS)) {
+ ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+ }
+ mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+ int ret;
+
+ if (!dev->base_addr || !dev->irq) {
+ return -ENODEV;
+ }
+
+ if (check_region(dev->base_addr, hw->io_extent)) {
+ return -EAGAIN;
+ }
+
+ request_region(dev->base_addr, hw->io_extent, dev->name);
+
+ hw->board.chanA.ctrlio=dev->base_addr + 5;
+ hw->board.chanA.dataio=dev->base_addr + 7;
+
+ hw->board.irq=dev->irq;
+ hw->board.chanA.netdevice=dev;
+ hw->board.chanA.dev=&hw->board;
+ hw->board.name=dev->name;
+ hw->board.chanA.txdma=TX_DMA;
+ hw->board.chanA.rxdma=RX_DMA;
+ hw->board.chanA.irqs=&z8530_nop;
+ hw->board.chanB.irqs=&z8530_nop;
+
+ if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT,
+ dev->name, &hw->board)) {
+ printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name,
+ dev->irq);
+ ret=-EAGAIN;
+ goto irq_fail;
+ }
+ if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+ printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n",
+ dev->name, TX_DMA);
+ ret=-EAGAIN;
+ goto dma1_fail;
+ }
+
+ if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+ printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n",
+ dev->name, RX_DMA);
+ ret=-EAGAIN;
+ goto dma2_fail;
+ }
+
+ save_flags(flags);
+ cli();
+
+ if(z8530_init(&hw->board)!=0)
+ {
+ printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+ ret=-ENODEV;
+ goto z8530_fail;
+ }
+
+ hw->board.chanA.dcdcheck=CTS;
+
+ z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+ z8530_channel_load(&hw->board.chanA, z8530_locomx);
+ z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+ z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+ if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+ goto z8530_fail;
+ }
+
+ restore_flags(flags);
+
+
+ hw->board.active=1;
+ hw->board.chanA.rx_function=locomx_rx;
+
+ ch->init_status |= HW_OPEN;
+ if (hw->board.chanA.status & DCD) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ comx_status(dev, ch->line_status);
+
+ init_timer(&hw->status_timer);
+ hw->status_timer.function=locomx_status_timerfun;
+ hw->status_timer.data=(unsigned long)dev;
+ hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+ add_timer(&hw->status_timer);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+ return 0;
+
+z8530_fail:
+ restore_flags(flags);
+ free_dma(RX_DMA);
+dma2_fail:
+ free_dma(TX_DMA);
+dma1_fail:
+ free_irq(dev->irq, &hw->board);
+irq_fail:
+ release_region(dev->base_addr, hw->io_extent);
+ return ret;
+}
+
+static int LOCOMX_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct locomx_data *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+ hw->board.chanA.rx_function=z8530_null_rx;
+ netif_stop_queue(dev);
+ z8530_sync_dma_close(dev, &hw->board.chanA);
+
+ z8530_shutdown(&hw->board);
+
+ del_timer(&hw->status_timer);
+ free_dma(RX_DMA);
+ free_dma(TX_DMA);
+ free_irq(dev->irq,&hw->board);
+ release_region(dev->base_addr,8);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int LOCOMX_statistics(struct net_device *dev,char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, "Hello\n");
+
+ return len;
+}
+
+static int LOCOMX_dump(struct net_device *dev) {
+ printk(KERN_INFO "LOCOMX_dump called\n");
+ return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ int val;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 0x360 && val != 0x368 && val != 0x370 &&
+ val != 0x378) {
+ printk(KERN_ERR "LoCOMX: incorrect io address!\n");
+ } else {
+ dev->base_addr = val;
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ val = simple_strtoul(page, NULL, 0);
+ if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+ printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+ } else {
+ dev->irq = val;
+ }
+ } else {
+ printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n",
+ entry->name);
+ free_page((unsigned long)page);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+
+
+static int LOCOMX_init(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+ struct locomx_data *hw;
+ struct proc_dir_entry *new_file;
+
+ /* Alloc data for private structure */
+ if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+ hw->io_extent = LOCOMX_IO_EXTENT;
+
+ /* Register /proc files */
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+/* No clock yet */
+/*
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &locomx_read_proc;
+ new_file->write_proc = &locomx_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+*/
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = LOCOMX_txe;
+ ch->HW_open = LOCOMX_open;
+ ch->HW_close = LOCOMX_close;
+ ch->HW_send_packet = LOCOMX_send_packet;
+ ch->HW_statistics = LOCOMX_statistics;
+ ch->HW_set_clock = NULL;
+
+ ch->current_stats = &hw->board.chanA.stats;
+ memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+ dev->base_addr = LOCOMX_DEFAULT_IO;
+ dev->irq = LOCOMX_DEFAULT_IRQ;
+
+
+ /* O.K. Count one more user on this module */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+static int LOCOMX_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_send_packet = NULL;
+ ch->HW_statistics = NULL;
+ ch->HW_set_clock = NULL;
+ memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+ ch->current_stats = &ch->stats;
+
+ kfree(ch->HW_privdata);
+
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+// remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+ "locomx",
+ VERSION,
+ LOCOMX_init,
+ LOCOMX_exit,
+ LOCOMX_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+int __init comx_hw_locomx_init(void)
+{
+ comx_register_hardware(&locomx_hw);
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware("locomx");
+ return;
+}
+#endif
diff --git a/drivers/net/wan/comx-hw-mixcom.c b/drivers/net/wan/comx-hw-mixcom.c
new file mode 100644
index 000000000..552443f88
--- /dev/null
+++ b/drivers/net/wan/comx-hw-mixcom.c
@@ -0,0 +1,948 @@
+/*
+ * Hardware driver for the MixCom synchronous serial board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.60 (99/06/11):
+ * - ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ * - recognize the one-channel MixCOM card (id byte = 0x13)
+ * - printk fixes
+ *
+ * Version 0.62 (99/07/15):
+ * - fixes according to the new hw docs
+ * - report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ * - line status report fixes
+ *
+ * Version 0.64 (99/12/01):
+ * - some more cosmetical fixes
+ */
+
+#define VERSION "0.64"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+ HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+ (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+ (1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+ u16 clock;
+ char channel;
+ char txbusy;
+ struct sk_buff *sending;
+ unsigned tx_ptr;
+ struct sk_buff *recving;
+ unsigned rx_ptr;
+ unsigned char status;
+ char card_has_status;
+};
+
+static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct net_device *dev, int cmd)
+{
+ unsigned long jiffs = jiffies;
+ unsigned char cec;
+ unsigned delay = 0;
+
+ while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) &&
+ (jiffs + HZ > jiffies)) {
+ udelay(1);
+ if (++delay > (100000 / HZ)) break;
+ }
+ if (cec) {
+ printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+ } else {
+ wr_hscx(dev, HSCX_CMDR, cmd);
+ }
+}
+
+static inline void hscx_fill_fifo(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+ outsb(dev->base_addr + HSCX_FIFO,
+ &(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+ if (to_send <= 32) {
+ hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ } else {
+ hscx_cmd(dev, HSCX_XTF);
+ hw->tx_ptr += 32;
+ }
+}
+
+static inline void hscx_empty_fifo(struct net_device *dev, int cnt)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->recving == NULL) {
+ if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+ ch->stats.rx_dropped++;
+ hscx_cmd(dev, HSCX_RHR);
+ } else {
+ skb_reserve(hw->recving, 16);
+ skb_put(hw->recving, HSCX_MTU);
+ }
+ hw->rx_ptr = 0;
+ }
+ if (cnt > 32 || !cnt || hw->recving == NULL) {
+ printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+ cnt, (void *)hw->recving);
+ return;
+ }
+
+ insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+ hw->rx_ptr += cnt;
+ hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct net_device *dev)
+{
+ unsigned long flags;
+ int id, vstr, ret=0;
+
+ save_flags(flags); cli();
+
+ id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f;
+
+ if (id != MIXCOM_ID ) {
+ ret=-ENODEV;
+ printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+ goto out;
+ }
+
+ vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+ if(vstr>=sizeof(hscx_versions)/sizeof(char*) ||
+ hscx_versions[vstr]==NULL) {
+ printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+ ret = -ENODEV;
+ } else {
+ printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+ ret = 0;
+ }
+
+out:
+
+ restore_flags(flags);
+ return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if (hw->clock) {
+ ;
+ } else {
+ ;
+ }
+}
+#endif
+
+static void mixcom_board_on(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+ outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON,
+ MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_board_off(struct net_device *dev)
+{
+ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+ udelay(1000);
+}
+
+static void mixcom_off(struct net_device *dev)
+{
+ wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+ wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+ wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+ wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+ wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+ hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+ if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ unsigned long flags;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+ }
+
+ if (!(ch->line_status & LINE_UP)) {
+ return FRAME_DROPPED;
+ }
+
+ if (skb->len > HSCX_MTU) {
+ ch->stats.tx_errors++;
+ return FRAME_ERROR;
+ }
+
+ save_flags(flags); cli();
+
+ if (test_and_set_bit(0, &hw->txbusy)) {
+ printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+ restore_flags(flags);
+ return FRAME_DROPPED;
+ }
+
+
+ hw->sending = skb;
+ hw->tx_ptr = 0;
+ hw->txbusy = 1;
+// atomic_inc(&skb->users); // save it
+ hscx_fill_fifo(dev);
+ restore_flags(flags);
+
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+
+ if (ch->debug_flags & DEBUG_HW_TX) {
+ comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte rsta;
+ register word length;
+
+ rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO |
+ HSCX_CRC | HSCX_RAB);
+ length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) |
+ rd_hscx(dev, HSCX_RBCL);
+
+ if ( length > hw->rx_ptr ) {
+ hscx_empty_fifo(dev, length - hw->rx_ptr);
+ }
+
+ if (!(rsta & HSCX_VFR)) {
+ ch->stats.rx_length_errors++;
+ }
+ if (rsta & HSCX_RDO) {
+ ch->stats.rx_over_errors++;
+ }
+ if (!(rsta & HSCX_CRC)) {
+ ch->stats.rx_crc_errors++;
+ }
+ if (rsta & HSCX_RAB) {
+ ch->stats.rx_frame_errors++;
+ }
+ ch->stats.rx_packets++;
+ ch->stats.rx_bytes += length;
+
+ if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+ skb_trim(hw->recving, hw->rx_ptr - 1);
+ if (ch->debug_flags & DEBUG_HW_RX) {
+ comx_debug_skb(dev, hw->recving,
+ "MIXCOM_interrupt receiving");
+ }
+ hw->recving->dev = dev;
+ if (ch->LINE_rx) {
+ ch->LINE_rx(dev, hw->recving);
+ }
+ }
+ else if(hw->recving) {
+ kfree_skb(hw->recving);
+ }
+ hw->recving = NULL;
+ hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+ struct mixcom_privdata *hw=ch->HW_privdata;
+ register byte exir;
+
+ exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+ if (exir & HSCX_RFO) {
+ ch->stats.rx_over_errors++;
+ if (hw->rx_ptr) {
+ kfree_skb(hw->recving);
+ hw->recving = NULL; hw->rx_ptr = 0;
+ }
+ printk(KERN_ERR "MIXCOM: rx overrun\n");
+ hscx_cmd(dev, HSCX_RHR);
+ }
+
+ if (exir & HSCX_XDU) { // xmit underrun
+ ch->stats.tx_errors++;
+ ch->stats.tx_aborted_errors++;
+ if (hw->tx_ptr) {
+ kfree_skb(hw->sending);
+ hw->sending = NULL;
+ hw->tx_ptr = 0;
+ }
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (ch->LINE_tx) {
+ ch->LINE_tx(dev);
+ }
+ printk(KERN_ERR "MIXCOM: tx underrun\n");
+ }
+
+ if (exir & HSCX_CSC) {
+ ch->stats.tx_carrier_errors++;
+ if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ } else if (ch->line_status & LINE_UP) {
+ ch->line_status &= ~LINE_UP;
+ if (ch->LINE_status) {
+ ch->LINE_status(dev,ch->line_status);
+ }
+ }
+ }
+ if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) &
+ HSCX_CTS)) { // Vonal fol
+ if (!test_and_set_bit(0,&ch->lineup_pending)) {
+ ch->lineup_timer.function = comx_lineup_func;
+ ch->lineup_timer.data = (unsigned long)dev;
+ ch->lineup_timer.expires = jiffies + HZ *
+ ch->lineup_delay;
+ add_timer(&ch->lineup_timer);
+ hscx_cmd(dev, HSCX_XRES);
+ clear_bit(0, &hw->txbusy);
+ if (hw->sending) {
+ kfree_skb(hw->sending);
+ }
+ hw->sending=NULL;
+ hw->tx_ptr = 0;
+ }
+ }
+ }
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct comx_channel *ch, *twin_ch;
+ struct mixcom_privdata *hw, *twin_hw;
+ register unsigned char ista;
+
+ if (dev==NULL) {
+ printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+ return;
+ }
+
+ ch = dev->priv;
+ hw = ch->HW_privdata;
+
+ save_flags(flags); cli();
+
+ while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF |
+ HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+ register byte ista2 = 0;
+
+ if (ista & HSCX_RME) {
+ mixcom_receive_frame(dev);
+ }
+ if (ista & HSCX_RPF) {
+ hscx_empty_fifo(dev, 32);
+ }
+ if (ista & HSCX_XPR) {
+ if (hw->tx_ptr) {
+ hscx_fill_fifo(dev);
+ } else {
+ clear_bit(0, &hw->txbusy);
+ ch->LINE_tx(dev);
+ }
+ }
+
+ if (ista & HSCX_EXB) {
+ mixcom_extended_interrupt(dev);
+ }
+
+ if ((ista & HSCX_EXA) && ch->twin) {
+ mixcom_extended_interrupt(ch->twin);
+ }
+
+ if ((ista & HSCX_ICA) && ch->twin &&
+ (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+ (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+ if (ista2 & HSCX_RME) {
+ mixcom_receive_frame(ch->twin);
+ }
+ if (ista2 & HSCX_RPF) {
+ hscx_empty_fifo(ch->twin, 32);
+ }
+ if (ista2 & HSCX_XPR) {
+ twin_ch=ch->twin->priv;
+ twin_hw=twin_ch->HW_privdata;
+ if (twin_hw->tx_ptr) {
+ hscx_fill_fifo(ch->twin);
+ } else {
+ clear_bit(0, &twin_hw->txbusy);
+ ch->LINE_tx(ch->twin);
+ }
+ }
+ }
+ }
+
+ restore_flags(flags);
+ return;
+}
+
+static int MIXCOM_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+ if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+ if(hw->channel==1) {
+ if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status &
+ IRQ_ALLOCATED)) {
+ printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+ return -EAGAIN;
+ }
+ }
+
+
+ /* Is our hw present at all ? Not checking for channel 0 if it is already
+ open */
+ if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+ if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+ return -EAGAIN;
+ }
+ if (mixcom_probe(dev)) {
+ return -ENODEV;
+ }
+ }
+
+ save_flags(flags); cli();
+
+ if(hw->channel==1) {
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ }
+
+ if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+ if (request_irq(dev->irq, MIXCOM_interrupt, 0,
+ dev->name, (void *)dev)) {
+ printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+ return -EAGAIN;
+ }
+ ch->init_status|=IRQ_ALLOCATED;
+ request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+ mixcom_board_on(dev);
+ }
+
+ mixcom_on(dev);
+
+
+ hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+ if(hw->status != 0xff) {
+ printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+ hw->card_has_status=1;
+ }
+
+ hw->txbusy = 0;
+ ch->init_status |= HW_OPEN;
+
+ if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+ ch->line_status |= LINE_UP;
+ } else {
+ ch->line_status &= ~LINE_UP;
+ }
+
+ restore_flags(flags);
+
+ ch->LINE_status(dev, ch->line_status);
+
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int MIXCOM_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ struct proc_dir_entry *procfile = ch->procdir->subdir;
+ unsigned long flags;
+
+
+ save_flags(flags); cli();
+
+ mixcom_off(dev);
+
+ /* This is channel 0, twin is not open, we can safely turn off everything */
+ if(hw->channel==0 && (!(TWIN(dev)) ||
+ !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+ mixcom_board_off(dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ ch->init_status &= ~IRQ_ALLOCATED;
+ }
+
+ /* This is channel 1, channel 0 has already been shutdown, we can release
+ this one too */
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+ mixcom_board_off(TWIN(dev));
+ free_irq(TWIN(dev)->irq, TWIN(dev));
+ release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+ COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+ }
+ }
+
+ /* the ioports for channel 1 can be safely released */
+ if(hw->channel==1) {
+ release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+ }
+
+ restore_flags(flags);
+
+ /* If we don't hold any hardware open */
+ if(!(ch->init_status & IRQ_ALLOCATED)) {
+ for (; procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ /* channel 0 was only waiting for us to close channel 1
+ close it completely */
+
+ if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+ for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir;
+ procfile ; procfile = procfile->next) {
+ if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+ strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+ strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+ strcmp(procfile->name, FILENAME_IRQ) == 0) {
+ procfile->mode = S_IFREG | 0644;
+ }
+ }
+ }
+
+ ch->init_status &= ~HW_OPEN;
+ return 0;
+}
+
+static int MIXCOM_statistics(struct net_device *dev,char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ // struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if(ch->init_status && IRQ_ALLOCATED) {
+ len += sprintf(page + len, "Mixcom board: hardware open\n");
+ }
+
+ return len;
+}
+
+static int MIXCOM_dump(struct net_device *dev) {
+ return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_IO) == 0) {
+ len = sprintf(page, "0x%x\n",
+ (unsigned int)MIXCOM_BOARD_BASE(dev));
+ } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+ len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+ } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+ if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+ else len = sprintf(page, "external\n");
+ } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+ len = sprintf(page, "%01d\n", hw->channel);
+ } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+ if (ch->twin) {
+ len = sprintf(page, "%s\n",ch->twin->name);
+ } else {
+ len = sprintf(page, "none\n");
+ }
+ } else {
+ printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+
+static struct net_device *mixcom_twin_check(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ struct net_device *twin;
+ struct comx_channel *ch_twin;
+ struct mixcom_privdata *hw_twin;
+
+
+ for ( ; procfile ; procfile = procfile->next) {
+ if(!S_ISDIR(procfile->mode)) continue;
+
+ twin = procfile->data;
+ ch_twin = twin->priv;
+ hw_twin = ch_twin->HW_privdata;
+
+
+ if (twin != dev && dev->irq && dev->base_addr &&
+ dev->irq == twin->irq &&
+ ch->hardware == ch_twin->hardware &&
+ dev->base_addr == twin->base_addr +
+ (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+ hw->channel == (1 - hw_twin->channel)) {
+ if (!TWIN(twin) || TWIN(twin)==dev) {
+ return twin;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+static void setup_twin(struct net_device* dev)
+{
+
+ if(TWIN(dev) && TWIN(TWIN(dev))) {
+ TWIN(TWIN(dev))=NULL;
+ }
+ if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+ if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+ TWIN(dev)=NULL;
+ } else {
+ TWIN(TWIN(dev))=dev;
+ }
+ }
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+ char *page;
+ int value;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_IO) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value != 0x180 && value != 0x280 && value != 0x380) {
+ printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+ } else {
+ dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+ }
+ } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+ printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+ } else {
+ dev->irq = value;
+ }
+ } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+ if (strncmp("ext", page, 3) == 0) {
+ hw->clock = 0;
+ } else {
+ int kbps;
+
+ kbps = simple_strtoul(page, NULL, 0);
+ if (!kbps) {
+ hw->clock = 0;
+ } else {
+ hw->clock = kbps;
+ }
+ if (hw->clock < 32 || hw->clock > 2000) {
+ hw->clock = 0;
+ printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+ }
+ }
+ if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+ ch->HW_set_clock(dev);
+ }
+ } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+ value = simple_strtoul(page, NULL, 0);
+ if (value > 2) {
+ printk(KERN_ERR "Invalid channel number\n");
+ } else {
+ dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+ hw->channel = value;
+ }
+ } else {
+ printk(KERN_ERR "hw_read_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ setup_twin(dev);
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int MIXCOM_init(struct net_device *dev) {
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw;
+ struct proc_dir_entry *new_file;
+
+ if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+ if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+#if 0
+ if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+#endif
+
+ if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &mixcom_read_proc;
+ new_file->write_proc = &mixcom_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->nlink = 1;
+
+ setup_twin(dev);
+
+ /* Fill in ch_struct hw specific pointers */
+ ch->HW_access_board = NULL;
+ ch->HW_release_board = NULL;
+ ch->HW_txe = MIXCOM_txe;
+ ch->HW_open = MIXCOM_open;
+ ch->HW_close = MIXCOM_close;
+ ch->HW_send_packet = MIXCOM_send_packet;
+ ch->HW_statistics = MIXCOM_statistics;
+ ch->HW_set_clock = NULL;
+
+ dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+ dev->irq = MIXCOM_DEFAULT_IRQ;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int MIXCOM_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct mixcom_privdata *hw = ch->HW_privdata;
+
+ if(hw->channel==0 && TWIN(dev)) {
+ return -EBUSY;
+ }
+
+ if(hw->channel==1 && TWIN(dev)) {
+ TWIN(TWIN(dev))=NULL;
+ }
+
+ kfree(ch->HW_privdata);
+ remove_proc_entry(FILENAME_IO, ch->procdir);
+ remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+ remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+ remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+ remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+ "mixcom",
+ VERSION,
+ MIXCOM_init,
+ MIXCOM_exit,
+ MIXCOM_dump,
+ NULL
+};
+
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+int __init comx_hw_mixcom_init(void)
+{
+ return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ comx_unregister_hardware("mixcom");
+}
+#endif
diff --git a/drivers/net/wan/comx-proto-fr.c b/drivers/net/wan/comx-proto-fr.c
new file mode 100644
index 000000000..ad62e310c
--- /dev/null
+++ b/drivers/net/wan/comx-proto-fr.c
@@ -0,0 +1,1006 @@
+/*
+ * Frame-relay protocol module for the COMX driver
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.70 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as builtin code
+ *
+ * Version 0.71 (99/06/25):
+ * - use skb priorities and queues for sending keepalive
+ * - use device queues for slave->master data transmit
+ * - set IFF_RUNNING only line protocol up
+ * - fixes on slave device flags
+ *
+ * Version 0.72 (99/07/09):
+ * - handle slave tbusy with master tbusy (should be fixed)
+ * - fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+ "for Linux kernel 2.2.X");
+
+#define FRAD_UI 0x03
+#define NLPID_IP 0xcc
+#define NLPID_Q933_LMI 0x08
+#define NLPID_CISCO_LMI 0x09
+#define Q933_ENQ 0x75
+#define Q933_LINESTAT 0x51
+#define Q933_COUNTERS 0x53
+
+#define MAXALIVECNT 3 /* No. of failures */
+
+struct fr_data {
+ u16 dlci;
+ struct net_device *master;
+ char keepa_pend;
+ char keepa_freq;
+ char keepalivecnt, keeploopcnt;
+ struct timer_list keepa_timer;
+ u8 local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct sk_buff *skb;
+ u8 *fr_packet;
+
+ skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+
+ if(skb==NULL)
+ return;
+
+ skb_reserve(skb, dev->hard_header_len);
+
+ fr_packet=(u8*)skb_put(skb, 13);
+
+ fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+ fr_packet[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ fr_packet[2] = FRAD_UI;
+ fr_packet[3] = NLPID_Q933_LMI;
+ fr_packet[4] = 0;
+ fr_packet[5] = Q933_ENQ;
+ fr_packet[6] = Q933_LINESTAT;
+ fr_packet[7] = 0x01;
+ fr_packet[8] = 0x01;
+ fr_packet[9] = Q933_COUNTERS;
+ fr_packet[10] = 0x02;
+ fr_packet[11] = ++fr->local_cnt;
+ fr_packet[12] = fr->remote_cnt;
+
+ skb->dev = dev;
+ skb->priority = TC_PRIO_CONTROL;
+ dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (ch->init_status & LINE_OPEN) {
+ if (fr->keepalivecnt == MAXALIVECNT) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ dev->flags &= ~IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags &= ~IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status & ~PROTO_UP);
+ }
+ }
+ }
+ if (fr->keepalivecnt <= MAXALIVECNT) {
+ ++fr->keepalivecnt;
+ }
+ fr_keepalive_send(dev);
+ }
+ mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb,
+ u16 dlci, u8 nlpid)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+
+ if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+ return;
+ }
+
+ fr->remote_cnt = skb->data[7];
+ if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+ fr->keepalivecnt = 0;
+ if ((ch->line_status & LINE_UP) &&
+ !(ch->line_status & PROTO_UP)) {
+ comx_status(dev, ch->line_status |= PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata)
+ && (sfr->master == dev) &&
+ (sdev->flags & IFF_UP)) {
+ sdev->flags |= IFF_RUNNING;
+ comx_status(sdev,
+ sch->line_status | PROTO_UP);
+ }
+ }
+ }
+ }
+}
+
+static void fr_set_keepalive(struct net_device *dev, int keepa)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!keepa && fr->keepa_freq) { // switch off
+ fr->keepa_freq = 0;
+ if (ch->line_status & LINE_UP) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ dev->flags |= IFF_RUNNING;
+ del_timer(&fr->keepa_timer);
+ }
+ return;
+ }
+
+ if (keepa) { // bekapcs
+ if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+ del_timer(&fr->keepa_timer);
+ }
+ fr->keepa_freq = keepa;
+ fr->local_cnt = fr->remote_cnt = 0;
+ fr->keepa_timer.expires = jiffies + HZ;
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+ comx_status(dev, ch->line_status);
+ if(ch->line_status & LINE_UP) {
+ add_timer(&fr->keepa_timer);
+ }
+ }
+}
+
+static void fr_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ u16 dlci;
+ u8 nlpid;
+
+ if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+ dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+ if ((nlpid = skb->data[3]) == 0) { // Optional padding
+ nlpid = skb->data[4];
+ skb_pull(skb, 1);
+ }
+ skb_pull(skb, 4); /* DLCI and header throw away */
+
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n",
+ dlci, nlpid);
+ comx_debug_skb(dev, skb, "Contents");
+ }
+
+ /* Megkeressuk, kihez tartozik */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sfr->dlci == dlci)) {
+ skb->dev = sdev;
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug(dev, "Passing it to %s\n",sdev->name);
+ }
+ if (dev != sdev) {
+ sch->stats.rx_packets++;
+ sch->stats.rx_bytes += skb->len;
+ }
+ break;
+ }
+ }
+ switch(nlpid) {
+ case NLPID_IP:
+ skb->protocol = htons(ETH_P_IP);
+ skb->mac.raw = skb->data;
+ comx_rx(sdev, skb);
+ break;
+ case NLPID_Q933_LMI:
+ fr_rx_lmi(dev, skb, dlci, nlpid);
+ default:
+ kfree_skb(skb);
+ break;
+ }
+}
+
+static int fr_tx(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int cnt = 1;
+
+ /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel,
+ es annal a slave-nel aki eppen kuldott.
+ Egy helyen akkor all, ha a master kuldott.
+ Ez megint jo lesz majd, ha utemezni akarunk */
+
+ /* This should be fixed, the slave tbusy should be set when
+ the masters queue is full and reset when not */
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_wake_queue(sdev);
+ cnt++;
+ }
+ }
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static void fr_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (status & LINE_UP) {
+ if (!fr->keepa_freq) {
+ status |= PROTO_UP;
+ }
+ } else {
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ }
+
+ if (dev == fr->master && fr->keepa_freq) {
+ if (status & LINE_UP) {
+ fr->keepa_timer.expires = jiffies + HZ;
+ add_timer(&fr->keepa_timer);
+ fr->keepalivecnt = MAXALIVECNT + 1;
+ fr->keeploopcnt = 0;
+ } else {
+ del_timer(&fr->keepa_timer);
+ }
+ }
+
+ /* Itt a status valtozast vegig kell vinni az osszes slave-n */
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ if(status & LINE_UP) {
+ netif_wake_queue(sdev);
+ }
+ comx_status(sdev, status);
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ dev->flags |= IFF_RUNNING;
+ } else {
+ dev->flags &= ~IFF_RUNNING;
+ }
+ }
+ }
+}
+
+static int fr_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+ struct comx_channel *mch;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+ (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+ printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+ return -EINVAL;
+ }
+
+ if (!fr->master) {
+ return -ENODEV;
+ }
+ mch = fr->master->priv;
+ if (fr->master != dev && (!(mch->init_status & LINE_OPEN)
+ || (mch->protocol != &fr_master_protocol))) {
+ printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+ "unable to open %s\n", fr->master->name, dev->name);
+ return -ENODEV;
+ }
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ if (fr->master == dev) {
+ if (fr->keepa_freq) {
+ fr->keepa_timer.function = fr_keepalive_timerfun;
+ fr->keepa_timer.data = (unsigned long)dev;
+ add_timer(&fr->keepa_timer);
+ } else {
+ if (ch->line_status & LINE_UP) {
+ ch->line_status |= PROTO_UP;
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+ } else {
+ ch->line_status = mch->line_status;
+ if(fr->master->flags & IFF_RUNNING) {
+ dev->flags |= IFF_RUNNING;
+ }
+ }
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+// comx_status(dev, ch->line_status);
+ return 0;
+}
+
+static int fr_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct proc_dir_entry *comxdir = ch->procdir;
+
+ if (fr->master == dev) { // Ha master
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (fr->keepa_freq) {
+ del_timer(&fr->keepa_timer);
+ }
+
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) &&
+ (sch->init_status & LINE_OPEN)) {
+ dev_close(sdev);
+ }
+ }
+ }
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+ dev->flags &= ~IFF_RUNNING;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+ strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+ strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+ return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct comx_channel *sch, *mch;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct fr_data *sfr;
+ struct net_device *sdev;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ if (!fr->master) {
+ printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+ return 0;
+ }
+
+ mch = fr->master->priv;
+
+ /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+ if (ch->debug_flags & DEBUG_COMX_DLCI) {
+ comx_debug_skb(dev, skb, "Sending frame");
+ }
+
+ if (dev != fr->master) {
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ newskb->dev=fr->master;
+ dev_queue_xmit(newskb);
+ dev_kfree_skb(skb);
+ ch->stats.tx_packets++;
+ ch->stats.tx_bytes += skb->len;
+ } else {
+ netif_stop_queue(dev);
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (netif_queue_stopped(sdev))) {
+ netif_stop_queue(sdev);
+ }
+ }
+
+ switch(mch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ skb_push(skb, dev->hard_header_len);
+ /* Put in DLCI */
+ skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+ skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
+ skb->data[2] = FRAD_UI;
+ skb->data[3] = NLPID_IP;
+
+ return dev->hard_header_len;
+}
+
+static int fr_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ int len = 0;
+
+ if (fr->master == dev) {
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+ struct net_device *sdev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ int slaves = 0;
+
+ len += sprintf(page + len,
+ "This is a Frame Relay master device\nSlaves: ");
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) &&
+ (sfr->master == dev) && (sdev != dev)) {
+ slaves++;
+ len += sprintf(page + len, "%s ", sdev->name);
+ }
+ }
+ len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+ if (fr->keepa_freq) {
+ len += sprintf(page + len, "Line keepalive (value %d) "
+ "status %s [%d]\n", fr->keepa_freq,
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN",
+ fr->keepalivecnt);
+ } else {
+ len += sprintf(page + len, "Line keepalive protocol "
+ "is not set\n");
+ }
+ } else { // if slave
+ len += sprintf(page + len,
+ "This is a Frame Relay slave device, master: %s\n",
+ fr->master ? fr->master->name : "(not set)");
+ }
+ return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ int len = 0;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (strcmp(file->name, FILENAME_DLCI) == 0) {
+ len = sprintf(page, "%04d\n", fr->dlci);
+ } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+ len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+ "(none)");
+ } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+ len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq)
+ : sprintf(page, "off\n");
+ } else {
+ printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) *eof = 1;
+ return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = NULL;
+ char *page;
+
+ if (ch) {
+ fr = ch->LINE_privdata;
+ }
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+ u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+ if (dlci_new > 1023) {
+ printk(KERN_ERR "Invalid DLCI value\n");
+ }
+ else fr->dlci = dlci_new;
+ } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+ struct net_device *new_master = dev_get_by_name(page);
+
+ if (new_master && new_master->type == ARPHRD_FRAD) {
+ struct comx_channel *sch = new_master->priv;
+ struct fr_data *sfr = sch->LINE_privdata;
+
+ if (sfr && sfr->master == new_master) {
+ if(fr->master)
+ dev_put(fr->master);
+ fr->master = new_master;
+ /* Megorokli a master statuszat */
+ ch->line_status = sch->line_status;
+ }
+ }
+ } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+ int keepa_new = -1;
+
+ if (strcmp(page, KEEPALIVE_OFF) == 0) {
+ keepa_new = 0;
+ } else {
+ keepa_new = simple_strtoul(page, NULL, 10);
+ }
+
+ if (keepa_new < 0 || keepa_new > 100) {
+ printk(KERN_ERR "invalid keepalive\n");
+ } else {
+ if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ if (keepa_new) {
+ fr_set_keepalive(dev, keepa_new);
+ }
+ }
+ } else {
+ printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static int fr_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+ struct net_device *sdev = dev;
+ struct comx_channel *sch;
+ struct fr_data *sfr;
+ struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+ /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+ if (fr->master && fr->master == dev) {
+ for (; dir ; dir = dir->next) {
+ if(!S_ISDIR(dir->mode)) {
+ continue;
+ }
+ if ((sdev = dir->data) && (sch = sdev->priv) &&
+ (sdev->type == ARPHRD_DLCI) &&
+ (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+ dev_close(sdev);
+ sfr->master = NULL;
+ }
+ }
+ }
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ ch->LINE_status = 0;
+
+ if (fr->master != dev) { // if not master, remove dlci
+ if(fr->master)
+ dev_put(fr->master);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ remove_proc_entry(FILENAME_MASTER, ch->procdir);
+ } else {
+ if (fr->keepa_freq) {
+ fr_set_keepalive(dev, 0);
+ }
+ remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+ remove_proc_entry(FILENAME_DLCI, ch->procdir);
+ }
+
+ kfree(fr);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int fr_master_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+ fr->master = dev; // this means master
+ fr->dlci = 0; // let's say default
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_FRAD;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 4;
+ new_file->nlink = 1;
+
+ fr_set_keepalive(dev, 0);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int fr_slave_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr;
+ struct proc_dir_entry *new_file;
+
+ if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(fr, 0, sizeof(struct fr_data));
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_DLCI;
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = fr_rx;
+ ch->LINE_tx = fr_tx;
+ ch->LINE_status = fr_status;
+ ch->LINE_open = fr_open;
+ ch->LINE_close = fr_close;
+ ch->LINE_xmit = fr_xmit;
+ ch->LINE_header = fr_header;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = fr_statistics;
+
+ if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 5;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
+ ch->procdir)) == NULL) {
+ return -EIO;
+ }
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &fr_read_proc;
+ new_file->write_proc = &fr_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = 10;
+ new_file->nlink = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status |= HW_OPEN;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->init_status &= ~HW_OPEN;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_txe(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct fr_data *fr = ch->LINE_privdata;
+
+ if (!fr->master) {
+ return 0;
+ }
+
+ ch = fr->master->priv;
+ fr = ch->LINE_privdata;
+ return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct net_device *dev, char *page)
+{
+ return 0;
+}
+
+static int dlci_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = dlci_open;
+ ch->HW_close = dlci_close;
+ ch->HW_txe = dlci_txe;
+ ch->HW_statistics = dlci_statistics;
+
+ /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ ch->HW_open = NULL;
+ ch->HW_close = NULL;
+ ch->HW_txe = NULL;
+ ch->HW_statistics = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int dlci_dump(struct net_device *dev)
+{
+ printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+ return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+ "frad",
+ VERSION,
+ ARPHRD_FRAD,
+ fr_master_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_protocol fr_slave_protocol = {
+ "ietf-ip",
+ VERSION,
+ ARPHRD_DLCI,
+ fr_slave_init,
+ fr_exit,
+ NULL
+};
+
+static struct comx_hardware fr_dlci = {
+ "dlci",
+ VERSION,
+ dlci_init,
+ dlci_exit,
+ dlci_dump,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+int __init comx_proto_fr_init(void)
+{
+ int ret;
+
+ if ((ret = comx_register_hardware(&fr_dlci))) {
+ return ret;
+ }
+ if ((ret = comx_register_protocol(&fr_master_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_hardware(fr_dlci.name);
+ comx_unregister_protocol(fr_master_protocol.name);
+ comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c
new file mode 100644
index 000000000..c0fc0c6bd
--- /dev/null
+++ b/drivers/net/wan/comx-proto-lapb.c
@@ -0,0 +1,548 @@
+/*
+ * LAPB protocol module for the COMX driver
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/14):
+ * - cleaned up the source code a bit
+ * - ported back to kernel, now works as non-module
+ *
+ */
+
+#define VERSION "0.80"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ if (!dev || !dev->priv) {
+ dev_kfree_skb(skb);
+ } else {
+ lapb_data_received(dev->priv, skb);
+ }
+}
+
+static int comxlapb_tx(struct net_device *dev)
+{
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ return dev->hard_header_len;
+}
+
+static void comxlapb_status(struct net_device *dev, unsigned short status)
+{
+ struct comx_channel *ch;
+
+ if (!dev || !(ch = dev->priv)) {
+ return;
+ }
+ if (status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int comxlapb_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int err = 0;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ err = lapb_connect_request(ch);
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb opened, error code: %d\n",
+ dev->name, err);
+ }
+
+ if (!err) {
+ ch->init_status |= LINE_OPEN;
+ MOD_INC_USE_COUNT;
+ }
+ return err;
+}
+
+static int comxlapb_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!(ch->init_status & HW_OPEN)) {
+ return -ENODEV;
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb closed\n", dev->name);
+ }
+
+ lapb_disconnect_request(ch);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~PROTO_UP;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct sk_buff *skb2;
+
+ if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+ return -ENODEV;
+ }
+
+ if (dev->type == ARPHRD_X25) { // first byte tells what to do
+ switch(skb->data[0]) {
+ case 0x00:
+ break; // transmit
+ case 0x01:
+ lapb_connect_request(ch);
+ kfree_skb(skb);
+ return 0;
+ case 0x02:
+ lapb_disconnect_request(ch);
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ skb_pull(skb,1);
+ }
+
+ netif_stop_queue(dev);
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ lapb_data_request(ch, skb2);
+ }
+
+ return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct net_device *dev, char *page)
+{
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ len += sprintf(page + len, "Line status: ");
+ if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+ len += sprintf(page + len, "not initialized\n");
+ return len;
+ }
+ len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+ "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+ parms.t1timer, parms.t1, parms.t2timer, parms.t2,
+ parms.n2count, parms.n2, parms.window);
+
+ return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct lapb_parms_struct parms;
+ int len = 0;
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (strcmp(file->name, FILENAME_T1) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t1timer, parms.t1);
+ } else if (strcmp(file->name, FILENAME_T2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.t2timer, parms.t2);
+ } else if (strcmp(file->name, FILENAME_N2) == 0) {
+ len += sprintf(page + len, "%02u / %02u\n",
+ parms.n2count, parms.n2);
+ } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+ len += sprintf(page + len, "%u\n", parms.window);
+ } else if (strcmp(file->name, FILENAME_MODE) == 0) {
+ len += sprintf(page + len, "%s, %s\n",
+ parms.mode & LAPB_DCE ? "DCE" : "DTE",
+ parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+ } else {
+ printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+ return -EBADF;
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer,
+ u_long count, void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = entry->parent->data;
+ struct lapb_parms_struct parms;
+ unsigned long parm;
+ char *page;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (lapb_getparms(dev->priv, &parms)) {
+ return -ENODEV;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ copy_from_user(page, buffer, count);
+ if (*(page + count - 1) == '\n') {
+ *(page + count - 1) = 0;
+ }
+
+ if (strcmp(entry->name, FILENAME_T1) == 0) {
+ parm=simple_strtoul(page,NULL,10);
+ if (parm > 0 && parm < 100) {
+ parms.t1=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_T2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.t2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_N2) == 0) {
+ parm=simple_strtoul(page, NULL, 10);
+ if (parm > 0 && parm < 100) {
+ parms.n2=parm;
+ lapb_setparms(dev->priv, &parms);
+ }
+ } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+ parms.window = simple_strtoul(page, NULL, 10);
+ lapb_setparms(dev->priv, &parms);
+ } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+ if (comx_strcasecmp(page, "dte") == 0) {
+ parms.mode &= ~(LAPB_DCE | LAPB_DTE);
+ parms.mode |= LAPB_DTE;
+ } else if (comx_strcasecmp(page, "dce") == 0) {
+ parms.mode &= ~(LAPB_DTE | LAPB_DCE);
+ parms.mode |= LAPB_DCE;
+ } else if (comx_strcasecmp(page, "std") == 0 ||
+ comx_strcasecmp(page, "standard") == 0) {
+ parms.mode &= ~LAPB_EXTENDED;
+ parms.mode |= LAPB_STANDARD;
+ } else if (comx_strcasecmp(page, "ext") == 0 ||
+ comx_strcasecmp(page, "extended") == 0) {
+ parms.mode &= ~LAPB_STANDARD;
+ parms.mode |= LAPB_EXTENDED;
+ }
+ lapb_setparms(dev->priv, &parms);
+ } else {
+ printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n",
+ entry->name);
+ return -EBADF;
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb connected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x01; // link established
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0444;
+ }
+ }
+
+
+ ch->line_status |= PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+ struct comx_channel *ch = token;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n",
+ ch->dev->name, reason);
+ }
+
+ if (ch->dev->type == ARPHRD_X25) {
+ unsigned char *p;
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "comxlapb: out of memory!\n");
+ return;
+ }
+ p = skb_put(skb,1);
+ *p = 0x02; // link disconnected
+ skb->dev = ch->dev;
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ }
+
+ for (; comxdir; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+ comxdir->mode = S_IFREG | 0644;
+ }
+ }
+
+ ch->line_status &= ~PROTO_UP;
+ comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->dev->type == ARPHRD_X25) {
+ skb_push(skb, 1);
+ skb->data[0] = 0; // indicate data for X25
+ skb->protocol = htons(ETH_P_X25);
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ }
+
+ skb->dev = ch->dev;
+ skb->mac.raw = skb->data;
+ comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+ struct comx_channel *ch = token;
+
+ if (ch->HW_send_packet) {
+ ch->HW_send_packet(ch->dev, skb);
+ }
+}
+
+static int comxlapb_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+ dev->hard_header_len = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+ }
+ lapb_unregister(dev->priv);
+
+ remove_proc_entry(FILENAME_T1, ch->procdir);
+ remove_proc_entry(FILENAME_T2, ch->procdir);
+ remove_proc_entry(FILENAME_N2, ch->procdir);
+ remove_proc_entry(FILENAME_MODE, ch->procdir);
+ remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct lapb_register_struct lapbreg;
+
+ dev->mtu = 1500;
+ dev->hard_header_len = 4;
+ dev->addr_len = 0;
+
+ ch->LINE_rx = comxlapb_rx;
+ ch->LINE_tx = comxlapb_tx;
+ ch->LINE_status = comxlapb_status;
+ ch->LINE_open = comxlapb_open;
+ ch->LINE_close = comxlapb_close;
+ ch->LINE_xmit = comxlapb_xmit;
+ ch->LINE_header = comxlapb_header;
+ ch->LINE_statistics = comxlapb_statistics;
+
+ lapbreg.connect_confirmation = comxlapb_connected;
+ lapbreg.connect_indication = comxlapb_connected;
+ lapbreg.disconnect_confirmation = comxlapb_disconnected;
+ lapbreg.disconnect_indication = comxlapb_disconnected;
+ lapbreg.data_indication = comxlapb_data_indication;
+ lapbreg.data_transmit = comxlapb_data_transmit;
+ if (lapb_register(dev->priv, &lapbreg)) {
+ return -ENOMEM;
+ }
+ if (ch->debug_flags & DEBUG_COMX_LAPB) {
+ comx_debug(dev, "%s: lapb registered\n", dev->name);
+ }
+
+ if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+ return -ENOMEM;
+ }
+ if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+ return -ENOMEM;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comxlapb_init_lapb(struct net_device *dev)
+{
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->type = ARPHRD_LAPB;
+
+ return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct net_device *dev)
+{
+ dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_X25;
+
+ return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comxlapb_read_proc;
+ new_file->write_proc = &comxlapb_write_proc;
+ new_file->proc_iops = &comx_normal_inode_ops;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+ "lapb",
+ VERSION,
+ ARPHRD_LAPB,
+ comxlapb_init_lapb,
+ comxlapb_exit,
+ NULL
+};
+
+static struct comx_protocol comx25_protocol = {
+ "x25",
+ VERSION,
+ ARPHRD_X25,
+ comxlapb_init_x25,
+ comxlapb_exit,
+ NULL
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+ int ret;
+
+ if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+ return ret;
+ }
+ return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(comxlapb_protocol.name);
+ comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx-proto-ppp.c b/drivers/net/wan/comx-proto-ppp.c
new file mode 100644
index 000000000..0b791685d
--- /dev/null
+++ b/drivers/net/wan/comx-proto-ppp.c
@@ -0,0 +1,269 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ * - written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ * - added hdlc protocol
+ * - protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ * - some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ * - don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ * Version 0.23 (99/12/02):
+ * - tbusy fixes
+ *
+ */
+
+#define VERSION "0.23"
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include "syncppp.h"
+#include "comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+ struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+ struct net_device *dev=(struct net_device *)d;
+ struct comx_channel *ch=dev->priv;
+ struct syncppp_data *spch=ch->LINE_privdata;
+ struct sppp *sp = (struct sppp *)sppp_of(dev);
+
+ if(!(ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_UP)) {
+ comx_status(dev, ch->line_status | PROTO_UP);
+ }
+ if((ch->line_status & PROTO_UP) &&
+ (sp->pp_link_state==SPPP_LINK_DOWN)) {
+ comx_status(dev, ch->line_status & ~PROTO_UP);
+ }
+ mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct net_device *dev)
+{
+ struct comx_channel *ch=dev->priv;
+
+ if(ch->line_status & LINE_UP) {
+ netif_wake_queue(dev);
+ }
+ return 0;
+}
+
+static void syncppp_status(struct net_device *dev, unsigned short status)
+{
+ status &= ~(PROTO_UP | PROTO_LOOP);
+ if(status & LINE_UP) {
+ netif_wake_queue(dev);
+ sppp_open(dev);
+ } else {
+ /* Line went down */
+ netif_stop_queue(dev);
+ sppp_close(dev);
+ }
+ comx_status(dev, status);
+}
+
+static int syncppp_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+ ch->init_status |= LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ if(ch->line_status & LINE_UP) {
+ sppp_open(dev);
+ }
+
+ init_timer(&spch->status_timer);
+ spch->status_timer.function=syncppp_status_timerfun;
+ spch->status_timer.data=(unsigned long)dev;
+ spch->status_timer.expires=jiffies + HZ*3;
+ add_timer(&spch->status_timer);
+
+ return 0;
+}
+
+static int syncppp_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct syncppp_data *spch = ch->LINE_privdata;
+
+ if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+ del_timer(&spch->status_timer);
+
+ sppp_close(dev);
+
+ ch->init_status &= ~LINE_OPEN;
+ ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+ return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ netif_stop_queue(dev);
+ switch(ch->HW_send_packet(dev, skb)) {
+ case FRAME_QUEUED:
+ netif_wake_queue(dev);
+ break;
+ case FRAME_ACCEPTED:
+ case FRAME_DROPPED:
+ break;
+ case FRAME_ERROR:
+ printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
+ dev->name, skb->len);
+ break;
+ }
+ return 0;
+}
+
+
+static int syncppp_statistics(struct net_device *dev, char *page)
+{
+ int len = 0;
+
+ len += sprintf(page + len, " ");
+ return len;
+}
+
+
+static int syncppp_exit(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+
+ sppp_detach(dev);
+
+ dev->flags = 0;
+ dev->type = 0;
+ dev->mtu = 0;
+
+ ch->LINE_rx = NULL;
+ ch->LINE_tx = NULL;
+ ch->LINE_status = NULL;
+ ch->LINE_open = NULL;
+ ch->LINE_close = NULL;
+ ch->LINE_xmit = NULL;
+ ch->LINE_header = NULL;
+ ch->LINE_rebuild_header = NULL;
+ ch->LINE_statistics = NULL;
+
+ kfree(ch->LINE_privdata);
+ ch->LINE_privdata = NULL;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int syncppp_init(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
+
+ ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+ pppdev->dev = dev;
+ sppp_attach(pppdev);
+
+ if(ch->protocol == &hdlc_protocol) {
+ pppdev->sppp.pp_flags |= PP_CISCO;
+ dev->type = ARPHRD_HDLC;
+ } else {
+ pppdev->sppp.pp_flags &= ~PP_CISCO;
+ dev->type = ARPHRD_PPP;
+ }
+
+ ch->LINE_rx = sppp_input;
+ ch->LINE_tx = syncppp_tx;
+ ch->LINE_status = syncppp_status;
+ ch->LINE_open = syncppp_open;
+ ch->LINE_close = syncppp_close;
+ ch->LINE_xmit = syncppp_xmit;
+ ch->LINE_header = NULL;
+ ch->LINE_statistics = syncppp_statistics;
+
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+ "ppp",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+static struct comx_protocol hdlc_protocol = {
+ "hdlc",
+ VERSION,
+ ARPHRD_PPP,
+ syncppp_init,
+ syncppp_exit,
+ NULL
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+int __init comx_proto_ppp_init(void)
+{
+ int ret;
+
+ if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+ return ret;
+ }
+ return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ comx_unregister_protocol(syncppp_protocol.name);
+ comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c
new file mode 100644
index 000000000..d3ca69e86
--- /dev/null
+++ b/drivers/net/wan/comx.c
@@ -0,0 +1,1238 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ *
+ * for Linux kernel 2.2.X
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version 0.80 (99/06/11):
+ * - clean up source code (playing a bit of indent)
+ * - port back to kernel, add support for non-module versions
+ * - add support for board resets when channel protocol is down
+ * - reset the device structure after protocol exit
+ * the syncppp driver needs it
+ * - add support for /proc/comx/protocols and
+ * /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ * - comment out the board reset support code, the locomx
+ * driver seems not buggy now
+ * - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ * - Handle stats correctly if the lowlevel driver is
+ * is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ * - reset line_status when interface is down
+ *
+ * Version 0.84 (99/12/01):
+ * - comx_status should not check for IFF_UP (to report
+ * line status from dev->open())
+ */
+
+#define VERSION "0.84"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#ifndef CONFIG_PROC_FS
+#error For now, COMX really needs the /proc filesystem
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops;
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops; // with open/relase
+static struct file_operations comx_debug_file_ops; // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ &comx_delete_dentry /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+ 0, 4, "comx",
+ S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &comx_root_inode_ops,
+ NULL, comx_fill_inode,
+ NULL, &proc_root, NULL
+};
+
+struct comx_debugflags_struct comx_debugflags[] = {
+ { "comx_rx", DEBUG_COMX_RX },
+ { "comx_tx", DEBUG_COMX_TX },
+ { "hw_tx", DEBUG_HW_TX },
+ { "hw_rx", DEBUG_HW_RX },
+ { "hdlc_keepalive", DEBUG_HDLC_KEEPALIVE },
+ { "comxppp", DEBUG_COMX_PPP },
+ { "comxlapb", DEBUG_COMX_LAPB },
+ { "dlci", DEBUG_COMX_DLCI },
+ { NULL, 0 }
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct net_device *dev, char *fmt, ...)
+{
+ struct comx_channel *ch = dev->priv;
+ char *page,*str;
+ va_list args;
+ int len;
+
+ if (!ch->debug_area) return 0;
+
+ if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+ va_start(args, fmt);
+ len = vsprintf(str = page, fmt, args);
+ va_end(args);
+
+ if (len >= PAGE_SIZE) {
+ printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+
+ while (len) {
+ int to_copy;
+ int free = (ch->debug_start - ch->debug_end + ch->debug_size)
+ % ch->debug_size;
+
+ to_copy = min( free ? free : ch->debug_size,
+ min (ch->debug_size - ch->debug_end, len) );
+ memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+ str += to_copy;
+ len -= to_copy;
+ ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+ if (ch->debug_start == ch->debug_end) // Full ? push start away
+ ch->debug_start = (ch->debug_start + len + 1) %
+ ch->debug_size;
+ ch->debug_file->size = (ch->debug_end - ch->debug_start +
+ ch->debug_size) % ch->debug_size;
+ }
+
+ free_page((unsigned long)page);
+ return 0;
+}
+
+int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+ if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+ if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+ return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg)
+{
+ int pos = 0;
+ struct comx_channel *ch = dev->priv;
+
+ if (!ch->debug_area) return 0;
+
+ comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+ while (pos != len) {
+ char line[80];
+ int i = 0;
+
+ memset(line, 0, 80);
+ sprintf(line,"%04d ", pos);
+ do {
+ sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+ sprintf(line + 60 + (pos % 16), "%c",
+ isprint(bytes[pos]) ? bytes[pos] : '.');
+ pos++;
+ } while (pos != len && pos % 16);
+
+ while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+ line[77] = '\n';
+ line[78] = 0;
+
+ comx_debug(dev, "%s", line);
+ }
+ comx_debug(dev, "\n");
+ return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+ ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] =
+ ch->current_stats->tx_bytes;
+
+ ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+ mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+ if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+ ch->HW_reset(dev);
+ }
+ }
+
+ mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif
+
+static int comx_open(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret=0;
+
+ if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+ if ((ret = ch->HW_open(dev))) return ret;
+ if ((ret = ch->LINE_open(dev))) {
+ ch->HW_close(dev);
+ return ret;
+ };
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0444;
+ }
+
+#if 0
+ ch->reset_pending = 1;
+ ch->reset_timeout = 30;
+ ch->reset_timer.function = comx_reset_timerfun;
+ ch->reset_timer.data = (unsigned long)dev;
+ ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+ add_timer(&ch->reset_timer);
+#endif
+
+ return 0;
+}
+
+static int comx_close(struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ struct proc_dir_entry *comxdir = ch->procdir->subdir;
+ int ret = -ENODEV;
+
+ if (test_and_clear_bit(0, &ch->lineup_pending)) {
+ del_timer(&ch->lineup_timer);
+ }
+
+#if 0
+ del_timer(&ch->reset_timer);
+#endif
+
+ if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+ ret = ch->LINE_close(dev);
+ }
+
+ if (ret) return ret;
+
+ if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+ ret = ch->HW_close(dev);
+ }
+
+ ch->line_status=0;
+
+ for (; comxdir ; comxdir = comxdir->next) {
+ if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+ strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+ comxdir->mode = S_IFREG | 0644;
+ }
+
+ return ret;
+}
+
+void comx_status(struct net_device *dev, int status)
+{
+ struct comx_channel *ch = dev->priv;
+
+#if 0
+ if(status & (PROTO_UP | PROTO_LOOP)) {
+ clear_bit(0,&ch->reset_pending);
+ }
+#endif
+
+ printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+ dev->name, status & LINE_UP ? "UP" : "DOWN",
+ status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ?
+ "UP" : "DOWN");
+
+ ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct comx_channel *ch = dev->priv;
+ int rc;
+
+ if (skb->len > dev->mtu + dev->hard_header_len) {
+ printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+ (int)skb->len, dev->mtu);
+ }
+
+ if (ch->debug_flags & DEBUG_COMX_TX) {
+ comx_debug_skb(dev, skb, "comx_xmit skb");
+ }
+
+ rc=ch->LINE_xmit(skb, dev);
+// if (!rc) dev_kfree_skb(skb);
+
+ return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_header) {
+ return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+ } else {
+ return 0;
+ }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_rebuild_header) {
+ return(ch->LINE_rebuild_header(skb));
+ } else {
+ return 0;
+ }
+}
+
+int comx_rx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->debug_flags & DEBUG_COMX_RX) {
+ comx_debug_skb(dev, skb, "comx_rx skb");
+ }
+ if (skb) {
+ netif_rx(skb);
+ }
+ return 0;
+}
+
+static struct net_device_stats *comx_stats(struct net_device *dev)
+{
+ struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+ return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+ struct net_device *dev = (struct net_device *)d;
+ struct comx_channel *ch = dev->priv;
+
+ del_timer(&ch->lineup_timer);
+ clear_bit(0, &ch->lineup_pending);
+
+ if (ch->LINE_status) {
+ ch->LINE_status(dev, ch->line_status |= LINE_UP);
+ }
+}
+
+#define LOADAVG(avg, off) (int) \
+ ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off] - ch->avg_bytes[(ch->loadavg_counter - 1 \
+ - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+ % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct net_device *dev, char *page)
+{
+ struct comx_channel *ch = dev->priv;
+ int len = 0;
+ int tmp;
+ int i = 0;
+ char tmpstr[20];
+ int tmpstrlen = 0;
+
+ len += sprintf(page + len, "Interface administrative status is %s, "
+ "modem status is %s, protocol is %s\n",
+ dev->flags & IFF_UP ? "UP" : "DOWN",
+ ch->line_status & LINE_UP ? "UP" : "DOWN",
+ ch->line_status & PROTO_LOOP ? "LOOP" :
+ ch->line_status & PROTO_UP ? "UP" : "DOWN");
+ len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+ "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+ LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+ tmpstr[0] = 0;
+ for (i=0; i != 3; i++) {
+ char tf;
+
+ tf = ch->loadavg[i] % 60 == 0 &&
+ ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+ tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s",
+ ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf,
+ i == 2 ? ")\n" : "/");
+ }
+ len += sprintf(page + len,
+ "%s (output): %d / %d / %d bits/s (%s", tmpstr,
+ LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size),
+ LOADAVG(2, ch->loadavg_size), tmpstr);
+
+ len += sprintf(page + len, "Debug flags: ");
+ tmp = len; i = 0;
+ while (comx_debugflags[i].name) {
+ if (ch->debug_flags & comx_debugflags[i].value)
+ len += sprintf(page + len, "%s ",
+ comx_debugflags[i].name);
+ i++;
+ }
+ len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+ len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+ "aborts: %lu\n buffer overrun: %lu, pbuffer overrun: %lu\n"
+ "TX errors: underrun: %lu\n",
+ ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors,
+ ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors,
+ ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+ ch->current_stats->tx_fifo_errors);
+
+ if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+ len += ch->LINE_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Line status: driver not initialized\n");
+ }
+ if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+ len += ch->HW_statistics(dev, page + len);
+ } else {
+ len += sprintf(page+len, "Board status: driver not initialized\n");
+ }
+
+ return len;
+}
+
+static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct comx_channel *ch = dev->priv;
+
+ if (ch->LINE_ioctl) {
+ return(ch->LINE_ioctl(dev, ifr, cmd));
+ }
+ return -EINVAL;
+}
+
+static void comx_reset_dev(struct net_device *dev)
+{
+ dev->open = comx_open;
+ dev->stop = comx_close;
+ dev->hard_start_xmit = comx_xmit;
+ dev->hard_header = comx_header;
+ dev->rebuild_header = comx_rebuild_header;
+ dev->get_stats = comx_stats;
+ dev->do_ioctl = comx_ioctl;
+ dev->change_mtu = NULL;
+ dev->tx_queue_len = 20;
+ dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct net_device *dev)
+{
+ struct comx_channel *ch;
+
+ if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch, 0, sizeof(struct comx_channel));
+
+ ch->loadavg[0] = 5;
+ ch->loadavg[1] = 300;
+ ch->loadavg[2] = 900;
+ ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1;
+ if ((ch->avg_bytes = kmalloc(ch->loadavg_size *
+ sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+ ch->loadavg_counter = 0;
+ ch->loadavg_timer.function = comx_loadavg_timerfun;
+ ch->loadavg_timer.data = (unsigned long)dev;
+ ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+ add_timer(&ch->loadavg_timer);
+
+ dev->priv = (void *)ch;
+ ch->dev = dev;
+ ch->line_status &= ~LINE_UP;
+
+ ch->current_stats = &ch->stats;
+
+ comx_reset_dev(dev);
+ return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct net_device *dev = file->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_STATUS) == 0) {
+ len = comx_statistics(dev, page);
+ } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+ len = sprintf(page, "%s\n", ch->hardware ?
+ ch->hardware->name : HWNAME_NONE);
+ } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+ len = sprintf(page, "%s\n", ch->protocol ?
+ ch->protocol->name : PROTONAME_NONE);
+ } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+ len = sprintf(page, "%01d\n", ch->lineup_delay);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+ struct comx_hardware *hw;
+ struct comx_protocol *line;
+
+ int len = 0;
+
+ if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+ for(hw=comx_channels;hw;hw=hw->next)
+ len+=sprintf(page+len, "%s\n", hw->name);
+ } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+ for(line=comx_lines;line;line=line->next)
+ len+=sprintf(page+len, "%s\n", line->name);
+ }
+
+ if (off >= len) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = page + off;
+ if (count >= len - off) {
+ *eof = 1;
+ }
+ return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+ void *data)
+{
+ struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+ struct net_device *dev = (struct net_device *)entry->parent->data;
+ struct comx_channel *ch=(struct comx_channel *)dev->priv;
+ char *page;
+ struct comx_hardware *hw = comx_channels;
+ struct comx_protocol *line = comx_lines;
+ char str[30];
+ int ret=0;
+
+ if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+ printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+ return -EIO;
+ }
+
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+ copy_from_user(page, buffer, count);
+
+ if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+ if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+ int i;
+ int ret = 0;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (ch->debug_area) kfree(ch->debug_area);
+ if ((ch->debug_area = kmalloc(ch->debug_size = i,
+ GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ }
+ ch->debug_start = ch->debug_end = 0;
+ restore_flags(flags);
+ free_page((unsigned long)page);
+ return count;
+ }
+
+ if (*page != '+' && *page != '-') {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (comx_debugflags[i].value &&
+ strncmp(comx_debugflags[i].name, page + 1,
+ strlen(comx_debugflags[i].name))) {
+ i++;
+ }
+
+ if (comx_debugflags[i].value == 0) {
+ printk(KERN_ERR "Invalid debug option\n");
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ if (*page == '+') {
+ ch->debug_flags |= comx_debugflags[i].value;
+ } else {
+ ch->debug_flags &= ~comx_debugflags[i].value;
+ }
+ } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (hw) {
+ if (strcmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+ sprintf(str,"comx-hw-%s",page);
+ request_module(str);
+ }
+ hw=comx_channels;
+ while (hw) {
+ if (comx_strcasecmp(hw->name, page) == 0) {
+ break;
+ } else {
+ hw = hw->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+ if (ch->init_status & HW_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->hardware = hw;
+ entry->size = strlen(page) + 1;
+ if (hw && hw->hw_init) hw->hw_init(dev);
+ } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+ if(strlen(page)>10) {
+ free_page((unsigned long)page);
+ return -EINVAL;
+ }
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#ifdef CONFIG_KMOD
+ if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+ sprintf(str,"comx-proto-%s",page);
+ request_module(str);
+ }
+ line=comx_lines;
+ while (line) {
+ if (comx_strcasecmp(line->name, page) == 0) {
+ break;
+ } else {
+ line = line->next;
+ }
+ }
+#endif
+
+ if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+ free_page((unsigned long)page);
+ return -ENODEV;
+ }
+
+ if (ch->init_status & LINE_OPEN) {
+ free_page((unsigned long)page);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ free_page((unsigned long)page);
+ return ret;
+ }
+ ch->protocol = line;
+ entry->size = strlen(page) + 1;
+ comx_reset_dev(dev);
+ if (line && line->line_init) line->line_init(dev);
+ } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+ int i;
+
+ if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+ if (i >=0 && i < 10) {
+ ch->lineup_delay = i;
+ } else {
+ printk(KERN_ERR "comx: invalid lineup_delay value\n");
+ }
+ }
+ }
+
+ free_page((unsigned long)page);
+ return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+ switch(orig) {
+ case 0:
+ file->f_pos = max(0, min(offset,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 1:
+ file->f_pos = max(0, min(offset + file->f_pos,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ case 2:
+ file->f_pos = max(0,
+ min(offset + file->f_dentry->d_inode->i_size,
+ file->f_dentry->d_inode->i_size));
+ return(file->f_pos);
+ }
+ return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+ if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+ return -EACCES;
+ }
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+ struct net_device *dev = de->parent->data;
+ struct comx_channel *ch = dev->priv;
+ loff_t copied = 0;
+ unsigned long flags;
+
+ save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+ // from irq inside read. no problem if the buffer is
+ // large enough
+
+ while (count > 0 && ch->debug_start != ch->debug_end) {
+ int len;
+
+ len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+ %ch->debug_size, min (ch->debug_size -
+ ch->debug_start, count));
+
+ if (len) copy_to_user(buffer + copied,
+ ch->debug_area + ch->debug_start, len);
+ ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+ de->size -= len;
+ count -= len;
+ copied += len;
+ }
+
+ restore_flags(flags);
+ return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct proc_dir_entry *new_dir, *debug_file;
+ struct net_device *dev;
+ struct comx_channel *ch;
+
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR,
+ &comx_root_dir)) == NULL) {
+ return -EIO;
+ }
+
+ new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->nlink = 2;
+ new_dir->data = NULL; // ide jon majd a struct dev
+
+ /* Ezek kellenek */
+ if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644,
+ strlen(HWNAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644,
+ strlen(PROTONAME_NONE) + 1, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+ return -ENOMEM;
+ }
+ if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+ return -ENOMEM;
+ }
+
+ if ((debug_file = create_proc_entry(FILENAME_DEBUG,
+ S_IFREG | 0644, new_dir)) == NULL) {
+ return -ENOMEM;
+ }
+ debug_file->ops = &comx_debug_inode_ops;
+ debug_file->data = (void *)debug_file;
+ debug_file->read_proc = NULL; // see below
+ debug_file->write_proc = &comx_write_proc;
+ debug_file->nlink = 1;
+
+ if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct net_device));
+ dev->name = (char *)new_dir->name;
+ dev->init = comx_init_dev;
+
+ if (register_netdevice(dev)) {
+ return -EIO;
+ }
+ ch=dev->priv;
+ if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memset(ch->if_ptr, 0, sizeof(struct ppp_device));
+ ch->debug_file = debug_file;
+ ch->procdir = new_dir;
+ new_dir->data = dev;
+
+ ch->debug_start = ch->debug_end = 0;
+ if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE,
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+ struct net_device *dev = entry->data;
+ struct comx_channel *ch = dev->priv;
+ int ret;
+
+ /* Egyelore miert ne ? */
+ if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+ return -EBUSY;
+ }
+
+ if (ch->protocol && ch->protocol->line_exit &&
+ (ret=ch->protocol->line_exit(dev))) {
+ return ret;
+ }
+ if (ch->hardware && ch->hardware->hw_exit &&
+ (ret=ch->hardware->hw_exit(dev))) {
+ if(ch->protocol && ch->protocol->line_init) {
+ ch->protocol->line_init(dev);
+ }
+ return ret;
+ }
+ ch->protocol = NULL;
+ ch->hardware = NULL;
+
+ del_timer(&ch->loadavg_timer);
+ kfree(ch->avg_bytes);
+
+ unregister_netdev(dev);
+ if (ch->debug_area) {
+ kfree(ch->debug_area);
+ }
+ if (dev->priv) {
+ kfree(dev->priv);
+ }
+ kfree(dev);
+
+ remove_proc_entry(FILENAME_DEBUG, entry);
+ remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+ remove_proc_entry(FILENAME_STATUS, entry);
+ remove_proc_entry(FILENAME_HARDWARE, entry);
+ remove_proc_entry(FILENAME_PROTOCOL, entry);
+ remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct proc_dir_entry *de;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ return ERR_PTR(-ENOTDIR);
+ }
+
+ if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+ for (de = de->subdir ; de ; de = de->next) {
+ if ((de && de->low_ino) &&
+ (de->namelen == dentry->d_name.len) &&
+ (memcmp(dentry->d_name.name, de->name,
+ de->namelen) == 0)) {
+ if ((inode = proc_get_inode(dir->i_sb,
+ de->low_ino, de)) == NULL) {
+ printk(KERN_ERR "COMX: lookup error\n");
+ return ERR_PTR(-EINVAL);
+ }
+ break;
+ }
+ }
+ }
+ dentry->d_op = &comx_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+ d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+ int size, struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *new_file;
+
+ if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = (void *)new_file;
+ new_file->read_proc = &comx_read_proc;
+ new_file->write_proc = &comx_write_proc;
+ new_file->size = size;
+ new_file->nlink = 1;
+ }
+ return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ comx_channels = comx_hw;
+ } else {
+ while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+ hw = hw->next;
+ }
+ if (strcmp(comx_hw->name, hw->name) == 0) {
+ return -1;
+ }
+ hw->next = comx_hw;
+ }
+
+ printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+ return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+ struct comx_hardware *hw = comx_channels;
+
+ if (!hw) {
+ return -1;
+ }
+
+ if (strcmp(hw->name, name) == 0) {
+ comx_channels = comx_channels->next;
+ return 0;
+ }
+
+ while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+ hw = hw->next;
+ }
+
+ if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+ hw->next = hw->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ comx_lines = comx_line;
+ } else {
+ while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+ pr = pr->next;
+ }
+ if (strcmp(comx_line->name, pr->name) == 0) {
+ return -1;
+ }
+ pr->next = comx_line;
+ }
+
+ printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+ return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+ struct comx_protocol *pr = comx_lines;
+
+ if (!pr) {
+ return -1;
+ }
+
+ if (strcmp(pr->name, name) == 0) {
+ comx_lines = comx_lines->next;
+ return 0;
+ }
+
+ while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+ pr = pr->next;
+ }
+
+ if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+ pr->next = pr->next->next;
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+ struct proc_dir_entry *new_file;
+
+ memcpy(&comx_root_inode_ops, &proc_dir_inode_operations,
+ sizeof(struct inode_operations));
+ comx_root_inode_ops.lookup = &comx_lookup;
+ comx_root_inode_ops.mkdir = &comx_mkdir;
+ comx_root_inode_ops.rmdir = &comx_rmdir;
+
+ memcpy(&comx_normal_inode_ops, &proc_net_inode_operations,
+ sizeof(struct inode_operations));
+ comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+ comx_normal_inode_ops.lookup = &comx_lookup;
+
+ memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops,
+ sizeof(struct inode_operations));
+ comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+ memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+ sizeof(struct file_operations));
+ comx_normal_file_ops.open = &comx_file_open;
+ comx_normal_file_ops.release = &comx_file_release;
+
+ memcpy(&comx_debug_file_ops, &comx_normal_file_ops,
+ sizeof(struct file_operations));
+ comx_debug_file_ops.llseek = &comx_debug_lseek;
+ comx_debug_file_ops.read = &comx_debug_read;
+
+ if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+ if ((new_file = create_proc_entry(FILENAME_HARDWARELIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+ if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST,
+ S_IFREG | 0444, &comx_root_dir)) == NULL) {
+ return -ENOMEM;
+ }
+
+ new_file->ops = &comx_normal_inode_ops;
+ new_file->data = new_file;
+ new_file->read_proc = &comx_root_read_proc;
+ new_file->write_proc = NULL;
+ new_file->nlink = 1;
+
+
+ printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n",
+ VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+ comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+ comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+ comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+ comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+ comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+ comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+ comx_proto_fr_init();
+#endif
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+ remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+ proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h
new file mode 100644
index 000000000..e02849b90
--- /dev/null
+++ b/drivers/net/wan/comx.h
@@ -0,0 +1,240 @@
+/*
+ * General definitions for the COMX driver
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * net_device_stats:
+ * rx_length_errors rec_len < 4 || rec_len > 2000
+ * rx_over_errors receive overrun (OVR)
+ * rx_crc_errors rx crc error
+ * rx_frame_errors aborts rec'd (ABO)
+ * rx_fifo_errors status fifo overrun (PBUFOVR)
+ * rx_missed_errors receive buffer overrun (BUFOVR)
+ * tx_aborted_errors ?
+ * tx_carrier_errors modem line status changes
+ * tx_fifo_errors tx underrun (locomx)
+ */
+#include <linux/config.h>
+
+struct comx_protocol {
+ char *name;
+ char *version;
+ unsigned short encap_type;
+ int (*line_init)(struct net_device *dev);
+ int (*line_exit)(struct net_device *dev);
+ struct comx_protocol *next;
+ };
+
+struct comx_hardware {
+ char *name;
+ char *version;
+ int (*hw_init)(struct net_device *dev);
+ int (*hw_exit)(struct net_device *dev);
+ int (*hw_dump)(struct net_device *dev);
+ struct comx_hardware *next;
+ };
+
+struct comx_channel {
+ void *if_ptr; // General purpose pointer
+ struct net_device *dev; // Where we belong to
+ struct net_device *twin; // On dual-port cards
+ struct proc_dir_entry *procdir; // the directory
+
+ unsigned char init_status;
+ unsigned char line_status;
+
+ struct timer_list lineup_timer; // against line jitter
+ int lineup_pending;
+ unsigned char lineup_delay;
+
+#if 0
+ struct timer_list reset_timer; // for board resetting
+ int reset_pending;
+ int reset_timeout;
+#endif
+
+ struct net_device_stats stats;
+ struct net_device_stats *current_stats;
+#if 0
+ unsigned long board_resets;
+#endif
+ unsigned long *avg_bytes;
+ int loadavg_counter, loadavg_size;
+ int loadavg[3];
+ struct timer_list loadavg_timer;
+ int debug_flags;
+ char *debug_area;
+ int debug_start, debug_end, debug_size;
+ struct proc_dir_entry *debug_file;
+#ifdef CONFIG_COMX_DEBUG_RAW
+ char *raw;
+ int raw_len;
+#endif
+ // LINE specific
+ struct comx_protocol *protocol;
+ void (*LINE_rx)(struct net_device *dev, struct sk_buff *skb);
+ int (*LINE_tx)(struct net_device *dev);
+ void (*LINE_status)(struct net_device *dev, u_short status);
+ int (*LINE_open)(struct net_device *dev);
+ int (*LINE_close)(struct net_device *dev);
+ int (*LINE_xmit)(struct sk_buff *skb, struct net_device *dev);
+ int (*LINE_header)(struct sk_buff *skb, struct net_device *dev,
+ u_short type,void *daddr, void *saddr,
+ unsigned len);
+ int (*LINE_rebuild_header)(struct sk_buff *skb);
+ int (*LINE_statistics)(struct net_device *dev, char *page);
+ int (*LINE_parameter_check)(struct net_device *dev);
+ int (*LINE_ioctl)(struct net_device *dev, struct ifreq *ifr,
+ int cmd);
+ void (*LINE_mod_use)(int);
+ void * LINE_privdata;
+
+ // HW specific
+
+ struct comx_hardware *hardware;
+ void (*HW_board_on)(struct net_device *dev);
+ void (*HW_board_off)(struct net_device *dev);
+ struct net_device *(*HW_access_board)(struct net_device *dev);
+ void (*HW_release_board)(struct net_device *dev, struct net_device *savep);
+ int (*HW_txe)(struct net_device *dev);
+ int (*HW_open)(struct net_device *dev);
+ int (*HW_close)(struct net_device *dev);
+ int (*HW_send_packet)(struct net_device *dev,struct sk_buff *skb);
+ int (*HW_statistics)(struct net_device *dev, char *page);
+#if 0
+ int (*HW_reset)(struct net_device *dev, char *page);
+#endif
+ int (*HW_load_board)(struct net_device *dev);
+ void (*HW_set_clock)(struct net_device *dev);
+ void *HW_privdata;
+ };
+
+struct comx_debugflags_struct {
+ char *name;
+ int value;
+ };
+
+#define COMX_ROOT_DIR_NAME "comx"
+
+#define FILENAME_HARDWARE "boardtype"
+#define FILENAME_HARDWARELIST "boardtypes"
+#define FILENAME_PROTOCOL "protocol"
+#define FILENAME_PROTOCOLLIST "protocols"
+#define FILENAME_DEBUG "debug"
+#define FILENAME_CLOCK "clock"
+#define FILENAME_STATUS "status"
+#define FILENAME_IO "io"
+#define FILENAME_IRQ "irq"
+#define FILENAME_KEEPALIVE "keepalive"
+#define FILENAME_LINEUPDELAY "lineup_delay"
+#define FILENAME_CHANNEL "channel"
+#define FILENAME_FIRMWARE "firmware"
+#define FILENAME_MEMADDR "memaddr"
+#define FILENAME_TWIN "twin"
+#define FILENAME_T1 "t1"
+#define FILENAME_T2 "t2"
+#define FILENAME_N2 "n2"
+#define FILENAME_WINDOW "window"
+#define FILENAME_MODE "mode"
+#define FILENAME_DLCI "dlci"
+#define FILENAME_MASTER "master"
+#ifdef CONFIG_COMX_DEBUG_RAW
+#define FILENAME_RAW "raw"
+#endif
+
+#define PROTONAME_NONE "none"
+#define HWNAME_NONE "none"
+#define KEEPALIVE_OFF "off"
+
+#define FRAME_ACCEPTED 0 /* sending and xmitter busy */
+#define FRAME_DROPPED 1
+#define FRAME_ERROR 2 /* xmitter error */
+#define FRAME_QUEUED 3 /* sending but more can come */
+
+#define LINE_UP 1 /* Modem UP */
+#define PROTO_UP 2
+#define PROTO_LOOP 4
+
+#define HW_OPEN 1
+#define LINE_OPEN 2
+#define FW_LOADED 4
+#define IRQ_ALLOCATED 8
+
+#define DEBUG_COMX_RX 2
+#define DEBUG_COMX_TX 4
+#define DEBUG_HW_TX 16
+#define DEBUG_HW_RX 32
+#define DEBUG_HDLC_KEEPALIVE 64
+#define DEBUG_COMX_PPP 128
+#define DEBUG_COMX_LAPB 256
+#define DEBUG_COMX_DLCI 512
+
+#define DEBUG_PAGESIZE 3072
+#define DEFAULT_DEBUG_SIZE 4096
+#define DEFAULT_LINEUP_DELAY 1
+#define FILE_PAGESIZE 3072
+
+#ifndef COMX_PPP_MAJOR
+#define COMX_PPP_MAJOR 88
+#endif
+
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8 byte;
+#endif
+#ifndef word
+typedef u16 word;
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int comx_register_hardware(struct comx_hardware *comx_hw);
+extern int comx_unregister_hardware(char *name);
+extern int comx_register_protocol(struct comx_protocol *comx_line);
+extern int comx_unregister_protocol(char *name);
+
+extern int comx_rx(struct net_device *dev, struct sk_buff *skb);
+extern void comx_status(struct net_device *dev, int status);
+extern void comx_lineup_func(unsigned long d);
+
+extern int comx_debug(struct net_device *dev, char *fmt, ...);
+extern int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg);
+extern int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len,
+ char *msg);
+extern int comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff --git a/drivers/net/wan/comxhw.h b/drivers/net/wan/comxhw.h
new file mode 100644
index 000000000..15230dc1f
--- /dev/null
+++ b/drivers/net/wan/comxhw.h
@@ -0,0 +1,113 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors: Arpad Bakay <bakay.arpad@synergon.hu>,
+ * Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define LOCOMX_IO_EXTENT 8
+#define COMX_IO_EXTENT 4
+#define HICOMX_IO_EXTENT 16
+
+#define COMX_MAX_TX_SIZE 1600
+#define COMX_MAX_RX_SIZE 2048
+
+#define COMX_JAIL_OFFSET 0xffff
+#define COMX_JAIL_VALUE 0xfe
+#define COMX_MEMORY_SIZE 65536
+#define HICOMX_MEMORY_SIZE 16384
+#define COMX_MEM_MIN 0xa0000
+#define COMX_MEM_MAX 0xf0000
+
+#define COMX_DEFAULT_IO 0x360
+#define COMX_DEFAULT_IRQ 10
+#define COMX_DEFAULT_MEMADDR 0xd0000
+#define HICOMX_DEFAULT_IO 0x320
+#define HICOMX_DEFAULT_IRQ 10
+#define HICOMX_DEFAULT_MEMADDR 0xd0000
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+#define MAX_CHANNELNO 2
+
+#define COMX_CHANNEL_OFFSET 0x2000
+
+#define COMX_ENABLE_BOARD_IT 0x40
+#define COMX_BOARD_RESET 0x20
+#define COMX_ENABLE_BOARD_MEM 0x10
+#define COMX_DISABLE_BOARD_MEM 0
+#define COMX_DISABLE_ALL 0x00
+
+#define HICOMX_DISABLE_ALL 0x00
+#define HICOMX_ENABLE_BOARD_MEM 0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET 0x01
+#define HICOMX_PRG_MEM 4
+#define HICOMX_DATA_MEM 0
+#define HICOMX_ID_BYTE 0x55
+
+#define CMX_ID_BYTE 0x31
+#define COMX_CLOCK_CONST 8000
+
+#define LINKUP_READY 3
+
+#define OFF_FW_L1_ID 0x01e /* ID bytes */
+#define OFF_FW_L2_ID 0x1006
+#define FW_L1_ID_1 0xab
+#define FW_L1_ID_2_COMX 0xc0
+#define FW_L1_ID_2_HICOMX 0xc1
+#define FW_L2_ID_1 0xab
+
+#define OFF_A_L2_CMD 0x130 /* command register for L2 */
+#define OFF_A_L2_CMDPAR 0x131 /* command parameter byte */
+#define OFF_A_L1_STATB 0x122 /* stat. block for L1 */
+#define OFF_A_L1_ABOREC 0x122 /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123 /* receive overrun counter */
+#define OFF_A_L1_CRCREC 0x124 /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125 /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126 /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127 /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE 0x127 /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC 0x128 /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80 0x129 /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC 0x12a /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80 0x12b /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA 0x12c /* IT rep disable */
+#define OFF_A_L1_CHNR 0x12d /* L1 channel logical number */
+#define OFF_A_L1_CLKINI 0x12e /* Timer Const */
+#define OFF_A_L2_LINKUP 0x132 /* Linkup byte */
+#define OFF_A_L2_DAV 0x134 /* Rx DAV */
+#define OFF_A_L2_RxBUFP 0x136 /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138 /* Tx Empty */
+#define OFF_A_L2_TxBUFP 0x13a /* Tx Buf */
+#define OFF_A_L2_NBUFFS 0x144 /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164 /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165 /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC 0x166 /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167 /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168 /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169 /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR 0x16C /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D /* LAPB no. of invalid frames */
+
+#define OFF_A_L2_T1 0x174 /* T1 timer */
+#define OFF_A_L2_ADDR 0x176 /* DCE = 1, DTE = 3 */
+
+#define COMX_CMD_INIT 1
+#define COMX_CMD_EXIT 2
+#define COMX_CMD_OPEN 16
+#define COMX_CMD_CLOSE 17
+
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 633514166..544fcb8dd 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -1150,19 +1150,19 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
{
switch(cmd) {
case COSAIORSET: /* Reset the device */
- if (!suser())
+ if (!capable(CAP_NET_ADMIN))
return -EACCES;
return cosa_reset(cosa);
case COSAIOSTRT: /* Start the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_start(cosa, arg);
case COSAIODOWNLD: /* Download the firmware */
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_download(cosa, (struct cosa_download *)arg);
case COSAIORMEM:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
return cosa_readmem(cosa, (struct cosa_download *)arg);
case COSAIORTYPE:
@@ -1189,7 +1189,7 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
case COSAIONRCHANS:
return cosa->nchannels;
case COSAIOBMSET:
- if (!suser())
+ if (!capable(CAP_SYS_RAWIO))
return -EACCES;
if (is_8bit(cosa))
return -EINVAL;
diff --git a/drivers/net/wan/hscx.h b/drivers/net/wan/hscx.h
new file mode 100644
index 000000000..675b7b1f1
--- /dev/null
+++ b/drivers/net/wan/hscx.h
@@ -0,0 +1,103 @@
+#define HSCX_MTU 1600
+
+#define HSCX_ISTA 0x00
+#define HSCX_MASK 0x00
+#define HSCX_STAR 0x01
+#define HSCX_CMDR 0x01
+#define HSCX_MODE 0x02
+#define HSCX_TIMR 0x03
+#define HSCX_EXIR 0x04
+#define HSCX_XAD1 0x04
+#define HSCX_RBCL 0x05
+#define HSCX_SAD2 0x05
+#define HSCX_RAH1 0x06
+#define HSCX_RSTA 0x07
+#define HSCX_RAH2 0x07
+#define HSCX_RAL1 0x08
+#define HSCX_RCHR 0x09
+#define HSCX_RAL2 0x09
+#define HSCX_XBCL 0x0a
+#define HSCX_BGR 0x0b
+#define HSCX_CCR2 0x0c
+#define HSCX_RBCH 0x0d
+#define HSCX_XBCH 0x0d
+#define HSCX_VSTR 0x0e
+#define HSCX_RLCR 0x0e
+#define HSCX_CCR1 0x0f
+#define HSCX_FIFO 0x1e
+
+#define HSCX_HSCX_CHOFFS 0x400
+#define HSCX_SEROFFS 0x1000
+
+#define HSCX_RME 0x80
+#define HSCX_RPF 0x40
+#define HSCX_RSC 0x20
+#define HSCX_XPR 0x10
+#define HSCX_TIN 0x08
+#define HSCX_ICA 0x04
+#define HSCX_EXA 0x02
+#define HSCX_EXB 0x01
+
+#define HSCX_XMR 0x80
+#define HSCX_XDU 0x40
+#define HSCX_EXE 0x40
+#define HSCX_PCE 0x20
+#define HSCX_RFO 0x10
+#define HSCX_CSC 0x08
+#define HSCX_RFS 0x04
+
+#define HSCX_XDOV 0x80
+#define HSCX_XFW 0x40
+#define HSCX_XRNR 0x20
+#define HSCX_RRNR 0x10
+#define HSCX_RLI 0x08
+#define HSCX_CEC 0x04
+#define HSCX_CTS 0x02
+#define HSCX_WFA 0x01
+
+#define HSCX_RMC 0x80
+#define HSCX_RHR 0x40
+#define HSCX_RNR 0x20
+#define HSCX_XREP 0x20
+#define HSCX_STI 0x10
+#define HSCX_XTF 0x08
+#define HSCX_XIF 0x04
+#define HSCX_XME 0x02
+#define HSCX_XRES 0x01
+
+#define HSCX_AUTO 0x00
+#define HSCX_NONAUTO 0x40
+#define HSCX_TRANS 0x80
+#define HSCX_XTRANS 0xc0
+#define HSCX_ADM16 0x20
+#define HSCX_ADM8 0x00
+#define HSCX_TMD_EXT 0x00
+#define HSCX_TMD_INT 0x10
+#define HSCX_RAC 0x08
+#define HSCX_RTS 0x04
+#define HSCX_TLP 0x01
+
+#define HSCX_VFR 0x80
+#define HSCX_RDO 0x40
+#define HSCX_CRC 0x20
+#define HSCX_RAB 0x10
+
+#define HSCX_CIE 0x04
+#define HSCX_RIE 0x02
+
+#define HSCX_DMA 0x80
+#define HSCX_NRM 0x40
+#define HSCX_CAS 0x20
+#define HSCX_XC 0x10
+
+#define HSCX_OV 0x10
+
+#define HSCX_CD 0x80
+
+#define HSCX_RC 0x80
+
+#define HSCX_PU 0x80
+#define HSCX_NRZ 0x00
+#define HSCX_NRZI 0x40
+#define HSCX_ODS 0x10
+#define HSCX_ITF 0x08
diff --git a/drivers/net/wan/mixcom.h b/drivers/net/wan/mixcom.h
new file mode 100644
index 000000000..1815eef75
--- /dev/null
+++ b/drivers/net/wan/mixcom.h
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#define MIXCOM_IO_EXTENT 0x20
+
+#define MIXCOM_DEFAULT_IO 0x180
+#define MIXCOM_DEFAULT_IRQ 5
+
+#define MIXCOM_ID 0x11
+#define MIXCOM_SERIAL_OFFSET 0x1000
+#define MIXCOM_CHANNEL_OFFSET 0x400
+#define MIXCOM_IT_OFFSET 0xc14
+#define MIXCOM_STATUS_OFFSET 0xc14
+#define MIXCOM_ID_OFFSET 0xc10
+#define MIXCOM_ON 0x1
+#define MIXCOM_OFF 0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB 0x1
+#define MIXCOM_CTSA 0x2
+#define MIXCOM_CHANNELNO 0x20
+#define MIXCOM_POWERFAIL 0x40
+#define MIXCOM_BOOT 0x80
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index b161fbabc..be665f0ae 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -101,7 +101,7 @@ static void sbni_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int sbni_close(struct net_device *dev);
static void sbni_drop_tx_queue(struct net_device *dev);
static struct enet_statistics *sbni_get_stats(struct net_device *dev);
-void card_start(struct net_device *dev);
+static void card_start(struct net_device *dev);
static inline unsigned short sbni_recv(struct net_device *dev);
void change_level(struct net_device *dev);
static inline void sbni_xmit(struct net_device *dev);
@@ -647,7 +647,7 @@ static int sbni_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-void card_start(struct net_device *dev)
+static void card_start(struct net_device *dev)
{
struct net_local *lp = (struct net_local*)dev->priv;
@@ -1200,6 +1200,8 @@ static int sbni_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
case SIOCDEVRESINSTATS:
{
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
DP( printk("%s: SIOCDEVRESINSTATS\n",dev->name); )
lp->in_stats.all_rx_number = 0;
lp->in_stats.bad_rx_number = 0;
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index e7875649e..0454118a8 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -1247,7 +1247,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct frad_local *flp;
- if(!suser())
+ if(!capable(CAP_NET_ADMIN))
return -EPERM;
flp = dev->priv;
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index e039bbc28..5b8504616 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -147,13 +147,17 @@ static void sppp_print_bytes (u8 *p, u16 len);
static int debug = 0;
+MODULE_PARM(debug,"1i");
+
/*
* Interface down stub
*/
static void if_down(struct net_device *dev)
{
- ;
+ struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+ sp->pp_link_state=SPPP_LINK_DOWN;
}
/*
@@ -182,8 +186,19 @@ static void sppp_clear_timeout(struct sppp *p)
}
}
-/*
- * Process the received packet.
+/**
+ * sppp_input - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ *
+ * This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
+ *
+ * We process the options in the card. If the frame is destined for
+ * the protocol stacks then it requeues the frame for the upper level
+ * protocol. If it is a control from it is processed and discarded
+ * here.
*/
void sppp_input (struct net_device *dev, struct sk_buff *skb)
@@ -194,7 +209,7 @@ void sppp_input (struct net_device *dev, struct sk_buff *skb)
skb->dev=dev;
skb->mac.raw=skb->data;
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_RUNNING)
{
/* Count received bytes, add FCS and one flag */
sp->ibytes+= skb->len + 3;
@@ -324,7 +339,7 @@ static int sppp_hard_header(struct sk_buff *skb, struct net_device *dev, __u16 t
h=(struct ppp_header *)skb->data;
if(sp->pp_flags&PP_CISCO)
{
- h->address = CISCO_MULTICAST;
+ h->address = CISCO_UNICAST;
h->control = 0;
}
else
@@ -370,7 +385,7 @@ static void sppp_keepalive (unsigned long dummy)
/* Keepalive mode disabled or channel down? */
if (! (sp->pp_flags & PP_KEEPALIVE) ||
- ! (dev->flags & IFF_RUNNING))
+ ! (dev->flags & IFF_UP))
continue;
/* No keepalive in PPP mode if LCP not opened yet. */
@@ -530,10 +545,10 @@ badreq:
if (h->ident != sp->lcp.confid)
break;
sppp_clear_timeout (sp);
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
+ if ((sp->pp_link_state != SPPP_LINK_UP) &&
+ (dev->flags & IFF_UP)) {
/* Coming out of loopback mode. */
- dev->flags |= IFF_UP;
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
switch (sp->lcp.state) {
@@ -698,9 +713,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
break;
}
sp->pp_loopcnt = 0;
- if (! (dev->flags & IFF_UP) &&
- (dev->flags & IFF_RUNNING)) {
- dev->flags |= IFF_UP;
+ if (sp->pp_link_state==SPPP_LINK_DOWN &&
+ (dev->flags & IFF_UP)) {
+ sp->pp_link_state=SPPP_LINK_UP;
printk (KERN_INFO "%s: up\n", dev->name);
}
break;
@@ -825,11 +840,19 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
dev_queue_xmit(skb);
}
+/**
+ * sppp_close - close down a synchronous PPP or Cisco HDLC link
+ * @dev: The network device to drop the link of
+ *
+ * This drops the logical interface to the channel. It is not
+ * done politely as we assume we will also be dropping DTR. Any
+ * timeouts are killed.
+ */
int sppp_close (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
- dev->flags &= ~IFF_RUNNING;
+ sp->pp_link_state = SPPP_LINK_DOWN;
sp->lcp.state = LCP_STATE_CLOSED;
sp->ipcp.state = IPCP_STATE_CLOSED;
sppp_clear_timeout (sp);
@@ -838,24 +861,49 @@ int sppp_close (struct net_device *dev)
EXPORT_SYMBOL(sppp_close);
+/**
+ * sppp_open - open a synchronous PPP or Cisco HDLC link
+ * @dev: Network device to activate
+ *
+ * Close down any existing synchronous session and commence
+ * from scratch. In the PPP case this means negotiating LCP/IPCP
+ * and friends, while for Cisco HDLC we simply need to staet sending
+ * keepalives
+ */
int sppp_open (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
- if (!(sp->pp_flags & PP_CISCO))
+ if (!(sp->pp_flags & PP_CISCO)) {
sppp_lcp_open (sp);
+ }
+ sp->pp_link_state = SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_open);
+/**
+ * sppp_reopen - notify of physical link loss
+ * @dev: Device that lost the link
+ *
+ * This function informs the synchronous protocol code that
+ * the underlying link died (for example a carrier drop on X.21)
+ *
+ * We increment the magic numbers to ensure that if the other end
+ * failed to notice we will correctly start a new session. It happens
+ * do to the nature of telco circuits is that you can lose carrier on
+ * one endonly.
+ *
+ * Having done this we go back to negotiating. This function may
+ * be called from an interrupt context.
+ */
+
int sppp_reopen (struct net_device *dev)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
sppp_close(dev);
- dev->flags |= IFF_RUNNING;
if (!(sp->pp_flags & PP_CISCO))
{
sp->lcp.magic = jiffies;
@@ -864,12 +912,23 @@ int sppp_reopen (struct net_device *dev)
sp->ipcp.state = IPCP_STATE_CLOSED;
/* Give it a moment for the line to settle then go */
sppp_set_timeout (sp, 1);
- }
+ }
+ sp->pp_link_state=SPPP_LINK_DOWN;
return 0;
}
EXPORT_SYMBOL(sppp_reopen);
+/**
+ * sppp_change_mtu - Change the link MTU
+ * @dev: Device to change MTU on
+ * @new_mtu: New MTU
+ *
+ * Change the MTU on the link. This can only be called with
+ * the link down. It returns an error if the link is up or
+ * the mtu is out of range.
+ */
+
int sppp_change_mtu(struct net_device *dev, int new_mtu)
{
if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
@@ -880,6 +939,18 @@ int sppp_change_mtu(struct net_device *dev, int new_mtu)
EXPORT_SYMBOL(sppp_change_mtu);
+/**
+ * sppp_do_ioctl - Ioctl handler for ppp/hdlc
+ * @dev: Device subject to ioctl
+ * @ifr: Interface request block from the user
+ * @cmd: Command that is being issued
+ *
+ * This function handles the ioctls that may be issued by the user
+ * to control the settings of a PPP/HDLC link. It does both busy
+ * and security checks. This function is intended to be wrapped by
+ * callers who wish to add additional ioctl calls of their own.
+ */
+
int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct sppp *sp = (struct sppp *)sppp_of(dev);
@@ -913,6 +984,16 @@ int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
EXPORT_SYMBOL(sppp_do_ioctl);
+/**
+ * sppp_attach - attach synchronous PPP/HDLC to a device
+ * @pd: PPP device to initialise
+ *
+ * This initialises the PPP/HDLC support on an interface. At the
+ * time of calling the dev element must point to the network device
+ * that this interface is attached to. The interface should not yet
+ * be registered.
+ */
+
void sppp_attach(struct ppp_device *pd)
{
struct net_device *dev = pd->dev;
@@ -973,6 +1054,15 @@ void sppp_attach(struct ppp_device *pd)
EXPORT_SYMBOL(sppp_attach);
+/**
+ * sppp_detach - release PPP resources from a device
+ * @dev: Network device to release
+ *
+ * Stop and free up any PPP/HDLC resources used by this
+ * interface. This must be called before the device is
+ * freed.
+ */
+
void sppp_detach (struct net_device *dev)
{
struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
@@ -1187,7 +1277,7 @@ static void sppp_cp_timeout (unsigned long arg)
cli();
sp->pp_flags &= ~PP_TIMO;
- if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+ if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
restore_flags(flags);
return;
}
@@ -1273,18 +1363,24 @@ static void sppp_print_bytes (u_char *p, u16 len)
printk ("-%x", *p++);
}
-/*
+/**
+ * sppp_rcv - receive and process a WAN PPP frame
+ * @skb: The buffer to process
+ * @dev: The device it arrived on
+ * @p: Unused
+ *
* Protocol glue. This drives the deferred processing mode the poorer
- * cards use.
+ * cards use. This can be called directly by cards that do not have
+ * timing constraints but is normally called from the network layer
+ * after interrupt servicing to process frames queued via netif_rx.
*/
-int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
+static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p)
{
sppp_input(dev,skb);
return 0;
}
-EXPORT_SYMBOL(sppp_rcv);
struct packet_type sppp_packet_type=
{
@@ -1305,8 +1401,6 @@ void sync_ppp_init(void)
dev_add_pack(&sppp_packet_type);
}
-EXPORT_SYMBOL(sync_ppp_init);
-
#ifdef MODULE
int init_module(void)
diff --git a/drivers/net/wan/syncppp.h b/drivers/net/wan/syncppp.h
index 1e2056869..8f6ed5c1f 100644
--- a/drivers/net/wan/syncppp.h
+++ b/drivers/net/wan/syncppp.h
@@ -47,6 +47,7 @@ struct sppp
u32 ipkts,opkts; /* Packets in/out */
struct timer_list pp_timer;
struct net_device *pp_if;
+ char pp_link_state; /* Link status */
};
struct ppp_device
@@ -75,6 +76,9 @@ struct ppp_device
#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */
#define IPCP_STATE_OPENED 3 /* IPCP state: opened */
+#define SPPP_LINK_DOWN 0 /* link down - no keepalive */
+#define SPPP_LINK_UP 1 /* link is up - keepalive ok */
+
void sppp_attach (struct ppp_device *pd);
void sppp_detach (struct net_device *dev);
void sppp_input (struct net_device *dev, struct sk_buff *m);
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 2ff818b04..f1c618a90 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -55,7 +55,7 @@
static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
/**
- * z8530_read_port:
+ * z8530_read_port - Architecture specific interface function
* @p: port to read
*
* Provided port access methods. The Comtrol SV11 requires no delays
@@ -79,7 +79,7 @@ extern __inline__ int z8530_read_port(unsigned long p)
}
/**
- * z8530_write_port:
+ * z8530_write_port - Architecture specific interface function
* @p: port to write
* @d: value to write
*
@@ -108,7 +108,7 @@ static void z8530_tx_done(struct z8530_channel *c);
/**
- * read_zsreg:
+ * read_zsreg - Read a register from a Z85230
* @c: Z8530 channel to read from (2 per chip)
* @reg: Register to read
* FIXME: Use a spinlock.
@@ -133,7 +133,7 @@ extern inline u8 read_zsreg(struct z8530_channel *c, u8 reg)
}
/**
- * read_zsdata:
+ * read_zsdata - Read the data port of a Z8530 channel
* @c: The Z8530 channel to read the data port from
*
* The data port provides fast access to some things. We still
@@ -148,7 +148,7 @@ extern inline u8 read_zsdata(struct z8530_channel *c)
}
/**
- * write_zsreg:
+ * write_zsreg - Write to a Z8530 channel register
* @c: The Z8530 channel
* @reg: Register number
* @val: Value to write
@@ -169,11 +169,28 @@ extern inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val)
restore_flags(flags);
}
+/**
+ * write_zsctrl - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the control register on the Z8530
+ */
+
extern inline void write_zsctrl(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->ctrlio, val);
}
+/**
+ * write_zsdata - Write to a Z8530 control register
+ * @c: The Z8530 channel
+ * @val: Value to write
+ *
+ * Write directly to the data register on the Z8530
+ */
+
+
extern inline void write_zsdata(struct z8530_channel *c, u8 val)
{
z8530_write_port(c->dataio, val);
@@ -249,7 +266,7 @@ u8 z8530_hdlc_kilostream_85230[]=
EXPORT_SYMBOL(z8530_hdlc_kilostream_85230);
/**
- * z8530_flush_fifo:
+ * z8530_flush_fifo - Flush on chip RX FIFO
* @c: Channel to flush
*
* Flush the receive FIFO. There is no specific option for this, we
@@ -276,8 +293,8 @@ static void z8530_flush_fifo(struct z8530_channel *c)
}
/**
- * z8530_rtsdtr:
- * @c: The Z8530 channel to contro;
+ * z8530_rtsdtr - Control the outgoing DTS/RTS line
+ * @c: The Z8530 channel to control;
* @set: 1 to set, 0 to clear
*
* Sets or clears DTR/RTS on the requested line. All locking is handled
@@ -296,7 +313,7 @@ static void z8530_rtsdtr(struct z8530_channel *c, int set)
}
/**
- * z8530_rx:
+ * z8530_rx - Handle a PIO receive event
* @c: Z8530 channel to process
*
* Receive handler for receiving in PIO mode. This is much like the
@@ -378,7 +395,7 @@ static void z8530_rx(struct z8530_channel *c)
/**
- * z8530_tx:
+ * z8530_tx - Handle a PIO transmit event
* @c: Z8530 channel to process
*
* Z8530 transmit interrupt handler for the PIO mode. The basic
@@ -421,7 +438,7 @@ static void z8530_tx(struct z8530_channel *c)
}
/**
- * z8530_status:
+ * z8530_status - Handle a PIO status exception
* @chan: Z8530 channel to process
*
* A status event occured in PIO synchronous mode. There are several
@@ -479,7 +496,7 @@ struct z8530_irqhandler z8530_sync=
EXPORT_SYMBOL(z8530_sync);
/**
- * z8530_dma_rx:
+ * z8530_dma_rx - Handle a DMA RX event
* @chan: Channel to handle
*
* Non bus mastering DMA interfaces for the Z8x30 devices. This
@@ -514,7 +531,7 @@ static void z8530_dma_rx(struct z8530_channel *chan)
}
/**
- * z8530_dma_tx:
+ * z8530_dma_tx - Handle a DMA TX event
* @chan: The Z8530 channel to handle
*
* We have received an interrupt while doing DMA transmissions. It
@@ -525,17 +542,17 @@ static void z8530_dma_tx(struct z8530_channel *chan)
{
if(!chan->dma_tx)
{
- printk("Hey who turned the DMA off?\n");
+ printk(KERN_WARNING "Hey who turned the DMA off?\n");
z8530_tx(chan);
return;
}
/* This shouldnt occur in DMA mode */
- printk(KERN_ERR "DMA tx ??\n");
+ printk(KERN_ERR "DMA tx - bogus event!\n");
z8530_tx(chan);
}
/**
- * z8530_dma_status:
+ * z8530_dma_status - Handle a DMA status exception
* @chan: Z8530 channel to process
*
* A status event occured on the Z8530. We receive these for two reasons
@@ -606,7 +623,7 @@ struct z8530_irqhandler z8530_txdma_sync=
EXPORT_SYMBOL(z8530_txdma_sync);
/**
- * z8530_rx_clear:
+ * z8530_rx_clear - Handle RX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Receive interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -635,7 +652,7 @@ static void z8530_rx_clear(struct z8530_channel *c)
}
/**
- * z8530_tx_clear:
+ * z8530_tx_clear - Handle TX events from a stopped chip
* @c: Z8530 channel to shut up
*
* Transmit interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -650,7 +667,7 @@ static void z8530_tx_clear(struct z8530_channel *c)
}
/**
- * z8530_status_clear:
+ * z8530_status_clear - Handle status events from a stopped chip
* @chan: Z8530 channel to shut up
*
* Status interrupt vectors for a Z8530 that is in 'parked' mode.
@@ -678,7 +695,7 @@ struct z8530_irqhandler z8530_nop=
EXPORT_SYMBOL(z8530_nop);
/**
- * z8530_interrupt:
+ * z8530_interrupt - Handle an interrupt from a Z8530
* @irq: Interrupt number
* @dev_id: The Z8530 device that is interrupting.
* @regs: unused
@@ -758,7 +775,7 @@ static char reg_init[16]=
/**
- * z8530_sync_open:
+ * z8530_sync_open - Open a Z8530 channel for PIO
* @dev: The network interface we are using
* @c: The Z8530 channel to open in synchronous PIO mode
*
@@ -789,7 +806,7 @@ int z8530_sync_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_open);
/**
- * z8530_sync_close:
+ * z8530_sync_close - Close a PIO Z8530 channel
* @dev: Network device to close
* @c: Z8530 channel to disassociate and move to idle
*
@@ -814,7 +831,7 @@ int z8530_sync_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_close);
/**
- * z8530_sync_dma_open:
+ * z8530_sync_dma_open - Open a Z8530 for DMA I/O
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -934,7 +951,7 @@ int z8530_sync_dma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_open);
/**
- * z8530_sync_dma_close:
+ * z8530_sync_dma_close - Close down DMA I/O
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -999,7 +1016,7 @@ int z8530_sync_dma_close(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_dma_close);
/**
- * z8530_sync_txdma_open:
+ * z8530_sync_txdma_open - Open a Z8530 for TX driven DMA
* @dev: The network device to attach
* @c: The Z8530 channel to configure in sync DMA mode.
*
@@ -1099,7 +1116,7 @@ int z8530_sync_txdma_open(struct net_device *dev, struct z8530_channel *c)
EXPORT_SYMBOL(z8530_sync_txdma_open);
/**
- * z8530_sync_txdma_close:
+ * z8530_sync_txdma_close - Close down a TX driven DMA channel
* @dev: Network device to detach
* @c: Z8530 channel to move into discard mode
*
@@ -1168,7 +1185,7 @@ static char *z8530_type_name[]={
};
/**
- * z8530_describe:
+ * z8530_describe - Uniformly describe a Z8530 port
* @dev: Z8530 device to describe
* @mapping: string holding mapping type (eg "I/O" or "Mem")
* @io: the port value in question
@@ -1191,7 +1208,7 @@ void z8530_describe(struct z8530_dev *dev, char *mapping, unsigned long io)
EXPORT_SYMBOL(z8530_describe);
/**
- * z8530_init:
+ * z8530_init - Initialise a Z8530 device
* @dev: Z8530 device to initialise.
*
* Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
@@ -1273,7 +1290,7 @@ int z8530_init(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_init);
/**
- * z8530_shutdown:
+ * z8530_shutdown - Shutdown a Z8530 device
* @dev: The Z8530 chip to shutdown
*
* We set the interrupt handlers to silence any interrupts. We then
@@ -1294,7 +1311,7 @@ int z8530_shutdown(struct z8530_dev *dev)
EXPORT_SYMBOL(z8530_shutdown);
/**
- * z8530_channel_load:
+ * z8530_channel_load - Load channel data
* @c: Z8530 channel to configure
* @rtable: Table of register, value pairs
* FIXME: ioctl to allow user uploaded tables
@@ -1333,7 +1350,7 @@ EXPORT_SYMBOL(z8530_channel_load);
/**
- * z8530_tx_begin:
+ * z8530_tx_begin - Begin packet transmission
* @c: The Z8530 channel to kick
*
* This is the speed sensitive side of transmission. If we are called
@@ -1430,7 +1447,7 @@ static void z8530_tx_begin(struct z8530_channel *c)
}
/**
- * z8530_tx_done:
+ * z8530_tx_done - TX complete callback
* @c: The channel that completed a transmit.
*
* This is called when we complete a packet send. We wake the queue,
@@ -1461,7 +1478,7 @@ static void z8530_tx_done(struct z8530_channel *c)
}
/**
- * z8530_null_rx:
+ * z8530_null_rx - Discard a packet
* @c: The channel the packet arrived on
* @skb: The buffer
*
@@ -1477,7 +1494,7 @@ void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_null_rx);
/**
- * z8530_rx_done:
+ * z8530_rx_done - Receive completion callback
* @c: The channel that completed a receive
*
* A new packet is complete. Our goal here is to get back into receive
@@ -1630,7 +1647,7 @@ static void z8530_rx_done(struct z8530_channel *c)
}
/**
- * spans_boundary:
+ * spans_boundary - Check a packet can be ISA DMA'd
* @skb: The buffer to check
*
* Returns true if the buffer cross a DMA boundary on a PC. The poor
@@ -1642,15 +1659,12 @@ extern inline int spans_boundary(struct sk_buff *skb)
unsigned long a=(unsigned long)skb->data;
a^=(a+skb->len);
if(a&0x00010000) /* If the 64K bit is different.. */
- {
- printk("spanner\n");
return 1;
- }
return 0;
}
/**
- * z8530_queue_xmit:
+ * z8530_queue_xmit - Queue a packet
* @c: The channel to use
* @skb: The packet to kick down the channel
*
@@ -1707,7 +1721,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
EXPORT_SYMBOL(z8530_queue_xmit);
/**
- * z8530_get_stats:
+ * z8530_get_stats - Get network statistics
* @c: The channel to use
*
* Get the statistics block. We keep the statistics in software as
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
index 9fc75e5e6..2162d5ff0 100644
--- a/drivers/net/wavelan.c
+++ b/drivers/net/wavelan.c
@@ -1990,7 +1990,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
}
/* only super-user can see encryption key */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2224,7 +2224,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
/* ------------------ PRIVATE IOCTL ------------------ */
case SIOCSIPQTHR:
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
@@ -2248,7 +2248,7 @@ static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is
#ifdef HISTOGRAM
case SIOCSIPHISTO:
/* Verify that the user is root. */
- if (!suser()) {
+ if (!capable(CAP_NET_ADMIN)) {
ret = -EPERM;
break;
}
diff --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h
index f55bf48e2..38534c934 100644
--- a/drivers/net/wavelan.h
+++ b/drivers/net/wavelan.h
@@ -26,7 +26,7 @@
* product (OEM, like DEC RoamAbout, Digital Ocean, or Epson),
* you might need to modify this part to accommodate your hardware.
*/
-const char MAC_ADDRESSES[][3] =
+static const char MAC_ADDRESSES[][3] =
{
{ 0x08, 0x00, 0x0E }, /* AT&T WaveLAN (standard) & DEC RoamAbout */
{ 0x08, 0x00, 0x6A }, /* AT&T WaveLAN (alternate) */
@@ -49,14 +49,14 @@ const char MAC_ADDRESSES[][3] =
* (as read in the offset register of the dac area).
* Used to map channel numbers used by `wfreqsel' to frequencies
*/
-const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
+static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
0xD0, 0xF0, 0xF8, 0x150 };
/* Frequencies of the 1.0 modem (fixed frequencies).
* Use to map the PSA `subband' to a frequency
* Note : all frequencies apart from the first one need to be multiplied by 10
*/
-const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 85febe47d..f28feea43 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -77,7 +77,6 @@ static int gx_fix = 0;
#include <linux/version.h>
#include <linux/module.h>
-#include <linux/modversions.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>