aboutsummaryrefslogtreecommitdiffstats
path: root/net/phonet
Commit message (Collapse)AuthorAge
...
* Phonet: mark the pipe controller as EXPERIMENTALRémi Denis-Courmont2010-10-08
| | | | | | | | | | | | | | | There are a bunch of issues that need to be fixed, including: - GFP_KERNEL allocations from atomic context (and GFP_ATOMIC in process context), - abuse of the setsockopt() call convention, - unprotected/unlocked static variables... IMHO, we will need to alter the userspace ABI when we fix it. So mark the configuration option as EXPERIMENTAL for the time being (or should it be BROKEN instead?). Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: cleanup pipe enable socket optionRémi Denis-Courmont2010-10-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The current code works like this: int garbage, status; socklen_t len = sizeof(status); /* enable pipe */ setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &garbage, sizeof(garbage)); /* disable pipe */ setsockopt(fd, SOL_PNPIPE, PNPIPE_DISABLE, &garbage, sizeof(garbage)); /* get status */ getsockopt(fd, SOL_PNPIPE, PNPIPE_INQ, &status, &len); ...which does not follow the usual socket option pattern. This patch merges all three "options" into a single gettable&settable option, before Linux 2.6.37 gets out: int status; socklen_t len = sizeof(status); /* enable pipe */ status = 1; setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, sizeof(status)); /* disable pipe */ status = 0; setsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, sizeof(status)); /* get status */ getsockopt(fd, SOL_PNPIPE, PNPIPE_ENABLE, &status, &len); This also fixes the error code from EFAULT to ENOTCONN. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Cc: Kumar Sanghvi <kumar.sanghvi@stericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: advise against enabling the pipe controllerRémi Denis-Courmont2010-10-08
| | | | | | | | | As it currently is, the new code path is not compatible with existing Nokia modems. This would break existing userspace for Nokia modem, such as the existing oFono ISI driver. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Merge branch 'master' of ↵David S. Miller2010-10-04
|\ | | | | | | | | | | | | | | master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6 Conflicts: net/ipv4/Kconfig net/ipv4/tcp_timer.c
| * Phonet: Correct header retrieval after pskb_may_pullKumar Sanghvi2010-09-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Retrieve the header after doing pskb_may_pull since, pskb_may_pull could change the buffer structure. This is based on the comment given by Eric Dumazet on Phonet Pipe controller patch for a similar problem. Signed-off-by: Kumar Sanghvi <kumar.sanghvi@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Acked-by: Eric Dumazet <eric.dumazet@gmail.com> Acked-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: restore flow control credits when sending failsRémi Denis-Courmont2010-09-30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This patch restores the below flow control patch submitted by Rémi Denis-Courmont, which accidentaly got lost due to Pipe controller patch on Phonet. commit 1a98214feef2221cd7c24b17cd688a5a9d85b2ea Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Date: Mon Aug 30 12:57:03 2010 +0000 Phonet: restore flow control credits when sending fails Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Kumar Sanghvi <kumar.sanghvi@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: Implement Pipe Controller to support Nokia Slim ModemsKumar Sanghvi2010-09-28
| | | | | | | | | | | | | | | | | | | | | | | | | | Phonet stack assumes the presence of Pipe Controller, either in Modem or on Application Processing Engine user-space for the Pipe data. Nokia Slim Modems like WG2.5 used in ST-Ericsson U8500 platform do not implement Pipe controller in them. This patch adds Pipe Controller implemenation to Phonet stack to support Pipe data over Phonet stack for Nokia Slim Modems. Signed-off-by: Kumar Sanghvi <kumar.sanghvi@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | phonet: Fix build warning.David S. Miller2010-09-16
| | | | | | | | | | | | | | net/phonet/socket.c: In function ‘pn_res_seq_show’: net/phonet/socket.c:726: warning: format ‘%02X’ expects type ‘unsigned int’, but argument 3 has type ‘long int’ Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: list subscribed resources via proc_fsRémi Denis-Courmont2010-09-16
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: look up the resource routing table when forwardingRémi Denis-Courmont2010-09-16
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: hook resource routing to userspace via ioctl()'sRémi Denis-Courmont2010-09-16
| | | | | | | | | | | | | | | | | | | | I wish we could use something cleaner, such as bind(). But that would not work since resource subscription is orthogonal/in addition to the normal object ID allocated via bind(). This is similar to multicasting which also uses ioctl()'s. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: resource routing backendRémi Denis-Courmont2010-09-16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When both destination device and object are nul, Phonet routes the packet according to the resource field. In fact, this is the most common pattern when sending Phonet "request" packets. In this case, the packet is delivered to whichever endpoint (socket) has registered the resource. This adds a new table so that Linux processes can register their Phonet sockets to Phonet resources, if they have adequate privileges. (Namespace support is not implemented at the moment.) Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: remove dangling pipe if an endpoint is closed earlyRémi Denis-Courmont2010-09-16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Closing a pipe endpoint is not normally allowed by the Phonet pipe, other than as a side after-effect of removing the pipe between two endpoints. But there is no way to prevent Linux userspace processes from being killed or suffering from bugs, so this can still happen. We might as well forcefully close Phonet pipe endpoints then. The cellular modem supports only a few existing pipes at a time. So we really should not leak them. This change instructs the modem to destroy the pipe if either of the pipe's endpoint (Linux socket) is closed too early. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: do not set POLLOUT in case of send buffer overflowRémi Denis-Courmont2010-08-31
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: correct sendmsg() error code from sock_alloc_send_skb()Rémi Denis-Courmont2010-08-31
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | Phonet: restore flow control credits when sending failsRémi Denis-Courmont2010-08-31
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | phonet: use for_each_set_bitAkinobu Mita2010-08-28
|/ | | | | | | | | Replace open-coded loop with for_each_set_bit(). Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller <davem@davemloft.net>
* Merge branch 'master' of ↵David S. Miller2010-07-20
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6 Conflicts: drivers/vhost/net.c net/bridge/br_device.c Fix merge conflict in drivers/vhost/net.c with guidance from Stephen Rothwell. Revert the effects of net-2.6 commit 573201f36fd9c7c6d5218cdcd9948cee700b277d since net-next-2.6 has fixes that make bridge netpoll work properly thus we don't need it disabled. Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: fix skb leak in pipe endpoint accept()Rémi Denis-Courmont2010-07-09
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* | phonet: use call_rcu for phonet device freeJiri Pirko2010-06-09
|/ | | | | | | | Use call_rcu rather than synchronize_rcu. Signed-off-by: Jiri Pirko <jpirko@redhat.com> Acked-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: listening socket lock protects the connected socket listRémi Denis-Courmont2010-05-29
| | | | | | | | | The accept()'d socket need to be unhashed while the (listen()'ing) socket lock is held. This fixes a race condition that could lead to an OOPS. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: fix potential use-after-free in pep_sock_close()Rémi Denis-Courmont2010-05-25
| | | | | | | | sk_common_release() might destroy our last reference to the socket. So an extra temporary reference is needed during cleanup. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* net: sock_def_readable() and friends RCU conversionEric Dumazet2010-05-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | sk_callback_lock rwlock actually protects sk->sk_sleep pointer, so we need two atomic operations (and associated dirtying) per incoming packet. RCU conversion is pretty much needed : 1) Add a new structure, called "struct socket_wq" to hold all fields that will need rcu_read_lock() protection (currently: a wait_queue_head_t and a struct fasync_struct pointer). [Future patch will add a list anchor for wakeup coalescing] 2) Attach one of such structure to each "struct socket" created in sock_alloc_inode(). 3) Respect RCU grace period when freeing a "struct socket_wq" 4) Change sk_sleep pointer in "struct sock" by sk_wq, pointer to "struct socket_wq" 5) Change sk_sleep() function to use new sk->sk_wq instead of sk->sk_sleep 6) Change sk_has_sleeper() to wq_has_sleeper() that must be used inside a rcu_read_lock() section. 7) Change all sk_has_sleeper() callers to : - Use rcu_read_lock() instead of read_lock(&sk->sk_callback_lock) - Use wq_has_sleeper() to eventually wakeup tasks. - Use rcu_read_unlock() instead of read_unlock(&sk->sk_callback_lock) 8) sock_wake_async() is modified to use rcu protection as well. 9) Exceptions : macvtap, drivers/net/tun.c, af_unix use integrated "struct socket_wq" instead of dynamically allocated ones. They dont need rcu freeing. Some cleanups or followups are probably needed, (possible sk_callback_lock conversion to a spinlock for example...). Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* phonet: use phonet_pernet instead of directly net_genericJiri Pirko2010-04-26
| | | | | | | | As in for example pppoe introduce phonet_pernet and use it instead of calling net_generic directly. Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* include cleanup: Update gfp.h and slab.h includes to prepare for breaking ↵Tejun Heo2010-03-30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
* phonet: use for_each_set_bit()Akinobu Mita2010-03-15
| | | | | | | | | | Replace open-coded loop with for_each_set_bit(). Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* net: spread __net_init, __net_exitAlexey Dobriyan2010-01-17
| | | | | | | | | | | __net_init/__net_exit are apparently not going away, so use them to full extent. In some cases __net_init was removed, because it was called from __net_exit code. Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: reject unsupported sendmsg/recvmsg flagsRémi Denis-Courmont2010-01-07
| | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: zero-copy GPRS TXRémi Denis-Courmont2010-01-07
| | | | | | | | Send aligned pipe payload if requested to do so. Then, the socket buffer needs not be fragmented anymore. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Phonet: zero-copy aligned GPRS RXRémi Denis-Courmont2010-01-07
| | | | | | | Newer Nokia cellular modems can use aligned payload for their GPRS pipe. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
* Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6Linus Torvalds2009-12-08
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1815 commits) mac80211: fix reorder buffer release iwmc3200wifi: Enable wimax core through module parameter iwmc3200wifi: Add wifi-wimax coexistence mode as a module parameter iwmc3200wifi: Coex table command does not expect a response iwmc3200wifi: Update wiwi priority table iwlwifi: driver version track kernel version iwlwifi: indicate uCode type when fail dump error/event log iwl3945: remove duplicated event logging code b43: fix two warnings ipw2100: fix rebooting hang with driver loaded cfg80211: indent regulatory messages with spaces iwmc3200wifi: fix NULL pointer dereference in pmkid update mac80211: Fix TX status reporting for injected data frames ath9k: enable 2GHz band only if the device supports it airo: Fix integer overflow warning rt2x00: Fix padding bug on L2PAD devices. WE: Fix set events not propagated b43legacy: avoid PPC fault during resume b43: avoid PPC fault during resume tcp: fix a timewait refcnt race ... Fix up conflicts due to sysctl cleanups (dead sysctl_check code and CTL_UNNUMBERED removed) in kernel/sysctl_check.c net/ipv4/sysctl_net_ipv4.c net/ipv6/addrconf.c net/sctp/sysctl.c
| * net: Simplify phonet pernet operations.Eric W. Biederman2009-12-01
| | | | | | | | | | | | | | | | Take advantage of the new pernet automatic storage management, and stop using compatibility network namespace functions. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * net: Move && and || to end of previous lineJoe Perches2009-11-29
| | | | | | | | | | | | | | | | | | | | | | Not including net/atm/ Compiled tested x86 allyesconfig only Added a > 80 column line or two, which I ignored. Existing checkpatch plaints willfully, cheerfully ignored. Signed-off-by: Joe Perches <joe@perches.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: convert devices list to RCURémi Denis-Courmont2009-11-18
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * netns: net_identifiers should be read_mostlyEric Dumazet2009-11-18
| | | | | | | | | | Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: missing rcu_dereference()Rémi Denis-Courmont2009-11-17
| | | | | | | | | | | | Reported-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: convert routing table to RCURémi Denis-Courmont2009-11-13
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: put protocols array under RCURémi Denis-Courmont2009-11-13
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: allocate and copy for pipe TX without sock lockRémi Denis-Courmont2009-11-10
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: put sockets in a hash tableRémi Denis-Courmont2009-11-10
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * net: pass kern to net_proto_family create functionEric Paris2009-11-06
| | | | | | | | | | | | | | | | | | | | | | The generic __sock_create function has a kern argument which allows the security system to make decisions based on if a socket is being created by the kernel or by userspace. This patch passes that flag to the net_proto_family specific create function, so it can do the same thing. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: remove tautologiesRémi Denis-Courmont2009-11-04
| | | | | | | | | | | | | | These checks don't make sense anymore since rtnl_notify() cannot fail. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: hold socket before giving it to sk_deliver_skb()Rémi Denis-Courmont2009-10-15
| | | | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Acked-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * net: sk_drops consolidationEric Dumazet2009-10-14
| | | | | | | | | | | | | | | | | | | | | | sock_queue_rcv_skb() can update sk_drops itself, removing need for callers to take care of it. This is more consistent since sock_queue_rcv_skb() also reads sk_drops when queueing a skb. This adds sk_drops managment to many protocols that not cared yet. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: forward incoming packetsRémi Denis-Courmont2009-10-14
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: route outgoing packetsRémi Denis-Courmont2009-10-14
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: routing table Netlink interfaceRémi Denis-Courmont2009-10-14
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: routing table backendRémi Denis-Courmont2009-10-14
| | | | | | | | | | | | | | | | The Phonet "universe" only has 64 addresses, so we keep a trivial flat routing table. Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * Phonet: deliver broadcast packets to broadcast socketsRémi Denis-Courmont2009-10-14
| | | | | | | | | | Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
| * net: mark net_proto_ops as constStephen Hemminger2009-10-07
| | | | | | | | | | | | | | All usages of structure net_proto_ops should be declared const. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@davemloft.net>
.org> 2005-04-16 18:20:36 -0400 committer Linus Torvalds <torvalds@ppc970.osdl.org> 2005-04-16 18:20:36 -0400 Linux-2.6.12-rc2' href='/cgit/cgit.cgi/litmus-rt-ext-res.git/commit/drivers/net/declance.c?id=1da177e4c3f41524e886b7f1b8a0c1fc7321cac2'>1da177e4c3f4
1da177e4c3f4

3b6e8fe7eca1
1da177e4c3f4



1da177e4c3f4









3b6e8fe7eca1




1da177e4c3f4



































































































3b6e8fe7eca1

1da177e4c3f4
3b6e8fe7eca1
6aa20a223553
1da177e4c3f4
5b057c6b1a25
1da177e4c3f4



09f75cd7bf13
1da177e4c3f4
3b6e8fe7eca1


1da177e4c3f4
3b6e8fe7eca1
1da177e4c3f4

3b6e8fe7eca1



1da177e4c3f4






1da177e4c3f4





1da177e4c3f4

3b6e8fe7eca1

1da177e4c3f4






3b6e8fe7eca1



1da177e4c3f4


3b6e8fe7eca1



1da177e4c3f4











3b6e8fe7eca1
1da177e4c3f4






3b6e8fe7eca1
1da177e4c3f4

1da177e4c3f4
















3b6e8fe7eca1
1da177e4c3f4
3b6e8fe7eca1
1da177e4c3f4













257b346d20cd
1da177e4c3f4






257b346d20cd
1da177e4c3f4



1da177e4c3f4


257b346d20cd
c23135573f37
257b346d20cd




4cf1653aa90c
257b346d20cd


1da177e4c3f4
1da177e4c3f4
















1da177e4c3f4
1da177e4c3f4
36156cdff17a
1da177e4c3f4




4569504a36cb
1da177e4c3f4

36156cdff17a
1da177e4c3f4









3b6e8fe7eca1
1da177e4c3f4

3b6e8fe7eca1
1da177e4c3f4


3b6e8fe7eca1
1da177e4c3f4


3b6e8fe7eca1


1da177e4c3f4




6684b4e28247
1da177e4c3f4

e8f7f7f11d07
1da177e4c3f4
257b346d20cd



c23135573f37
257b346d20cd

c23135573f37
257b346d20cd


1da177e4c3f4
257b346d20cd
3b6e8fe7eca1
1da177e4c3f4
257b346d20cd
1da177e4c3f4







3b6e8fe7eca1
1da177e4c3f4






3b6e8fe7eca1


1da177e4c3f4



1da177e4c3f4

36156cdff17a

3b6e8fe7eca1
36156cdff17a
1da177e4c3f4






3b6e8fe7eca1
1da177e4c3f4

3b6e8fe7eca1
1da177e4c3f4


3b6e8fe7eca1
1da177e4c3f4


3b6e8fe7eca1


1da177e4c3f4







257b346d20cd
1da177e4c3f4












257b346d20cd
1da177e4c3f4








257b346d20cd
1da177e4c3f4








0795af5729b1
1da177e4c3f4

0795af5729b1
1da177e4c3f4

0795af5729b1
1da177e4c3f4

0795af5729b1
1da177e4c3f4
1da177e4c3f4
e174961ca1a0
1da177e4c3f4





1da177e4c3f4
























257b346d20cd
1da177e4c3f4

257b346d20cd



1da177e4c3f4



257b346d20cd




b07db75a9f54
1da177e4c3f4




257b346d20cd










1da177e4c3f4

257b346d20cd
1da177e4c3f4


1da177e4c3f4

257b346d20cd
1da177e4c3f4
1da177e4c3f4
257b346d20cd
1da177e4c3f4






257b346d20cd
1da177e4c3f4



b07db75a9f54
1da177e4c3f4
1da177e4c3f4




257b346d20cd






















































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
  





                                                        
                                                               

































                                                                               



                                                                          

                                                             

   











                              
                     
                        

                          

                       




                                 
                           

                                     
                                                           




                                                                     

                                         






                    





























                                                                               


















                                                                           



























                                                                       





                                                                     
  



                                                                       
  



                                                                     



                                                                   

                                                                 

                                                                    
                                                                             



                                                                   

                                                                 

                                                                    
                            





                                                                          
 

                                                                       


                                                                     
                                                                       
                                                                      
                                                                        

                     






                                                      
                                                      
 

                                                                         
 
















                                                                         



                                

                                       





                             








                                                                 

                                          



















                                                                          
                                            











                                                                         
                   




















                                                   
                                                                          



                                      


                                        















































                                             
                                                                            



                                      


                                        






















































                                                    

                                                          

              







                                                                     





                                                                        


                                            



                                                                       
                 

                                                           

                                         



                                                                       
                 

                                                           





                                            






                                                                               
                                  

                                                                      





                                            







                                                                            
                                  

                                                                      
















                                                                

                                                                      


                                    

                                                                      











                                                    




                                                          







                                                    

                                                                           

                                                                    

                                                                           





                                                                    



                                                                  


                                                      

                                                    




                                                                  
                                                            
                                             
                                                           
                                             
                                                            
                                             
                                                             
                                             
                                                       
                        
                                                                             




                                                                                 
                                                        




                                                                            

                                         
                                                   
 



                                                                   
                                                                            


                                                                 
                                                


                                                   




                                                                               






                                                    
                                                          
                                                
                         

                   
 




                                             
                                                        
                                                              
                                                             

                              

                                                               
 
                                               
                                               
                                                               
                                                
                                                              

                                                  
                                                               












                                                                        
                                                            










                                                                               

                                                                        


                                                                       
                                                                     

                                                                   
                                                                      
                                                        

                                                                     
                                                                       
                                                           
 
                                                











                                               
                                                            
 
                                        
 
                                                      
                           

 
                                                         
 
                                        





















                                                                          
                                       

                              
                                       
















                                                                           

                                             
                                                          



                                                    









                                                                   




                                              



































































































                                                                             

                                                          
 
                       
 
                             
                                             



                                 
                                   
 


                                                                
 
                                                                               

                                               



                                                            






                                                    





                                   

                                                        

                                                          






                                               



                                                           


                                        



                                              











                                                    
                                                                             






                                                       
                                                          

                                                
















                                                                      
                                                           
                
                                                            













                                                               
                                                                      






                                               
                                           



                                


                                                      
                 
                                                                   




                                     
                                              


                                                     
         
















                                                                               
                       
                        
                                                                             




                                                                       
                                                        

                                                           
                                                                       









                                                                       
                                                                              

                                                               
                                                                    


                                                    
                                                                              


                                                                          


                                                              




                                                                
                                                             

                      
                
                        



                                                                
                                                                      

                                                                       
                                               


                                         
 
                                                   
                                                         
                                                           
                                                      







                                                                          
                                                                    






                                                                          


                                                              



                      

                                                        

                                                                             
                                                               
                                                                       






                                                                
                                                                              

                                                               
                                                                    


                                                    
                                                                              


                                                                          


                                                              







                                                                               
                                 












                                                                         
                                      








                                                                          
                                              








                                                                          
                                                         

                        
                                            

                        
                                                       

                      
                               
                                               
 
                                                                    





                                                 
























                                                                            
                                      

         



                                          



                                                           




                                               
                         




                   










                                                        

                                                                
                                                


                      

                                                             
                                                                   
                                        
                                           
                                                                   






                                         
                                                  



                                                            
 
                                       




                                          






















































                                                          
/*
 *    Lance ethernet driver for the MIPS processor based
 *      DECstation family
 *
 *
 *      adopted from sunlance.c by Richard van den Berg
 *
 *      Copyright (C) 2002, 2003, 2005, 2006  Maciej W. Rozycki
 *
 *      additional sources:
 *      - PMAD-AA TURBOchannel Ethernet Module Functional Specification,
 *        Revision 1.2
 *
 *      History:
 *
 *      v0.001: The kernel accepts the code and it shows the hardware address.
 *
 *      v0.002: Removed most sparc stuff, left only some module and dma stuff.
 *
 *      v0.003: Enhanced base address calculation from proposals by
 *              Harald Koerfgen and Thomas Riemer.
 *
 *      v0.004: lance-regs is pointing at the right addresses, added prom
 *              check. First start of address mapping and DMA.
 *
 *      v0.005: started to play around with LANCE-DMA. This driver will not
 *              work for non IOASIC lances. HK
 *
 *      v0.006: added pointer arrays to lance_private and setup routine for
 *              them in dec_lance_init. HK
 *
 *      v0.007: Big shit. The LANCE seems to use a different DMA mechanism to
 *              access the init block. This looks like one (short) word at a
 *              time, but the smallest amount the IOASIC can transfer is a
 *              (long) word. So we have a 2-2 padding here. Changed
 *              lance_init_block accordingly. The 16-16 padding for the buffers
 *              seems to be correct. HK
 *
 *      v0.008: mods to make PMAX_LANCE work. 01/09/1999 triemer
 *
 *      v0.009: Module support fixes, multiple interfaces support, various
 *              bits. macro
 *
 *      v0.010: Fixes for the PMAD mapping of the LANCE buffer and for the
 *              PMAX requirement to only use halfword accesses to the
 *              buffer. macro
 *
 *      v0.011: Converted the PMAD to the driver model. macro
 */

#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/if_ether.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/tc.h>
#include <linux/types.h>

#include <asm/addrspace.h>
#include <asm/system.h>

#include <asm/dec/interrupts.h>
#include <asm/dec/ioasic.h>
#include <asm/dec/ioasic_addrs.h>
#include <asm/dec/kn01.h>
#include <asm/dec/machtype.h>
#include <asm/dec/system.h>

static char version[] __devinitdata =
"declance.c: v0.011 by Linux MIPS DECstation task force\n";

MODULE_AUTHOR("Linux MIPS DECstation task force");
MODULE_DESCRIPTION("DEC LANCE (DECstation onboard, PMAD-xx) driver");
MODULE_LICENSE("GPL");

#define __unused __attribute__ ((unused))

/*
 * card types
 */
#define ASIC_LANCE 1
#define PMAD_LANCE 2
#define PMAX_LANCE 3


#define LE_CSR0 0
#define LE_CSR1 1
#define LE_CSR2 2
#define LE_CSR3 3

#define LE_MO_PROM      0x8000	/* Enable promiscuous mode */

#define	LE_C0_ERR	0x8000	/* Error: set if BAB, SQE, MISS or ME is set */
#define	LE_C0_BABL	0x4000	/* BAB:  Babble: tx timeout. */
#define	LE_C0_CERR	0x2000	/* SQE:  Signal quality error */
#define	LE_C0_MISS	0x1000	/* MISS: Missed a packet */
#define	LE_C0_MERR	0x0800	/* ME:   Memory error */
#define	LE_C0_RINT	0x0400	/* Received interrupt */
#define	LE_C0_TINT	0x0200	/* Transmitter Interrupt */
#define	LE_C0_IDON	0x0100	/* IFIN: Init finished. */
#define	LE_C0_INTR	0x0080	/* Interrupt or error */
#define	LE_C0_INEA	0x0040	/* Interrupt enable */
#define	LE_C0_RXON	0x0020	/* Receiver on */
#define	LE_C0_TXON	0x0010	/* Transmitter on */
#define	LE_C0_TDMD	0x0008	/* Transmitter demand */
#define	LE_C0_STOP	0x0004	/* Stop the card */
#define	LE_C0_STRT	0x0002	/* Start the card */
#define	LE_C0_INIT	0x0001	/* Init the card */

#define	LE_C3_BSWP	0x4	/* SWAP */
#define	LE_C3_ACON	0x2	/* ALE Control */
#define	LE_C3_BCON	0x1	/* Byte control */

/* Receive message descriptor 1 */
#define LE_R1_OWN	0x8000	/* Who owns the entry */
#define LE_R1_ERR	0x4000	/* Error: if FRA, OFL, CRC or BUF is set */
#define LE_R1_FRA	0x2000	/* FRA: Frame error */
#define LE_R1_OFL	0x1000	/* OFL: Frame overflow */
#define LE_R1_CRC	0x0800	/* CRC error */
#define LE_R1_BUF	0x0400	/* BUF: Buffer error */
#define LE_R1_SOP	0x0200	/* Start of packet */
#define LE_R1_EOP	0x0100	/* End of packet */
#define LE_R1_POK	0x0300	/* Packet is complete: SOP + EOP */

/* Transmit message descriptor 1 */
#define LE_T1_OWN	0x8000	/* Lance owns the packet */
#define LE_T1_ERR	0x4000	/* Error summary */
#define LE_T1_EMORE	0x1000	/* Error: more than one retry needed */
#define LE_T1_EONE	0x0800	/* Error: one retry needed */
#define LE_T1_EDEF	0x0400	/* Error: deferred */
#define LE_T1_SOP	0x0200	/* Start of packet */
#define LE_T1_EOP	0x0100	/* End of packet */
#define LE_T1_POK	0x0300	/* Packet is complete: SOP + EOP */

#define LE_T3_BUF       0x8000	/* Buffer error */
#define LE_T3_UFL       0x4000	/* Error underflow */
#define LE_T3_LCOL      0x1000	/* Error late collision */
#define LE_T3_CLOS      0x0800	/* Error carrier loss */
#define LE_T3_RTY       0x0400	/* Error retry */
#define LE_T3_TDR       0x03ff	/* Time Domain Reflectometry counter */

/* Define: 2^4 Tx buffers and 2^4 Rx buffers */

#ifndef LANCE_LOG_TX_BUFFERS
#define LANCE_LOG_TX_BUFFERS 4
#define LANCE_LOG_RX_BUFFERS 4
#endif

#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)

#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)

#define PKT_BUF_SZ		1536
#define RX_BUFF_SIZE            PKT_BUF_SZ
#define TX_BUFF_SIZE            PKT_BUF_SZ

#undef TEST_HITS
#define ZERO 0

/*
 * The DS2100/3100 have a linear 64 kB buffer which supports halfword
 * accesses only.  Each halfword of the buffer is word-aligned in the
 * CPU address space.
 *
 * The PMAD-AA has a 128 kB buffer on-board.
 *
 * The IOASIC LANCE devices use a shared memory region.  This region
 * as seen from the CPU is (max) 128 kB long and has to be on an 128 kB
 * boundary.  The LANCE sees this as a 64 kB long continuous memory
 * region.
 *
 * The LANCE's DMA address is used as an index in this buffer and DMA
 * takes place in bursts of eight 16-bit words which are packed into
 * four 32-bit words by the IOASIC.  This leads to a strange padding:
 * 16 bytes of valid data followed by a 16 byte gap :-(.
 */

struct lance_rx_desc {
	unsigned short rmd0;		/* low address of packet */
	unsigned short rmd1;		/* high address of packet
					   and descriptor bits */
	short length;			/* 2s complement (negative!)
					   of buffer length */
	unsigned short mblength;	/* actual number of bytes received */
};

struct lance_tx_desc {
	unsigned short tmd0;		/* low address of packet */
	unsigned short tmd1;		/* high address of packet
					   and descriptor bits */
	short length;			/* 2s complement (negative!)
					   of buffer length */
	unsigned short misc;
};


/* First part of the LANCE initialization block, described in databook. */
struct lance_init_block {
	unsigned short mode;		/* pre-set mode (reg. 15) */

	unsigned short phys_addr[3];	/* physical ethernet address */
	unsigned short filter[4];	/* multicast filter */

	/* Receive and transmit ring base, along with extra bits. */
	unsigned short rx_ptr;		/* receive descriptor addr */
	unsigned short rx_len;		/* receive len and high addr */
	unsigned short tx_ptr;		/* transmit descriptor addr */
	unsigned short tx_len;		/* transmit len and high addr */

	short gap[4];

	/* The buffer descriptors */
	struct lance_rx_desc brx_ring[RX_RING_SIZE];
	struct lance_tx_desc btx_ring[TX_RING_SIZE];
};

#define BUF_OFFSET_CPU sizeof(struct lance_init_block)
#define BUF_OFFSET_LNC sizeof(struct lance_init_block)

#define shift_off(off, type)						\
	(type == ASIC_LANCE || type == PMAX_LANCE ? off << 1 : off)

#define lib_off(rt, type)						\
	shift_off(offsetof(struct lance_init_block, rt), type)

#define lib_ptr(ib, rt, type) 						\
	((volatile u16 *)((u8 *)(ib) + lib_off(rt, type)))

#define rds_off(rt, type)						\
	shift_off(offsetof(struct lance_rx_desc, rt), type)

#define rds_ptr(rd, rt, type) 						\
	((volatile u16 *)((u8 *)(rd) + rds_off(rt, type)))

#define tds_off(rt, type)						\
	shift_off(offsetof(struct lance_tx_desc, rt), type)

#define tds_ptr(td, rt, type) 						\
	((volatile u16 *)((u8 *)(td) + tds_off(rt, type)))

struct lance_private {
	struct net_device *next;
	int type;
	int dma_irq;
	volatile struct lance_regs *ll;

	spinlock_t	lock;

	int rx_new, tx_new;
	int rx_old, tx_old;

	unsigned short busmaster_regval;

	struct timer_list       multicast_timer;

	/* Pointers to the ring buffers as seen from the CPU */
	char *rx_buf_ptr_cpu[RX_RING_SIZE];
	char *tx_buf_ptr_cpu[TX_RING_SIZE];

	/* Pointers to the ring buffers as seen from the LANCE */
	uint rx_buf_ptr_lnc[RX_RING_SIZE];
	uint tx_buf_ptr_lnc[TX_RING_SIZE];
};

#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
			lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
			lp->tx_old - lp->tx_new-1)

/* The lance control ports are at an absolute address, machine and tc-slot
 * dependent.
 * DECstations do only 32-bit access and the LANCE uses 16 bit addresses,
 * so we have to give the structure an extra member making rap pointing
 * at the right address
 */
struct lance_regs {
	volatile unsigned short rdp;	/* register data port */
	unsigned short pad;
	volatile unsigned short rap;	/* register address port */
};

int dec_lance_debug = 2;

static struct tc_driver dec_lance_tc_driver;
static struct net_device *root_lance_dev;

static inline void writereg(volatile unsigned short *regptr, short value)
{
	*regptr = value;
	iob();
}

/* Load the CSR registers */
static void load_csrs(struct lance_private *lp)
{
	volatile struct lance_regs *ll = lp->ll;
	uint leptr;

	/* The address space as seen from the LANCE
	 * begins at address 0. HK
	 */
	leptr = 0;

	writereg(&ll->rap, LE_CSR1);
	writereg(&ll->rdp, (leptr & 0xFFFF));
	writereg(&ll->rap, LE_CSR2);
	writereg(&ll->rdp, leptr >> 16);
	writereg(&ll->rap, LE_CSR3);
	writereg(&ll->rdp, lp->busmaster_regval);

	/* Point back to csr0 */
	writereg(&ll->rap, LE_CSR0);
}

/*
 * Our specialized copy routines
 *
 */
static void cp_to_buf(const int type, void *to, const void *from, int len)
{
	unsigned short *tp, *fp, clen;
	unsigned char *rtp, *rfp;

	if (type == PMAD_LANCE) {
		memcpy(to, from, len);
	} else if (type == PMAX_LANCE) {
		clen = len >> 1;
		tp = (unsigned short *) to;
		fp = (unsigned short *) from;

		while (clen--) {
			*tp++ = *fp++;
			tp++;
		}

		clen = len & 1;
		rtp = (unsigned char *) tp;
		rfp = (unsigned char *) fp;
		while (clen--) {
			*rtp++ = *rfp++;
		}
	} else {
		/*
		 * copy 16 Byte chunks
		 */
		clen = len >> 4;
		tp = (unsigned short *) to;
		fp = (unsigned short *) from;
		while (clen--) {
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			tp += 8;
		}

		/*
		 * do the rest, if any.
		 */
		clen = len & 15;
		rtp = (unsigned char *) tp;
		rfp = (unsigned char *) fp;
		while (clen--) {
			*rtp++ = *rfp++;
		}
	}

	iob();
}

static void cp_from_buf(const int type, void *to, const void *from, int len)
{
	unsigned short *tp, *fp, clen;
	unsigned char *rtp, *rfp;

	if (type == PMAD_LANCE) {
		memcpy(to, from, len);
	} else if (type == PMAX_LANCE) {
		clen = len >> 1;
		tp = (unsigned short *) to;
		fp = (unsigned short *) from;
		while (clen--) {
			*tp++ = *fp++;
			fp++;
		}

		clen = len & 1;

		rtp = (unsigned char *) tp;
		rfp = (unsigned char *) fp;

		while (clen--) {
			*rtp++ = *rfp++;
		}
	} else {

		/*
		 * copy 16 Byte chunks
		 */
		clen = len >> 4;
		tp = (unsigned short *) to;
		fp = (unsigned short *) from;
		while (clen--) {
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			*tp++ = *fp++;
			fp += 8;
		}

		/*
		 * do the rest, if any.
		 */
		clen = len & 15;
		rtp = (unsigned char *) tp;
		rfp = (unsigned char *) fp;
		while (clen--) {
			*rtp++ = *rfp++;
		}


	}

}

/* Setup the Lance Rx and Tx rings */
static void lance_init_ring(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	uint leptr;
	int i;

	/* Lock out other processes while setting up hardware */
	netif_stop_queue(dev);
	lp->rx_new = lp->tx_new = 0;
	lp->rx_old = lp->tx_old = 0;

	/* Copy the ethernet address to the lance init block.
	 * XXX bit 0 of the physical address registers has to be zero
	 */
	*lib_ptr(ib, phys_addr[0], lp->type) = (dev->dev_addr[1] << 8) |
				     dev->dev_addr[0];
	*lib_ptr(ib, phys_addr[1], lp->type) = (dev->dev_addr[3] << 8) |
				     dev->dev_addr[2];
	*lib_ptr(ib, phys_addr[2], lp->type) = (dev->dev_addr[5] << 8) |
				     dev->dev_addr[4];
	/* Setup the initialization block */

	/* Setup rx descriptor pointer */
	leptr = offsetof(struct lance_init_block, brx_ring);
	*lib_ptr(ib, rx_len, lp->type) = (LANCE_LOG_RX_BUFFERS << 13) |
					 (leptr >> 16);
	*lib_ptr(ib, rx_ptr, lp->type) = leptr;
	if (ZERO)
		printk("RX ptr: %8.8x(%8.8x)\n",
		       leptr, lib_off(brx_ring, lp->type));

	/* Setup tx descriptor pointer */
	leptr = offsetof(struct lance_init_block, btx_ring);
	*lib_ptr(ib, tx_len, lp->type) = (LANCE_LOG_TX_BUFFERS << 13) |
					 (leptr >> 16);
	*lib_ptr(ib, tx_ptr, lp->type) = leptr;
	if (ZERO)
		printk("TX ptr: %8.8x(%8.8x)\n",
		       leptr, lib_off(btx_ring, lp->type));

	if (ZERO)
		printk("TX rings:\n");

	/* Setup the Tx ring entries */
	for (i = 0; i < TX_RING_SIZE; i++) {
		leptr = lp->tx_buf_ptr_lnc[i];
		*lib_ptr(ib, btx_ring[i].tmd0, lp->type) = leptr;
		*lib_ptr(ib, btx_ring[i].tmd1, lp->type) = (leptr >> 16) &
							   0xff;
		*lib_ptr(ib, btx_ring[i].length, lp->type) = 0xf000;
						/* The ones required by tmd2 */
		*lib_ptr(ib, btx_ring[i].misc, lp->type) = 0;
		if (i < 3 && ZERO)
			printk("%d: 0x%8.8x(0x%8.8x)\n",
			       i, leptr, (uint)lp->tx_buf_ptr_cpu[i]);
	}

	/* Setup the Rx ring entries */
	if (ZERO)
		printk("RX rings:\n");
	for (i = 0; i < RX_RING_SIZE; i++) {
		leptr = lp->rx_buf_ptr_lnc[i];
		*lib_ptr(ib, brx_ring[i].rmd0, lp->type) = leptr;
		*lib_ptr(ib, brx_ring[i].rmd1, lp->type) = ((leptr >> 16) &
							    0xff) |
							   LE_R1_OWN;
		*lib_ptr(ib, brx_ring[i].length, lp->type) = -RX_BUFF_SIZE |
							     0xf000;
		*lib_ptr(ib, brx_ring[i].mblength, lp->type) = 0;
		if (i < 3 && ZERO)
			printk("%d: 0x%8.8x(0x%8.8x)\n",
			       i, leptr, (uint)lp->rx_buf_ptr_cpu[i]);
	}
	iob();
}

static int init_restart_lance(struct lance_private *lp)
{
	volatile struct lance_regs *ll = lp->ll;
	int i;

	writereg(&ll->rap, LE_CSR0);
	writereg(&ll->rdp, LE_C0_INIT);

	/* Wait for the lance to complete initialization */
	for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) {
		udelay(10);
	}
	if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
		       i, ll->rdp);
		return -1;
	}
	if ((ll->rdp & LE_C0_ERR)) {
		printk("LANCE unopened after %d ticks, csr0=%4.4x.\n",
		       i, ll->rdp);
		return -1;
	}
	writereg(&ll->rdp, LE_C0_IDON);
	writereg(&ll->rdp, LE_C0_STRT);
	writereg(&ll->rdp, LE_C0_INEA);

	return 0;
}

static int lance_rx(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	volatile u16 *rd;
	unsigned short bits;
	int entry, len;
	struct sk_buff *skb;

#ifdef TEST_HITS
	{
		int i;

		printk("[");
		for (i = 0; i < RX_RING_SIZE; i++) {
			if (i == lp->rx_new)
				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
						      lp->type) &
					     LE_R1_OWN ? "_" : "X");
			else
				printk("%s", *lib_ptr(ib, brx_ring[i].rmd1,
						      lp->type) &
					     LE_R1_OWN ? "." : "1");
		}
		printk("]");
	}
#endif

	for (rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type);
	     !((bits = *rds_ptr(rd, rmd1, lp->type)) & LE_R1_OWN);
	     rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type)) {
		entry = lp->rx_new;

		/* We got an incomplete frame? */
		if ((bits & LE_R1_POK) != LE_R1_POK) {
			dev->stats.rx_over_errors++;
			dev->stats.rx_errors++;
		} else if (bits & LE_R1_ERR) {
			/* Count only the end frame as a rx error,
			 * not the beginning
			 */
			if (bits & LE_R1_BUF)
				dev->stats.rx_fifo_errors++;
			if (bits & LE_R1_CRC)
				dev->stats.rx_crc_errors++;
			if (bits & LE_R1_OFL)
				dev->stats.rx_over_errors++;
			if (bits & LE_R1_FRA)
				dev->stats.rx_frame_errors++;
			if (bits & LE_R1_EOP)
				dev->stats.rx_errors++;
		} else {
			len = (*rds_ptr(rd, mblength, lp->type) & 0xfff) - 4;
			skb = dev_alloc_skb(len + 2);

			if (skb == 0) {
				printk("%s: Memory squeeze, deferring packet.\n",
				       dev->name);
				dev->stats.rx_dropped++;
				*rds_ptr(rd, mblength, lp->type) = 0;
				*rds_ptr(rd, rmd1, lp->type) =
					((lp->rx_buf_ptr_lnc[entry] >> 16) &
					 0xff) | LE_R1_OWN;
				lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
				return 0;
			}
			dev->stats.rx_bytes += len;

			skb_reserve(skb, 2);	/* 16 byte align */
			skb_put(skb, len);	/* make room */

			cp_from_buf(lp->type, skb->data,
				    (char *)lp->rx_buf_ptr_cpu[entry], len);

			skb->protocol = eth_type_trans(skb, dev);
			netif_rx(skb);
			dev->stats.rx_packets++;
		}

		/* Return the packet to the pool */
		*rds_ptr(rd, mblength, lp->type) = 0;
		*rds_ptr(rd, length, lp->type) = -RX_BUFF_SIZE | 0xf000;
		*rds_ptr(rd, rmd1, lp->type) =
			((lp->rx_buf_ptr_lnc[entry] >> 16) & 0xff) | LE_R1_OWN;
		lp->rx_new = (entry + 1) & RX_RING_MOD_MASK;
	}
	return 0;
}

static void lance_tx(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	volatile struct lance_regs *ll = lp->ll;
	volatile u16 *td;
	int i, j;
	int status;

	j = lp->tx_old;

	spin_lock(&lp->lock);

	for (i = j; i != lp->tx_new; i = j) {
		td = lib_ptr(ib, btx_ring[i], lp->type);
		/* If we hit a packet not owned by us, stop */
		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_OWN)
			break;

		if (*tds_ptr(td, tmd1, lp->type) & LE_T1_ERR) {
			status = *tds_ptr(td, misc, lp->type);

			dev->stats.tx_errors++;
			if (status & LE_T3_RTY)
				dev->stats.tx_aborted_errors++;
			if (status & LE_T3_LCOL)
				dev->stats.tx_window_errors++;

			if (status & LE_T3_CLOS) {
				dev->stats.tx_carrier_errors++;
				printk("%s: Carrier Lost\n", dev->name);
				/* Stop the lance */
				writereg(&ll->rap, LE_CSR0);
				writereg(&ll->rdp, LE_C0_STOP);
				lance_init_ring(dev);
				load_csrs(lp);
				init_restart_lance(lp);
				goto out;
			}
			/* Buffer errors and underflows turn off the
			 * transmitter, restart the adapter.
			 */
			if (status & (LE_T3_BUF | LE_T3_UFL)) {
				dev->stats.tx_fifo_errors++;

				printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
				       dev->name);
				/* Stop the lance */
				writereg(&ll->rap, LE_CSR0);
				writereg(&ll->rdp, LE_C0_STOP);
				lance_init_ring(dev);
				load_csrs(lp);
				init_restart_lance(lp);
				goto out;
			}
		} else if ((*tds_ptr(td, tmd1, lp->type) & LE_T1_POK) ==
			   LE_T1_POK) {
			/*
			 * So we don't count the packet more than once.
			 */
			*tds_ptr(td, tmd1, lp->type) &= ~(LE_T1_POK);

			/* One collision before packet was sent. */
			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EONE)
				dev->stats.collisions++;

			/* More than one collision, be optimistic. */
			if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EMORE)
				dev->stats.collisions += 2;

			dev->stats.tx_packets++;
		}
		j = (j + 1) & TX_RING_MOD_MASK;
	}
	lp->tx_old = j;
out:
	if (netif_queue_stopped(dev) &&
	    TX_BUFFS_AVAIL > 0)
		netif_wake_queue(dev);

	spin_unlock(&lp->lock);
}

static irqreturn_t lance_dma_merr_int(int irq, void *dev_id)
{
	struct net_device *dev = dev_id;

	printk(KERN_ERR "%s: DMA error\n", dev->name);
	return IRQ_HANDLED;
}

static irqreturn_t lance_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = dev_id;
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;
	int csr0;

	writereg(&ll->rap, LE_CSR0);
	csr0 = ll->rdp;

	/* Acknowledge all the interrupt sources ASAP */
	writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT));

	if ((csr0 & LE_C0_ERR)) {
		/* Clear the error condition */
		writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
			 LE_C0_CERR | LE_C0_MERR);
	}
	if (csr0 & LE_C0_RINT)
		lance_rx(dev);

	if (csr0 & LE_C0_TINT)
		lance_tx(dev);

	if (csr0 & LE_C0_BABL)
		dev->stats.tx_errors++;

	if (csr0 & LE_C0_MISS)
		dev->stats.rx_errors++;

	if (csr0 & LE_C0_MERR) {
		printk("%s: Memory error, status %04x\n", dev->name, csr0);

		writereg(&ll->rdp, LE_C0_STOP);

		lance_init_ring(dev);
		load_csrs(lp);
		init_restart_lance(lp);
		netif_wake_queue(dev);
	}

	writereg(&ll->rdp, LE_C0_INEA);
	writereg(&ll->rdp, LE_C0_INEA);
	return IRQ_HANDLED;
}

static int lance_open(struct net_device *dev)
{
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;
	int status = 0;

	/* Stop the Lance */
	writereg(&ll->rap, LE_CSR0);
	writereg(&ll->rdp, LE_C0_STOP);

	/* Set mode and clear multicast filter only at device open,
	 * so that lance_init_ring() called at any error will not
	 * forget multicast filters.
	 *
	 * BTW it is common bug in all lance drivers! --ANK
	 */
	*lib_ptr(ib, mode, lp->type) = 0;
	*lib_ptr(ib, filter[0], lp->type) = 0;
	*lib_ptr(ib, filter[1], lp->type) = 0;
	*lib_ptr(ib, filter[2], lp->type) = 0;
	*lib_ptr(ib, filter[3], lp->type) = 0;

	lance_init_ring(dev);
	load_csrs(lp);

	netif_start_queue(dev);

	/* Associate IRQ with lance_interrupt */
	if (request_irq(dev->irq, &lance_interrupt, 0, "lance", dev)) {
		printk("%s: Can't get IRQ %d\n", dev->name, dev->irq);
		return -EAGAIN;
	}
	if (lp->dma_irq >= 0) {
		unsigned long flags;

		if (request_irq(lp->dma_irq, &lance_dma_merr_int, 0,
				"lance error", dev)) {
			free_irq(dev->irq, dev);
			printk("%s: Can't get DMA IRQ %d\n", dev->name,
				lp->dma_irq);
			return -EAGAIN;
		}

		spin_lock_irqsave(&ioasic_ssr_lock, flags);

		fast_mb();
		/* Enable I/O ASIC LANCE DMA.  */
		ioasic_write(IO_REG_SSR,
			     ioasic_read(IO_REG_SSR) | IO_SSR_LANCE_DMA_EN);

		fast_mb();
		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);
	}

	status = init_restart_lance(lp);
	return status;
}

static int lance_close(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;

	netif_stop_queue(dev);
	del_timer_sync(&lp->multicast_timer);

	/* Stop the card */
	writereg(&ll->rap, LE_CSR0);
	writereg(&ll->rdp, LE_C0_STOP);

	if (lp->dma_irq >= 0) {
		unsigned long flags;

		spin_lock_irqsave(&ioasic_ssr_lock, flags);

		fast_mb();
		/* Disable I/O ASIC LANCE DMA.  */
		ioasic_write(IO_REG_SSR,
			     ioasic_read(IO_REG_SSR) & ~IO_SSR_LANCE_DMA_EN);

		fast_iob();
		spin_unlock_irqrestore(&ioasic_ssr_lock, flags);

		free_irq(lp->dma_irq, dev);
	}
	free_irq(dev->irq, dev);
	return 0;
}

static inline int lance_reset(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;
	int status;

	/* Stop the lance */
	writereg(&ll->rap, LE_CSR0);
	writereg(&ll->rdp, LE_C0_STOP);

	lance_init_ring(dev);
	load_csrs(lp);
	dev->trans_start = jiffies;
	status = init_restart_lance(lp);
	return status;
}

static void lance_tx_timeout(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;

	printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
		dev->name, ll->rdp);
	lance_reset(dev);
	netif_wake_queue(dev);
}

static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile struct lance_regs *ll = lp->ll;
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	int entry, len;

	len = skb->len;

	if (len < ETH_ZLEN) {
		if (skb_padto(skb, ETH_ZLEN))
			return 0;
		len = ETH_ZLEN;
	}

	dev->stats.tx_bytes += len;

	entry = lp->tx_new;
	*lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len);
	*lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0;

	cp_to_buf(lp->type, (char *)lp->tx_buf_ptr_cpu[entry], skb->data, len);

	/* Now, give the packet to the lance */
	*lib_ptr(ib, btx_ring[entry].tmd1, lp->type) =
		((lp->tx_buf_ptr_lnc[entry] >> 16) & 0xff) |
		(LE_T1_POK | LE_T1_OWN);
	lp->tx_new = (entry + 1) & TX_RING_MOD_MASK;

	if (TX_BUFFS_AVAIL <= 0)
		netif_stop_queue(dev);

	/* Kick the lance: transmit now */
	writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD);

	dev->trans_start = jiffies;
	dev_kfree_skb(skb);

 	return 0;
}

static void lance_load_multicast(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	struct dev_mc_list *dmi = dev->mc_list;
	char *addrs;
	int i;
	u32 crc;

	/* set all multicast bits */
	if (dev->flags & IFF_ALLMULTI) {
		*lib_ptr(ib, filter[0], lp->type) = 0xffff;
		*lib_ptr(ib, filter[1], lp->type) = 0xffff;
		*lib_ptr(ib, filter[2], lp->type) = 0xffff;
		*lib_ptr(ib, filter[3], lp->type) = 0xffff;
		return;
	}
	/* clear the multicast filter */
	*lib_ptr(ib, filter[0], lp->type) = 0;
	*lib_ptr(ib, filter[1], lp->type) = 0;
	*lib_ptr(ib, filter[2], lp->type) = 0;
	*lib_ptr(ib, filter[3], lp->type) = 0;

	/* Add addresses */
	for (i = 0; i < dev->mc_count; i++) {
		addrs = dmi->dmi_addr;
		dmi = dmi->next;

		/* multicast address? */
		if (!(*addrs & 1))
			continue;

		crc = ether_crc_le(ETH_ALEN, addrs);
		crc = crc >> 26;
		*lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf);
	}
	return;
}

static void lance_set_multicast(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	volatile u16 *ib = (volatile u16 *)dev->mem_start;
	volatile struct lance_regs *ll = lp->ll;

	if (!netif_running(dev))
		return;

	if (lp->tx_old != lp->tx_new) {
		mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100);
		netif_wake_queue(dev);
		return;
	}

	netif_stop_queue(dev);

	writereg(&ll->rap, LE_CSR0);
	writereg(&ll->rdp, LE_C0_STOP);

	lance_init_ring(dev);

	if (dev->flags & IFF_PROMISC) {
		*lib_ptr(ib, mode, lp->type) |= LE_MO_PROM;
	} else {
		*lib_ptr(ib, mode, lp->type) &= ~LE_MO_PROM;
		lance_load_multicast(dev);
	}
	load_csrs(lp);
	init_restart_lance(lp);
	netif_wake_queue(dev);
}

static void lance_set_multicast_retry(unsigned long _opaque)
{
	struct net_device *dev = (struct net_device *) _opaque;

	lance_set_multicast(dev);
}

static int __init dec_lance_probe(struct device *bdev, const int type)
{
	static unsigned version_printed;
	static const char fmt[] = "declance%d";
	char name[10];
	struct net_device *dev;
	struct lance_private *lp;
	volatile struct lance_regs *ll;
	resource_size_t start = 0, len = 0;
	int i, ret;
	unsigned long esar_base;
	unsigned char *esar;

	if (dec_lance_debug && version_printed++ == 0)
		printk(version);

	if (bdev)
		snprintf(name, sizeof(name), "%s", dev_name(bdev));
	else {
		i = 0;
		dev = root_lance_dev;
		while (dev) {
			i++;
			lp = netdev_priv(dev);
			dev = lp->next;
		}
		snprintf(name, sizeof(name), fmt, i);
	}

	dev = alloc_etherdev(sizeof(struct lance_private));
	if (!dev) {
		printk(KERN_ERR "%s: Unable to allocate etherdev, aborting.\n",
			name);
		ret = -ENOMEM;
		goto err_out;
	}

	/*
	 * alloc_etherdev ensures the data structures used by the LANCE
	 * are aligned.
	 */
	lp = netdev_priv(dev);
	spin_lock_init(&lp->lock);

	lp->type = type;
	switch (type) {
	case ASIC_LANCE:
		dev->base_addr = CKSEG1ADDR(dec_kn_slot_base + IOASIC_LANCE);

		/* buffer space for the on-board LANCE shared memory */
		/*
		 * FIXME: ugly hack!
		 */
		dev->mem_start = CKSEG1ADDR(0x00020000);
		dev->mem_end = dev->mem_start + 0x00020000;
		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
		esar_base = CKSEG1ADDR(dec_kn_slot_base + IOASIC_ESAR);

		/* Workaround crash with booting KN04 2.1k from Disk */
		memset((void *)dev->mem_start, 0,
		       dev->mem_end - dev->mem_start);

		/*
		 * setup the pointer arrays, this sucks [tm] :-(
		 */
		for (i = 0; i < RX_RING_SIZE; i++) {
			lp->rx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
					 2 * i * RX_BUFF_SIZE);
			lp->rx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
		}
		for (i = 0; i < TX_RING_SIZE; i++) {
			lp->tx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
					 2 * i * TX_BUFF_SIZE);
			lp->tx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC +
				 RX_RING_SIZE * RX_BUFF_SIZE +
				 i * TX_BUFF_SIZE);
		}

		/* Setup I/O ASIC LANCE DMA.  */
		lp->dma_irq = dec_interrupt[DEC_IRQ_LANCE_MERR];
		ioasic_write(IO_REG_LANCE_DMA_P,
			     CPHYSADDR(dev->mem_start) << 3);

		break;
#ifdef CONFIG_TC
	case PMAD_LANCE:
		dev_set_drvdata(bdev, dev);

		start = to_tc_dev(bdev)->resource.start;
		len = to_tc_dev(bdev)->resource.end - start + 1;
		if (!request_mem_region(start, len, dev_name(bdev))) {
			printk(KERN_ERR
			       "%s: Unable to reserve MMIO resource\n",
			       dev_name(bdev));
			ret = -EBUSY;
			goto err_out_dev;
		}

		dev->mem_start = CKSEG1ADDR(start);
		dev->mem_end = dev->mem_start + 0x100000;
		dev->base_addr = dev->mem_start + 0x100000;
		dev->irq = to_tc_dev(bdev)->interrupt;
		esar_base = dev->mem_start + 0x1c0002;
		lp->dma_irq = -1;

		for (i = 0; i < RX_RING_SIZE; i++) {
			lp->rx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + BUF_OFFSET_CPU +
					 i * RX_BUFF_SIZE);
			lp->rx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
		}
		for (i = 0; i < TX_RING_SIZE; i++) {
			lp->tx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + BUF_OFFSET_CPU +
					 RX_RING_SIZE * RX_BUFF_SIZE +
					 i * TX_BUFF_SIZE);
			lp->tx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC +
				 RX_RING_SIZE * RX_BUFF_SIZE +
				 i * TX_BUFF_SIZE);
		}

		break;
#endif
	case PMAX_LANCE:
		dev->irq = dec_interrupt[DEC_IRQ_LANCE];
		dev->base_addr = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE);
		dev->mem_start = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE_MEM);
		dev->mem_end = dev->mem_start + KN01_SLOT_SIZE;
		esar_base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_ESAR + 1);
		lp->dma_irq = -1;

		/*
		 * setup the pointer arrays, this sucks [tm] :-(
		 */
		for (i = 0; i < RX_RING_SIZE; i++) {
			lp->rx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
					 2 * i * RX_BUFF_SIZE);
			lp->rx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC + i * RX_BUFF_SIZE);
		}
		for (i = 0; i < TX_RING_SIZE; i++) {
			lp->tx_buf_ptr_cpu[i] =
				(char *)(dev->mem_start + 2 * BUF_OFFSET_CPU +
					 2 * RX_RING_SIZE * RX_BUFF_SIZE +
					 2 * i * TX_BUFF_SIZE);
			lp->tx_buf_ptr_lnc[i] =
				(BUF_OFFSET_LNC +
				 RX_RING_SIZE * RX_BUFF_SIZE +
				 i * TX_BUFF_SIZE);
		}

		break;

	default:
		printk(KERN_ERR "%s: declance_init called with unknown type\n",
			name);
		ret = -ENODEV;
		goto err_out_dev;
	}

	ll = (struct lance_regs *) dev->base_addr;
	esar = (unsigned char *) esar_base;

	/* prom checks */
	/* First, check for test pattern */
	if (esar[0x60] != 0xff && esar[0x64] != 0x00 &&
	    esar[0x68] != 0x55 && esar[0x6c] != 0xaa) {
		printk(KERN_ERR
			"%s: Ethernet station address prom not found!\n",
			name);
		ret = -ENODEV;
		goto err_out_resource;
	}
	/* Check the prom contents */
	for (i = 0; i < 8; i++) {
		if (esar[i * 4] != esar[0x3c - i * 4] &&
		    esar[i * 4] != esar[0x40 + i * 4] &&
		    esar[0x3c - i * 4] != esar[0x40 + i * 4]) {
			printk(KERN_ERR "%s: Something is wrong with the "
				"ethernet station address prom!\n", name);
			ret = -ENODEV;
			goto err_out_resource;
		}
	}

	/* Copy the ethernet address to the device structure, later to the
	 * lance initialization block so the lance gets it every time it's
	 * (re)initialized.
	 */
	switch (type) {
	case ASIC_LANCE:
		printk("%s: IOASIC onboard LANCE", name);
		break;
	case PMAD_LANCE:
		printk("%s: PMAD-AA", name);
		break;
	case PMAX_LANCE:
		printk("%s: PMAX onboard LANCE", name);
		break;
	}
	for (i = 0; i < 6; i++)
		dev->dev_addr[i] = esar[i * 4];

	printk(", addr = %pM, irq = %d\n", dev->dev_addr, dev->irq);

	dev->open = &lance_open;
	dev->stop = &lance_close;
	dev->hard_start_xmit = &lance_start_xmit;
	dev->tx_timeout = &lance_tx_timeout;
	dev->watchdog_timeo = 5*HZ;
	dev->set_multicast_list = &lance_set_multicast;

	/* lp->ll is the location of the registers for lance card */
	lp->ll = ll;

	/* busmaster_regval (CSR3) should be zero according to the PMAD-AA
	 * specification.
	 */
	lp->busmaster_regval = 0;

	dev->dma = 0;

	/* We cannot sleep if the chip is busy during a
	 * multicast list update event, because such events
	 * can occur from interrupts (ex. IPv6).  So we
	 * use a timer to try again later when necessary. -DaveM
	 */
	init_timer(&lp->multicast_timer);
	lp->multicast_timer.data = (unsigned long) dev;
	lp->multicast_timer.function = &lance_set_multicast_retry;

	ret = register_netdev(dev);
	if (ret) {
		printk(KERN_ERR
			"%s: Unable to register netdev, aborting.\n", name);
		goto err_out_resource;
	}

	if (!bdev) {
		lp->next = root_lance_dev;
		root_lance_dev = dev;
	}

	printk("%s: registered as %s.\n", name, dev->name);
	return 0;

err_out_resource:
	if (bdev)
		release_mem_region(start, len);

err_out_dev:
	free_netdev(dev);

err_out:
	return ret;
}

static void __exit dec_lance_remove(struct device *bdev)
{
	struct net_device *dev = dev_get_drvdata(bdev);
	resource_size_t start, len;

	unregister_netdev(dev);
	start = to_tc_dev(bdev)->resource.start;
	len = to_tc_dev(bdev)->resource.end - start + 1;
	release_mem_region(start, len);
	free_netdev(dev);
}

/* Find all the lance cards on the system and initialize them */
static int __init dec_lance_platform_probe(void)
{
	int count = 0;

	if (dec_interrupt[DEC_IRQ_LANCE] >= 0) {
		if (dec_interrupt[DEC_IRQ_LANCE_MERR] >= 0) {
			if (dec_lance_probe(NULL, ASIC_LANCE) >= 0)
				count++;
		} else if (!TURBOCHANNEL) {
			if (dec_lance_probe(NULL, PMAX_LANCE) >= 0)
				count++;
		}
	}

	return (count > 0) ? 0 : -ENODEV;
}

static void __exit dec_lance_platform_remove(void)
{
	while (root_lance_dev) {
		struct net_device *dev = root_lance_dev;
		struct lance_private *lp = netdev_priv(dev);

		unregister_netdev(dev);
		root_lance_dev = lp->next;
		free_netdev(dev);
	}
}

#ifdef CONFIG_TC
static int __init dec_lance_tc_probe(struct device *dev);
static int __exit dec_lance_tc_remove(struct device *dev);

static const struct tc_device_id dec_lance_tc_table[] = {
	{ "DEC     ", "PMAD-AA " },
	{ }
};
MODULE_DEVICE_TABLE(tc, dec_lance_tc_table);

static struct tc_driver dec_lance_tc_driver = {
	.id_table	= dec_lance_tc_table,
	.driver		= {
		.name	= "declance",
		.bus	= &tc_bus_type,
		.probe	= dec_lance_tc_probe,
		.remove	= __exit_p(dec_lance_tc_remove),
	},
};

static int __init dec_lance_tc_probe(struct device *dev)
{
        int status = dec_lance_probe(dev, PMAD_LANCE);
        if (!status)
                get_device(dev);
        return status;
}

static int __exit dec_lance_tc_remove(struct device *dev)
{
        put_device(dev);
        dec_lance_remove(dev);
        return 0;
}
#endif

static int __init dec_lance_init(void)
{
	int status;

	status = tc_register_driver(&dec_lance_tc_driver);
	if (!status)
		dec_lance_platform_probe();
	return status;
}

static void __exit dec_lance_exit(void)
{
	dec_lance_platform_remove();
	tc_unregister_driver(&dec_lance_tc_driver);
}


module_init(dec_lance_init);
module_exit(dec_lance_exit);