]> git.neil.brown.name Git - history.git/commitdiff
Import 2.3.23pre6 2.3.23pre6
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:27:57 +0000 (15:27 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:27:57 +0000 (15:27 -0500)
82 files changed:
CREDITS
Documentation/Changes
Documentation/Configure.help
MAINTAINERS
arch/arm/boot/compressed/misc.c
arch/arm/mm/init.c
arch/i386/defconfig
arch/i386/kernel/setup.c
arch/ppc/kernel/apus_setup.c
arch/ppc/kernel/chrp_setup.c
arch/ppc/kernel/gemini_setup.c
arch/ppc/kernel/idle.c
arch/ppc/kernel/irq.c
arch/ppc/kernel/m8xx_setup.c
arch/ppc/kernel/mbx_setup.c
arch/ppc/kernel/misc.S
arch/ppc/kernel/pci.c
arch/ppc/kernel/pmac_pci.c
arch/ppc/kernel/pmac_pic.c
arch/ppc/kernel/pmac_setup.c
arch/ppc/kernel/ppc_ksyms.c
arch/ppc/kernel/prep_setup.c
arch/ppc/kernel/setup.c
arch/ppc/mm/init.c
drivers/block/icside.c
drivers/block/rd.c
drivers/block/sl82c105.c
drivers/char/mem.c
drivers/char/vt.c
drivers/macintosh/macserial.c
drivers/macintosh/macserial.h
drivers/misc/acpi.c
drivers/net/Makefile
drivers/net/irda/w83977af_ir.c
drivers/net/pcmcia/3c574_cs.c [new file with mode: 0644]
drivers/net/pcmcia/3c589_cs.c
drivers/net/pcmcia/Config.in
drivers/net/pcmcia/Makefile
drivers/net/pcmcia/fmvj18x_cs.c [new file with mode: 0644]
drivers/net/pcmcia/i82593.h [new file with mode: 0644]
drivers/net/pcmcia/netwave_cs.c [new file with mode: 0644]
drivers/net/pcmcia/nmclan_cs.c [new file with mode: 0644]
drivers/net/pcmcia/ositech.h [new file with mode: 0644]
drivers/net/pcmcia/pcnet_cs.c
drivers/net/pcmcia/smc91c92_cs.c [new file with mode: 0644]
drivers/net/pcmcia/wavelan.h [new file with mode: 0644]
drivers/net/pcmcia/wavelan_cs.c [new file with mode: 0644]
drivers/net/pcmcia/wavelan_cs.h [new file with mode: 0644]
drivers/net/pcmcia/xirc2ps_cs.c [new file with mode: 0644]
drivers/pcmcia/Config.in
drivers/pcmcia/Makefile
drivers/pcmcia/cardbus.c
drivers/pcmcia/cb_enabler.c
drivers/pcmcia/cs.c
drivers/pcmcia/i82365.c
drivers/pcmcia/o2micro.h
drivers/pcmcia/rsrc_mgr.c
drivers/pcmcia/rsrc_mgr.h
drivers/sound/dev_table.h
drivers/sound/waveartist.c
drivers/sound/waveartist.h
drivers/usb/uhci.c
drivers/usb/uhci.h
drivers/usb/usb.h
drivers/video/fbmem.c
fs/exec.c
include/asm-arm/checksum.h
include/asm-ppc/machdep.h
include/asm-ppc/page.h
include/asm-ppc/pci-bridge.h
include/asm-ppc/pgtable.h
include/linux/acpi.h
include/linux/highmem.h
include/pcmcia/cs.h
include/pcmcia/cs_types.h
include/pcmcia/driver_ops.h
include/pcmcia/ds.h
include/pcmcia/version.h
kernel/ptrace.c
mm/memory.c
mm/swap_state.c
mm/vmalloc.c

diff --git a/CREDITS b/CREDITS
index 70a53842e246adadb7654a38265ffb033452e576..56427bd902120c40c3a0033217186f5f6cca1e94 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -860,7 +860,7 @@ S: Germany
 
 N: David Hinds
 E: dhinds@zen.stanford.edu
-W: http://hyper.stanford.edu/~dhinds
+W: http://tao.stanford.edu/~dhinds
 D: PCMCIA and CardBus stuff, PCMCIA-HOWTO, PCMCIA client drivers
 S: 2019 W. Middlefield Rd #1
 S: Mountain View, CA  94043
@@ -1323,11 +1323,11 @@ S: 198 00 Praha 9
 S: Czech Republic
 
 N: Paul Mackerras
-E: paulus@cs.anu.edu.au
+E: paulus@linuxcare.com
 D: Linux port for PCI Power Macintosh
-S: Dept. of Computer Science
-S: Australian National University
-S: Canberra  ACT  0200
+S: Linuxcare, Inc.
+S: 24 Marcus Clarke Street
+S: Canberra ACT 2601
 S: Australia
 
 N: Pat Mackinlay
index 7048d0f14c60b7d144b34ffa0f034f913b165005..a1c994b2a13e82f4da094328ec42d98fcc2a549e 100644 (file)
@@ -60,7 +60,7 @@ running, the suggested command should tell you.
 - NFS                    2.2beta40               ; showmount --version
 - Bash                   1.14.7                  ; bash -version
 - Ncpfs                  2.2.0                   ; ncpmount -v
-- Pcmcia-cs              3.0.7                   ; cardmgr -V
+- Pcmcia-cs              3.1.2                   ; cardmgr -V
 - PPP                    2.3.9                   ; pppd --version
 - Util-linux             2.9i                    ; chsh -v
 
@@ -684,8 +684,8 @@ ftp://ftp.samba.org/pub/samba/samba-2.0.0.tar.gz
 Pcmcia-cs
 =========
 
-The 3.0.7 release:
-ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.7.tar.gz
+The 3.1.2 release:
+ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.2.tar.gz
 
 Setserial
 =========
index 619bc25aaaec3057595c100d2bacff450e2edc02..0e4511e5b21e81ba80b2f923d92b347bcda19d50 100644 (file)
@@ -1832,20 +1832,41 @@ CONFIG_ISAPNP
 
   If unsure, say Y.
 
-PCMCIA/Cardbus support
+PCMCIA/CardBus support
 CONFIG_PCMCIA
-  Say Y here if you want to attach PCMCIA's (PC-cards) to your Linux
-  computer. These are credit-card size devices such as network cards,
-  modems or hard drives popular with laptops.
+  Include kernel support for PCMCIA and CardBus devices.  Because
+  PCMCIA support requires additional components that are not part of
+  the kernel (i.e., the pcmcia-cs package), building PCMCIA into the
+  kernel is generally not recommended unless you have a specific
+  need.  If unsure, say N.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  When compiled this way, there will be modules called pcmcia_core.o
+  and ds.o.  If you want to compile it as a module, say M here and
+  read Documentation/modules.txt.
 
   You will also need David Hinds' pcmcia-cs package (see the file
-  Documentation/Changes for location).
+  Documentation/Changes for location).  For more information, see the
+  PCMCIA-HOWTO.
 
 CardBus support
 CONFIG_CARDBUS
-  CardBus is a bus mastering architecture for PC-cards (it allows
-  PC-cards to talk to the rest of the stuff inside your computer). If
-  unsure, say Y.
+  There are two types of PCMCIA devices: 16-bit PC Cards, and higher
+  performance 32-bit CardBus devices.  Use this option to include
+  support for CardBus devices.  If unsure, say Y.
+
+i82365/Yenta compatible bridge support
+CONFIG_I82365
+  Include support for PCMCIA and CardBus host bridges that are
+  register compatible with the Intel i82365 and/or the Yenta
+  specification: this includes virtually all modern PCMCIA bridges.
+  If unsure, say Y.
+
+Databook TCIC host bridge support
+CONFIG_TCIC
+  Include support for the Databook TCIC family of PCMCIA host bridges.
+  These are only found on a handful of old systems.  If unsure, say N.
 
 System V IPC
 CONFIG_SYSVIPC
@@ -5478,10 +5499,48 @@ CONFIG_X25_ASY
   say M here and read Documentation/modules.txt. The module will be
   called x25_asy.o. If unsure, say N.
 
-PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)
+PCMCIA network device support
+CONFIG_NET_PCMCIA
+  Say Y if you would like to include support for any PCMCIA network
+  adapters.  If unsure, say N.
+
+3Com 3c589 PCMCIA support
+CONFIG_PCMCIA_3C589
+  Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
+  (PC-card) Ethernet card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called 3c589_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+3Com 3c574 PCMCIA support
+CONFIG_PCMCIA_3C574
+  Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
+  (PC-card) Fast Ethernet card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called 3c574_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+Fujitsu FMV-J18x PCMCIA support
+CONFIG_PCMCIA_FMVJ18X
+  Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
+  PCMCIA (PC-card) Ethernet card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called fmvj18x_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+NE2000 compatible PCMCIA support
 CONFIG_PCMCIA_PCNET
   Say Y here if you intend to attach an NE2000 compatible PCMCIA
-  (PC-card) Ethernet networking card to your computer.
+  (PC-card) Ethernet or Fast Ethernet card to your computer.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -5489,18 +5548,40 @@ CONFIG_PCMCIA_PCNET
   module, say M here and read Documentation/modules.txt. If unsure,
   say N.
 
-3Com 3c589 PCMCIA card
-CONFIG_PCMCIA_3C589
-  Say Y here if you intend to attach a 3Com 3c589 PCMCIA
-  (PC-card) Ethernet networking card to your computer.
+New Media PCMCIA support
+CONFIG_PCMCIA_NMCLAN
+  Say Y here if you intend to attach a New Media Ethernet or LiveWire
+  PCMCIA (PC-card) Ethernet card to your computer.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will be called 3c589_cs.o. If you want to compile it as a
+  The module will be called nmclan_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+SMC 91Cxx PCMCIA support
+CONFIG_PCMCIA_SMC91C92
+  Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called smc91c92_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+Xircom 16-bit PCMCIA support
+CONFIG_PCMCIA_XIRC2PS
+  Say Y here if you intend to attach a Xircom 16-bit PCMCIA
+  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called xirc2ps_cs.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt. If unsure,
   say N.
 
-Aviator/Raytheon 2.4MHz wireless
+Aviator/Raytheon 2.4MHz wireless support
 CONFIG_PCMCIA_RAYCS
   Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
   (PC-card) wireless Ethernet networking card to your computer.
@@ -5511,6 +5592,29 @@ CONFIG_PCMCIA_RAYCS
   module, say M here and read Documentation/modules.txt. If unsure,
   say N.
 
+Xircom Netwave AirSurfer wireless support
+CONFIG_PCMCIA_NETWAVE
+  Say Y here if you intend to attach a Xircom Netwave AirSurfer PCMCIA
+  (PC-card) wireless Ethernet networking card to your computer.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called netwave_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
+AT&T/Lucent Wavelan wireless support
+CONFIG_PCMCIA_WAVELAN
+  Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
+  (PC-card) wireless Ethernet networking card to your computer.  This
+  driver is for the non-IEEE-802.11 Wavelan cards.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called wavelan_cs.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. If unsure,
+  say N.
+
 PLIP (parallel port) support
 CONFIG_PLIP
   PLIP (Parallel Line Internet Protocol) is used to create a
index 628715592dad70de4d1a98fdded71a7cf6f80460..e92c24e876c421b489a540ec4ca9b1d10b25e80f 100644 (file)
@@ -396,6 +396,12 @@ M: mikulas@artax.karlin.mff.cuni.cz
 W:     http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
 S:     Maintained
 
+i386 BOOT CODE
+P:     Riley H. Williams
+M:     rhw@memalpha.cx
+L:     Linux-Kernel@vger.rutgers.edu
+S:     Maintained
+
 IBM MCA SCSI SUBSYSTEM DRIVER
 P:     Michael Lang
 M:     langa2@kph.uni-mainz.de
@@ -502,8 +508,9 @@ S:  Maintained
 
 LINUX FOR POWER MACINTOSH
 P:     Paul Mackerras
-M:     paulus@cs.anu.edu.au
-L:     linux-pmac@samba.anu.edu.au
+M:     paulus@linuxcare.com
+W:     http://www.linuxppc.org/
+L:     linuxppc-dev@lists.linuxppc.org
 S:     Maintained
 
 M68K
@@ -687,6 +694,7 @@ PCMCIA SUBSYSTEM
 P:     David Hinds
 M:     dhinds@zen.stanford.edu
 L:     linux-kernel@vger.rutgers.edu
+W:     http://pcmcia.sourceforge.org
 S:     Maintained
 
 PCNET32 NETWORK DRIVER
index 0a18737b247b881266efff9c4d095ca58bf87729..66b4f2d0e870fd7b000e9485075bd9336ebb42d6 100644 (file)
@@ -295,7 +295,7 @@ decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
        makecrc();
        puts("Uncompressing Linux...");
        gunzip();
-       puts("done.\nNow booting the kernel\n");
+       puts(" done, booting the kernel.\n");
        return output_ptr;
 }
 #else
index 0c2ffec4eeccb8bedf0f46300d5f944c8db864dd..115cec885d829926f53237b134878f608f6d9202 100644 (file)
@@ -97,27 +97,38 @@ pte_t __bad_page(void)
 
 void show_mem(void)
 {
-       extern void show_net_buffers(void);
-       int i,free = 0,total = 0,reserved = 0;
-       int shared = 0;
+       int free = 0, total = 0, reserved = 0;
+       int shared = 0, cached = 0;
+       struct page *page, *end;
 
        printk("Mem-info:\n");
        show_free_areas();
        printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
-       i = MAP_NR(high_memory);
-       while (i-- > 0) {
+       for (page = mem_map, end = mem_map + max_mapnr;
+            page < end; page++) {
+               if (PageSkip(page)) {
+                       if (page->next_hash < page)
+                               break;
+                       page = page->next_hash;
+               }
                total++;
-               if (PageReserved(mem_map+i))
+               if (PageReserved(page))
                        reserved++;
-               else if (!atomic_read(&mem_map[i].count))
+               else if (PageSwapCache(page))
+                       cached++;
+               else if (!atomic_read(&page->count))
                        free++;
                else
-                       shared += atomic_read(&mem_map[i].count) - 1;
+                       shared += atomic_read(&page->count) - 1;
        }
-       printk("%d pages of RAM\n",total);
-       printk("%d free pages\n",free);
-       printk("%d reserved pages\n",reserved);
-       printk("%d pages shared\n",shared);
+       printk("%d pages of RAM\n", total);
+       printk("%d free pages\n", free);
+       printk("%d reserved pages\n", reserved);
+       printk("%d pages shared\n", shared);
+       printk("%d pages swap cached\n", cached);
+#ifndef CONFIG_NO_PGT_CACHE
+       printk("%ld page tables cached\n", pgtable_cache_size);
+#endif
        show_buffers();
 #ifdef CONFIG_NET
        show_net_buffers();
index d6821a99333db08308a23dee4f5037afb4a85b90..8b63706674b336621066207045008b98db16cf25 100644 (file)
@@ -54,10 +54,12 @@ CONFIG_PCI_DIRECT=y
 # CONFIG_MCA is not set
 
 #
-# PCMCIA/Cardbus support
+# PCMCIA/CardBus support
 #
 CONFIG_PCMCIA=y
 CONFIG_CARDBUS=y
+CONFIG_I82365=y
+# CONFIG_TCIC is not set
 CONFIG_SYSVIPC=y
 # CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_SYSCTL=y
@@ -284,11 +286,19 @@ CONFIG_EEXPRESS_PRO100=y
 # CONFIG_WAN is not set
 
 #
-# PCMCIA network devices
+# PCMCIA network device support
 #
-CONFIG_PCMCIA_PCNET=y
+CONFIG_NET_PCMCIA=y
 # CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=y
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
 CONFIG_PCMCIA_RAYCS=y
+# CONFIG_PCMCIA_NETWAVE is not set
+# CONFIG_PCMCIA_WAVELAN is not set
 CONFIG_PCMCIA_NETCARD=y
 
 #
index e6a61e8215772cb9257ebf71a42a83c1a9dfa1df..31c77bb1d3b0dcc0b70050a485d31787285d2517 100644 (file)
@@ -402,7 +402,11 @@ void __init add_memory_region(unsigned long start,
 } /* add_memory_region */
 
 
-#define LOWMEMSIZE()   ((*(unsigned short *)__va(0x413)) * 1024)
+/*
+ * Do NOT EVER look at the BIOS memory size location.
+ * It does not work on many machines.
+ */
+#define LOWMEMSIZE()   (0x9f000)
 
 void __init setup_memory_region(void)
 {
index 80377490a32681af5fe6fc7172961c139e0a96eb..353482a18170babdbded94c9ce5da00954db2397 100644 (file)
@@ -255,8 +255,7 @@ struct pci_bus * __init pci_scan_peer_bridge(int bus)
 
 /*********************************************************** SETUP */
 /* From arch/m68k/kernel/setup.c. */
-void __init apus_setup_arch(unsigned long * memory_start_p,
-                               unsigned long * memory_end_p)
+void __init apus_setup_arch(void)
 {
 #ifdef CONFIG_APUS
        extern char cmd_line[];
index 934f377b50c9fad3b8aadf20b7e00d1e54e03a7c..58d2003c495fe3a1d46c20b1ba95acb2eed3ebee 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/version.h>
 #include <linux/adb.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -226,7 +227,7 @@ static void __init sio_init(void)
 
 
 void __init
-chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+chrp_setup_arch(void)
 {
        extern char cmd_line[];
        struct device_node *device;
@@ -277,7 +278,7 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
 #ifdef CONFIG_DUMMY_CONSOLE
        conswitchp = &dummy_con;
 #endif
-       *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+       pmac_find_bridges();
 
        /* Get the event scan rate for the rtas so we know how
         * often it expects a heartbeat. -- Cort
@@ -362,10 +363,7 @@ int chrp_get_irq( struct pt_regs *regs )
                        irq = *chrp_int_ack_special;
                else
                        irq = i8259_irq( smp_processor_id() );
-                /*
-                 * Acknowledge as soon as possible to allow i8259
-                 * interrupt nesting                         */
-                openpic_eoi( smp_processor_id() );
+               openpic_eoi( smp_processor_id() );
         }
         if (irq == OPENPIC_VEC_SPURIOUS)
                 /*
@@ -373,6 +371,11 @@ int chrp_get_irq( struct pt_regs *regs )
                  * acknowledged
                  */
                irq = -1;
+       /*
+        * I would like to openpic_eoi here but there seem to be timing problems
+        * between the openpic ack and the openpic eoi.
+        *   -- Cort
+        */
        return irq;
 }
 
@@ -381,89 +384,15 @@ void chrp_post_irq(int irq)
        /*
         * If it's an i8259 irq then we've already done the
         * openpic irq.  So we just check to make sure the controller
-        * is an openpic and if it is then eoi -- Cort
+        * is an openpic and if it is then eoi
+        *
+        * We do it this way since our irq_desc[irq].ctl can change
+        * with RTL and no longer be open_pic -- Cort
         */
-       if ( irq_desc[irq].ctl == &open_pic )
+       if ( irq >= open_pic.irq_offset)
                openpic_eoi( smp_processor_id() );
 }
 
-#if 0
-void
-chrp_do_IRQ(struct pt_regs *regs,
-           int            cpu,
-            int            isfake)
-{
-        int irq;
-        unsigned long bits = 0;
-        int openpic_eoi_done = 0;
-
-#ifdef __SMP__
-        {
-                unsigned int loops = 1000000;
-                while (test_bit(0, &global_irq_lock)) {
-                        if (smp_processor_id() == global_irq_holder) {
-                                printk("uh oh, interrupt while we hold global irq lock!\n");
-#ifdef CONFIG_XMON
-                                xmon(0);
-#endif
-                                break;
-                        }
-                        if (loops-- == 0) {
-                                printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder);
-#ifdef CONFIG_XMON
-                                xmon(0);
-#endif
-                        }
-                }
-        }
-#endif /* __SMP__ */
-
-        irq = openpic_irq(0);
-        if (irq == IRQ_8259_CASCADE)
-        {
-                /*
-                 * This magic address generates a PCI IACK cycle.
-                 *
-                 * This should go in the above mask/ack code soon. -- Cort
-                 */
-               if ( chrp_int_ack_special )
-                       irq = *chrp_int_ack_special;
-               else
-                       irq = i8259_irq(0);
-                /*
-                 * Acknowledge as soon as possible to allow i8259
-                 * interrupt nesting                         */
-                openpic_eoi(0);
-                openpic_eoi_done = 1;
-        }
-        if (irq == OPENPIC_VEC_SPURIOUS)
-        {
-                /*
-                 * Spurious interrupts should never be
-                 * acknowledged
-                 */
-                ppc_spurious_interrupts++;
-                openpic_eoi_done = 1;
-               goto out;
-        }
-        bits = 1UL << irq;
-
-        if (irq < 0)
-        {
-                printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
-                       irq, regs->nip);
-                ppc_spurious_interrupts++;
-        }
-       else
-        {
-               ppc_irq_dispatch_handler( regs, irq );
-       }
-out:
-        if (!openpic_eoi_done)
-                openpic_eoi(0);
-}
-#endif
-
 void __init chrp_init_IRQ(void)
 {
        struct device_node *np;
index 6e599cff2134ee1fe799a57d3f93814570b17f4b..23060ba2673eab12220e3a319d8ccd0a196d42b4 100644 (file)
@@ -134,7 +134,7 @@ extern int root_mountflags;
 extern char cmd_line[];
 
 
-void __init gemini_setup_arch(unsigned long *memstart, unsigned long *memend)
+void __init gemini_setup_arch(void)
 {
        unsigned int cpu;
        extern char cmd_line[];
index 09066a84870a6e91fe8874365fcdb7df1c608bf4..42ebfd9f4f634ad1067c4da980afaf6d6915f672 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.67 1999/09/10 05:05:47 paulus Exp $
+ * $Id: idle.c,v 1.68 1999/10/15 18:16:03 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -293,11 +293,8 @@ void power_save(void)
                
                        /* set the POW bit in the MSR, and enable interrupts
                         * so we wake up sometime! */
+                       __sti(); /* this keeps rtl from getting confused -- Cort */
                        _nmask_and_or_msr(0, MSR_POW | MSR_EE);
-
-                       /* Disable interrupts again so restore_flags will
-                        * work. */
-                       _nmask_and_or_msr(MSR_EE, 0);
                }
                restore_flags(msr);
        default:
index 9c157d41b9fd57df3a9f877a25a072b8fb3f71f5..9ab3589f6cb8e19d9dcb105b71de904a4f560140 100644 (file)
@@ -296,15 +296,19 @@ asmlinkage void do_IRQ(struct pt_regs *regs, int isfake)
        irq = ppc_md.get_irq( regs );
        if ( irq < 0 )
        {
-                printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
-                       irq, regs->nip);
-                ppc_spurious_interrupts++;
-               return;
+               /* -2 means ignore, already handled */
+               if (irq != -2) {
+                       printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
+                              irq, regs->nip);
+                       ppc_spurious_interrupts++;
+               }
+               goto out;
        }
        ppc_irq_dispatch_handler( regs, irq );
        if ( ppc_md.post_irq )
                ppc_md.post_irq( irq );
-       
+
+ out:  
         hardirq_exit( cpu );
 }
 
index f99f5b3357d8111cb17d1f6570d3736f66c90cb2..4d510612bd1b2cfaeaa4506e473257cd0a196aad 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/blk.h>
 #include <linux/ioport.h>
 #include <linux/ide.h>
+#include <linux/bootmem.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -83,13 +84,12 @@ void __init adbdev_init(void)
 }
 
 void __init
-m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+m8xx_setup_arch(void)
 {
        int     cpm_page;
        extern char cmd_line[];
        
-       cpm_page = *memory_start_p;
-       *memory_start_p += PAGE_SIZE;
+       cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
        
        printk("Boot arguments: %s\n", cmd_line);
 
@@ -108,6 +108,9 @@ m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
        rd_doload = 1;
        rd_image_start = 0;
 #endif
+#if 0  /* XXX this may need to be updated for the new bootmem stuff,
+          or possibly just deleted (see set_phys_avail() in init.c).
+          - paulus. */
        /* initrd_start and size are setup by boot/head.S and kernel/head.S */
        if ( initrd_start )
        {
@@ -120,6 +123,7 @@ m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
                }
        }
 #endif
+#endif
 }
 
 void
index 5ba7d878e914ecd8fa3000e790e7b6b8568758b3..f6487783bcf82fbfc2f5f1fad03cc1f86afaa7e2 100644 (file)
@@ -76,13 +76,12 @@ void __init adbdev_init(void)
 }
 
 void __init
-mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+mbx_setup_arch(void)
 {
        int     cpm_page;
        extern char cmd_line[];
        
-       cpm_page = *memory_start_p;
-       *memory_start_p += PAGE_SIZE;
+       cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
        
        sprintf(cmd_line,
 "%s root=/dev/nfs nfsroot=/sys/mbxroot",
@@ -104,6 +103,9 @@ mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
        rd_doload = 1;
        rd_image_start = 0;
 #endif
+#if 0  /* XXX this may need to be updated for the new bootmem stuff,
+          or possibly just deleted (see set_phys_avail() in init.c).
+          - paulus. */
        /* initrd_start and size are setup by boot/head.S and kernel/head.S */
        if ( initrd_start )
        {
@@ -116,6 +118,7 @@ mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
                }
        }
 #endif
+#endif
 
 #ifdef notdef
        request_region(0x20,0x20,"pic1");
index 4d041cc98b18f6f66d62db915acbe0c196ca9839..89994881beb02e2787edcc8eb992e73849ed76ae 100644 (file)
@@ -235,9 +235,9 @@ _GLOBAL(flush_dcache_range)
  * snoop from the data cache.
  * This is a no-op on the 601 which has a unified cache.
  *
- *     void flush_page_to_ram(void *page)
+ *     void __flush_page_to_ram(void *page)
  */
-_GLOBAL(flush_page_to_ram)
+_GLOBAL(__flush_page_to_ram)
        mfspr   r5,PVR
        rlwinm  r5,r5,16,16,31
        cmpi    0,r5,1
index de0a0a36d59211932ef99d9925a5a05555eded39..0d07c289d9065426dc0036aeddb5291d8b58b089 100644 (file)
@@ -146,3 +146,25 @@ int pcibios_assign_resource(struct pci_dev *pdev, int resource)
 {
        return 0;
 }
+
+/* the next two are stolen from the alpha port... */
+void __init
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+                       struct resource *res, int resource)
+{
+        unsigned long where, size;
+        u32 reg;
+
+        where = PCI_BASE_ADDRESS_0 + (resource * 4);
+        size = res->end - res->start;
+        pci_read_config_dword(dev, where, &reg);
+        reg = (reg & size) | (((u32)(res->start - root->start)) & ~size);
+        pci_write_config_dword(dev, where, reg);
+}
+
+void __init
+pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+       pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+       /* XXX FIXME - update OF device tree node interrupt property */
+}
index 932e7dbb6d64cdacd6f9300a0fc10de2a5578698..62161f68a54b61091c7e8761425c83b060f4ba9a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/delay.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/bootmem.h>
 
 #include <asm/init.h>
 #include <asm/io.h>
@@ -30,7 +31,7 @@
 struct bridge_data **bridges, *bridge_list;
 static int max_bus;
 
-static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
+static void add_bridges(struct device_node *dev);
 
 /*
  * Magic constants for enabling cache coherency in the bandit/PSX bridge.
@@ -362,24 +363,22 @@ static void __init init_bandit(struct bridge_data *bp)
               bp->io_base);
 }
 
-unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)
+void __init pmac_find_bridges(void)
 {
        int bus;
        struct bridge_data *bridge;
 
        bridge_list = 0;
        max_bus = 0;
-       add_bridges(find_devices("bandit"), &mem_start);
-       add_bridges(find_devices("chaos"), &mem_start);
-       add_bridges(find_devices("pci"), &mem_start);
-       bridges = (struct bridge_data **) mem_start;
-       mem_start += (max_bus + 1) * sizeof(struct bridge_data *);
+       add_bridges(find_devices("bandit"));
+       add_bridges(find_devices("chaos"));
+       add_bridges(find_devices("pci"));
+       bridges = (struct bridge_data **)
+               alloc_bootmem((max_bus + 1) * sizeof(struct bridge_data *));
        memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *));
        for (bridge = bridge_list; bridge != NULL; bridge = bridge->next)
                for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus)
                        bridges[bus] = bridge;
-
-       return mem_start;
 }
 
 /*
@@ -387,7 +386,7 @@ unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long me
  * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise,
  * if we have one or more bandit or chaos bridges, we don't have a MPC106.
  */
-static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr)
+static void __init add_bridges(struct device_node *dev)
 {
        int *bus_range;
        int len;
@@ -413,8 +412,7 @@ static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr)
                        printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
                               bus_range[1]);
                printk(" controlled by %s at %x\n", dev->name, addr->address);
-               bp = (struct bridge_data *) *mem_ptr;
-               *mem_ptr += sizeof(struct bridge_data);
+               bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp));
                if (strcmp(dev->name, "pci") != 0) {
                        bp->cfg_addr = (volatile unsigned int *)
                                ioremap(addr->address + 0x800000, 0x1000);
index 683aac568282d70abbbd4c3e45bdcf10c62efcdb..f7224f5dd38d0a33472bc3409d6b2ad502a0516c 100644 (file)
@@ -225,7 +225,7 @@ pmac_get_irq(struct pt_regs *regs)
                        xmon(regs);
 #endif
                smp_message_recv();
-               return -1;
+               return -2;      /* ignore, already handled */
         }
 
         {
@@ -374,7 +374,7 @@ pmac_pic_init(void)
                }
                
                /* get addresses of second controller */
-               irqctrler = (irqctrler->next) ? irqctrler->next : NULL;
+               irqctrler = irqctrler->next;
                if (irqctrler && irqctrler->n_addrs > 0) {
                        addr = (unsigned long) 
                                ioremap(irqctrler->addrs[0].address, 0x40);
@@ -382,6 +382,11 @@ pmac_pic_init(void)
                                pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
                                        (addr + (4 - i) * 0x10);
                }
+       } else {
+               /* older powermacs have a GC (grand central) or ohare at
+                  f3000000, with interrupt control registers at f3000020. */
+               addr = (unsigned long) ioremap(0xf3000000, 0x40);
+               pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20);
        }
 
        /* disable all interrupts in all controllers */
index 41ff21aed173060bf0e595dacc59b6b4f5ae2049..0a23c4473d03bf2fe5260905612505033c9e903e 100644 (file)
@@ -238,7 +238,7 @@ pmac_mksound(unsigned int hz, unsigned int ticks)
 static volatile u32 *sysctrl_regs;
 
 void __init
-pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
+pmac_setup_arch(void)
 {
        struct device_node *cpu;
        int *fp;
@@ -269,7 +269,7 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
        __ioremap(0xffc00000, 0x400000, pgprot_val(PAGE_READONLY));
        ohare_init();
 
-       *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+       pmac_find_bridges();
        init_p2pbridge();
 
        /* Checks "l2cr-value" property in the registry */
index cabce054c90010feb5486cc711ad6b930cba8ec3..399b99052ba98c3f40929558c6d31120cfa937be 100644 (file)
@@ -60,7 +60,6 @@ EXPORT_SYMBOL(do_signal);
 EXPORT_SYMBOL(syscall_trace);
 EXPORT_SYMBOL(transfer_to_handler);
 EXPORT_SYMBOL(do_IRQ);
-EXPORT_SYMBOL(init_task_union);
 EXPORT_SYMBOL(MachineCheckException);
 EXPORT_SYMBOL(AlignmentException);
 EXPORT_SYMBOL(ProgramCheckException);
index a04347d4b97be4a7613604f95dcd2b00481ea4c1..fe7043206541dff86d85cb1a289c5bc5843da0f8 100644 (file)
@@ -210,7 +210,7 @@ no_l2:
 }
 
 void __init
-prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)
+prep_setup_arch()
 {
        extern char cmd_line[];
        unsigned char reg;
index 7177c800677b58095cbda24efee4ca115a23769d..518446bf96e49d82922b28d8b798dd20d9b86b37 100644 (file)
@@ -539,14 +539,12 @@ void __init ppc_init(void)
        }
 }
 
-void __init setup_arch(char **cmdline_p,
-                      unsigned long * memory_start_p, unsigned long * memory_end_p)
+void __init setup_arch(char **cmdline_p)
 {
        extern int panic_timeout;
        extern char _etext[], _edata[];
        extern char *klimit;
-       extern unsigned long find_available_memory(void);
-       extern unsigned long *end_of_DRAM;
+       extern void do_init_bootmem(void);
 
 #ifdef CONFIG_XMON
        extern void xmon_map_scc(void);
@@ -555,22 +553,22 @@ void __init setup_arch(char **cmdline_p,
                xmon(0);
 #endif /* CONFIG_XMON */
 
-       /* reboot on panic */   
+       /* reboot on panic */
        panic_timeout = 180;
 
        init_mm.start_code = PAGE_OFFSET;
        init_mm.end_code = (unsigned long) _etext;
        init_mm.end_data = (unsigned long) _edata;
-       init_mm.brk = (unsigned long) klimit;   
+       init_mm.brk = (unsigned long) klimit;
 
        /* Save unparsed command line copy for /proc/cmdline */
        strcpy(saved_command_line, cmd_line);
        *cmdline_p = cmd_line;
 
-       *memory_start_p = find_available_memory();
-       *memory_end_p = (unsigned long) end_of_DRAM;
+       /* set up the bootmem stuff with available memory */
+       do_init_bootmem();
 
-       ppc_md.setup_arch(memory_start_p, memory_end_p);
+       ppc_md.setup_arch();
        /* clear the progress line */
        if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab);
 }
index a708c59426bf1561feed4f506102a756426f49c2..7ccbe656f552d0941a9662e89d21826d0edd4f9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  $Id: init.c,v 1.193 1999/10/11 18:50:35 geert Exp $
+ *  $Id: init.c,v 1.195 1999/10/15 16:39:39 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -35,6 +35,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/openpic.h>
+#include <linux/bootmem.h>
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>         /* for initrd_* */
 #endif
@@ -59,6 +60,9 @@ int prom_trashed;
 atomic_t next_mmu_context;
 unsigned long *end_of_DRAM;
 int mem_init_done;
+int init_bootmem_done;
+int boot_mapsize;
+unsigned long totalram_pages = 0;
 extern pgd_t swapper_pg_dir[];
 extern char _start[], _end[];
 extern char etext[], _stext[];
@@ -108,9 +112,9 @@ struct mem_pieces {
 };
 struct mem_pieces phys_mem;
 struct mem_pieces phys_avail;
-struct mem_pieces prom_mem;
 
 static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
+static void set_phys_avail(void);
 void *find_mem_piece(unsigned, unsigned);
 static void print_mem_pieces(struct mem_pieces *);
 #if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
@@ -202,7 +206,7 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
                        pte = (pte_t *) MMU_get_page();
                else if ((pte = (pte_t *) get_zero_page_fast()) == NULL)
                        if ((pte = (pte_t *) __get_free_page(GFP_KERNEL)))
-                               clear_page((unsigned long)pte);
+                               clear_page(pte);
                 if (pte) {
                         pmd_val(*pmd) = (unsigned long)pte;
                         return pte + offset;
@@ -246,20 +250,20 @@ int do_check_pgt_cache(int low, int high)
  * ZERO_PAGE is a special page that is used for zero-initialized
  * data and COW.
  */
-unsigned long empty_bad_page_table;
+pte_t *empty_bad_page_table;
 
 pte_t * __bad_pagetable(void)
 {
-       __clear_user((void *)empty_bad_page_table, PAGE_SIZE);
-       return (pte_t *) empty_bad_page_table;
+       clear_page(empty_bad_page_table);
+       return empty_bad_page_table;
 }
 
-unsigned long empty_bad_page;
+void *empty_bad_page;
 
 pte_t __bad_page(void)
 {
-       __clear_user((void *)empty_bad_page, PAGE_SIZE);
-       return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED));
+       clear_page(empty_bad_page);
+       return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED));
 }
 
 void show_mem(void)
@@ -346,7 +350,7 @@ void si_meminfo(struct sysinfo *val)
        val->totalram = 0;
        val->sharedram = 0;
        val->freeram = nr_free_pages << PAGE_SHIFT;
-       val->bufferram = atomic_read(&buffermem);
+       val->bufferram = atomic_read(&buffermem_pages);
        while (i-- > 0)  {
                if (PageReserved(mem_map+i))
                        continue;
@@ -590,6 +594,37 @@ mmu_context_overflow(void)
 }
 #endif /* CONFIG_8xx */
 
+/*
+ * Set phys_avail to phys_mem less the kernel text/data/bss.
+ */
+static void __init set_phys_avail(void)
+{
+       unsigned long kstart, ksize;
+
+       /* we can't call the prom any more at this stage, so
+          all of memory is available (after klimit) */
+       phys_avail = phys_mem;
+
+       /*
+        * phys_avail records memory we can use.
+        * Make sure the kernel text/data/bss is not in it.
+        */
+       kstart = __pa(_stext);  /* should be 0 */
+       ksize = PAGE_ALIGN(klimit - _stext);
+       remove_mem_piece(&phys_avail, kstart, ksize, 0);
+       remove_mem_piece(&phys_avail, 0, 0x4000, 0);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start) {
+               /*
+                * Remove the initialized ramdisk from the available memory.
+                */
+               remove_mem_piece(&phys_avail, __pa(initrd_start),
+                                initrd_end - initrd_start, 1);
+       }
+#endif /* CONFIG_BLK_DEV_INITRD */
+}
+
 /*
  * Scan a region for a piece of a given size with the required alignment.
  */
@@ -682,7 +717,7 @@ static void __init print_mem_pieces(struct mem_pieces *mp)
        printk("\n");
 }
 
-#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_PPC_ALL)
+#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
 /*
  * Add some memory to an array of pieces
  */
@@ -834,8 +869,8 @@ static void __init mapin_ram(void)
 {
        int i;
        unsigned long v, p, s, f;
-#ifndef CONFIG_8xx
 
+#ifndef CONFIG_8xx
        if (!__map_without_bats) {
                unsigned long tot, mem_base, bl, done;
                unsigned long max_size = (256<<20);
@@ -870,24 +905,7 @@ static void __init mapin_ram(void)
                               RAM_PAGE);
                }
        }
-       v = KERNELBASE;
-       for (i = 0; i < phys_mem.n_regions; ++i) {
-               p = phys_mem.regions[i].address;
-               for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
-                       f = _PAGE_PRESENT | _PAGE_ACCESSED;
-                       if ((char *) v < _stext || (char *) v >= etext)
-                               f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
-                       else
-                               /* On the powerpc, no user access
-                                  forces R/W kernel access */
-                               f |= _PAGE_USER;
-                       map_page(v, p, f);
-                       v += PAGE_SIZE;
-                       p += PAGE_SIZE;
-               }
-       }
-
-#else  /* CONFIG_8xx */
+#endif /* CONFIG_8xx */
 
        for (i = 0; i < phys_mem.n_regions; ++i) {
                v = (ulong)__va(phys_mem.regions[i].address);
@@ -897,37 +915,35 @@ static void __init mapin_ram(void)
                          * don't get ASID compares on kernel space.
                          */
                        f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED;
-
-                        /* I don't really need the rest of this code, but
-                         * I grabbed it because I think the line:
-                         *      f |= _PAGE_USER
-                         * is incorrect.  It needs to be set to bits we
-                         * don't define to cause a kernel read-only.  On
-                         * the MPC8xx, the PAGE_DIRTY takes care of that
-                         * for us (along with the RW software state).
-                         */
                        if ((char *) v < _stext || (char *) v >= etext)
                                f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
+#ifndef CONFIG_8xx
+                       else
+                               /* On the powerpc (not 8xx), no user access
+                                  forces R/W kernel access */
+                               f |= _PAGE_USER;
+#endif /* CONFIG_8xx */
                        map_page(v, p, f);
                        v += PAGE_SIZE;
                        p += PAGE_SIZE;
                }
-       }           
-#endif /* CONFIG_8xx */
+       }
 }
 
-/* This can get called from ioremap, so don't make it an __init, OK? */
+/* In fact this is only called until mem_init is done. */
 static void __init *MMU_get_page(void)
 {
        void *p;
 
        if (mem_init_done) {
                p = (void *) __get_free_page(GFP_KERNEL);
-               if (p == 0)
-                       panic("couldn't get a page in MMU_get_page");
+       } else if (init_bootmem_done) {
+               p = alloc_bootmem_pages(PAGE_SIZE);
        } else {
                p = find_mem_piece(PAGE_SIZE, PAGE_SIZE);
        }
+       if (p == 0)
+               panic("couldn't get a page in MMU_get_page");
        __clear_user(p, PAGE_SIZE);
        return p;
 }
@@ -1106,6 +1122,51 @@ void __init MMU_init(void)
        if ( ppc_md.progress ) ppc_md.progress("MMU:exit", 0x211);
 }
 
+/*
+ * Initialize the bootmem system and give it all the memory we
+ * have available.
+ */
+void __init do_init_bootmem(void)
+{
+       unsigned long start, size;
+       int i;
+
+       /*
+        * Find an area to use for the bootmem bitmap.
+        * We look for the first area which is at least
+        * 128kB in length (128kB is enough for a bitmap
+        * for 4GB of memory, using 4kB pages), plus 1 page
+        * (in case the address isn't page-aligned).
+        */
+       start = 0;
+       size = 0;
+       for (i = 0; i < phys_avail.n_regions; ++i) {
+               unsigned long a = phys_avail.regions[i].address;
+               unsigned long s = phys_avail.regions[i].size;
+               if (s <= size)
+                       continue;
+               start = a;
+               size = s;
+               if (s >= 33 * PAGE_SIZE)
+                       break;
+       }
+       start = PAGE_ALIGN(start);
+
+       boot_mapsize = init_bootmem(start >> PAGE_SHIFT,
+                                   __pa(end_of_DRAM) >> PAGE_SHIFT);
+
+       /* remove the bootmem bitmap from the available memory */
+       remove_mem_piece(&phys_avail, start, start + boot_mapsize, 1);
+
+       /* add everything in phys_avail into the bootmem map */
+       for (i = 0; i < phys_avail.n_regions; ++i)
+               free_bootmem(phys_avail.regions[i].address,
+                            phys_avail.regions[i].size);
+
+       init_bootmem_done = 1;
+}
+
+#if 0
 /*
  * Find some memory for setup_arch to return.
  * We use the largest chunk of available memory as the area
@@ -1144,97 +1205,56 @@ unsigned long __init find_available_memory(void)
        avail_start = (unsigned long) __va(a);
        return avail_start;
 }
+#endif /* 0 */
 
 /*
  * paging_init() sets up the page tables - in fact we've already done this.
  */
-unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem)
+void __init paging_init(void)
 {
-       extern unsigned long free_area_init(unsigned long, unsigned long);
        /*
         * Grab some memory for bad_page and bad_pagetable to use.
         */
-       empty_bad_page = PAGE_ALIGN(start_mem);
-       empty_bad_page_table = empty_bad_page + PAGE_SIZE;
-       start_mem = empty_bad_page + 2 * PAGE_SIZE;
-
-       /* note: free_area_init uses its second argument
-          to size the mem_map array. */
-       start_mem = free_area_init(start_mem, end_mem);
-       return start_mem;
+       empty_bad_page = alloc_bootmem_pages(PAGE_SIZE);
+       empty_bad_page_table = alloc_bootmem_pages(PAGE_SIZE);
+
+       free_area_init(max_low_pfn);
 }
 
-void __init mem_init(unsigned long start_mem, unsigned long end_mem)
+void __init mem_init(void)
 {
        unsigned long addr;
-       int i;
-       unsigned long a, lim;
        int codepages = 0;
        int datapages = 0;
        int initpages = 0;
        extern unsigned int rtas_data, rtas_size;
 
-       end_mem &= PAGE_MASK;
-       high_memory = (void *) end_mem;
-       max_mapnr = MAP_NR(high_memory);
-
-       /* mark usable pages in the mem_map[] */
-       start_mem = PAGE_ALIGN(start_mem);
-
+       max_mapnr = max_low_pfn;
+       high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
        num_physpages = max_mapnr;      /* RAM is assumed contiguous */
-       remove_mem_piece(&phys_avail, __pa(avail_start),
-                        start_mem - avail_start, 1);
 
-       for (i = 0; i < phys_avail.n_regions; ++i) {
-               a = (unsigned long) __va(phys_avail.regions[i].address);
-               lim = (a + phys_avail.regions[i].size) & PAGE_MASK;
-               a = PAGE_ALIGN(a);
-               for (; a < lim; a += PAGE_SIZE)
-                       clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
-       }
+       totalram_pages += free_all_bootmem();
 
 #ifdef CONFIG_BLK_DEV_INITRD
        /* if we are booted from BootX with an initial ramdisk,
           make sure the ramdisk pages aren't reserved. */
        if (initrd_start) {
-               for (a = initrd_start; a < initrd_end; a += PAGE_SIZE)
-                       clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
+               for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE)
+                       clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
        }
 #endif /* CONFIG_BLK_DEV_INITRD */
        
-       /* free the prom's memory - no-op on prep */
-       for (i = 0; i < prom_mem.n_regions; ++i) {
-               a = (unsigned long) __va(prom_mem.regions[i].address);
-               lim = (a + prom_mem.regions[i].size) & PAGE_MASK;
-               a = PAGE_ALIGN(a);
-               for (; a < lim; a += PAGE_SIZE)
-                       clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
-       }
-
-       prom_trashed = 1;
-       
-       for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
-               if (PageReserved(mem_map + MAP_NR(addr))) {
-                       if (addr < (ulong) etext)
-                               codepages++;
-                       else if (addr >= (unsigned long)&__init_begin
-                                && addr < (unsigned long)&__init_end)
-                                initpages++;
-                        else if (addr < (ulong) start_mem)
-                               datapages++;
+       for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM;
+            addr += PAGE_SIZE) {
+               if (!PageReserved(mem_map + MAP_NR(addr)))
                        continue;
-               }
-               set_page_count(mem_map + MAP_NR(addr), 1);
-#ifdef CONFIG_BLK_DEV_INITRD
-               if (!initrd_start ||
-                   addr < (initrd_start & PAGE_MASK) || addr >= initrd_end)
-#endif /* CONFIG_BLK_DEV_INITRD */
-#ifndef CONFIG_8xx
-                       if ( !rtas_data ||
-                            addr < (rtas_data & PAGE_MASK) ||
-                            addr >= (rtas_data+rtas_size))
-#endif /* CONFIG_8xx */
-                               free_page(addr);
+               if (addr < (ulong) etext)
+                       codepages++;
+               else if (addr >= (unsigned long)&__init_begin
+                        && addr < (unsigned long)&__init_end)
+                       initpages++;
+               else if (addr < (ulong) klimit)
+                       datapages++;
        }
 
         printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n",
@@ -1242,7 +1262,7 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem)
               codepages << (PAGE_SHIFT-10),
               datapages << (PAGE_SHIFT-10), 
               initpages << (PAGE_SHIFT-10),
-              PAGE_OFFSET, end_mem);
+              PAGE_OFFSET, (unsigned long) end_of_DRAM);
        mem_init_done = 1;
 }
 
@@ -1258,11 +1278,9 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem)
 unsigned long __init *pmac_find_end_of_memory(void)
 {
        unsigned long a, total;
-       unsigned long kstart, ksize;
-       int i;
        
        /* max amount of RAM we allow -- Cort */
-#define RAM_LIMIT (256<<20)
+#define RAM_LIMIT (768<<20)
 
        memory_node = find_devices("memory");
        if (memory_node == NULL) {
@@ -1310,32 +1328,8 @@ unsigned long __init *pmac_find_end_of_memory(void)
                phys_mem.n_regions = 1;
        }
 
-       if (boot_infos == 0) {
-               /* record which bits the prom is using */
-               get_mem_prop("available", &phys_avail);
-               prom_mem = phys_mem;
-               for (i = 0; i < phys_avail.n_regions; ++i)
-                       remove_mem_piece(&prom_mem,
-                                        phys_avail.regions[i].address,
-                                        phys_avail.regions[i].size, 0);
-       } else {
-               /* booted from BootX - it's all available (after klimit) */
-               phys_avail = phys_mem;
-               prom_mem.n_regions = 0;
-       }
+       set_phys_avail();
 
-       /*
-        * phys_avail records memory we can use now.
-        * prom_mem records memory allocated by the prom that we
-        * don't want to use now, but we'll reclaim later.
-        * Make sure the kernel text/data/bss is in neither.
-        */
-       kstart = __pa(_stext);  /* should be 0 */
-       ksize = PAGE_ALIGN(klimit - _stext);
-       remove_mem_piece(&phys_avail, kstart, ksize, 0);
-       remove_mem_piece(&prom_mem, kstart, ksize, 0);
-       remove_mem_piece(&phys_avail, 0, 0x4000, 0);
-       remove_mem_piece(&prom_mem, 0, 0x4000, 0);
 #undef RAM_LIMIT
        return __va(total);
 }
@@ -1350,7 +1344,6 @@ unsigned long __init *pmac_find_end_of_memory(void)
  */
 unsigned long __init *prep_find_end_of_memory(void)
 {
-       unsigned long kstart, ksize;
        unsigned long total;
        total = res->TotalMemory;
 
@@ -1365,11 +1358,7 @@ unsigned long __init *prep_find_end_of_memory(void)
                printk("Ramsize default to be %ldM\n", total>>20);
        }
        append_mem_piece(&phys_mem, 0, total);
-       phys_avail = phys_mem;
-       kstart = __pa(_stext);  /* should be 0 */
-       ksize = PAGE_ALIGN(klimit - _stext);
-       remove_mem_piece(&phys_avail, kstart, ksize, 0);
-       remove_mem_piece(&phys_avail, 0, 0x4000, 0);
+       set_phys_avail();
 
        return (__va(total));
 }
@@ -1379,7 +1368,7 @@ unsigned long __init *prep_find_end_of_memory(void)
 #if defined(CONFIG_GEMINI)
 unsigned long __init *gemini_find_end_of_memory(void)
 {
-       unsigned long total, kstart, ksize, *ret;
+       unsigned long total, *ret;
        unsigned char reg;
 
        reg = readb(GEMINI_MEMCFG);
@@ -1391,10 +1380,7 @@ unsigned long __init *gemini_find_end_of_memory(void)
        phys_mem.n_regions = 1;
        
        ret = __va(phys_mem.regions[0].size);
-       phys_avail = phys_mem;
-       kstart = __pa(_stext);
-       ksize = PAGE_ALIGN( _end - _stext );
-       remove_mem_piece( &phys_avail, kstart, ksize, 0 );
+       set_phys_avail();
        return ret;
 }
 #endif /* defined(CONFIG_GEMINI) || defined(CONFIG_ALL_PPC) */
@@ -1430,15 +1416,8 @@ unsigned long __init *apus_find_end_of_memory(void)
        }
 
        /* Now register the memory block. */
-       {
-               unsigned long kstart, ksize;
-
-               append_mem_piece(&phys_mem, memory[0].addr, memory[0].size);
-               phys_avail = phys_mem;
-               kstart = __pa(_stext);
-               ksize = PAGE_ALIGN(klimit - _stext);
-               remove_mem_piece(&phys_avail, kstart, ksize, 0);
-       }
+       append_mem_piece(&phys_mem, memory[0].addr, memory[0].size);
+       set_phys_avail();
 
        /* Remove the memory chunks that are controlled by special
            Phase5 hardware. */
@@ -1587,7 +1566,6 @@ static void __init hash_init(void)
  */
 unsigned long __init *m8xx_find_end_of_memory(void)
 {
-       unsigned long kstart, ksize;
        bd_t    *binfo;
        unsigned long *ret;
        extern unsigned char __res[];
@@ -1601,11 +1579,7 @@ unsigned long __init *m8xx_find_end_of_memory(void)
        ret = __va(phys_mem.regions[0].address+
                   phys_mem.regions[0].size);
 
-       phys_avail = phys_mem;
-
-       kstart = __pa(_stext);  /* should be 0 */
-       ksize = PAGE_ALIGN(_end - _stext);
-       remove_mem_piece(&phys_avail, kstart, ksize, 0);
+       set_phys_avail();
        return ret;
 }
 #endif /* ndef CONFIG_8xx */
index a9282de3fa5b09d987e8a23e0544cb495f305a78..5d007fce3c521972313955764dd3999b4f558e7a 100644 (file)
@@ -288,7 +288,6 @@ icside_build_dmatable(ide_drive_t *drive, int reading)
 static int
 icside_config_drive(ide_drive_t *drive, int mode)
 {
-       ide_hwif_t *hwif = HWIF(drive);
        int speed, err;
 
        if (mode == 2) {
index 7aeb3a30f846076e33ff8dd59e71a1f84cef1e27..7c2b23dab11529c3700aa4e35efe9a39a7d4e5d9 100644 (file)
@@ -704,8 +704,9 @@ void __init initrd_load(void)
 
 #define OF(args)  args
 
+#ifndef memzero
 #define memzero(s, n)     memset ((s), 0, (n))
-
+#endif
 
 typedef unsigned char  uch;
 typedef unsigned short ush;
index d4e22d33b94507aa8096a780302126d0a8d8250c..fa47adbb70feeb2165c405e70ab4591b4d3a7005 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Maintainer unknown.
  *
- * Drive tuning added from Corel Computer's kernel sources
+ * Drive tuning added from Rebel.com's kernel sources
  *  -- Russell King (15/11/98) linux@arm.linux.org.uk
  */
 
index 42ea12a2c73622ea79a911841d590f76c5c0f9c5..7d5e5f11ea97568f5bcd427d50891322f2ee4481 100644 (file)
@@ -158,6 +158,9 @@ static inline unsigned long pgprot_noncached(unsigned long prot)
                prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S;
 #elif defined(__mips__)
        prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
+#elif defined(__arm__) && defined(CONFIG_CPU_32)
+       /* Turn off caching for all I/O areas */
+       prot &= ~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE);
 #endif
 
        return prot;
index 3595fff91f8e84884b3a8330d304e98d303cea56..3480c1bf041e1752df8146d3144d108c7ee0a0f8 100644 (file)
@@ -61,7 +61,7 @@ struct vt_struct *vt_cons[MAX_NR_CONSOLES];
  */
 unsigned char keyboard_type = KB_101;
 
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__arm__)
 asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on);
 #endif
 
@@ -89,7 +89,8 @@ unsigned int video_scan_lines;
  */
 
 #if defined(__i386__) || defined(__alpha__) || defined(__powerpc__) \
-    || (defined(__mips__) && !defined(CONFIG_SGI))
+    || (defined(__mips__) && !defined(CONFIG_SGI)) \
+    || (defined(__arm__) && defined(CONFIG_HOST_FOOTBRIDGE))
 
 static void
 kd_nosound(unsigned long ignored)
@@ -470,7 +471,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                ucval = keyboard_type;
                goto setchar;
 
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__arm__)
                /*
                 * These cannot be implemented on any machine that implements
                 * ioperm() in user level (such as Alpha PCs).
index 53590a42a4bd3227a7a79659b4809fe03a3b58c7..6b5c57f9931682ee62af1afcf7689d5767104e58 100644 (file)
@@ -5,6 +5,10 @@
  *
  * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>.
+ *
+ * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $
  */
 
 #include <linux/config.h>
@@ -26,6 +30,7 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 #include <linux/console.h>
 #endif
+#include <linux/slab.h>
 
 #include <asm/init.h>
 #include <asm/io.h>
@@ -41,6 +46,7 @@
 #ifdef CONFIG_KGDB
 #include <asm/kgdb.h>
 #endif
+#include <asm/dbdma.h>
 
 #include "macserial.h"
 
@@ -52,6 +58,8 @@ static struct pmu_sleep_notifier serial_sleep_notifier = {
 };
 #endif
 
+#define SUPPORT_SERIAL_DMA
+
 /*
  * It would be nice to dynamically allocate everything that
  * depends on NUM_SERIAL, so we could support any number of
@@ -127,6 +135,13 @@ static void change_speed(struct mac_serial *info, struct termios *old);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int set_scc_power(struct mac_serial * info, int state);
 static int setup_scc(struct mac_serial * info);
+static void dbdma_reset(volatile struct dbdma_regs *dma);
+static void dbdma_flush(volatile struct dbdma_regs *dma);
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs);
+static void dma_init(struct mac_serial * info);
+static void rxdma_start(struct mac_serial * info, int current);
+static void rxdma_to_tty(struct mac_serial * info);
 
 static struct tty_struct *serial_table[NUM_CHANNELS];
 static struct termios *serial_termios[NUM_CHANNELS];
@@ -287,6 +302,39 @@ static inline void rs_recv_clear(struct mac_zschannel *zsc)
        write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
 }
 
+/*
+ * Reset a Descriptor-Based DMA channel.
+ */
+static void dbdma_reset(volatile struct dbdma_regs *dma)
+{
+       int i;
+
+       out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
+
+       /*
+        * Yes this looks peculiar, but apparently it needs to be this
+        * way on some machines.  (We need to make sure the DBDMA
+        * engine has actually got the write above and responded
+        * to it. - paulus)
+        */
+       for (i = 200; i > 0; --i)
+               if (ld_le32(&dma->control) & RUN)
+                       udelay(1);
+}
+
+/*
+ * Tells a DBDMA channel to stop and write any buffered data
+ * it might have to memory.
+ */
+static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma)
+{
+       int i = 0;
+
+       out_le32(&dma->control, (FLUSH << 16) | FLUSH);
+       while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100))
+               udelay(1);
+}
+
 /*
  * ----------------------------------------------------------------------
  *
@@ -310,6 +358,22 @@ static _INLINE_ void rs_sched_event(struct mac_serial *info,
        mark_bh(MACSERIAL_BH);
 }
 
+/* Work out the flag value for a z8530 status value. */
+static _INLINE_ int stat_to_flag(int stat)
+{
+       int flag;
+
+       if (stat & Rx_OVR) {
+               flag = TTY_OVERRUN;
+       } else if (stat & FRM_ERR) {
+               flag = TTY_FRAME;
+       } else if (stat & PAR_ERR) {
+               flag = TTY_PARITY;
+       } else
+               flag = 0;
+       return flag;
+}
+
 static _INLINE_ void receive_chars(struct mac_serial *info,
                                   struct pt_regs *regs)
 {
@@ -347,14 +411,7 @@ static _INLINE_ void receive_chars(struct mac_serial *info,
                        if (flip_max_cnt < tty->flip.count)
                                flip_max_cnt = tty->flip.count;
                }
-               if (stat & Rx_OVR) {
-                       flag = TTY_OVERRUN;
-               } else if (stat & FRM_ERR) {
-                       flag = TTY_FRAME;
-               } else if (stat & PAR_ERR) {
-                       flag = TTY_PARITY;
-               } else
-                       flag = 0;
+               flag = stat_to_flag(stat);
                if (flag)
                        /* reset the error indication */
                        write_zsreg(info->zs_channel, 0, ERR_RES);
@@ -450,6 +507,32 @@ static _INLINE_ void status_handle(struct mac_serial *info)
        info->read_reg_zero = status;
 }
 
+static _INLINE_ void receive_special_dma(struct mac_serial *info)
+{
+       unsigned char stat, flag;
+       volatile struct dbdma_regs *rd = &info->rx->dma;
+       int where = RX_BUF_SIZE;
+
+       spin_lock(&info->rx_dma_lock);
+       if ((ld_le32(&rd->status) & ACTIVE) != 0)
+               dbdma_flush(rd);
+       if (in_le32(&rd->cmdptr)
+           == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1))
+               where -= in_le16(&info->rx->res_count);
+       where--;
+       
+       stat = read_zsreg(info->zs_channel, R1);
+
+       flag = stat_to_flag(stat);
+       if (flag) {
+               info->rx_flag_buf[info->rx_cbuf][where] = flag;
+               /* reset the error indication */
+               write_zsreg(info->zs_channel, 0, ERR_RES);
+       }
+
+       spin_unlock(&info->rx_dma_lock);
+}
+
 /*
  * This is the serial driver's generic interrupt routine
  */
@@ -459,6 +542,12 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        unsigned char zs_intreg;
        int shift;
 
+       if (!(info->flags & ZILOG_INITIALIZED)) {
+               printk("rs_interrupt: irq %d, port not initialized\n", irq);
+               disable_irq(irq);
+               return;
+       }
+
        /* NOTE: The read register 3, which holds the irq status,
         *       does so for both channels on each chip.  Although
         *       the status value itself must be read from the A
@@ -475,19 +564,21 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        for (;;) {
                zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
 #ifdef SERIAL_DEBUG_INTR
-               printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg);
+               printk("rs_interrupt: irq %d, zs_intreg 0x%x\n",
+                      irq, (int)zs_intreg);
 #endif 
 
                if ((zs_intreg & CHAN_IRQMASK) == 0)
                        break;
 
-               if (!(info->flags & ZILOG_INITIALIZED)) {
-                       printk("rs_interrupt: irq %d, port not initialized\n", irq);
-                       break;
+               if (zs_intreg & CHBRxIP) {
+                       /* If we are doing DMA, we only ask for interrupts
+                          on characters with errors or special conditions. */
+                       if (info->dma_initted)
+                               receive_special_dma(info);
+                       else
+                               receive_chars(info, regs);
                }
-
-               if (zs_intreg & CHBRxIP)
-                       receive_chars(info, regs);
                if (zs_intreg & CHBTxIP)
                        transmit_chars(info);
                if (zs_intreg & CHBEXT)
@@ -495,6 +586,39 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        }
 }
 
+/* Transmit DMA interrupt - not used at present */
+static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/*
+ * Receive DMA interrupt.
+ */
+static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct mac_serial *info = (struct mac_serial *) dev_id;
+       volatile struct dbdma_cmd *cd;
+
+       if (!info->dma_initted)
+               return;
+       spin_lock(&info->rx_dma_lock);
+       /* First, confirm that this interrupt is, indeed, coming */
+       /* from Rx DMA */
+       cd = info->rx_cmds[info->rx_cbuf] + 2;
+       if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) {
+               spin_unlock(&info->rx_dma_lock);
+               return;
+       }
+       if (info->rx_fbuf != RX_NO_FBUF) {
+               info->rx_cbuf = info->rx_fbuf;
+               if (++info->rx_fbuf == info->rx_nbuf)
+                       info->rx_fbuf = 0;
+               if (info->rx_fbuf == info->rx_ubuf)
+                       info->rx_fbuf = RX_NO_FBUF;
+       }
+       spin_unlock(&info->rx_dma_lock);
+}
+
 /*
  * -------------------------------------------------------------------
  * Here ends the serial interrupt routines.
@@ -590,10 +714,6 @@ static void do_softint(void *private_)
        }
 }
 
-static void rs_timer(void)
-{
-}
-
 static int startup(struct mac_serial * info, int can_sleep)
 {
        int delay;
@@ -629,6 +749,10 @@ static int startup(struct mac_serial * info, int can_sleep)
 
        info->flags |= ZILOG_INITIALIZED;
        enable_irq(info->irq);
+       if (info->dma_initted) {
+//             enable_irq(info->tx_dma_irq);
+               enable_irq(info->rx_dma_irq);
+       }
 
        if (delay) {
                if (can_sleep) {
@@ -642,6 +766,187 @@ static int startup(struct mac_serial * info, int can_sleep)
        return 0;
 }
 
+static _INLINE_ void rxdma_start(struct mac_serial * info, int current)
+{
+       volatile struct dbdma_regs *rd = &info->rx->dma;
+       volatile struct dbdma_cmd *cd = info->rx_cmds[current];
+
+//printk(KERN_DEBUG "SCC: rxdma_start\n");
+
+       st_le32(&rd->cmdptr, virt_to_bus(cd));
+       out_le32(&rd->control, (RUN << 16) | RUN);
+}
+
+static void rxdma_to_tty(struct mac_serial *info)
+{
+       struct tty_struct       *tty = info->tty;
+       volatile struct dbdma_regs *rd = &info->rx->dma;
+       unsigned long flags;
+       int residue, available, space, do_queue;
+
+       if (!tty)
+               return;
+
+       do_queue = 0;
+       spin_lock_irqsave(&info->rx_dma_lock, flags);
+more:
+       space = TTY_FLIPBUF_SIZE - tty->flip.count;
+       if (!space) {
+               do_queue++;
+               goto out;
+       }
+       residue = 0;
+       if (info->rx_ubuf == info->rx_cbuf) {
+               if ((ld_le32(&rd->status) & ACTIVE) != 0) {
+                       dbdma_flush(rd);
+                       if (in_le32(&rd->cmdptr)
+                           == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1))
+                               residue = in_le16(&info->rx->res_count);
+               }
+       }
+       available = RX_BUF_SIZE - residue - info->rx_done_bytes;
+       if (available > space)
+               available = space;
+       if (available) {
+               memcpy(tty->flip.char_buf_ptr,
+                      info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes,
+                      available);
+               memcpy(tty->flip.flag_buf_ptr,
+                      info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+                      available);
+               tty->flip.char_buf_ptr += available;
+               tty->flip.count += available;
+               tty->flip.flag_buf_ptr += available;
+               memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes,
+                      0, available);
+               info->rx_done_bytes += available;
+               do_queue++;
+       }
+       if (info->rx_done_bytes == RX_BUF_SIZE) {
+               volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf];
+
+               if (info->rx_ubuf == info->rx_cbuf)
+                       goto out;
+               /* mark rx_char_buf[rx_ubuf] free */
+               st_le16(&cd->command, DBDMA_NOP);
+               cd++;
+               st_le32(&cd->cmd_dep, 0);
+               st_le32((unsigned int *)&cd->res_count, 0);
+               cd++;
+               st_le16(&cd->xfer_status, 0);
+
+               if (info->rx_fbuf == RX_NO_FBUF) {
+                       info->rx_fbuf = info->rx_ubuf;
+                       if (!(ld_le32(&rd->status) & ACTIVE)) {
+                               dbdma_reset(&info->rx->dma);
+                               rxdma_start(info, info->rx_ubuf);
+                               info->rx_cbuf = info->rx_ubuf;
+                       }
+               }
+               info->rx_done_bytes = 0;
+               if (++info->rx_ubuf == info->rx_nbuf)
+                       info->rx_ubuf = 0;
+               if (info->rx_fbuf == info->rx_ubuf)
+                       info->rx_fbuf = RX_NO_FBUF;
+               goto more;
+       }
+out:
+       spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+       if (do_queue)
+               queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void poll_rxdma(void *private_)
+{
+       struct mac_serial       *info = (struct mac_serial *) private_;
+       unsigned long flags;
+
+       rxdma_to_tty(info);
+       spin_lock_irqsave(&info->rx_dma_lock, flags);
+       mod_timer(&info->poll_dma_timer, RX_DMA_TIMER);
+       spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+}
+
+static void dma_init(struct mac_serial * info)
+{
+       int i, size;
+       volatile struct dbdma_cmd *cd;
+       unsigned char *p;
+
+//printk(KERN_DEBUG "SCC: dma_init\n");
+
+       info->rx_nbuf = 8;
+
+       /* various mem set up */
+       size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2)
+               + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds)
+                  + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf))
+               * info->rx_nbuf;
+       info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA);
+       if (info->dma_priv == NULL)
+               return;
+       memset(info->dma_priv, 0, size);
+
+       info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv;
+       info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf);
+       info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf;
+       p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf);
+       for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+               info->rx_char_buf[i] = p;
+       for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE)
+               info->rx_flag_buf[i] = p;
+
+       /* a bit of DMA programming */
+       cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p);
+       st_le16(&cd->command, DBDMA_NOP);
+       cd++;
+       st_le16(&cd->req_count, RX_BUF_SIZE);
+       st_le16(&cd->command, INPUT_MORE);
+       st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0]));
+       cd++;
+       st_le16(&cd->req_count, 4);
+       st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+       st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+       st_le32(&cd->cmd_dep, DBDMA_STOP);
+       for (i = 1; i < info->rx_nbuf; i++) {
+               info->rx_cmds[i] = ++cd;
+               st_le16(&cd->command, DBDMA_NOP);
+               cd++;
+               st_le16(&cd->req_count, RX_BUF_SIZE);
+               st_le16(&cd->command, INPUT_MORE);
+               st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i]));
+               cd++;
+               st_le16(&cd->req_count, 4);
+               st_le16(&cd->command, STORE_WORD | INTR_ALWAYS);
+               st_le32(&cd->phy_addr, virt_to_bus(cd-2));
+               st_le32(&cd->cmd_dep, DBDMA_STOP);
+       }
+       cd++;
+       st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS);
+       st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0]));
+
+       /* setup DMA to our liking */
+       dbdma_reset(&info->rx->dma);
+       st_le32(&info->rx->dma.intr_sel, 0x10001);
+       st_le32(&info->rx->dma.br_sel, 0x10001);
+       out_le32(&info->rx->dma.wait_sel, 0x10001);
+
+       /* set various flags */
+       info->rx_ubuf = 0;
+       info->rx_cbuf = 0;
+       info->rx_fbuf = info->rx_ubuf + 1;
+       if (info->rx_fbuf == info->rx_nbuf)
+               info->rx_fbuf = RX_NO_FBUF;
+       info->rx_done_bytes = 0;
+
+       /* setup polling */
+       init_timer(&info->poll_dma_timer);
+       info->poll_dma_timer.function = (void *)&poll_rxdma;
+       info->poll_dma_timer.data = (unsigned long)info;
+
+       info->dma_initted = 1;
+}
+
 static int setup_scc(struct mac_serial * info)
 {
        unsigned long flags;
@@ -666,6 +971,12 @@ static int setup_scc(struct mac_serial * info)
        ZS_CLEARFIFO(info->zs_channel);
        info->xmit_fifo_size = 1;
 
+       /*
+        * Reset DMAs
+        */
+       if (info->has_dma)
+               dma_init(info);
+
        /*
         * Clear the interrupt registers.
         */
@@ -680,7 +991,23 @@ static int setup_scc(struct mac_serial * info)
        /*
         * Finally, enable sequencing and interrupts
         */
-       info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+       if (!info->dma_initted) {
+               /* interrupt on ext/status changes, all received chars,
+                  transmit ready */
+               info->curregs[1] = (info->curregs[1] & ~0x18)
+                               | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+       } else {
+               /* interrupt on ext/status changes, W/Req pin is
+                  receive DMA request */
+               info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB))
+                               | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+               write_zsreg(info->zs_channel, 1, info->curregs[1]);
+               /* enable W/Req pin */
+               info->curregs[1] |= WT_RDY_ENAB;
+               write_zsreg(info->zs_channel, 1, info->curregs[1]);
+               /* enable interrupts on transmit ready and receive errors */
+               info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB;
+       }
        info->pendregs[1] = info->curregs[1];
        info->curregs[3] |= (RxENABLE | Rx8);
        info->pendregs[3] = info->curregs[3];
@@ -706,6 +1033,14 @@ static int setup_scc(struct mac_serial * info)
 
        restore_flags(flags);
 
+       if (info->dma_initted) {
+               spin_lock_irqsave(&info->rx_dma_lock, flags);
+               rxdma_start(info, 0);
+               info->poll_dma_timer.expires = RX_DMA_TIMER;
+               add_timer(&info->poll_dma_timer);
+               spin_unlock_irqrestore(&info->rx_dma_lock, flags);
+       }
+
        return 0;
 }
 
@@ -727,7 +1062,14 @@ static void shutdown(struct mac_serial * info)
        
                return;
        }
-       
+
+       if (info->has_dma) {
+               del_timer(&info->poll_dma_timer);
+               dbdma_reset(info->tx_dma);
+               dbdma_reset(&info->rx->dma);
+               disable_irq(info->tx_dma_irq);
+               disable_irq(info->rx_dma_irq);
+       }
        disable_irq(info->irq);
 
        info->pendregs[1] = info->curregs[1] = 0;
@@ -753,6 +1095,12 @@ static void shutdown(struct mac_serial * info)
                info->xmit_buf = 0;
        }
 
+       if (info->has_dma && info->dma_priv) {
+               kfree(info->dma_priv);
+               info->dma_priv = NULL;
+               info->dma_initted = 0;
+       }
+
        memset(info->curregs, 0, sizeof(info->curregs));
        memset(info->curregs, 0, sizeof(info->pendregs));
 
@@ -795,7 +1143,7 @@ static int set_scc_power(struct mac_serial * info, int state)
                        feature_set(info->dev_node, FEATURE_Modem_Reset);
                        mdelay(5);
                        feature_clear(info->dev_node, FEATURE_Modem_Reset);
-                       delay = 1000;   /* wait for 1s before using */
+                       delay = 2500;   /* wait for 2.5s before using */
                }
 #ifdef CONFIG_PMAC_PBOOK
                if (info->is_pwbk_ir)
@@ -1050,7 +1398,6 @@ static int rs_write(struct tty_struct * tty, int from_user,
        if (!tty || !info->xmit_buf || !tmp_buf)
                return 0;
 
-       save_flags(flags);
        if (from_user) {
                down(&tmp_buf_sem);
                while (1) {
@@ -1066,6 +1413,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
                                        ret = -EFAULT;
                                break;
                        }
+                       save_flags(flags);
                        cli();
                        c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
                                       SERIAL_XMIT_SIZE - info->xmit_head));
@@ -1081,6 +1429,7 @@ static int rs_write(struct tty_struct * tty, int from_user,
                up(&tmp_buf_sem);
        } else {
                while (1) {
+                       save_flags(flags);
                        cli();          
                        c = MIN(count,
                                MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
@@ -1102,7 +1451,6 @@ static int rs_write(struct tty_struct * tty, int from_user,
        if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
            && !info->tx_active)
                transmit_chars(info);
-       restore_flags(flags);
        return ret;
 }
 
@@ -1131,12 +1479,13 @@ static int rs_chars_in_buffer(struct tty_struct *tty)
 static void rs_flush_buffer(struct tty_struct *tty)
 {
        struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
                                
        if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
                return;
-       cli();
+       save_flags(flags); cli();
        info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-       sti();
+       restore_flags(flags);
        wake_up_interruptible(&tty->write_wait);
        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
            tty->ldisc.write_wakeup)
@@ -1156,7 +1505,6 @@ static void rs_throttle(struct tty_struct * tty)
        struct mac_serial *info = (struct mac_serial *)tty->driver_data;
        unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
        
        printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
@@ -1193,7 +1541,6 @@ static void rs_unthrottle(struct tty_struct * tty)
        struct mac_serial *info = (struct mac_serial *)tty->driver_data;
        unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
        
        printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty));
 #endif
@@ -1471,6 +1818,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
        save_flags(flags); cli();
        
        if (tty_hung_up_p(filp)) {
+               MOD_DEC_USE_COUNT;
                restore_flags(flags);
                return;
        }
@@ -1496,6 +1844,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
                info->count = 0;
        }
        if (info->count) {
+               MOD_DEC_USE_COUNT;
                restore_flags(flags);
                return;
        }
@@ -1516,8 +1865,12 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
        printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait);
 #endif
        tty->closing = 1;
-       if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+       if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) {
+               restore_flags(flags);
                tty_wait_until_sent(tty, info->closing_wait);
+               save_flags(flags); cli();
+       }
+
        /*
         * At this point we stop accepting input.  To do this, we
         * disable the receiver and receive interrupts.
@@ -1537,7 +1890,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 #ifdef SERIAL_DEBUG_OPEN
                printk("waiting end of Rx...\n");
 #endif
+               restore_flags(flags);
                rs_wait_until_sent(tty, info->timeout);
+               save_flags(flags); cli();
        }
 
        shutdown(info);
@@ -1563,6 +1918,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
        info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
                         ZILOG_CLOSING);
        wake_up_interruptible(&info->close_wait);
+       MOD_DEC_USE_COUNT;
 }
 
 /*
@@ -1772,14 +2128,19 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
        int                     retval, line;
        unsigned long           page;
 
+       MOD_INC_USE_COUNT;
        line = MINOR(tty->device) - tty->driver.minor_start;
-       if ((line < 0) || (line >= zs_channels_found))
+       if ((line < 0) || (line >= zs_channels_found)) {
+               MOD_DEC_USE_COUNT;
                return -ENODEV;
+       }
        info = zs_soft + line;
 
 #ifdef CONFIG_KGDB
-       if (info->kgdb_channel)
+       if (info->kgdb_channel) {
+               MOD_DEC_USE_COUNT;
                return -ENODEV;
+       }
 #endif
        if (serial_paranoia_check(info, tty->device, "rs_open"))
                return -ENODEV;
@@ -1862,7 +2223,58 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 
 static void show_serial_version(void)
 {
-       printk("PowerMac Z8530 serial driver version 1.01\n");
+       printk("PowerMac Z8530 serial driver version 2.0\n");
+}
+
+/*
+ * Initialize one channel, both the mac_serial and mac_zschannel
+ * structs.  We use the dev_node field of the mac_serial struct.
+ */
+static void
+chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
+         struct mac_zschannel *zs_chan_a)
+{
+       struct device_node *ch = zss->dev_node;
+       char *conn;
+       int len;
+
+       zss->irq = ch->intrs[0].line;
+       zss->has_dma = 0;
+#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA)
+       if (ch->n_addrs == 3 && ch->n_intrs == 3)
+               zss->has_dma = 1;
+#endif
+       zss->dma_initted = 0;
+
+       zs_chan->control = (volatile unsigned char *)
+               ioremap(ch->addrs[0].address, 0x1000);
+       zs_chan->data = zs_chan->control + 0x10;
+       spin_lock_init(&zs_chan->lock);
+       zs_chan->parent = zss;
+       zss->zs_channel = zs_chan;
+       zss->zs_chan_a = zs_chan_a;
+
+       /* setup misc varariables */
+       zss->kgdb_channel = 0;
+       zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
+
+       /* XXX tested only with wallstreet PowerBook,
+          should do no harm anyway */
+       conn = get_property(ch, "AAPL,connector", &len);
+       zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0);
+
+       if (zss->has_dma) {
+               zss->dma_priv = NULL;
+               /* it seems that the last two addresses are the
+                  DMA controllers */
+               zss->tx_dma = (volatile struct dbdma_regs *)
+                       ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100);
+               zss->rx = (volatile struct mac_dma *)
+                       ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100);
+               zss->tx_dma_irq = ch->intrs[1].line;
+               zss->rx_dma_irq = ch->intrs[2].line;
+               spin_lock_init(&zss->rx_dma_lock);
+       }
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -1871,51 +2283,63 @@ probe_sccs()
 {
        struct device_node *dev, *ch;
        struct mac_serial **pp;
-       int n, lenp;
-       char *conn;
+       int n, chip, nchan;
+       struct mac_zschannel *zs_chan;
+       int chan_a_index;
 
        n = 0;
        pp = &zs_chain;
+       zs_chan = zs_channels;
        for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+               nchan = 0;
+               chip = n;
                if (n >= NUM_CHANNELS) {
                        printk("Sorry, can't use %s: no more channels\n",
                               dev->full_name);
                        continue;
                }
+               chan_a_index = 0;
                for (ch = dev->child; ch != 0; ch = ch->sibling) {
+                       if (nchan >= 2) {
+                               printk(KERN_WARNING "SCC: Only 2 channels per "
+                                       "chip are supported\n");
+                               break;
+                       }
                        if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) {
                                printk("Can't use %s: %d addrs %d intrs\n",
                                      ch->full_name, ch->n_addrs, ch->n_intrs);
                                continue;
                        }
-                       zs_channels[n].control = (volatile unsigned char *)
-                               ioremap(ch->addrs[0].address, 0x1000);
-                       zs_channels[n].data = zs_channels[n].control + 0x10;
-                       spin_lock_init(&zs_channels[n].lock);
-                       zs_soft[n].zs_channel = &zs_channels[n];
-                       zs_soft[n].dev_node = ch;
-                       zs_soft[n].irq = ch->intrs[0].line;
-                       zs_soft[n].zs_channel->parent = &zs_soft[n];
-                       zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
-
-                       /* XXX tested only with wallstreet PowerBook,
-                          should do no harm anyway */
-                       conn = get_property(ch, "AAPL,connector", &lenp);
-                       zs_soft[n].is_pwbk_ir =
-                               conn && (strcmp(conn, "infrared") == 0);
-
-                       /* XXX this assumes the prom puts chan A before B */
-                       if (n & 1)
-                               zs_soft[n].zs_chan_a = &zs_channels[n-1];
-                       else
-                               zs_soft[n].zs_chan_a = &zs_channels[n];
 
+                       /* The channel with the higher address
+                          will be the A side. */
+                       if (nchan > 0 &&
+                           ch->addrs[0].address
+                           > zs_soft[n-1].dev_node->addrs[0].address)
+                               chan_a_index = 1;
+
+                       /* minimal initialization for now */
+                       zs_soft[n].dev_node = ch;
                        *pp = &zs_soft[n];
                        pp = &zs_soft[n].zs_next;
+                       ++nchan;
                        ++n;
                }
+               if (nchan == 0)
+                       continue;
+
+               /* set up A side */
+               chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+               ++zs_chan;
+
+               /* set up B side, if it exists */
+               if (nchan > 1)
+                       chan_init(&zs_soft[chip + 1 - chan_a_index],
+                                 zs_chan, zs_chan - 1);
+               ++zs_chan;
        }
        *pp = 0;
+
        zs_channels_found = n;
 #ifdef CONFIG_PMAC_PBOOK
        if (n)
@@ -1932,8 +2356,6 @@ int macserial_init(void)
 
        /* Setup base handler, and timer table. */
        init_bh(MACSERIAL_BH, do_serial_bh);
-       timer_table[RS_TIMER].fn = rs_timer;
-       timer_table[RS_TIMER].expires = 0;
 
        /* Find out how many Z8530 SCCs we have */
        if (zs_chain == 0)
@@ -1945,6 +2367,18 @@ int macserial_init(void)
        /* Register the interrupt handler for each one */
        save_flags(flags); cli();
        for (i = 0; i < zs_channels_found; ++i) {
+               if (zs_soft[i].has_dma) {
+                       if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
+                                       "SCC-txdma", &zs_soft[i]))
+                               printk(KERN_ERR "macserial: can't get irq %d\n",
+                                      zs_soft[i].tx_dma_irq);
+                       disable_irq(zs_soft[i].tx_dma_irq);
+                       if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0,
+                                       "SCC-rxdma", &zs_soft[i]))
+                               printk(KERN_ERR "macserial: can't get irq %d\n",
+                                      zs_soft[i].rx_dma_irq);
+                       disable_irq(zs_soft[i].rx_dma_irq);
+               }
                if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
                                "SCC", &zs_soft[i]))
                        printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2083,6 +2517,7 @@ int macserial_init(void)
                /* By default, disable the port */
                set_scc_power(info, 0);
        }
+       tmp_buf = 0;
 
        return 0;
 }
@@ -2103,11 +2538,26 @@ void cleanup_module(void)
        for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
                set_scc_power(info, 0);
        save_flags(flags); cli();
-       for (i = 0; i < zs_channels_found; ++i)
+       for (i = 0; i < zs_channels_found; ++i) {
                free_irq(zs_soft[i].irq, &zs_soft[i]);
+               if (zs_soft[i].has_dma) {
+                       free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
+                       free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+               }
+       }
        restore_flags(flags);
        tty_unregister_driver(&callout_driver);
        tty_unregister_driver(&serial_driver);
+
+       if (tmp_buf) {
+               free_page((unsigned long) tmp_buf);
+               tmp_buf = 0;
+       }
+
+#ifdef CONFIG_PMAC_PBOOK
+       if (zs_channels_found)
+               pmu_unregister_sleep_notifier(&serial_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
 }
 #endif /* MODULE */
 
@@ -2224,6 +2674,8 @@ static int __init serial_console_setup(struct console *co, char *options)
        if (zs_chain == 0)
                return -1;
 
+       set_scc_power(info, 1);
+
        /* Reset the channel */
        write_zsreg(info->zs_channel, R9, CHRA);
 
@@ -2467,14 +2919,13 @@ void __init zs_kgdb_hook(int tty_num)
        if (zs_chain == 0)
                probe_sccs();
 
-       set_scc_power(&zs_soft[n], 1);
+       set_scc_power(&zs_soft[tty_num], 1);
        
        zs_kgdbchan = zs_soft[tty_num].zs_channel;
        zs_soft[tty_num].change_needed = 0;
        zs_soft[tty_num].clk_divisor = 16;
        zs_soft[tty_num].zs_baud = 38400;
        zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
-       zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
 
        /* Turn on transmitter/receiver at 8-bits/char */
         kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400);
index e2f137f73916e3562586d7079c730118e9a3cb8c..3ba2e60624faed9da29ce5bf9bbb6752de495f46 100644 (file)
@@ -92,6 +92,13 @@ struct mac_zschannel {
        struct mac_serial*      parent;
 };
 
+struct mac_dma {
+       volatile struct dbdma_regs      dma;
+       volatile unsigned short         res_count;
+       volatile unsigned short         command;
+       volatile unsigned int           buf_addr;
+};
+
 struct mac_serial {
        struct mac_serial *zs_next;     /* For IRQ servicing chain */
        struct mac_zschannel *zs_channel; /* Channel registers */
@@ -156,6 +163,28 @@ struct mac_serial {
        struct termios          callout_termios;
        wait_queue_head_t       open_wait;
        wait_queue_head_t       close_wait;
+
+       volatile struct dbdma_regs *tx_dma;
+       int                     tx_dma_irq;
+       volatile struct dbdma_cmd *tx_cmds;
+       volatile struct mac_dma *rx;
+       int                     rx_dma_irq;
+       volatile struct dbdma_cmd **rx_cmds;
+       unsigned char           **rx_char_buf;
+       unsigned char           **rx_flag_buf;
+#define        RX_BUF_SIZE     256
+       int                     rx_nbuf;
+       int                     rx_done_bytes;
+       int                     rx_ubuf;
+       int                     rx_fbuf;
+#define        RX_NO_FBUF      (-1)
+       int                     rx_cbuf;
+       spinlock_t              rx_dma_lock;
+       int                     has_dma;
+       int                     dma_initted;
+       void                    *dma_priv;
+       struct timer_list       poll_dma_timer;
+#define RX_DMA_TIMER   (jiffies + 10*HZ/1000)
 };
 
 
@@ -226,9 +255,9 @@ struct mac_serial {
 #define        INT_ALL_Rx      0x10    /* Int on all Rx Characters or error */
 #define        INT_ERR_Rx      0x18    /* Int on error only */
 
-#define        WT_RDY_RT       0x20    /* Wait/Ready on R/T */
-#define        WT_FN_RDYFN     0x40    /* Wait/FN/Ready FN */
-#define        WT_RDY_ENAB     0x80    /* Wait/Ready Enable */
+#define        WT_RDY_RT       0x20    /* W/Req reflects recv if 1, xmit if 0 */
+#define        WT_FN_RDYFN     0x40    /* W/Req pin is DMA request if 1, wait if 0 */
+#define        WT_RDY_ENAB     0x80    /* Enable W/Req pin */
 
 /* Write Register #2 (Interrupt Vector) */
 
@@ -286,6 +315,9 @@ struct mac_serial {
 
 /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
 
+/* Write Register 7' (Some enhanced feature control) */
+#define        ENEXREAD        0x40    /* Enable read of some write registers */
+
 /* Write Register 8 (transmit buffer) */
 
 /* Write Register 9 (Master interrupt control) */
@@ -346,7 +378,9 @@ struct mac_serial {
 #define        SNRZI   0xe0    /* Set NRZI mode */
 
 /* Write Register 15 (external/status interrupt control) */
+#define        EN85C30 1       /* Enable some 85c30-enhanced registers */
 #define        ZCIE    2       /* Zero count IE */
+#define        ENSTFIFO 4      /* Enable status FIFO (SDLC) */
 #define        DCDIE   8       /* DCD IE */
 #define        SYNCIE  0x10    /* Sync/hunt IE */
 #define        CTSIE   0x20    /* CTS IE */
@@ -382,6 +416,15 @@ struct mac_serial {
 #define        END_FR          0x80    /* End of Frame (SDLC) */
 
 /* Read Register 2 (channel b only) - Interrupt vector */
+#define        CHB_Tx_EMPTY    0x00
+#define        CHB_EXT_STAT    0x02
+#define        CHB_Rx_AVAIL    0x04
+#define        CHB_SPECIAL     0x06
+#define        CHA_Tx_EMPTY    0x08
+#define        CHA_EXT_STAT    0x0a
+#define        CHA_Rx_AVAIL    0x0c
+#define        CHA_SPECIAL     0x0e
+#define        STATUS_MASK     0x06
 
 /* Read Register 3 (interrupt pending register) ch a only */
 #define        CHBEXT  0x1             /* Channel B Ext/Stat IP */
index e6878dde68268bc314c68aa7d800bd50bae3af16..03c39fb613a0255f96ab6733d4df82ed3665735d 100644 (file)
@@ -246,18 +246,18 @@ static int __init acpi_map_tables(void)
        struct acpi_table *rsdt;
        u32 *rsdt_entry;
        int rsdt_entry_count;
-       u8 *i;
+       unsigned long i;
 
        // search BIOS memory for RSDP
        for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) {
-               rsdp = (struct acpi_rsdp *) i;
-               if (readl(rsdp->signature) == ACPI_RSDP1_SIG
-                   && readl(rsdp->signature + 1) == ACPI_RSDP2_SIG) {
+               rsdp = (struct acpi_rsdp *) phys_to_virt(i);
+               if (rsdp->signature[0] == ACPI_RSDP1_SIG &&
+                   rsdp->signature[1] == ACPI_RSDP2_SIG) {
                        char oem[7];
                        int j;
 
                        // strip trailing space and print OEM identifier
-                       memcpy_fromio(oem, rsdp->oem, 6);
+                       memcpy(oem, rsdp->oem, 6);
                        oem[6] = '\0';
                        for (j = 5;
                             j > 0 && (oem[j] == '\0' || oem[j] == ' ');
@@ -275,7 +275,7 @@ static int __init acpi_map_tables(void)
                return -ENODEV;
        }
        // fetch RSDT from RSDP
-       rsdt = acpi_map_table(readl(&rsdp->rsdt));
+       rsdt = acpi_map_table(rsdp->rsdt);
        if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) {
                printk(KERN_ERR "ACPI: no RSDT found\n");
                acpi_unmap_table(rsdt);
index fc354104888007a570dc2b1bb467441d5011ac64..c2971c8daabf85bbb7b90d1864e46becfa5d2c9b 100644 (file)
@@ -27,10 +27,10 @@ CONFIG_82596_MODULE :=
 
 ifeq ($(CONFIG_PCMCIA),y)
   SUB_DIRS += pcmcia
-  MOD_SUB_DIRS += pcmcia
+  MOD_IN_SUB_DIRS += pcmcia
 else
   ifeq ($(CONFIG_PCMCIA),m)
-    MOD_SUB_DIRS += pcmcia
+    MOD_IN_SUB_DIRS += pcmcia
   endif
 endif
 
@@ -829,14 +829,6 @@ else
   endif
 endif
 
-ifeq ($(CONFIG_PCMCIA_PCNET),y)
-CONFIG_8390_BUILTIN = y
-else
-  ifeq ($(CONFIG_PCMCIA_PCNET),m)
-  CONFIG_8390_MODULE = y
-  endif
-endif
-
 # If anything built-in uses the 8390, then build it into the kernel also.
 # If not, but a module uses it, build as a module.
 ifdef CONFIG_8390_BUILTIN
index 0885e74a3037049d3d77856f56363b30e9aaffc0..32e965275e59e985d224387fe292e1ac66a8fd0e 100644 (file)
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
- *     Copyright (c) 1998 Corel Computer Corp.
+ *     Copyright (c) 1998-1999 Rebel.com
  *      
  *     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.
  *  
- *     Neither Paul VanderSpek nor Corel Computer Corp. admit liability
- *     nor provide warranty for any of this software. This material is 
- *     provided "AS-IS" and at no charge.
+ *     Neither Paul VanderSpek nor Rebel.com admit liability nor provide
+ *     warranty for any of this software. This material is provided "AS-IS"
+ *     and at no charge.
  *     
  *     If you find bugs in this file, its very likely that the same bug
  *     will also be in pc87108.c since the implementations is quite
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
new file mode 100644 (file)
index 0000000..40c53a2
--- /dev/null
@@ -0,0 +1,1494 @@
+/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
+
+       Written 1993-1998 by
+       Donald Becker, becker@cesdis.gsfc.nasa.gov, (driver core) and
+       David Hinds, dhinds@allegro.stanford.edu (derived from his PC card code).
+
+       This software may be used and distributed according to the terms of
+       the GNU Public License, incorporated herein by reference.
+
+       This driver derives from Donald Becker's 3c509 core, which has the
+       following copyright:
+       Copyright 1993 United States Government as represented by the
+       Director, National Security Agency.
+
+*/
+
+/* Driver author info must always be in the binary.  Version too.. */
+static const char *tc574_version =
+"3c574_cs.c v1.08 9/24/98 Donald Becker/David Hinds, becker@cesdis.gsfc.nasa.gov.\n";
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
+Adapter.
+
+II. Board-specific settings
+
+None -- PC cards are autoconfigured.
+
+III. Driver operation
+
+The 3c574 uses a Boomerang-style interface, without the bus-master capability.
+See the Boomerang driver and documentation for most details.
+
+IV. Notes and chip documentation.
+
+Two added registers are used to enhance PIO performance, RunnerRdCtrl and
+RunnerWrCtrl.  These are 11 bit down-counters that are preloaded with the
+count of word (16 bits) reads or writes the driver is about to do to the Rx
+or Tx FIFO.  The chip is then able to hide the internal-PCI-bus to PC-card
+translation latency by buffering the I/O operations with an 8 word FIFO.
+Note: No other chip accesses are permitted when this buffer is used.
+
+A second enhancement is that both attribute and common memory space
+0x0800-0x0fff can translated to the PIO FIFO.  Thus memory operations (faster
+with *some* PCcard bridges) may be used instead of I/O operations.
+This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
+
+Some slow PC card bridges work better if they never see a WAIT signal.
+This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
+Only do this after testing that it is reliable and improves performance.
+
+The upper five bits of RunnerRdCtrl are used to window into PCcard
+configuration space registers.  Window 0 is the regular Boomerang/Odie
+register set, 1-5 are various PC card control registers, and 16-31 are
+the (reversed!) CIS table.
+
+A final note: writing the InternalConfig register in window 3 with an
+invalid ramWidth is Very Bad.
+
+V. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.national.com/pf/DP/DP83840.html
+
+Thanks to Terry Murphy of 3Com for providing development information for
+earlier 3Com products.
+
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+/* A few values that may be tweaked. */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(full_duplex, "i");
+#ifdef BROKEN_FEATURES
+MODULE_PARM(use_fifo_buffer, "i");
+MODULE_PARM(use_memory_ops, "i");
+MODULE_PARM(no_wait, "i");
+#endif
+
+/* Now-standard PC card module parameters. */
+static u_int irq_mask = 0xdeb8;                        /* IRQ3,4,5,7,9,10,11,12,14,15 */
+static int irq_list[4] = { -1 };
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((800*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 64;
+
+/* Force full duplex modes? */
+static int full_duplex = 0;
+
+#ifdef BROKEN_FEATURES
+/* Performance features: best left disabled. */
+/* Set to buffer all Tx/RxFIFO accesses. */
+static int use_fifo_buffer = 0;
+/* Set iff memory ops are faster than I/O ops. */
+static int use_memory_ops = 0;
+/* Set iff disabling the WAIT signal is reliable and faster. */
+static int no_wait = 0;
+#endif
+
+/* To minimize the size of the driver source and make the driver more
+   readable not all constants are symbolically defined.
+   You'll need the manual if you want to understand driver details anyway. */
+/* Offsets from base I/O address. */
+#define EL3_DATA       0x00
+#define EL3_CMD                0x0e
+#define EL3_STATUS     0x0e
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+   11 bits are the parameter, if applicable. */
+enum el3_cmds {
+       TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+       RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+       TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+       FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+       SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+       SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+       StatsDisable = 22<<11, StopCoax = 23<<11,
+};
+
+enum elxl_status {
+       IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+       TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+       IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+       RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+enum Window0 {
+       Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
+       IntrStatus=0x0E,                /* Valid in all windows. */
+};
+/* These assumes the larger EEPROM. */
+enum Win0_EEPROM_cmds {
+       EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
+       EEPROM_EWENB = 0x30,            /* Enable erasing/writing for 10 msec. */
+       EEPROM_EWDIS = 0x00,            /* Disable EWENB before 10 msec timeout. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+   On the "Odie" this window is always mapped at offsets 0x10-0x1f.
+   Except for TxFree, which is overlapped by RunnerWrCtrl. */
+enum Window1 {
+       TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14,
+       RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B,
+       TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
+       RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
+};
+
+enum Window3 {                 /* Window 3: MAC/config bits. */
+       Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+       int i;
+       struct w3_config_fields {
+               unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+               int pad8:8;
+               unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+               int pad24:7;
+       } u;
+};
+
+enum Window4 {         /* Window 4: Xcvr/media bits. */
+       Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
+};
+
+#define MEDIA_TP       0x00C0  /* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+       dev_node_t node;
+       struct net_device_stats stats;
+       u16 advertising, partner;                       /* NWay media advertisement */
+       unsigned char phys[2];                          /* MII device addresses. */
+       unsigned int
+         autoselect:1, default_media:3;        /* Read from the EEPROM/Wn3_Config. */
+       /* for transceiver monitoring */
+       struct timer_list media;
+       u_short media_status;
+       u_short fast_poll;
+       u_long last_irq;
+};
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with the original DP83840 on older 3c905 boards, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"3c574_cs.c 1.000 1998/1/8 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* Index of functions. */
+
+static void tc574_config(dev_link_t *link);
+static void tc574_release(u_long arg);
+static int tc574_event(event_t event, int priority,
+                                          event_callback_args_t *args);
+
+static void mdio_sync(ioaddr_t ioaddr, int bits);
+static int mdio_read(ioaddr_t ioaddr, int phy_id, int location);
+static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value);
+static u_short read_eeprom(ioaddr_t ioaddr, int index);
+static void wait_for_completion(struct net_device *dev, int cmd);
+
+static void tc574_reset(struct net_device *dev);
+static void media_check(u_long arg);
+static int el3_open(struct net_device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void update_stats(ioaddr_t addr, struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev, int worklimit);
+static int el3_close(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);
+
+static dev_info_t dev_info = "3c574_cs";
+
+static dev_link_t *tc574_attach(void);
+static void tc574_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+static void flush_stale_links(void)
+{
+       dev_link_t *link, *next;
+       for (link = dev_list; link; link = next) {
+               next = link->next;
+           if (link->state & DEV_STALE_LINK)
+                       tc574_detach(link);
+    }
+}
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+#if CS_RELEASE_CODE < 0x2911
+    CardServices(ReportError, dev_info, (void *)func, (void *)ret);
+#else
+       error_info_t err = { func, ret };
+       CardServices(ReportError, handle, &err);
+#endif
+}
+
+/*
+   We never need to do anything when a tc574 device is "initialized"
+   by the net software, because we only register already-found cards.
+*/
+
+static int tc574_init(struct net_device *dev)
+{
+       return 0;
+}
+
+/*
+       tc574_attach() creates an "instance" of the driver, allocating
+       local data structures for one device.  The device is registered
+       with Card Services.
+*/
+
+static dev_link_t *tc574_attach(void)
+{
+       client_reg_t client_reg;
+       dev_link_t *link;
+       struct net_device *dev;
+       int i, ret;
+
+       DEBUG(0, "3c574_attach()\n");
+       flush_stale_links();
+
+       /* Create the PC card device object. */
+       link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+       memset(link, 0, sizeof(struct dev_link_t));
+       link->release.function = &tc574_release;
+       link->release.data = (u_long)link;
+       link->io.NumPorts1 = 32;
+       link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+       link->io.IOAddrLines = 5;
+       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+       link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+       if (irq_list[0] == -1)
+               link->irq.IRQInfo2 = irq_mask;
+       else
+               for (i = 0; i < 4; i++)
+                       link->irq.IRQInfo2 |= 1 << irq_list[i];
+       link->irq.Handler = &el3_interrupt;
+       link->conf.Attributes = CONF_ENABLE_IRQ;
+       link->conf.Vcc = 50;
+       link->conf.IntType = INT_MEMORY_AND_IO;
+       link->conf.ConfigIndex = 1;
+       link->conf.Present = PRESENT_OPTION;
+
+       /* Create the network device object. */
+       dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+       memset(dev, 0, sizeof(struct net_device));
+
+       /* Make up a Odie-specific data structure. */
+       dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+       memset(dev->priv, 0, sizeof(struct el3_private));
+
+       /* The EL3-specific entries in the device structure. */
+       dev->hard_start_xmit = &el3_start_xmit;
+       dev->get_stats = &el3_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+       dev->do_ioctl = &private_ioctl;
+#endif
+       dev->set_multicast_list = &set_rx_mode;
+       ether_setup(dev);
+       dev->name = ((struct el3_private *)dev->priv)->node.dev_name;
+       dev->init = &tc574_init;
+       dev->open = &el3_open;
+       dev->stop = &el3_close;
+       dev->tbusy = 1;
+
+       link->priv = dev;
+#if CS_RELEASE_CODE > 0x2911
+       link->irq.Instance = dev;
+#endif
+
+       /* Register with Card Services */
+       link->next = dev_list;
+       dev_list = link;
+       client_reg.dev_info = &dev_info;
+       client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+       client_reg.EventMask =
+               CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+                       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+                               CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+       client_reg.event_handler = &tc574_event;
+       client_reg.Version = 0x0210;
+       client_reg.event_callback_args.client_data = link;
+       ret = CardServices(RegisterClient, &link->handle, &client_reg);
+       if (ret != 0) {
+               cs_error(link->handle, RegisterClient, ret);
+               tc574_detach(link);
+               return NULL;
+       }
+
+       return link;
+} /* tc574_attach */
+
+/*
+
+       This deletes a driver "instance".  The device is de-registered
+       with Card Services.  If it has been released, all local data
+       structures are freed.  Otherwise, the structures will be freed
+       when the device is released.
+
+*/
+
+static void tc574_detach(dev_link_t *link)
+{
+       dev_link_t **linkp;
+       long flags;
+
+       DEBUG(0, "3c574_detach(0x%p)\n", link);
+
+       /* Locate device structure */
+       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+               if (*linkp == link) break;
+       if (*linkp == NULL)
+       return;
+
+       save_flags(flags);
+       cli();
+       if (link->state & DEV_RELEASE_PENDING) {
+               del_timer(&link->release);
+               link->state &= ~DEV_RELEASE_PENDING;
+       }
+       restore_flags(flags);
+
+       if (link->state & DEV_CONFIG) {
+               tc574_release((u_long)link);
+               if (link->state & DEV_STALE_CONFIG) {
+                       link->state |= DEV_STALE_LINK;
+                       return;
+               }
+       }
+
+       if (link->handle)
+               CardServices(DeregisterClient, link->handle);
+
+       /* Unlink device structure, free bits */
+       *linkp = link->next;
+       if (link->priv) {
+               struct net_device *dev = link->priv;
+               if (link->dev != NULL)
+                       unregister_netdev(dev);
+               if (dev->priv)
+                       kfree(dev->priv);
+               kfree(link->priv);
+       }
+       kfree(link);
+
+} /* tc574_detach */
+
+/*
+       tc574_config() is scheduled to run after a CARD_INSERTION event
+       is received, to configure the PCMCIA socket, and to make the
+       ethernet device available to the system.
+*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void tc574_config(dev_link_t *link)
+{
+       client_handle_t handle;
+       struct net_device *dev;
+       struct el3_private *lp;
+       tuple_t tuple;
+       cisparse_t parse;
+       u_short buf[32];
+       int last_fn, last_ret, i, j;
+       ioaddr_t ioaddr;
+       u16 *phys_addr;
+       char *cardname;
+
+       handle = link->handle;
+       dev = link->priv;
+       phys_addr = (u16 *)dev->dev_addr;
+
+       DEBUG(0, "3c574_config(0x%p)\n", link);
+
+       tuple.Attributes = 0;
+       tuple.DesiredTuple = CISTPL_CONFIG;
+       CS_CHECK(GetFirstTuple, handle, &tuple);
+       tuple.TupleData = (cisdata_t *)buf;
+       tuple.TupleDataMax = 64;
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetTupleData, handle, &tuple);
+       CS_CHECK(ParseTuple, handle, &tuple, &parse);
+       link->conf.ConfigBase = parse.config.base;
+       link->conf.Present = parse.config.rmask[0];
+
+       /* Configure card */
+       link->state |= DEV_CONFIG;
+
+       for (i = j = 0; j < 0x400; j += 0x20) {
+               link->io.BasePort1 = j ^ 0x300;
+               i = CardServices(RequestIO, link->handle, &link->io);
+               if (i == CS_SUCCESS) break;
+       }
+       if (i != CS_SUCCESS) {
+               cs_error(link->handle, RequestIO, i);
+               goto failed;
+       }
+       CS_CHECK(RequestIRQ, link->handle, &link->irq);
+       CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+
+       dev->mem_start = 0;
+#ifdef BROKEN_FEATURES
+       if (use_memory_ops) {
+               win_req_t req;
+               memreq_t mem;
+               req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM |
+                       WIN_ENABLE | WIN_USE_WAIT;
+               req.Base = 0;
+               req.Size = 0x1000;
+               req.AccessSpeed = 0;
+               link->win = (window_handle_t)link->handle;
+               i = CardServices(RequestWindow, &link->win, &req);
+               if (i == CS_SUCCESS) {
+                       mem.Page = mem.CardOffset = 0;
+                       CardServices(MapMemPage, link->win, &mem);
+                       dev->mem_start = (long)(ioremap(req.Base, 0x1000)) + 0x800;
+               } else
+                       cs_error(link->handle, RequestWindow, i);
+       }
+#endif
+
+       dev->irq = link->irq.AssignedIRQ;
+       dev->base_addr = link->io.BasePort1;
+
+       dev->tbusy = 0;
+       if (register_netdev(dev) != 0) {
+               printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n");
+               goto failed;
+       }
+
+       link->state &= ~DEV_CONFIG_PENDING;
+
+       ioaddr = dev->base_addr;
+       lp = (struct el3_private *)dev->priv;
+       link->dev = &lp->node;
+
+       /* The 3c574 normally uses an EEPROM for configuration info, including
+          the hardware address.  The future products may include a modem chip
+          and put the address in the CIS. */
+       tuple.DesiredTuple = 0x88;
+       if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
+               CardServices(GetTupleData, handle, &tuple);
+               for (i = 0; i < 3; i++)
+                       phys_addr[i] = htons(buf[i]);
+       } else {
+               EL3WINDOW(0);
+               for (i = 0; i < 3; i++)
+                       phys_addr[i] = htons(read_eeprom(ioaddr, i + 10));
+               if (phys_addr[0] == 0x6060) {
+                       printk(KERN_NOTICE "3c574_cs: IO port conflict at 0x%03lx"
+                                  "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
+                       goto failed;
+               }
+       }
+       tuple.DesiredTuple = CISTPL_VERS_1;
+       if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS &&
+               CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS &&
+               CardServices(ParseTuple, handle, &tuple, &parse) == CS_SUCCESS) {
+               cardname = parse.version_1.str + parse.version_1.ofs[1];
+       } else
+               cardname = "3Com 3c574";
+
+       printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ",
+                  dev->name, cardname, dev->base_addr, dev->irq);
+
+       for (i = 0; i < 6; i++)
+               printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n"));
+
+       if (dev->mem_start)
+               printk(KERN_INFO"  Acceleration window at memory base %#lx.\n",
+                          dev->mem_start);
+
+       {
+               u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+               union wn3_config config;
+               outw(2<<11, ioaddr + RunnerRdCtrl);
+               mcr = inb(ioaddr + 2);
+               outw(0<<11, ioaddr + RunnerRdCtrl);
+               printk(KERN_INFO "  ASIC rev %d,", mcr>>3);
+               EL3WINDOW(3);
+               config.i = inl(ioaddr + Wn3_Config);
+               printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n",
+                          8 << config.u.ram_size, ram_split[config.u.ram_split],
+                          config.u.autoselect ? "autoselect " : "");
+               lp->default_media = config.u.xcvr;
+               lp->autoselect = config.u.autoselect;
+       }
+
+       {
+               int phy, phy_idx = 0;
+               
+               /* Roadrunner only: Turn on the MII transceiver */
+               outw(0x8040, ioaddr + Wn3_Options);
+               udelay(1000);
+               outw(0xc040, ioaddr + Wn3_Options);
+               wait_for_completion(dev, TxReset);
+               wait_for_completion(dev, RxReset);
+               udelay(1000);
+               outw(0x8040, ioaddr + Wn3_Options);
+               
+               EL3WINDOW(4);
+               for (phy = 1; phy <= 32 && phy_idx < sizeof(lp->phys); phy++) {
+                       int mii_status;
+                       mdio_sync(ioaddr, 32);
+                       mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
+                       if (mii_status != 0xffff) {
+                               lp->phys[phy_idx++] = phy & 0x1f;
+                               DEBUG(0, "  MII transceiver at index %d, status %x.\n",
+                                         phy, mii_status);
+                               if ((mii_status & 0x0040) == 0)
+                                       mii_preamble_required = 1;
+                       }
+               }
+               if (phy_idx == 0) {
+                       printk(KERN_NOTICE "  No MII transceivers found!\n");
+                       goto failed;
+               }
+               i = mdio_read(ioaddr, lp->phys[0], 16) | 0x40;
+               mdio_write(ioaddr, lp->phys[0], 16, i);
+               lp->advertising = mdio_read(ioaddr, lp->phys[0], 4);
+               if (full_duplex) {
+                       /* Only advertise the FD media types. */
+                       lp->advertising &= ~0x02a0;
+                       mdio_write(ioaddr, lp->phys[0], 4, lp->advertising);
+               }
+       }
+
+       return;
+
+cs_failed:
+       cs_error(link->handle, last_fn, last_ret);
+failed:
+       tc574_release((u_long)link);
+       return;
+
+} /* tc574_config */
+
+/*
+       After a card is removed, tc574_release() will unregister the net
+       device, and release the PCMCIA configuration.  If the device is
+       still open, this will be postponed until it is closed.
+*/
+
+static void tc574_release(u_long arg)
+{
+       dev_link_t *link = (dev_link_t *)arg;
+       struct net_device *dev = link->priv;
+
+       DEBUG(0, "3c574_release(0x%p)\n", link);
+
+       if (link->open) {
+               DEBUG(1, "3c574_cs: release postponed, '%s' still open\n",
+                         link->dev->dev_name);
+               link->state |= DEV_STALE_CONFIG;
+               return;
+       }
+
+       CardServices(ReleaseConfiguration, link->handle);
+       CardServices(ReleaseIO, link->handle, &link->io);
+       CardServices(ReleaseIRQ, link->handle, &link->irq);
+       if (link->win) {
+               iounmap((void *)(dev->mem_start - 0x800));
+               CardServices(ReleaseWindow, link->win);
+       }
+
+       link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* tc574_release */
+
+/*
+       The card status event handler.  Mostly, this schedules other
+       stuff to run after an event is received.  A CARD_REMOVAL event
+       also sets some flags to discourage the net drivers from trying
+       to talk to the card any more.
+*/
+
+static int tc574_event(event_t event, int priority,
+                                          event_callback_args_t *args)
+{
+       dev_link_t *link = args->client_data;
+       struct net_device *dev = link->priv;
+
+       DEBUG(1, "3c574_event(0x%06x)\n", event);
+
+       switch (event) {
+       case CS_EVENT_CARD_REMOVAL:
+               link->state &= ~DEV_PRESENT;
+               if (link->state & DEV_CONFIG) {
+                       dev->tbusy = 1; dev->start = 0;
+                       link->release.expires = jiffies + HZ/20;
+                       add_timer(&link->release);
+               }
+               break;
+       case CS_EVENT_CARD_INSERTION:
+               link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+               tc574_config(link);
+               break;
+       case CS_EVENT_PM_SUSPEND:
+               link->state |= DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_RESET_PHYSICAL:
+               if (link->state & DEV_CONFIG) {
+                       if (link->open) {
+                               dev->tbusy = 1; dev->start = 0;
+                       }
+                       CardServices(ReleaseConfiguration, link->handle);
+               }
+               break;
+       case CS_EVENT_PM_RESUME:
+               link->state &= ~DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_CARD_RESET:
+               if (link->state & DEV_CONFIG) {
+                       CardServices(RequestConfiguration, link->handle, &link->conf);
+                       if (link->open) {
+                               tc574_reset(dev);
+                               dev->tbusy = 0; dev->start = 1;
+                       }
+               }
+               break;
+       }
+       return 0;
+} /* tc574_event */
+
+static void dump_status(struct net_device *dev)
+{
+       ioaddr_t ioaddr = dev->base_addr;
+       EL3WINDOW(1);
+    printk(KERN_INFO "  irq status %04x, rx status %04x, tx status "
+                  "%02x, tx free %04x\n", inw(ioaddr+EL3_STATUS),
+                  inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
+                  inw(ioaddr+TxFree));
+       EL3WINDOW(4);
+    printk(KERN_INFO "  diagnostics: fifo %04x net %04x ethernet %04x"
+                  " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06),
+                  inw(ioaddr+0x08), inw(ioaddr+0x0a));
+       EL3WINDOW(1);
+}
+
+/*
+  Use this for commands that may take time to finish
+*/
+static void wait_for_completion(struct net_device *dev, int cmd)
+{
+    int i = 1500;
+    outw(cmd, dev->base_addr + EL3_CMD);
+    while (--i > 0)
+               if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
+    if (i == 0)
+               printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n",
+                          dev->name, cmd);
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+   Assume that we are in register window zero.
+ */
+static u_short read_eeprom(ioaddr_t ioaddr, int index)
+{
+       int timer;
+       outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
+       /* Pause for at least 162 usec for the read to take place. */
+       for (timer = 1620; timer >= 0; timer--) {
+               if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+                       break;
+       }
+       return inw(ioaddr + Wn0EepromData);
+}
+
+/* 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 maxium data clock rate is 2.5 Mhz.  The timing is easily met by the
+   slow PC card interface. */
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN            0x00
+
+/* Generate the preamble required for initial synchronization and
+   a few older transceivers. */
+static void mdio_sync(ioaddr_t ioaddr, int bits)
+{
+       int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+       /* Establish sync by sending at least 32 logic ones. */
+       while (-- bits >= 0) {
+               outw(MDIO_DATA_WRITE1, mdio_addr);
+               outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+       }
+}
+
+static int mdio_read(ioaddr_t ioaddr, int phy_id, int location)
+{
+       int i;
+       int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       unsigned int retval = 0;
+       int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+       if (mii_preamble_required)
+               mdio_sync(ioaddr, 32);
+
+       /* Shift the read command bits out. */
+       for (i = 14; i >= 0; i--) {
+               int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+               outw(dataval, mdio_addr);
+               outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+       }
+       /* Read the two transition, 16 data, and wire-idle bits. */
+       for (i = 19; i > 0; i--) {
+               outw(MDIO_ENB_IN, mdio_addr);
+               retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+               outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+       }
+       return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value)
+{
+       int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+       int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+       int i;
+
+       if (mii_preamble_required)
+               mdio_sync(ioaddr, 32);
+
+       /* Shift the command bits out. */
+       for (i = 31; i >= 0; i--) {
+               int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+               outw(dataval, mdio_addr);
+               outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+       }
+       /* Leave the interface idle. */
+       for (i = 1; i >= 0; i--) {
+               outw(MDIO_ENB_IN, mdio_addr);
+               outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+       }
+
+       return;
+}
+
+/* Reset and restore all of the 3c574 registers. */
+static void tc574_reset(struct net_device *dev)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+       int i, ioaddr = dev->base_addr;
+
+       wait_for_completion(dev, TotalReset|0x10);
+
+#ifdef BROKEN_FEATURES
+       /* Set the PIO ctrl bits in the PC card LAN COR using Runner window 1. */
+       if (dev->mem_start || no_wait) {
+               u8 lan_cor;
+               outw(1<<11, ioaddr + RunnerRdCtrl);
+               lan_cor = inw(ioaddr) & ~0x30;
+               if (dev->mem_start)             /* Iff use_memory_ops worked! */
+                       lan_cor |= 0x10;
+               if (no_wait)
+                       lan_cor |= 0x20;
+               outw(lan_cor, ioaddr);
+       }
+#endif
+       /* Clear any transactions in progress. */
+       outw(0, ioaddr + RunnerWrCtrl);
+       outw(0, ioaddr + RunnerRdCtrl);
+
+       /* Set the station address and mask. */
+       EL3WINDOW(2);
+       for (i = 0; i < 6; i++)
+               outb(dev->dev_addr[i], ioaddr + i);
+       for (; i < 12; i+=2)
+               outw(0, ioaddr + i);
+
+       /* Reset config options */
+       EL3WINDOW(3);
+       outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+       outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
+                ioaddr + Wn3_Config);
+       
+       /* Roadrunner only: Turn on the MII transceiver. */
+       outw(0x8040, ioaddr + Wn3_Options);
+       udelay(1000);
+       outw(0xc040, ioaddr + Wn3_Options);
+       wait_for_completion(dev, TxReset);
+       wait_for_completion(dev, RxReset);
+       udelay(1000);
+       outw(0x8040, ioaddr + Wn3_Options);
+
+       /* Switch to the stats window, and clear all stats by reading. */
+       outw(StatsDisable, ioaddr + EL3_CMD);
+       EL3WINDOW(6);
+       for (i = 0; i < 10; i++)
+               inb(ioaddr + i);
+       inw(ioaddr + 10);
+       inw(ioaddr + 12);
+
+       EL3WINDOW(4);
+       /* New: On the Vortex/Odie we must also clear the BadSSD counter.. */
+       inb(ioaddr + 12);
+       /* .. enable any extra statistics bits.. */
+       outw(0x0040, ioaddr + Wn4_NetDiag);
+       /* .. re-sync MII and re-fill what NWay is advertising. */
+       mdio_sync(ioaddr, 32);
+       mdio_write(ioaddr, lp->phys[0], 4, lp->advertising);
+
+       /* Switch to register set 1 for normal use, just for TxFree. */
+       EL3WINDOW(1);
+
+       set_rx_mode(dev);
+       outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+       outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+       outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+       /* Allow status bits to be seen. */
+       outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+       /* Ack all pending events, and set active indicator mask. */
+       outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+                ioaddr + EL3_CMD);
+       outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+                | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
+}
+
+static int el3_open(struct net_device *dev)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+       dev_link_t *link;
+
+       for (link = dev_list; link; link = link->next)
+               if (link->priv == dev) break;
+       if (!DEV_OK(link))
+               return -ENODEV;
+       
+       link->open++;
+       MOD_INC_USE_COUNT;
+       dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+       
+       tc574_reset(dev);
+       lp->media.function = &media_check;
+       lp->media.data = (u_long)dev;
+       lp->media.expires = jiffies + HZ;
+       add_timer(&lp->media);
+       
+       DEBUG(2, "%s: opened, status %4.4x.\n",
+                 dev->name, inw(dev->base_addr + EL3_STATUS));
+       
+       return 0;                                       /* Always succeed */
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+       ioaddr_t ioaddr = dev->base_addr;
+       
+       printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name);
+       dump_status(dev);
+       lp->stats.tx_errors++;
+       dev->trans_start = jiffies;
+       /* Issue TX_RESET and TX_START commands. */
+       wait_for_completion(dev, TxReset);
+       outw(TxEnable, ioaddr + EL3_CMD);
+       dev->tbusy = 0;
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+    struct el3_private *lp = (struct el3_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int i;
+    
+    /* Clear the Tx status stack. */
+    for (i = 32; i > 0; i--) {
+               u_char tx_status = inb(ioaddr + TxStatus);
+               if (!(tx_status & 0x84)) break;
+               /* reset transmitter on jabber error or underrun */
+               if (tx_status & 0x30)
+                       wait_for_completion(dev, TxReset);
+               if (tx_status & 0x38) {
+                       DEBUG(1, "%s: transmit error: status 0x%02x\n",
+                                 dev->name, tx_status);
+                       outw(TxEnable, ioaddr + EL3_CMD);
+                       lp->stats.tx_aborted_errors++;
+               }
+               outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+    }
+}
+
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       ioaddr_t ioaddr = dev->base_addr;
+#ifdef BROKEN_FEATURES
+       long flags = 0;
+#endif
+
+       /* Transmitter timeout, serious problems. */
+       if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+               if (jiffies - dev->trans_start < TX_TIMEOUT)
+                       return 1;
+               el3_tx_timeout(dev);
+       }
+
+       DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+                 "status %4.4x.\n", dev->name, (long)skb->len,
+                 inw(ioaddr + EL3_STATUS));
+
+#ifdef BROKEN_FEATURES
+       if (use_fifo_buffer) {
+               /* Avoid other accesses to the chip while RunnerWrCtrl is non-zero. */
+               save_flags(flags);
+               cli();
+               outw((((skb->len + 7)>>2)<<1), ioaddr + RunnerWrCtrl);
+               DEBUG(0, "TxFree %x, tx length %x, RunnerWrCtrl is %4.4x.\n",
+                         inw(ioaddr+TxFree), skb->len, inw(ioaddr+RunnerWrCtrl));
+       }
+
+       /* Put out the doubleword header... */
+       /* ... and the packet rounded to a doubleword. */
+       if (dev->mem_start) {
+               writew(skb->len, (void *)dev->mem_start);
+               writew(0, (void *)dev->mem_start);
+               copy_to_pc((void*)dev->mem_start, skb->data, (skb->len+3)&~3);
+       } else {
+               outw(skb->len, ioaddr + TX_FIFO);
+               outw(0, ioaddr + TX_FIFO);
+               outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+       }
+
+       if (use_fifo_buffer) {
+               DEBUG(0, " RunnerWr/RdCtrl is %4.4x/%4.4x, TxFree %x.\n",
+                         inw(ioaddr + RunnerWrCtrl), inw(ioaddr + RunnerRdCtrl),
+                         inw(ioaddr + TxFree));
+               restore_flags(flags);
+       }
+#else
+       outw(skb->len, ioaddr + TX_FIFO);
+       outw(0, ioaddr + TX_FIFO);
+       outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+#endif
+
+       dev->trans_start = jiffies;
+
+       /* TxFree appears only in Window 1, not offset 0x1c. */
+       if (inw(ioaddr + TxFree) > 1536) {
+               dev->tbusy = 0;
+       } else
+               /* 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);
+
+       return 0;
+}
+
+/* The EL3 interrupt handler. */
+static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct el3_private *lp;
+       ioaddr_t ioaddr, status;
+       int work_budget = max_interrupt_work;
+
+       if ((dev == NULL) || !dev->start)
+               return;
+       lp = (struct el3_private *)dev->priv;
+       ioaddr = dev->base_addr;
+
+#ifdef PCMCIA_DEBUG
+       if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+               printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
+                          dev->name);
+               return;
+       }
+       DEBUG(3, "%s: interrupt, status %4.4x.\n",
+                 dev->name, inw(ioaddr + EL3_STATUS));
+#endif
+
+       while ((status = inw(ioaddr + EL3_STATUS)) &
+                  (IntLatch | RxComplete | RxEarly | StatsFull)) {
+               if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) {
+                       DEBUG(1, "%s: Interrupt from dead card\n", dev->name);
+                       break;
+               }
+
+               if (status & RxComplete)
+                       work_budget = el3_rx(dev, work_budget);
+
+               if (status & TxAvailable) {
+                       DEBUG(3, "  TX room bit was handled.\n");
+                       /* There's room in the FIFO for a full-sized packet. */
+                       outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+                       dev->tbusy = 0;
+                       mark_bh(NET_BH);
+               }
+
+               if (status & TxComplete)
+                       pop_tx_status(dev);
+
+               if (status & (AdapterFailure | RxEarly | StatsFull)) {
+                       /* Handle all uncommon interrupts. */
+                       if (status & StatsFull)
+                               update_stats(ioaddr, dev);
+                       if (status & RxEarly) {
+                               work_budget = el3_rx(dev, work_budget);
+                               outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+                       }
+                       if (status & AdapterFailure) {
+                               u16 fifo_diag;
+                               EL3WINDOW(4);
+                               fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+                               EL3WINDOW(1);
+                               printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic"
+                                          " register %04x.\n", dev->name, fifo_diag);
+                               if (fifo_diag & 0x0400) {
+                                       /* Tx overrun */
+                                       wait_for_completion(dev, TxReset);
+                                       outw(TxEnable, ioaddr + EL3_CMD);
+                               }
+                               if (fifo_diag & 0x2000) {
+                                       /* Rx underrun */
+                                       wait_for_completion(dev, RxReset);
+                                       set_rx_mode(dev);
+                                       outw(RxEnable, ioaddr + EL3_CMD);
+                               }
+                               outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+                       }
+               }
+
+               if (--work_budget < 0) {
+                       DEBUG(0, "%s: Too much work in interrupt, "
+                                 "status %4.4x.\n", dev->name, status);
+                       /* Clear all interrupts */
+                       outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+                       break;
+               }
+               /* Acknowledge the IRQ. */
+               outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+       }
+
+#ifdef PCMCIA_DEBUG
+       DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+                 dev->name, inw(ioaddr + EL3_STATUS));
+       dev->interrupt = 0;
+#endif
+       return;
+}
+
+/*
+    This timer serves two purposes: to check for missed interrupts
+       (and as a last resort, poll the NIC for events), and to monitor
+       the MII, reporting changes in cable status.
+*/
+static void media_check(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)(arg);
+    struct el3_private *lp = (struct el3_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    u_long flags;
+       u_short /* cable, */ media, partner;
+       
+    if (dev->start == 0) goto reschedule;
+       
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
+               (inb(ioaddr + Timer) == 0xff)) {
+               if (!lp->fast_poll)
+                       printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+               el3_interrupt(dev->irq, dev, NULL);
+               lp->fast_poll = HZ;
+    }
+    if (lp->fast_poll) {
+               lp->fast_poll--;
+               lp->media.expires = jiffies + 2;
+               add_timer(&lp->media);
+               return;
+    }
+
+       save_flags(flags);
+       cli();
+       EL3WINDOW(4);
+       media = mdio_read(ioaddr, lp->phys[0], 1);
+       partner = mdio_read(ioaddr, lp->phys[0], 5);
+       EL3WINDOW(1);
+       restore_flags(flags);
+
+       if (media != lp->media_status) {
+               if ((media ^ lp->media_status) & 0x0004)
+                       printk(KERN_INFO "%s: %s link beat\n", dev->name,
+                                  (lp->media_status & 0x0004) ? "lost" : "found");
+               if ((media ^ lp->media_status) & 0x0020) {
+                       lp->partner = 0;
+                       if (lp->media_status & 0x0020) {
+                               printk(KERN_INFO "%s: autonegotiation restarted\n",
+                                          dev->name);
+                       } else if (partner) {
+                               partner &= lp->advertising;
+                               lp->partner = partner;
+                               printk(KERN_INFO "%s: autonegotiation complete: "
+                                          "%sbaseT-%cD selected\n", dev->name,
+                                          ((partner & 0x0180) ? "100" : "10"),
+                                          ((partner & 0x0140) ? 'F' : 'H'));
+                       } else {
+                               printk(KERN_INFO "%s: link partner did not autonegotiate\n",
+                                          dev->name);
+                       }
+
+                       EL3WINDOW(3);
+                       outb((partner & 0x0140 ? 0x20 : 0) |
+                                (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+                       EL3WINDOW(1);
+
+               }
+               if (media & 0x0010)
+                       printk(KERN_INFO "%s: remote fault detected\n",
+                                  dev->name);
+               if (media & 0x0002)
+                       printk(KERN_INFO "%s: jabber detected\n", dev->name);
+               lp->media_status = media;
+       }
+
+reschedule:
+    lp->media.expires = jiffies + HZ;
+    add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+
+       if (dev->start)
+               update_stats(dev->base_addr, dev);
+       return &lp->stats;
+}
+
+/*  Update statistics.
+       Suprisingly this need not be run single-threaded, but it effectively is.
+       The counters clear when read, so the adds must merely be atomic.
+ */
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+       u8 upper_cnt;
+
+       DEBUG(2, "%s: updating the statistics.\n", dev->name);
+
+       if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
+               return;
+
+       /* Unlike the 3c509 we need not turn off stats updates while reading. */
+       /* Switch to the stats window, and read everything. */
+       EL3WINDOW(6);
+       lp->stats.tx_carrier_errors     += inb(ioaddr + 0);
+       lp->stats.tx_heartbeat_errors   += inb(ioaddr + 1);
+       /* Multiple collisions. */              inb(ioaddr + 2);
+       lp->stats.collisions            += inb(ioaddr + 3);
+       lp->stats.tx_window_errors      += inb(ioaddr + 4);
+       lp->stats.rx_fifo_errors        += inb(ioaddr + 5);
+       lp->stats.tx_packets            += inb(ioaddr + 6);
+       upper_cnt = inb(ioaddr + 9);
+       lp->stats.tx_packets            += (upper_cnt&0x30) << 4;
+       /* Rx packets   */                      inb(ioaddr + 7);
+       /* Tx deferrals */                      inb(ioaddr + 8);
+       lp->stats.rx_bytes += inw(ioaddr + 10);
+       lp->stats.tx_bytes += inw(ioaddr + 12);
+
+       /* With Vortex and later we must also clear the BadSSD counter. */
+       EL3WINDOW(4);
+       inb(ioaddr + 12);
+
+       EL3WINDOW(1);
+}
+
+static int el3_rx(struct net_device *dev, int worklimit)
+{
+       struct el3_private *lp = (struct el3_private *)dev->priv;
+       ioaddr_t ioaddr = dev->base_addr;
+       short rx_status;
+       
+       DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+                 dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+    while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
+                  (--worklimit >= 0)) {
+               if (rx_status & 0x4000) { /* Error, update stats. */
+                       short error = rx_status & 0x3800;
+                       lp->stats.rx_errors++;
+                       switch (error) {
+                       case 0x0000:            lp->stats.rx_over_errors++; break;
+                       case 0x0800:            lp->stats.rx_length_errors++; break;
+                       case 0x1000:            lp->stats.rx_frame_errors++; break;
+                       case 0x1800:            lp->stats.rx_length_errors++; break;
+                       case 0x2000:            lp->stats.rx_frame_errors++; break;
+                       case 0x2800:            lp->stats.rx_crc_errors++; break;
+                       }
+               } else {
+                       short pkt_len = rx_status & 0x7ff;
+                       struct sk_buff *skb;
+
+                       skb = dev_alloc_skb(pkt_len+5);
+
+                       DEBUG(3, "  Receiving packet size %d status %4.4x.\n",
+                                 pkt_len, rx_status);
+                       if (skb != NULL) {
+                               skb->dev = dev;
+                               skb_reserve(skb, 2);
+
+#ifdef BROKEN_FEATURES
+                               if (use_fifo_buffer) {
+                                       outw(((pkt_len+3)>>2)<<1, ioaddr + RunnerRdCtrl);
+                                       DEBUG(0,"Start Rx %x -- RunnerRdCtrl is %4.4x.\n",
+                                                 pkt_len, inw(ioaddr + RunnerRdCtrl));
+                               }
+                               if (dev->mem_start) {
+                                       copy_from_pc(skb_put(skb, pkt_len),
+                                                                (void*)dev->mem_start, (pkt_len+3)&~3);
+                               } else {
+                                       insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+                                                       ((pkt_len+3)>>2));
+                               }
+                               if (use_fifo_buffer) {
+                                       DEBUG(0," RunnerRdCtrl is now %4.4x.\n",
+                                                 inw(ioaddr + RunnerRdCtrl));
+                                       outw(0, ioaddr + RunnerRdCtrl);
+                                       DEBUG(0, " Rx packet with data %2.2x:%2.2x:%2.2x\n",
+                                                 skb->head[0], skb->head[1], skb->head[2]);
+                               }
+#else
+                               insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+                                               ((pkt_len+3)>>2));
+#endif
+
+                               skb->protocol = eth_type_trans(skb, dev);
+                               netif_rx(skb);
+                               lp->stats.rx_packets++;
+                       } else {
+                               DEBUG(1, "%s: couldn't allocate a sk_buff of"
+                                         " size %d.\n", dev->name, pkt_len);
+                               lp->stats.rx_dropped++;
+                       }
+               }
+               wait_for_completion(dev, RxDiscard);
+       }
+
+       return worklimit;
+}
+
+#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 el3_private *vp = (struct el3_private *)dev->priv;
+       ioaddr_t ioaddr = dev->base_addr;
+       u16 *data = (u16 *)&rq->ifr_data;
+       int phy = vp->phys[0] & 0x1f;
+
+       DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+                 dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+                 data[0], data[1], data[2], data[3]);
+
+    switch(cmd) {
+       case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+               data[0] = phy;
+       case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+               {
+                       int saved_window;
+                       long flags;
+
+                       save_flags(flags);
+                       cli();
+                       saved_window = inw(ioaddr + EL3_CMD) >> 13;
+                       EL3WINDOW(4);
+                       data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+                       EL3WINDOW(saved_window);
+                       restore_flags(flags);
+                       return 0;
+               }
+       case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+               {
+                       int saved_window;
+                       long flags;
+
+                       if (!suser())
+                               return -EPERM;
+                       save_flags(flags);
+                       cli();
+                       saved_window = inw(ioaddr + EL3_CMD) >> 13;
+                       EL3WINDOW(4);
+                       mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+                       EL3WINDOW(saved_window);
+                       restore_flags(flags);
+                       return 0;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+#endif  /* HAVE_PRIVATE_IOCTL */
+
+/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
+   documented.  Until it is we revert to receiving all multicast frames when
+   any multicast reception is desired.
+   Note: My other drivers emit a log message whenever promiscuous mode is
+   entered to help detect password sniffers.  This is less desirable on
+   typical PC card machines, so we omit the message.
+   */
+
+static void set_rx_mode(struct net_device *dev)
+{
+       ioaddr_t ioaddr = dev->base_addr;
+
+       if (dev->flags & IFF_PROMISC)
+               outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+                        ioaddr + EL3_CMD);
+       else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
+               outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+       else
+               outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static int el3_close(struct net_device *dev)
+{
+       ioaddr_t ioaddr = dev->base_addr;
+       dev_link_t *link;
+
+       for (link = dev_list; link; link = link->next)
+               if (link->priv == dev) break;
+       if (link == NULL)
+               return -ENODEV;
+
+       DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+       
+       if (DEV_OK(link)) {
+               /* Turn off statistics ASAP.  We update lp->stats below. */
+               outw(StatsDisable, ioaddr + EL3_CMD);
+               
+               /* Disable the receiver and transmitter. */
+               outw(RxDisable, ioaddr + EL3_CMD);
+               outw(TxDisable, ioaddr + EL3_CMD);
+               
+               /* Note: Switching to window 0 may disable the IRQ. */
+               EL3WINDOW(0);
+               
+               update_stats(ioaddr, dev);
+       }
+
+       link->open--;
+       dev->start = 0;
+       del_timer(&((struct el3_private *)dev->priv)->media);
+       if (link->state & DEV_STALE_CONFIG) {
+               link->release.expires = jiffies + HZ/20;
+               link->state |= DEV_RELEASE_PENDING;
+               add_timer(&link->release);
+       }
+
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+}
+
+static int __init init_3c574_cs(void)
+{
+       servinfo_t serv;
+
+       /* Always emit the version, before any failure. */
+       printk(KERN_INFO"%s", tc574_version);
+       DEBUG(0, "%s\n", version);
+       CardServices(GetCardServicesInfo, &serv);
+       if (serv.Revision != CS_RELEASE_CODE) {
+               printk(KERN_NOTICE "3c574_cs: Card Services release "
+                          "does not match!\n");
+               return -1;
+       }
+       register_pccard_driver(&dev_info, &tc574_attach, &tc574_detach);
+       return 0;
+}
+
+static void __exit exit_3c574_cs(void)
+{
+       DEBUG(0, "3c574_cs: unloading\n");
+       unregister_pccard_driver(&dev_info);
+       while (dev_list != NULL)
+               tc574_detach(dev_list);
+}
+
+module_init(init_3c574_cs);
+module_exit(exit_3c574_cs);
+
+/*
+ * Local variables:
+ *  compile-command: "make 3c574_cs.o"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index 6e511f4b7e3a99d0d257cadedcaafd9d6366cc11..dea14d53a2cb76475c315558a2c28688147e4e7a 100644 (file)
@@ -4,7 +4,7 @@
     
     Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
 
-    3c589_cs.c 1.134 1999/09/15 15:33:09
+    3c589_cs.c 1.135 1999/10/07 20:14:54
 
     The network driver code is based on Donald Becker's 3c589 code:
     
@@ -115,7 +115,7 @@ static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"3c589_cs.c 1.134 1999/09/15 15:33:09 (David Hinds)";
+"3c589_cs.c 1.135 1999/10/07 20:14:54 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -142,14 +142,14 @@ static void tc589_release(u_long arg);
 static int tc589_event(event_t event, int priority,
                       event_callback_args_t *args);
 
-static ushort read_eeprom(short ioaddr, int index);
+static u_short read_eeprom(ioaddr_t ioaddr, int index);
 static void tc589_reset(struct net_device *dev);
 static void media_check(u_long arg);
 static int el3_config(struct net_device *dev, struct ifmap *map);
 static int el3_open(struct net_device *dev);
 static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void update_stats(int addr, struct net_device *dev);
+static void update_stats(ioaddr_t addr, struct net_device *dev);
 static struct net_device_stats *el3_get_stats(struct net_device *dev);
 static int el3_rx(struct net_device *dev);
 static int el3_close(struct net_device *dev);
@@ -355,14 +355,14 @@ static void tc589_config(dev_link_t *link)
     struct net_device *dev;
     tuple_t tuple;
     cisparse_t parse;
-    u_short buf[32];
+    u_short buf[32], *phys_addr;
     int last_fn, last_ret, i, j, multi = 0;
-    short ioaddr, *phys_addr;
+    ioaddr_t ioaddr;
     char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
     
     handle = link->handle;
     dev = link->priv;
-    phys_addr = (short *)dev->dev_addr;
+    phys_addr = (u_short *)dev->dev_addr;
 
     DEBUG(0, "3c589_config(0x%p)\n", link);
 
@@ -571,7 +571,7 @@ static void wait_for_completion(struct net_device *dev, int cmd)
   Read a word from the EEPROM using the regular EEPROM access register.
   Assume that we are in register window zero.
 */
-static ushort read_eeprom(short ioaddr, int index)
+static u_short read_eeprom(ioaddr_t ioaddr, int index)
 {
     int i;
     outw(EEPROM_READ + index, ioaddr + 10);
@@ -589,7 +589,7 @@ static ushort read_eeprom(short ioaddr, int index)
 static void tc589_set_xcvr(struct net_device *dev, int if_port)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    ushort ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     
     EL3WINDOW(0);
     switch (if_port) {
@@ -611,7 +611,7 @@ static void tc589_set_xcvr(struct net_device *dev, int if_port)
 
 static void dump_status(struct net_device *dev)
 {
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     EL3WINDOW(1);
     printk(KERN_INFO "  irq status %04x, rx status %04x, tx status "
           "%02x  tx free %04x\n", inw(ioaddr+EL3_STATUS),
@@ -627,7 +627,7 @@ static void dump_status(struct net_device *dev)
 /* Reset and restore all of the 3c589 registers. */
 static void tc589_reset(struct net_device *dev)
 {
-    ushort ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     int i;
     
     EL3WINDOW(0);
@@ -709,7 +709,7 @@ static int el3_open(struct net_device *dev)
 static void el3_tx_timeout(struct net_device *dev)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     
     printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name);
     dump_status(dev);
@@ -724,7 +724,7 @@ static void el3_tx_timeout(struct net_device *dev)
 static void pop_tx_status(struct net_device *dev)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     int i;
     
     /* Clear the Tx status stack. */
@@ -747,7 +747,7 @@ static void pop_tx_status(struct net_device *dev)
 static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
 
     /* Transmitter timeout, serious problems. */
     if (dev->tbusy) {
@@ -791,7 +791,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
     struct net_device *dev = (struct net_device *)dev_id;
     struct el3_private *lp;
-    int ioaddr, status;
+    ioaddr_t ioaddr, status;
     int i = 0;
     
     if ((dev == NULL) || !dev->start)
@@ -885,7 +885,7 @@ static void media_check(u_long arg)
 {
     struct net_device *dev = (struct net_device *)(arg);
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     u_short media, errs;
     u_long flags;
 
@@ -984,7 +984,7 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev)
   operation, and it's simpler for the rest of the driver to assume that
   window 1 is always valid rather than use a special window-state variable.
 */
-static void update_stats(int ioaddr, struct net_device *dev)
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
     
@@ -1013,7 +1013,7 @@ static void update_stats(int ioaddr, struct net_device *dev)
 static int el3_rx(struct net_device *dev)
 {
     struct el3_private *lp = (struct el3_private *)dev->priv;
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     int worklimit = 32;
     short rx_status;
     
@@ -1073,7 +1073,7 @@ static int el3_rx(struct net_device *dev)
  */
 static void set_multicast_list(struct net_device *dev)
 {
-    short ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     dev_link_t *link;
     for (link = dev_list; link; link = link->next)
        if (link->priv == dev) break;
@@ -1099,7 +1099,7 @@ static void set_multicast_list(struct net_device *dev)
 
 static int el3_close(struct net_device *dev)
 {
-    int ioaddr = dev->base_addr;
+    ioaddr_t ioaddr = dev->base_addr;
     dev_link_t *link;
 
     for (link = dev_list; link; link = link->next)
index cc4efbf886dcfa605090870b5450525093c6188d..cca2ff57aa0d6435892c2cedbd895b303340ea90 100644 (file)
@@ -3,13 +3,34 @@
 #
 
 mainmenu_option next_comment
-comment 'PCMCIA network devices'
+comment 'PCMCIA network device support'
 
-dep_tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA
-dep_tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA
-dep_tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+bool 'PCMCIA network device support' CONFIG_NET_PCMCIA
+if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
+   dep_tristate '  3Com 3c589 PCMCIA support' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA
+   dep_tristate '  3Com 3c574 PCMCIA support' CONFIG_PCMCIA_3C574 $CONFIG_PCMCIA
+   dep_tristate '  Fujitsu FMV-J18x PCMCIA support' CONFIG_PCMCIA_FMVJ18X $CONFIG_PCMCIA
+   dep_tristate '  NE2000 compatible PCMCIA support' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA
+   dep_tristate '  New Media PCMCIA support' CONFIG_PCMCIA_NMCLAN $CONFIG_PCMCIA
+   dep_tristate '  SMC 91Cxx PCMCIA support' CONFIG_PCMCIA_SMC91C92 $CONFIG_PCMCIA
+   dep_tristate '  Xircom 16-bit PCMCIA support' CONFIG_PCMCIA_XIRC2PS $CONFIG_PCMCIA
 
-if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" ]; then
+   # if [ "$CONFIG_CARDBUS" = "y" ]; then
+      # dep_tristate '  3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 $CONFIG_PCMCIA
+      # dep_tristate '  DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP $CONFIG_PCMCIA
+      # dep_tristate '  SMC EPIC CardBus support' CONFIG_PCMCIA_EPIC100 $CONFIG_PCMCIA
+   # fi
+
+   dep_tristate '  Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+   dep_tristate '  Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
+   dep_tristate '  AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
+fi
+
+if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_3C574" = "y" -o \
+     "$CONFIG_PCMCIA_FMVJ18X" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" -o \
+     "$CONFIG_PCMCIA_NMCLAN" = "y" -o "$CONFIG_PCMCIA_SMC91C92" = "y" -o \
+     "$CONFIG_PCMCIA_XIRC2PS" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o \
+     "$CONFIG_PCMCIA_NETWAVE" = "y" -o "$CONFIG_PCMCIA_WAVELAN" = "y" ]; then
    define_bool CONFIG_PCMCIA_NETCARD y
 fi
 
index 3b59b1b93ed9d8950201ab4deda5b149689d44ee..779abb20ce47f4147cc92676adbbb357d6a5fb98 100644 (file)
@@ -13,22 +13,81 @@ O_OBJS   :=
 M_OBJS   :=
 MOD_LIST_NAME := PCMCIA_MODULES
 
+#CFLAGS_3c575_cb.o = -DCARDBUS
+#CFLAGS_tulip_cb.o = -DCARDBUS
+
+ifeq ($(CONFIG_PCMCIA_3C589),y)
+  O_OBJS += 3c589_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_3C589),m)
+    M_OBJS += 3c589_cs.o
+  endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_3C574),y)
+  O_OBJS += 3c574_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_3C574),m)
+    M_OBJS += 3c574_cs.o
+  endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_FMVJ18X),y)
+  O_OBJS += fmvj18x_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_FMVJ18X),m)
+    M_OBJS += fmvj18x_cs.o
+  endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_NMCLAN),y)
+  O_OBJS += nmclan_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_NMCLAN),m)
+    M_OBJS += nmclan_cs.o
+  endif
+endif
+
 ifeq ($(CONFIG_PCMCIA_PCNET),y)
   O_OBJS += pcnet_cs.o
 else
   ifeq ($(CONFIG_PCMCIA_PCNET),m)
-    MX_OBJS += pcnet_cs.o
+    M_OBJS += pcnet_cs.o
   endif
 endif
 
-ifeq ($(CONFIG_PCMCIA_3C589),y)
-  O_OBJS += 3c589_cs.o
+ifeq ($(CONFIG_PCMCIA_SMC91C92),y)
+  O_OBJS += smc91c92_cs.o
 else
-  ifeq ($(CONFIG_PCMCIA_3C589),m)
-    MX_OBJS += 3c589_cs.o
+  ifeq ($(CONFIG_PCMCIA_SMC91C92),m)
+    M_OBJS += smc91c92_cs.o
   endif
 endif
 
+#ifeq ($(CONFIG_PCMCIA_3C575),y)
+#  O_OBJS += 3c575_cb.o
+#else
+#  ifeq ($(CONFIG_PCMCIA_3C575),m)
+#    M_OBJS += 3c575_cb.o
+#  endif
+#endif
+
+#ifeq ($(CONFIG_PCMCIA_TULIP),y)
+#  O_OBJS += tulip_cb.o
+#else
+#  ifeq ($(CONFIG_PCMCIA_TULIP),m)
+#    M_OBJS += tulip_cb.o
+#  endif
+#endif
+
+#ifeq ($(CONFIG_PCMCIA_EPIC100),y)
+#  O_OBJS += epic100_cb.o
+#else
+#  ifeq ($(CONFIG_PCMCIA_EPIC100),m)
+#    M_OBJS += epic100_cb.o
+#  endif
+#endif
+
 ifeq ($(CONFIG_PCMCIA_RAYCS),y)
   OX_OBJS += ray_cs.o
 else
@@ -37,4 +96,23 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_PCMCIA_NETWAVE),y)
+  OX_OBJS += netwave_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_NETWAVE),m)
+  M_OBJS += netwave_cs.o
+  endif
+endif
+
+ifeq ($(CONFIG_PCMCIA_WAVELAN),y)
+  OX_OBJS += wavelan_cs.o
+else
+  ifeq ($(CONFIG_PCMCIA_WAVELAN),m)
+  M_OBJS += wavelan_cs.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
+
+#epic100_cb.o: ../epic100.c
+#      $(CC) $(CFLAGS) -DCARDBUS -c -o $@ ../epic100.c
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
new file mode 100644 (file)
index 0000000..82e3ce5
--- /dev/null
@@ -0,0 +1,1205 @@
+/*======================================================================
+    fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp
+
+    A fmvj18x (and its compatibles) PCMCIA client driver
+
+    Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp
+
+    TDK LAK-CD021 and CONTEC C-NET(PC)C support added by 
+    Nobuhiro Katayama, kata-n@po.iijnet.or.jp
+
+    The PCMCIA client code is based on code written by David Hinds.
+    Network code is based on the "FMV-18x driver" by Yutaka TAMIYA
+    but is actually largely Donald Becker's AT1700 driver, which
+    carries the following attribution:
+
+    Written 1993-94 by Donald Becker.
+
+    Copyright 1993 United States Government as represented by the
+    Director, National Security Agency.
+    
+    This software may be used and distributed according to the terms
+    of the GNU Public License, incorporated herein by reference.
+    
+    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
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*
+   For debugging this driver you may need more information.
+   To enable printing registers or status, set 'fmvj18x_debug=#' option .
+ */
+#ifdef FMVJ18X_DEBUG
+static int fmvj18x_debug = FMVJ18X_DEBUG;
+#else
+static int fmvj18x_debug = 2;
+#endif /* FMVJ18X_DEBUG */
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+/* SRAM configuration */
+/* 0:4KB*2 TX buffer   else:8KB*2 TX buffer */
+static int sram_config = 0;
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(sram_config, "i");
+
+/*====================================================================*/
+/* 
+   driver version infomation 
+ */
+#ifdef PCMCIA_DEBUG
+static char *version =
+ "fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp";
+#endif
+
+/*====================================================================*/
+/*
+    PCMCIA event handlers
+ */
+static void fmvj18x_config(dev_link_t *link);
+static void fmvj18x_release(u_long arg);
+static int fmvj18x_event(event_t event, int priority,
+                         event_callback_args_t *args);
+static int fmvj18x_init(struct net_device *dev);
+static dev_link_t *fmvj18x_attach(void);
+static void fmvj18x_detach(dev_link_t *);
+
+/*
+    LAN controler(MBH86960A) specific routines
+ */
+static int fjn_config(struct net_device *dev, struct ifmap *map);
+static int fjn_open(struct net_device *dev);
+static int fjn_close(struct net_device *dev);
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void fjn_rx(struct net_device *dev);
+static void fjn_reset(struct net_device *dev);
+static struct net_device_stats *fjn_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+static dev_info_t dev_info = "fmvj18x_cs";
+static dev_link_t *dev_list = NULL;
+
+/*
+    card type
+ */
+typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501 } cardtype_t;
+
+/*
+    driver specific data structure
+*/
+typedef struct local_info_t {
+    dev_node_t node;
+    struct net_device_stats stats;
+    long open_time;
+    uint tx_started:1;
+    uint tx_queue;
+    u_short tx_queue_len;
+    cardtype_t cardtype;
+    u_short sent;
+    u_char mc_filter[8];
+} local_info_t;
+
+#define MC_FILTERBREAK 64
+
+/*====================================================================*/
+/* 
+    ioport offset from the base address 
+ */
+#define TX_STATUS               0 /* transmit status register */
+#define RX_STATUS               1 /* receive status register */
+#define TX_INTR                 2 /* transmit interrupt mask register */
+#define RX_INTR                 3 /* receive interrupt mask register */
+#define TX_MODE                 4 /* transmit mode register */
+#define RX_MODE                 5 /* receive mode register */
+#define CONFIG_0                6 /* configuration register 0 */
+#define CONFIG_1                7 /* configuration register 1 */
+
+#define NODE_ID                 8 /* node ID register            (bank 0) */
+#define MAR_ADR                 8 /* multicast address registers (bank 1) */
+
+#define DATAPORT                8 /* buffer mem port registers   (bank 2) */
+#define TX_START               10 /* transmit start register */
+#define COL_CTRL               11 /* 16 collision control register */
+#define BMPR12                 12 /* reserved */
+#define BMPR13                 13 /* reserved */
+#define RX_SKIP                14 /* skip received packet register */
+
+#define LAN_CTRL               16 /* LAN card control register */
+
+#define MAC_ID               0x1a /* hardware address */
+
+/* 
+    control bits 
+ */
+#define ENA_TMT_OK           0x80
+#define ENA_TMT_REC          0x20
+#define ENA_COL              0x04
+#define ENA_16_COL           0x02
+#define ENA_TBUS_ERR         0x01
+
+#define ENA_PKT_RDY          0x80
+#define ENA_BUS_ERR          0x40
+#define ENA_LEN_ERR          0x08
+#define ENA_ALG_ERR          0x04
+#define ENA_CRC_ERR          0x02
+#define ENA_OVR_FLO          0x01
+
+/* flags */
+#define F_TMT_RDY            0x80 /* can accept new packet */
+#define F_NET_BSY            0x40 /* carrier is detected */
+#define F_TMT_OK             0x20 /* send packet successfully */
+#define F_SRT_PKT            0x10 /* short packet error */
+#define F_COL_ERR            0x04 /* collision error */
+#define F_16_COL             0x02 /* 16 collision error */
+#define F_TBUS_ERR           0x01 /* bus read error */
+
+#define F_PKT_RDY            0x80 /* packet(s) in buffer */
+#define F_BUS_ERR            0x40 /* bus read error */
+#define F_LEN_ERR            0x08 /* short packet */
+#define F_ALG_ERR            0x04 /* frame error */
+#define F_CRC_ERR            0x02 /* CRC error */
+#define F_OVR_FLO            0x01 /* overflow error */
+
+#define F_BUF_EMP            0x40 /* receive buffer is empty */
+
+#define F_SKP_PKT            0x05 /* drop packet in buffer */
+
+/* default bitmaps */
+#define D_TX_INTR  ( ENA_TMT_OK )
+#define D_RX_INTR  ( ENA_PKT_RDY | ENA_LEN_ERR \
+                  | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO )
+#define TX_STAT_M  ( F_TMT_RDY )
+#define RX_STAT_M  ( F_PKT_RDY | F_LEN_ERR \
+                   | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO )
+
+/* commands */
+#define D_TX_MODE            0x06 /* no tests, detect carrier */
+#define ID_MATCHED           0x02 /* (RX_MODE) */
+#define RECV_ALL             0x03 /* (RX_MODE) */
+#define CONFIG0_DFL          0x5a /* 16bit bus, 4K x 2 Tx queues */
+#define CONFIG0_DFL_1        0x5e /* 16bit bus, 8K x 2 Tx queues */
+#define CONFIG0_RST          0xda /* Data Link Controler off (CONFIG_0) */
+#define CONFIG0_RST_1        0xde /* Data Link Controler off (CONFIG_0) */
+#define BANK_0               0xa0 /* bank 0 (CONFIG_1) */
+#define BANK_1               0xa4 /* bank 1 (CONFIG_1) */
+#define BANK_2               0xa8 /* bank 2 (CONFIG_1) */
+#define CHIP_OFF             0x80 /* contrl chip power off (CONFIG_1) */
+#define DO_TX                0x80 /* do transmit packet */
+#define SEND_PKT             0x81 /* send a packet */
+#define AUTO_MODE            0x07 /* Auto skip packet on 16 col detected */
+#define MANU_MODE            0x03 /* Stop and skip packet on 16 col */
+#define TDK_AUTO_MODE        0x47 /* Auto skip packet on 16 col detected */
+#define TDK_MANU_MODE        0x43 /* Stop and skip packet on 16 col */
+#define INTR_OFF             0x0d /* LAN controler ignores interrupts */
+#define INTR_ON              0x1d /* LAN controler will catch interrupts */
+
+/*======================================================================
+
+    This bit of code is used to avoid unregistering network devices
+    at inappropriate times.  2.2 and later kernels are fairly picky
+    about when this can happen.
+    
+======================================================================*/
+
+static void flush_stale_links(void)
+{
+    dev_link_t *link, *next;
+    for (link = dev_list; link; link = next) {
+       next = link->next;
+       if (link->state & DEV_STALE_LINK)
+           fmvj18x_detach(link);
+    }
+}
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static dev_link_t *fmvj18x_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    int i, ret;
+    
+    DEBUG(0, "fmvj18x_attach()\n");
+    flush_stale_links();
+
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    memset(link, 0, sizeof(struct dev_link_t));
+
+    link->release.function = &fmvj18x_release;
+    link->release.data = (u_long)link;
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 32;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 5;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+       link->irq.IRQInfo2 = irq_mask;
+    else
+       for (i = 0; i < 4; i++)
+           link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->irq.Handler = &fjn_interrupt;
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Make up a FMVJ18x specific data structure */
+    dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+    memset(dev, 0, sizeof(struct net_device));
+    dev->priv = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    memset(dev->priv, 0, sizeof(local_info_t));
+
+    /* The FMVJ18x specific entries in the device structure. */
+    dev->hard_start_xmit = &fjn_start_xmit;
+    dev->set_config = &fjn_config;
+    dev->get_stats = &fjn_get_stats;
+    dev->set_multicast_list = &set_rx_mode;
+    ether_setup(dev);
+    dev->name = ((local_info_t *)dev->priv)->node.dev_name;
+    dev->init = &fmvj18x_init;
+    dev->open = &fjn_open;
+    dev->stop = &fjn_close;
+    dev->tbusy = 0xFF;
+    link->priv = link->irq.Instance = dev;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+       CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+       CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &fmvj18x_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != 0) {
+       cs_error(link->handle, RegisterClient, ret);
+       fmvj18x_detach(link);
+       return NULL;
+    }
+
+    return link;
+} /* fmvj18x_attach */
+
+/*====================================================================*/
+
+static void fmvj18x_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    long flags;
+    
+    DEBUG(0, "fmvj18x_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+       if (*linkp == link) break;
+    if (*linkp == NULL)
+       return;
+
+    save_flags(flags);
+    cli();
+    if (link->state & DEV_RELEASE_PENDING) {
+       del_timer(&link->release);
+       link->state &= ~DEV_RELEASE_PENDING;
+    }
+    restore_flags(flags);
+    
+    if (link->state & DEV_CONFIG) {
+       fmvj18x_release((u_long)link);
+       if (link->state & DEV_STALE_CONFIG) {
+           link->state |= DEV_STALE_LINK;
+           return;
+       }
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+       CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+       struct net_device *dev = link->priv;
+       if (link->dev != NULL)
+           unregister_netdev(dev);
+       if (dev->priv) 
+           kfree(dev->priv);
+       kfree(dev);
+    }
+    kfree(link);
+    
+} /* fmvj18x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void fmvj18x_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    struct net_device *dev;
+    u_short buf[32];
+    int i, last_fn, last_ret;
+    ioaddr_t ioaddr;
+    cardtype_t cardtype;
+    char *card_name = "unknown";
+    u_char *node_id;
+    
+    handle = link->handle;
+    dev =link->priv;
+
+    DEBUG(0, "fmvj18x_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    tuple.TupleData = (u_char *)buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    link->conf.ConfigBase = parse.config.base; 
+    link->conf.Present = parse.config.rmask[0];
+
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    tuple.TupleOffset = 0;
+    if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
+       /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+       CS_CHECK(GetFirstTuple, handle, &tuple);
+       CS_CHECK(GetTupleData, handle, &tuple);
+       CS_CHECK(ParseTuple, handle, &tuple, &parse);
+       link->conf.ConfigIndex = parse.cftable_entry.index;
+       tuple.DesiredTuple = CISTPL_MANFID;
+       CS_CHECK(GetFirstTuple, handle, &tuple);
+       CS_CHECK(GetTupleData, handle, &tuple);
+
+       switch (le16_to_cpu(buf[0])) {
+       case MANFID_TDK:
+           cardtype = TDK;
+           break;
+       case MANFID_CONTEC:
+           cardtype = CONTEC;
+           break;
+       case MANFID_FUJITSU:
+           if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10302)
+               cardtype = MBH10302;
+           else if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10304)
+               cardtype = MBH10304;
+           else
+               cardtype = LA501;
+           break;
+       default:
+           cardtype = MBH10304;
+       }
+    } else {
+       /* old type card */
+       cardtype = MBH10302;
+       link->conf.ConfigIndex = 1;
+    }
+    CS_CHECK(RequestIO, link->handle, &link->io);
+    CS_CHECK(RequestIRQ, link->handle, &link->irq);
+    CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    dev->tbusy = 0;
+    if (register_netdev(dev) != 0) {
+       printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
+       goto failed;
+    }
+
+    ioaddr = dev->base_addr;
+
+    /* Power On chip and select bank 0 */
+    outb(BANK_0, ioaddr + CONFIG_1);
+    /* Reset controler */
+    if( sram_config == 0 ) 
+       outb(CONFIG0_RST, ioaddr + CONFIG_0);
+    else
+       outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+    
+    /* Set hardware address */
+    switch (cardtype) {
+    case MBH10304:
+    case TDK:
+    case LA501:
+    case CONTEC:
+       tuple.DesiredTuple = CISTPL_FUNCE;
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetFirstTuple, handle, &tuple);
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetTupleData, handle, &tuple);
+       if (cardtype == MBH10304) {
+           /* MBH10304's CIS_FUNCE is corrupted */
+           node_id = &(tuple.TupleData[5]);
+           card_name = "FMV-J182";
+       } else {
+           while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) {
+               CS_CHECK(GetNextTuple, handle, &tuple) ;
+               CS_CHECK(GetTupleData, handle, &tuple) ;
+           }
+           node_id = &(tuple.TupleData[2]);
+           if( cardtype == TDK ) {
+               card_name = "TDK LAK-CD021";
+           } else if( cardtype == LA501 ) {
+               card_name = "LA501";
+           } else {
+               card_name = "C-NET(PC)C";
+           }
+       }
+       /* Read MACID from CIS */
+       for (i = 0; i < 6; i++)
+           dev->dev_addr[i] = node_id[i];
+       break;
+    case MBH10302:
+    default:
+       /* Read MACID from register */
+       for (i = 0; i < 6; i++) 
+           dev->dev_addr[i] = inb(ioaddr + MAC_ID + i);
+       card_name = "FMV-J181";
+       break;
+    }
+
+    link->dev = &((local_info_t *)dev->priv)->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    ((struct local_info_t *)dev->priv)->cardtype = cardtype ;
+    /* print current configuration */
+    printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, hw_addr ", 
+          dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", 
+          dev->base_addr, dev->irq);
+    for (i = 0; i < 6; i++)
+       printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    return;
+    
+cs_failed:
+    /* All Card Services errors end up here */
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    fmvj18x_release((u_long)link);
+
+} /* fmvj18x_config */
+/*====================================================================*/
+
+static void fmvj18x_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+
+    /*
+       If the device is currently in use, we won't release until it
+       is actually closed.
+    */
+    if (link->open) {
+       DEBUG(1, "fmvj18x_cs: release postponed, '%s' "
+             "still open\n", link->dev->dev_name);
+       link->state |= DEV_STALE_CONFIG;
+       return;
+    }
+
+    /* Don't bother checking to see if these succeed or not */
+    CardServices(ReleaseWindow, link->win);
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    
+    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+    
+} /* fmvj18x_release */
+
+/*====================================================================*/
+
+static int fmvj18x_event(event_t event, int priority,
+                         event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+
+    DEBUG(1, "fmvj18x_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+       link->state &= ~DEV_PRESENT;
+       if (link->state & DEV_CONFIG) {
+           dev->tbusy = 0xFF; 
+           dev->start = 0;
+           link->release.expires = jiffies + HZ/20;
+           add_timer(&link->release);
+       }
+       break;
+    case CS_EVENT_CARD_INSERTION:
+       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+       fmvj18x_config(link);
+       break;
+    case CS_EVENT_PM_SUSPEND:
+       link->state |= DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+       if (link->state & DEV_CONFIG) {
+           if (link->open) {
+               dev->tbusy = 0xFF; 
+               dev->start = 0;
+           }
+           CardServices(ReleaseConfiguration, link->handle);
+       }
+       break;
+    case CS_EVENT_PM_RESUME:
+       link->state &= ~DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+       if (link->state & DEV_CONFIG) {
+           CardServices(RequestConfiguration, link->handle, &link->conf);
+           if (link->open) {
+               dev->tbusy = 0;
+               dev->start = 1;
+               fjn_reset(dev);
+           }
+       }
+       break;
+    }
+    return 0;
+} /* fmvj18x_event */
+
+static int fmvj18x_init(struct net_device *dev)
+{
+    return 0;
+} /* fmvj18x_init */
+
+/*====================================================================*/
+
+static int __init init_fmvj18x_cs(void)
+{
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+       printk(KERN_NOTICE "fmvj18x: Card Services release "
+              "does not match!\n");
+       return -1;
+    }
+    register_pccard_driver(&dev_info, &fmvj18x_attach, &fmvj18x_detach);
+    return 0;
+}
+
+static void __exit exit_fmvj18x_cs(void)
+{
+    DEBUG(0, "fmvj18x_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+       fmvj18x_detach(dev_list);
+}
+
+module_init(init_fmvj18x_cs);
+module_exit(exit_fmvj18x_cs);
+
+/*====================================================================*/
+
+static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = (struct net_device *)dev_id;
+    ioaddr_t ioaddr;
+    local_info_t *lp;
+    unsigned short tx_stat, rx_stat;
+
+    if (dev == NULL) {
+        printk(KERN_NOTICE "fjn_interrupt(): irq %d for "
+              "unknown device.\n", irq);
+        return;
+    }
+    if (dev->interrupt) {
+        printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
+              dev->name);
+        return;
+    }
+    dev->interrupt = 1;
+    lp = (struct local_info_t *)dev->priv;
+    ioaddr = dev->base_addr;
+
+    /* avoid multiple interrupts */
+    outw(0x0000, ioaddr + TX_INTR);
+
+    /* wait for a while */
+    udelay(1);
+
+    /* get status */
+    tx_stat = inb(ioaddr + TX_STATUS);
+    rx_stat = inb(ioaddr + RX_STATUS);
+
+    /* clear status */
+    outb(tx_stat, ioaddr + TX_STATUS);
+    outb(rx_stat, ioaddr + RX_STATUS);
+    
+    if (fmvj18x_debug > 4) {
+        printk(KERN_DEBUG "%s: interrupt, rx_status %02x.\n",
+              dev->name, rx_stat);
+        printk(KERN_DEBUG "               tx_status %02x.\n",
+              tx_stat);
+    }
+    
+    if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+       /* there is packet(s) in rx buffer */
+       fjn_rx(dev);
+    }
+    if (tx_stat & F_TMT_RDY) {
+       lp->stats.tx_packets += lp->sent ;
+        lp->sent = 0 ;
+       if (lp->tx_queue) {
+           outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+           lp->sent = lp->tx_queue ;
+           lp->tx_queue = 0;
+           lp->tx_queue_len = 0;
+           dev->trans_start = jiffies;
+           dev->tbusy = 0;
+           mark_bh(NET_BH);    /* Inform upper layers. */
+       } else {
+           lp->tx_started = 0;
+           dev->tbusy = 0;
+           mark_bh(NET_BH);    /* Inform upper layers. */
+       }
+    }
+    if (fmvj18x_debug > 4) {
+        printk(KERN_DEBUG "%s: exiting interrupt,\n", dev->name);
+        printk(KERN_DEBUG "    tx_status %02x, rx_status %02x.\n",
+              tx_stat, rx_stat);
+    }
+
+    dev->interrupt = 0;
+    outb(D_TX_INTR, ioaddr + TX_INTR);
+    outb(D_RX_INTR, ioaddr + RX_INTR);
+
+    return;
+} /* fjn_interrupt */
+
+/*====================================================================*/
+
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+
+    if (dev->tbusy) {
+       /* If we get here, some higher level has decided we are broken.
+          There should really be a "kick me" function call instead. */
+       int tickssofar = jiffies - dev->trans_start;
+       if (tickssofar < 10)
+           return 1;
+       printk(KERN_NOTICE "%s: transmit timed out with status %04x, %s?\n",
+              dev->name, htons(inw(ioaddr + TX_STATUS)),
+              inb(ioaddr + TX_STATUS) & F_TMT_RDY
+              ? "IRQ conflict" : "network cable problem");
+       printk(KERN_NOTICE "%s: timeout registers: %04x %04x %04x "
+              "%04x %04x %04x %04x %04x.\n",
+              dev->name, htons(inw(ioaddr + 0)),
+              htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)),
+              htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)),
+              htons(inw(ioaddr +10)), htons(inw(ioaddr +12)),
+              htons(inw(ioaddr +14)));
+       lp->stats.tx_errors++;
+       /* ToDo: We should try to restart the adaptor... */
+       cli();
+
+       fjn_reset(dev);
+
+       lp->tx_started = 0;
+       lp->tx_queue = 0;
+       lp->tx_queue_len = 0;
+       lp->sent = 0;
+       lp->open_time = jiffies;
+       dev->interrupt = 0;
+       dev->tbusy = 0;
+       dev->start = 1;
+    
+       sti();
+    }
+
+    /* Block a timer-based transmit from overlapping.  This could better be
+       done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+    if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+       printk(KERN_NOTICE "%s: Transmitter access conflict.\n", dev->name);
+    else {
+       short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+       unsigned char *buf = skb->data;
+
+       if (length > ETH_FRAME_LEN) {
+           if (fmvj18x_debug)
+               printk(KERN_NOTICE "%s: Attempting to send a large packet"
+                      " (%d bytes).\n", dev->name, length);
+           return 1;
+       }
+
+       if (fmvj18x_debug > 4)
+           printk(KERN_DEBUG "%s: Transmitting a packet of length %lu.\n",
+                  dev->name, (unsigned long)skb->len);
+       lp->stats.tx_bytes += skb->len;
+
+       /* Disable both interrupts. */
+       outw(0x0000, ioaddr + TX_INTR);
+
+       /* wait for a while */
+       udelay(1);
+
+       outw(length, ioaddr + DATAPORT);
+       outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+       lp->tx_queue++;
+       lp->tx_queue_len += ((length+3) & ~1);
+
+       if (lp->tx_started == 0) {
+           /* If the Tx is idle, always trigger a transmit. */
+           outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+           lp->sent = lp->tx_queue ;
+           lp->tx_queue = 0;
+           lp->tx_queue_len = 0;
+           dev->trans_start = jiffies;
+           lp->tx_started = 1;
+           dev->tbusy = 0;
+       } else {
+           if( sram_config == 0 ) {
+               if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
+                   /* Yes, there is room for one more packet. */
+                   dev->tbusy = 0;
+           } else {
+               if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && 
+                                               lp->tx_queue < 127 )
+                   /* Yes, there is room for one more packet. */
+                   dev->tbusy = 0;
+           }
+       }
+
+       /* Re-enable interrupts */
+       outb(D_TX_INTR, ioaddr + TX_INTR);
+       outb(D_RX_INTR, ioaddr + RX_INTR);
+    }
+    dev_kfree_skb (skb);
+
+    return 0;
+} /* fjn_start_xmit */
+
+/*====================================================================*/
+
+static void fjn_reset(struct net_device *dev)
+{
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int i;
+
+    if (fmvj18x_debug > 4) {
+       printk(KERN_DEBUG "fjn_reset(%s) called.\n",dev->name);
+    }
+
+    /* Power On chip and select bank 0 */
+    outb(BANK_0, ioaddr + CONFIG_1);
+    /* Reset buffers */
+    if( sram_config == 0 ) 
+       outb(CONFIG0_RST, ioaddr + CONFIG_0);
+    else
+       outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+
+    /* Set Tx modes */
+    outb(D_TX_MODE, ioaddr + TX_MODE);
+    /* set Rx modes */
+    outb(ID_MATCHED, ioaddr + RX_MODE);
+
+    /* Set hardware address */
+    for (i = 0; i < 6; i++) 
+        outb(dev->dev_addr[i], ioaddr + NODE_ID + i);
+
+    if (fmvj18x_debug > 4) {
+       printk(KERN_DEBUG "node id: ");
+       for (i = 0; i < 6; i++) 
+           printk("%02X ",inb(ioaddr + NODE_ID + i));
+       printk("\n");
+    }
+    /* Switch to bank 1 */
+    outb(BANK_1, ioaddr + CONFIG_1);
+
+    /* set the multicast table to accept none. */
+    for (i = 0; i < 6; i++) 
+        outb(0x00, ioaddr + MAR_ADR + i);
+
+    /* Switch to bank 2 (runtime mode) */
+    outb(BANK_2, ioaddr + CONFIG_1);
+
+    /* set 16col ctrl bits */
+    if( lp->cardtype == TDK ) 
+        outb(TDK_AUTO_MODE, ioaddr + COL_CTRL);
+    else
+        outb(AUTO_MODE, ioaddr + COL_CTRL);
+
+    /* clear Reserved Regs */
+    outb(0x00, ioaddr + BMPR12);
+    outb(0x00, ioaddr + BMPR13);
+
+    /* reset Skip packet reg. */
+    outb(0x01, ioaddr + RX_SKIP);
+
+    /* Enable Tx and Rx */
+    if( sram_config == 0 )
+       outb(CONFIG0_DFL, ioaddr + CONFIG_0);
+    else
+       outb(CONFIG0_DFL_1, ioaddr + CONFIG_0);
+
+    /* Init receive pointer ? */
+    inw(ioaddr + DATAPORT);
+    inw(ioaddr + DATAPORT);
+
+    /* Clear all status */
+    outb(0xff, ioaddr + TX_STATUS);
+    outb(0xff, ioaddr + RX_STATUS);
+
+    if( lp->cardtype != TDK ) 
+               outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+    /* Turn on Rx interrupts */
+    outb(D_TX_INTR, ioaddr + TX_INTR);
+    outb(D_RX_INTR, ioaddr + RX_INTR);
+
+    /* Turn on interrupts from LAN card controler */
+    if( lp->cardtype != TDK ) 
+               outb(INTR_ON, ioaddr + LAN_CTRL);
+} /* fjn_reset */
+
+/*====================================================================*/
+
+static void fjn_rx(struct net_device *dev)
+{
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int boguscount = 10;       /* 5 -> 10: by agy 19940922 */
+
+    if (fmvj18x_debug > 4)
+        printk(KERN_DEBUG "%s: in rx_packet(), rx_status %02x.\n",
+               dev->name, inb(ioaddr + RX_STATUS));
+
+    while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+       u_short status = inw(ioaddr + DATAPORT);
+
+       if (fmvj18x_debug > 4)
+           printk(KERN_DEBUG "%s: Rxing packet mode %02x status %04x.\n",
+                  dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+       if (status == 0) {
+           outb(F_SKP_PKT, ioaddr + RX_SKIP);
+           break;
+       }
+#endif
+       if ((status & 0xF0) != 0x20) {  /* There was an error. */
+           lp->stats.rx_errors++;
+           if (status & F_LEN_ERR) lp->stats.rx_length_errors++;
+           if (status & F_ALG_ERR) lp->stats.rx_frame_errors++;
+           if (status & F_CRC_ERR) lp->stats.rx_crc_errors++;
+           if (status & F_OVR_FLO) lp->stats.rx_over_errors++;
+       } else {
+           u_short pkt_len = inw(ioaddr + DATAPORT);
+           /* Malloc up new buffer. */
+           struct sk_buff *skb;
+
+           if (pkt_len > 1550) {
+               printk(KERN_NOTICE "%s: The FMV-18x claimed a very "
+                      "large packet, size %d.\n", dev->name, pkt_len);
+               outb(F_SKP_PKT, ioaddr + RX_SKIP);
+               lp->stats.rx_errors++;
+               break;
+           }
+           skb = dev_alloc_skb(pkt_len+2);
+           if (skb == NULL) {
+               printk(KERN_NOTICE "%s: Memory squeeze, dropping "
+                      "packet (len %d).\n", dev->name, pkt_len);
+               outb(F_SKP_PKT, ioaddr + RX_SKIP);
+               lp->stats.rx_dropped++;
+               break;
+           }
+           skb->dev = dev;
+
+           skb_reserve(skb, 2);
+           insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
+                   (pkt_len + 1) >> 1);
+           skb->protocol = eth_type_trans(skb, dev);
+
+           if (fmvj18x_debug > 5) {
+               int i;
+               printk(KERN_DEBUG "%s: Rxed packet of length %d: ",
+                      dev->name, pkt_len);
+               for (i = 0; i < 14; i++)
+                   printk(" %02x", skb->data[i]);
+               printk(".\n");
+           }
+
+           netif_rx(skb);
+           lp->stats.rx_packets++;
+           lp->stats.rx_bytes += skb->len;
+       }
+       if (--boguscount <= 0)
+           break;
+    }
+
+    /* If any worth-while packets have been received, dev_rint()
+          has done a mark_bh(NET_BH) for us and will work on them
+          when we get to the bottom-half routine. */
+/*
+    if( lp->cardtype != TDK ) {
+       int i;
+       for (i = 0; i < 20; i++) {
+           if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP)
+               break;
+           (void)inw(ioaddr + DATAPORT);  /+ dummy status read +/
+           outb(F_SKP_PKT, ioaddr + RX_SKIP);
+       }
+
+       if (fmvj18x_debug > 5 && i > 0)
+           printk(KERN_DEBUG "%s: Exint Rx packet with mode %02x after"
+                  " %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
+    }
+*/
+
+    return;
+} /* fjn_rx */
+
+/*====================================================================*/
+
+static int fjn_config(struct net_device *dev, struct ifmap *map){
+    return 0;
+} /* fjn_config */
+
+static int fjn_open(struct net_device *dev)
+{
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    dev_link_t *link;
+
+    if (fmvj18x_debug > 4)
+        printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);
+
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    if (!DEV_OK(link))
+       return -ENODEV;
+    
+    link->open++;
+    
+    fjn_reset(dev);
+    
+    lp->tx_started = 0;
+    lp->tx_queue = 0;
+    lp->tx_queue_len = 0;
+    lp->open_time = jiffies;
+    dev->interrupt = 0;
+    dev->tbusy = 0;
+    dev->start = 1;
+    
+    MOD_INC_USE_COUNT;
+
+    return 0;
+} /* fjn_open */
+
+/*====================================================================*/
+
+static int fjn_close(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    dev_link_t *link;
+
+    if (fmvj18x_debug > 4)
+        printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);
+
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    if (link == NULL)
+       return -ENODEV;
+    
+    if (fmvj18x_debug > 2)
+       printk(KERN_DEBUG "%s: shutting down ethercard.\n", dev->name);
+
+    ((struct local_info_t *)dev->priv)->open_time = 0;
+
+    dev->tbusy = 1;
+    dev->start = 0;
+
+    /* Set configuration register 0 to disable Tx and Rx. */
+    if( sram_config == 0 ) 
+       outb(CONFIG0_RST ,ioaddr + CONFIG_0);
+    else
+       outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0);
+
+    /* Update the statistics -- ToDo. */
+
+    /* Power-down the chip.  Green, green, green! */
+    outb(CHIP_OFF ,ioaddr + CONFIG_1);
+
+    /* Set the ethernet adaptor disable IRQ */
+    if( lp->cardtype != TDK ) 
+               outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+    link->open--;
+    dev->start = 0;
+    if (link->state & DEV_STALE_CONFIG) {
+       link->release.expires = jiffies + HZ/20;
+       link->state |= DEV_RELEASE_PENDING;
+       add_timer(&link->release);
+    }
+    MOD_DEC_USE_COUNT;
+
+    return 0;
+} /* fjn_close */
+
+/*====================================================================*/
+
+static struct net_device_stats *fjn_get_stats(struct net_device *dev)
+{
+    local_info_t *lp = (local_info_t *)dev->priv;
+    return &lp->stats;
+} /* fjn_get_stats */
+
+/*====================================================================*/
+
+/*
+  Set the multicast/promiscuous mode for this adaptor.
+*/
+
+/* The little-endian AUTODIN II 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 unsigned ether_crc_le(int length, unsigned char *data)
+{
+    unsigned int 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 void set_rx_mode(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    struct local_info_t *lp = (struct local_info_t *)dev->priv;
+    unsigned char mc_filter[8];                 /* Multicast hash filter */
+    long flags;
+    int i;
+    
+    if (dev->flags & IFF_PROMISC) {
+       /* Unconditionally log net taps. */
+       printk("%s: Promiscuous mode enabled.\n", dev->name);
+       memset(mc_filter, 0xff, sizeof(mc_filter));
+       outb(3, ioaddr + RX_MODE);      /* Enable promiscuous mode */
+    } else if (dev->mc_count > MC_FILTERBREAK
+              ||  (dev->flags & IFF_ALLMULTI)) {
+       /* Too many to filter perfectly -- accept all multicasts. */
+       memset(mc_filter, 0xff, sizeof(mc_filter));
+       outb(2, ioaddr + RX_MODE);      /* Use normal mode. */
+    } else if (dev->mc_count == 0) {
+       memset(mc_filter, 0x00, sizeof(mc_filter));
+       outb(1, ioaddr + RX_MODE);      /* Ignore almost all multicasts. */
+    } else {
+       struct dev_mc_list *mclist;
+       int i;
+       
+       memset(mc_filter, 0, sizeof(mc_filter));
+       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) & 0x3f,
+                   mc_filter);
+    }
+    
+    save_flags(flags);
+    cli();
+    if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
+       int saved_bank = inb(ioaddr + CONFIG_1);
+       /* Switch to bank 1 and set the multicast table. */
+       outb(0xe4, ioaddr + CONFIG_1);
+       for (i = 0; i < 8; i++)
+           outb(mc_filter[i], ioaddr + 8 + i);
+       memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
+       outb(saved_bank, ioaddr + CONFIG_1);
+    }
+    restore_flags(flags);
+}
diff --git a/drivers/net/pcmcia/i82593.h b/drivers/net/pcmcia/i82593.h
new file mode 100644 (file)
index 0000000..6d66baf
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Definitions for Intel 82593 CSMA/CD Core LAN Controller
+ * The definitions are taken from the 1992 users manual with Intel
+ * order number 297125-001.
+ *
+ * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
+ *
+ * Copyright 1994, Anders Klemets <klemets@it.kth.se>
+ *
+ * This software may be freely distributed for noncommercial purposes
+ * as long as this notice is retained.
+ * 
+ * HISTORY
+ * i82593.h,v
+ * Revision 1.1  1996/07/17 15:23:12  root
+ * Initial revision
+ *
+ * Revision 1.3  1995/04/05  15:13:58  adj
+ * Initial alpha release
+ *
+ * Revision 1.2  1994/06/16  23:57:31  klemets
+ * Mirrored all the fields in the configuration block.
+ *
+ * Revision 1.1  1994/06/02  20:25:34  klemets
+ * Initial revision
+ *
+ *
+ */
+#ifndef        _I82593_H
+#define        _I82593_H
+
+/* Intel 82593 CSMA/CD Core LAN Controller */
+
+/* Port 0 Command Register definitions */
+
+/* Execution operations */
+#define OP0_NOP                        0       /* CHNL = 0 */
+#define OP0_SWIT_TO_PORT_1     0       /* CHNL = 1 */
+#define OP0_IA_SETUP           1
+#define OP0_CONFIGURE          2
+#define OP0_MC_SETUP           3
+#define OP0_TRANSMIT           4
+#define OP0_TDR                        5
+#define OP0_DUMP               6
+#define OP0_DIAGNOSE           7
+#define OP0_TRANSMIT_NO_CRC    9
+#define OP0_RETRANSMIT         12
+#define OP0_ABORT              13
+/* Reception operations */
+#define OP0_RCV_ENABLE         8
+#define OP0_RCV_DISABLE                10
+#define OP0_STOP_RCV           11
+/* Status pointer control operations */
+#define OP0_FIX_PTR            15      /* CHNL = 1 */
+#define OP0_RLS_PTR            15      /* CHNL = 0 */
+#define OP0_RESET              14
+
+#define CR0_CHNL               (1 << 4)        /* 0=Channel 0, 1=Channel 1 */
+#define CR0_STATUS_0           0x00
+#define CR0_STATUS_1           0x20
+#define CR0_STATUS_2           0x40
+#define CR0_STATUS_3           0x60
+#define CR0_INT_ACK            (1 << 7)        /* 0=No ack, 1=acknowledge */
+
+/* Port 0 Status Register definitions */
+
+#define SR0_NO_RESULT          0               /* dummy */
+#define SR0_EVENT_MASK         0x0f
+#define SR0_IA_SETUP_DONE      1
+#define SR0_CONFIGURE_DONE     2
+#define SR0_MC_SETUP_DONE      3
+#define SR0_TRANSMIT_DONE      4
+#define SR0_TDR_DONE           5
+#define SR0_DUMP_DONE          6
+#define SR0_DIAGNOSE_PASSED    7
+#define SR0_TRANSMIT_NO_CRC_DONE 9
+#define SR0_RETRANSMIT_DONE    12
+#define SR0_EXECUTION_ABORTED  13
+#define SR0_END_OF_FRAME       8
+#define SR0_RECEPTION_ABORTED  10
+#define SR0_DIAGNOSE_FAILED    15
+#define SR0_STOP_REG_HIT       11
+
+#define SR0_CHNL               (1 << 4)
+#define SR0_EXECUTION          (1 << 5)
+#define SR0_RECEPTION          (1 << 6)
+#define SR0_INTERRUPT          (1 << 7)
+#define SR0_BOTH_RX_TX         (SR0_EXECUTION | SR0_RECEPTION)
+
+#define SR3_EXEC_STATE_MASK    0x03
+#define SR3_EXEC_IDLE          0
+#define SR3_TX_ABORT_IN_PROGRESS 1
+#define SR3_EXEC_ACTIVE                2
+#define SR3_ABORT_IN_PROGRESS  3
+#define SR3_EXEC_CHNL          (1 << 2)
+#define SR3_STP_ON_NO_RSRC     (1 << 3)
+#define SR3_RCVING_NO_RSRC     (1 << 4)
+#define SR3_RCV_STATE_MASK     0x60
+#define SR3_RCV_IDLE           0x00
+#define SR3_RCV_READY          0x20
+#define SR3_RCV_ACTIVE         0x40
+#define SR3_RCV_STOP_IN_PROG   0x60
+#define SR3_RCV_CHNL           (1 << 7)
+
+/* Port 1 Command Register definitions */
+
+#define OP1_NOP                        0
+#define OP1_SWIT_TO_PORT_0     1
+#define OP1_INT_DISABLE                2
+#define OP1_INT_ENABLE         3
+#define OP1_SET_TS             5
+#define OP1_RST_TS             7
+#define OP1_POWER_DOWN         8
+#define OP1_RESET_RING_MNGMT   11
+#define OP1_RESET              14
+#define OP1_SEL_RST            15
+
+#define CR1_STATUS_4           0x00
+#define CR1_STATUS_5           0x20
+#define CR1_STATUS_6           0x40
+#define CR1_STOP_REG_UPDATE    (1 << 7)
+
+/* Receive frame status bits */
+
+#define        RX_RCLD                 (1 << 0)
+#define RX_IA_MATCH            (1 << 1)
+#define        RX_NO_AD_MATCH          (1 << 2)
+#define RX_NO_SFD              (1 << 3)
+#define RX_SRT_FRM             (1 << 7)
+#define RX_OVRRUN              (1 << 8)
+#define RX_ALG_ERR             (1 << 10)
+#define RX_CRC_ERR             (1 << 11)
+#define RX_LEN_ERR             (1 << 12)
+#define RX_RCV_OK              (1 << 13)
+#define RX_TYP_LEN             (1 << 15)
+
+/* Transmit status bits */
+
+#define TX_NCOL_MASK           0x0f
+#define TX_FRTL                        (1 << 4)
+#define TX_MAX_COL             (1 << 5)
+#define TX_HRT_BEAT            (1 << 6)
+#define TX_DEFER               (1 << 7)
+#define TX_UND_RUN             (1 << 8)
+#define TX_LOST_CTS            (1 << 9)
+#define TX_LOST_CRS            (1 << 10)
+#define TX_LTCOL               (1 << 11)
+#define TX_OK                  (1 << 13)
+#define TX_COLL                        (1 << 15)
+
+struct i82593_conf_block {
+  u_char fifo_limit : 4,
+        forgnesi   : 1,
+        fifo_32    : 1,
+        d6mod      : 1,
+        throttle_enb : 1;
+  u_char throttle   : 6,
+        cntrxint   : 1,
+        contin     : 1;
+  u_char addr_len   : 3,
+        acloc      : 1,
+        preamb_len : 2,
+        loopback   : 2;
+  u_char lin_prio   : 3,
+        tbofstop   : 1,
+        exp_prio   : 3,
+        bof_met    : 1;
+  u_char           : 4,
+        ifrm_spc   : 4;
+  u_char           : 5,
+        slottim_low : 3;
+  u_char slottim_hi : 3,
+                   : 1,
+        max_retr   : 4;
+  u_char prmisc     : 1,
+        bc_dis     : 1,
+                   : 1,
+        crs_1      : 1,
+        nocrc_ins  : 1,
+        crc_1632   : 1,
+                   : 1,
+        crs_cdt    : 1;
+  u_char cs_filter  : 3,
+        crs_src    : 1,
+        cd_filter  : 3,
+                   : 1;
+  u_char           : 2,
+        min_fr_len : 6;
+  u_char lng_typ    : 1,
+        lng_fld    : 1,
+        rxcrc_xf   : 1,
+        artx       : 1,
+        sarec      : 1,
+        tx_jabber  : 1,        /* why is this called max_len in the manual? */
+        hash_1     : 1,
+        lbpkpol    : 1;
+  u_char           : 6,
+        fdx        : 1,
+                   : 1;
+  u_char dummy_6    : 6,       /* supposed to be ones */
+        mult_ia    : 1,
+        dis_bof    : 1;
+  u_char dummy_1    : 1,       /* supposed to be one */
+        tx_ifs_retrig : 2,
+        mc_all     : 1,
+        rcv_mon    : 2,
+        frag_acpt  : 1,
+        tstrttrs   : 1;
+  u_char fretx     : 1,
+        runt_eop   : 1,
+        hw_sw_pin  : 1,
+        big_endn   : 1,
+        syncrqs    : 1,
+        sttlen     : 1,
+        tx_eop     : 1,
+        rx_eop     : 1;
+  u_char rbuf_size  : 5,
+        rcvstop    : 1,
+                   : 2;
+};
+
+#define I82593_MAX_MULTICAST_ADDRESSES 128     /* Hardware hashed filter */
+
+#endif _I82593_H
diff --git a/drivers/net/pcmcia/netwave_cs.c b/drivers/net/pcmcia/netwave_cs.c
new file mode 100644 (file)
index 0000000..0e9a104
--- /dev/null
@@ -0,0 +1,1709 @@
+/*********************************************************************
+ *                
+ * Filename:      netwave_cs.c
+ * Version:       0.4.1
+ * Description:   Netwave AirSurfer Wireless LAN PC Card driver
+ * Status:        Experimental.
+ * Authors:       John Markus Bjørndalen <johnm@cs.uit.no>
+ *                Dag Brattli <dagb@cs.uit.no>
+ *                David Hinds <dhinds@hyper.stanford.edu>
+ * Created at:    A long time ago!
+ * Modified at:   Mon Nov 10 11:54:37 1997
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1997 University of Tromsø, Norway
+ *
+ * Revision History:
+ *
+ *   08-Nov-97 15:14:47   John Markus Bjørndalen <johnm@cs.uit.no>
+ *    - Fixed some bugs in netwave_rx and cleaned it up a bit. 
+ *      (One of the bugs would have destroyed packets when receiving
+ *      multiple packets per interrupt). 
+ *    - Cleaned up parts of newave_hw_xmit. 
+ *    - A few general cleanups. 
+ *   24-Oct-97 13:17:36   Dag Brattli <dagb@cs.uit.no>
+ *    - Fixed netwave_rx receive function (got updated docs)
+ *   Others:
+ *    - Changed name from xircnw to netwave, take a look at 
+ *      http://www.netwave-wireless.com
+ *    - Some reorganizing of the code
+ *    - Removed possible race condition between interrupt handler and transmit
+ *      function
+ *    - Started to add wireless extensions, but still needs some coding
+ *    - Added watchdog for better handling of transmission timeouts 
+ *      (hopefully this works better)
+ ********************************************************************/
+
+/* To have statistics (just packets sent) define this */
+#undef NETWAVE_STATS
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.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/string.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+#define NETWAVE_REGOFF         0x8000
+/* The Netwave IO registers, offsets to iobase */
+#define NETWAVE_REG_COR        0x0
+#define NETWAVE_REG_CCSR       0x2
+#define NETWAVE_REG_ASR        0x4
+#define NETWAVE_REG_IMR        0xa
+#define NETWAVE_REG_PMR        0xc
+#define NETWAVE_REG_IOLOW      0x6
+#define NETWAVE_REG_IOHI       0x7
+#define NETWAVE_REG_IOCONTROL  0x8
+#define NETWAVE_REG_DATA       0xf
+/* The Netwave Extended IO registers, offsets to RamBase */
+#define NETWAVE_EREG_ASCC      0x114
+#define NETWAVE_EREG_RSER      0x120
+#define NETWAVE_EREG_RSERW     0x124
+#define NETWAVE_EREG_TSER      0x130
+#define NETWAVE_EREG_TSERW     0x134
+#define NETWAVE_EREG_CB        0x100
+#define NETWAVE_EREG_SPCQ      0x154
+#define NETWAVE_EREG_SPU       0x155
+#define NETWAVE_EREG_LIF       0x14e
+#define NETWAVE_EREG_ISPLQ     0x156
+#define NETWAVE_EREG_HHC       0x158
+#define NETWAVE_EREG_NI        0x16e
+#define NETWAVE_EREG_MHS       0x16b
+#define NETWAVE_EREG_TDP       0x140
+#define NETWAVE_EREG_RDP       0x150
+#define NETWAVE_EREG_PA        0x160
+#define NETWAVE_EREG_EC        0x180
+#define NETWAVE_EREG_CRBP      0x17a
+#define NETWAVE_EREG_ARW       0x166
+
+/*
+ * Commands used in the extended command buffer
+ * NETWAVE_EREG_CB (0x100-0x10F) 
+ */
+#define NETWAVE_CMD_NOP        0x00
+#define NETWAVE_CMD_SRC        0x01
+#define NETWAVE_CMD_STC        0x02
+#define NETWAVE_CMD_AMA        0x03
+#define NETWAVE_CMD_DMA        0x04
+#define NETWAVE_CMD_SAMA       0x05
+#define NETWAVE_CMD_ER         0x06
+#define NETWAVE_CMD_DR         0x07
+#define NETWAVE_CMD_TL         0x08
+#define NETWAVE_CMD_SRP        0x09
+#define NETWAVE_CMD_SSK        0x0a
+#define NETWAVE_CMD_SMD        0x0b
+#define NETWAVE_CMD_SAPD       0x0c
+#define NETWAVE_CMD_SSS        0x11
+/* End of Command marker */
+#define NETWAVE_CMD_EOC        0x00
+
+/* ASR register bits */
+#define NETWAVE_ASR_RXRDY   0x80
+#define NETWAVE_ASR_TXBA    0x01
+
+#define TX_TIMEOUT  20
+#define WATCHDOG_JIFFIES 32
+
+static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */
+static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */
+
+static const unsigned int corConfIENA   = 0x01; /* Interrupt enable */
+static const unsigned int corConfLVLREQ = 0x40; /* Keep high */
+
+static const unsigned int rxConfRxEna  = 0x80; /* Receive Enable */
+static const unsigned int rxConfMAC    = 0x20; /* MAC host receive mode*/ 
+static const unsigned int rxConfPro    = 0x10; /* Promiscuous */
+static const unsigned int rxConfAMP    = 0x08; /* Accept Multicast Packets */
+static const unsigned int rxConfBcast  = 0x04; /* Accept Broadcast Packets */
+
+static const unsigned int txConfTxEna  = 0x80; /* Transmit Enable */
+static const unsigned int txConfMAC    = 0x20; /* Host sends MAC mode */
+static const unsigned int txConfEUD    = 0x10; /* Enable Uni-Data packets */
+static const unsigned int txConfKey    = 0x02; /* Scramble data packets */
+static const unsigned int txConfLoop   = 0x01; /* Loopback mode */
+
+/*static int netwave_debug = 0;*/
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+static dev_info_t dev_info = "netwave_cs";
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Choose the domain, default is 0x100 */
+static u_int  domain = 0x100;
+
+/* Scramble key, range from 0x0 to 0xffff.  
+ * 0x0 is no scrambling. 
+ */
+static u_int  scramble_key = 0x0;
+
+/* Shared memory speed, in ns. The documentation states that 
+ * the card should not be read faster than every 400ns. 
+ * This timing should be provided by the HBA. If it becomes a 
+ * problem, try setting mem_speed to 400. 
+ */
+static int mem_speed = 0;
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(domain, "i");
+MODULE_PARM(scramble_key, "i");
+MODULE_PARM(mem_speed, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+/* PCMCIA (Card Services) related functions */
+static void netwave_release(u_long arg);     /* Card removal */
+static int  netwave_event(event_t event, int priority, 
+                                             event_callback_args_t *args);
+static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card 
+                                                                                                          insertion */
+static dev_link_t *netwave_attach(void);     /* Create instance */
+static void netwave_detach(dev_link_t *);    /* Destroy instance */
+static void netwave_flush_stale_links(void);        /* Destroy all staled instances */
+
+/* Hardware configuration */
+static void netwave_doreset(ioaddr_t iobase, u_char* ramBase);
+static void netwave_reset(struct net_device *dev);
+
+/* Misc device stuff */
+static int netwave_open(struct net_device *dev);  /* Open the device */
+static int netwave_close(struct net_device *dev); /* Close the device */
+static int netwave_config(struct net_device *dev, struct ifmap *map);
+
+/* Packet transmission and Packet reception */
+static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev);
+static int netwave_rx( struct net_device *dev);
+
+/* Interrupt routines */
+static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void netwave_watchdog(u_long);  /* Transmission watchdog */
+
+/* Statistics */
+static void update_stats(struct net_device *dev);
+static struct enet_statistics *netwave_get_stats(struct net_device *dev);
+
+/* Wireless extensions */
+#ifdef WIRELESS_EXT
+static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev);
+#endif
+static int netwave_ioctl(struct net_device *, struct ifreq *, int);
+
+static void set_multicast_list(struct net_device *dev);
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+
+#define SIOCGIPSNAP    SIOCDEVPRIVATE          /* Site Survey Snapshot */
+/*#define SIOCGIPQTHR  SIOCDEVPRIVATE + 1*/
+
+#define MAX_ESA 10
+
+typedef struct net_addr {
+    u_char addr48[6];
+} net_addr;
+
+struct site_survey {
+    u_short length;
+    u_char  struct_revision;
+    u_char  roaming_state;
+       
+    u_char  sp_existsFlag;
+    u_char  sp_link_quality;
+    u_char  sp_max_link_quality;
+    u_char  linkQualityGoodFairBoundary;
+    u_char  linkQualityFairPoorBoundary;
+    u_char  sp_utilization;
+    u_char  sp_goodness;
+    u_char  sp_hotheadcount;
+    u_char  roaming_condition;
+       
+    net_addr sp;
+    u_char   numAPs;
+    net_addr nearByAccessPoints[MAX_ESA];
+};     
+   
+typedef struct netwave_private {
+    dev_node_t node;
+    u_char     *ramBase;
+    int        timeoutCounter;
+    int        lastExec;
+    struct timer_list      watchdog;   /* To avoid blocking state */
+    struct site_survey     nss;
+    struct enet_statistics stats;
+#ifdef WIRELESS_EXT
+    struct iw_statistics   iw_stats;    /* Wireless stats */
+#endif
+} netwave_private;
+
+#ifdef NETWAVE_STATS
+static struct enet_statistics *netwave_get_stats(struct net_device *dev);
+#endif
+
+/*
+ * The Netwave card is little-endian, so won't work for big endian
+ * systems.
+ */
+static inline unsigned short get_uint16(u_char* staddr) 
+{
+    return readw(staddr); /* Return only 16 bits */
+}
+
+static inline short get_int16(u_char* staddr)
+{
+    return readw(staddr);
+}
+
+/**************************************************************************/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/* 
+ * Wait until the WOC (Write Operation Complete) bit in the 
+ * ASR (Adapter Status Register) is asserted. 
+ * This should have aborted if it takes too long time. 
+ */
+static inline void wait_WOC(unsigned int iobase)
+{
+    /* Spin lock */
+    while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; 
+}
+
+#ifdef WIRELESS_EXT
+static void netwave_snapshot(netwave_private *priv, u_char *ramBase, 
+                            ioaddr_t iobase) { 
+    u_short resultBuffer;
+
+    /* if time since last snapshot is > 1 sec. (100 jiffies?)  then take 
+     * new snapshot, else return cached data. This is the recommended rate.  
+     */
+    if ( jiffies - priv->lastExec > 100) { 
+       /* Take site survey  snapshot */ 
+       /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies -
+         priv->lastExec); */
+       wait_WOC(iobase); 
+       writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); 
+       writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); 
+       wait_WOC(iobase); 
+
+       /* Get result and copy to cach */ 
+       resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); 
+       copy_from_pc( &priv->nss, ramBase+resultBuffer, 
+                     sizeof(struct site_survey)); 
+    } 
+}
+#endif
+
+#ifdef WIRELESS_EXT
+/*
+ * Function netwave_get_wireless_stats (dev)
+ *
+ *    Wireless extensions statistics
+ *
+ */
+static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev)
+{      
+    unsigned long flags;
+    ioaddr_t iobase = dev->base_addr;
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    struct iw_statistics* wstats;
+       
+    wstats = &priv->iw_stats;
+
+    save_flags(flags);
+    cli();
+       
+    netwave_snapshot( priv, ramBase, iobase);
+
+    wstats->status = priv->nss.roaming_state;
+    wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); 
+    wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ);
+    wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f;
+    wstats->discard.nwid = 0L;
+    wstats->discard.code = 0L;
+    wstats->discard.misc = 0L;
+
+    restore_flags(flags);
+    
+    return &priv->iw_stats;
+}
+#endif
+
+/*
+ * Function netwave_init (dev)
+ *
+ *    We never need to do anything when a device is "initialized"
+ *    by the net software, because we only register already-found cards.
+ */
+int netwave_init(struct net_device *dev)
+{
+    /* We do all the initialization of this in netwave_attach instead */
+    return 0;
+}
+
+/*
+ * Function netwave_attach (void)
+ *
+ *     Creates an "instance" of the driver, allocating local data 
+ *     structures for one device.  The device is registered with Card 
+ *     Services.
+ *
+ *     The dev_link structure is initialized, but we don't actually
+ *     configure the card at this point -- we wait until we receive a
+ *     card insertion event.
+ */
+static dev_link_t *netwave_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    netwave_private *priv; 
+    int i, ret;
+    
+    DEBUG(0, "netwave_attach()\n");
+    
+    /* Perform some cleanup */
+    netwave_flush_stale_links();
+
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    memset(link, 0, sizeof(struct dev_link_t));
+    link->release.function = &netwave_release;
+    link->release.data = (u_long)link;
+       
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+    /* link->io.NumPorts2 = 16; 
+       link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */
+    link->io.IOAddrLines = 5;
+    
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+       link->irq.IRQInfo2 = irq_mask;
+    else
+       for (i = 0; i < 4; i++)
+           link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->irq.Handler = &netwave_interrupt;
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Allocate space for private device-specific data */
+    dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+    memset(dev, 0, sizeof(struct net_device));
+
+    dev->priv = kmalloc(sizeof(netwave_private), GFP_KERNEL);
+    memset(dev->priv, 0, sizeof(netwave_private));
+
+    /* Set the watchdog timer */
+    priv = (netwave_private *) dev->priv;
+    priv->watchdog.function = &netwave_watchdog;
+    priv->watchdog.data = (unsigned long) dev;
+
+    /* Netwave specific entries in the device structure */
+    dev->hard_start_xmit = &netwave_start_xmit;
+    dev->set_config = &netwave_config;
+    dev->get_stats  = &netwave_get_stats;
+    dev->set_multicast_list = &set_multicast_list;
+    /* wireless extensions */
+#ifdef WIRELESS_EXT
+    dev->get_wireless_stats = &netwave_get_wireless_stats;
+#endif
+    dev->do_ioctl = &netwave_ioctl;
+
+    ether_setup(dev);
+    dev->name = ((struct netwave_private *)dev->priv)->node.dev_name;
+    dev->init = &netwave_init;
+    dev->open = &netwave_open;
+    dev->stop = &netwave_close;
+    dev->tbusy = 1;
+    link->priv = link->irq.Instance = dev;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+       CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+       CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &netwave_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != 0) {
+       cs_error(link->handle, RegisterClient, ret);
+       netwave_detach(link);
+       return NULL;
+    }
+
+    return link;
+} /* netwave_attach */
+
+/*
+ * Function netwave_detach (link)
+ *
+ *    This deletes a driver "instance".  The device is de-registered
+ *    with Card Services.  If it has been released, all local data
+ *    structures are freed.  Otherwise, the structures will be freed
+ *    when the device is released.
+ */
+static void netwave_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    long flags;
+
+    DEBUG(0, "netwave_detach(0x%p)\n", link);
+  
+    save_flags(flags);
+    if (link->state & DEV_RELEASE_PENDING) {
+       del_timer(&link->release);
+       link->state &= ~DEV_RELEASE_PENDING;
+    }
+    cli();
+    restore_flags(flags);
+
+    /*
+         If the device is currently configured and active, we won't
+         actually delete it yet.  Instead, it is marked so that when
+         the release() function is called, that will trigger a proper
+         detach().
+       */
+    if (link->state & DEV_CONFIG) {
+       netwave_release((u_long) link);
+       if (link->state & DEV_STALE_CONFIG) {
+           DEBUG(1, "netwave_cs: detach postponed, '%s' still "
+                 "locked\n", link->dev->dev_name);
+           
+           link->state |= DEV_STALE_LINK;
+           return;
+       }
+    }
+       
+    /* Break the link with Card Services */
+    if (link->handle)
+       CardServices(DeregisterClient, link->handle);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+       if (*linkp == link) break;
+    if (*linkp == NULL)
+      {
+       DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n",
+             link->dev->dev_name);
+       return;
+      }
+
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+       struct net_device *dev = link->priv;
+       if (link->dev != NULL)
+         unregister_netdev(dev);
+       link->dev = NULL;
+       if (dev->priv)
+           kfree(dev->priv);
+       kfree(link->priv);
+    }
+    kfree(link);
+    
+} /* netwave_detach */
+
+/*
+ * Function netwave_flush_stale_links (void)
+ *
+ *    This deletes all driver "instances" that need to be deleted.
+ *    Sometimes, netwave_detach can't be performed following a call from
+ *    cardmgr (device still open) and the device is put in a STALE_LINK
+ *    state.
+ *    This function is in charge of making the cleanup...
+ */
+static void netwave_flush_stale_links(void)
+{
+    dev_link_t *       link;           /* Current node in linked list */
+    dev_link_t *       next;           /* Next node in linked list */
+
+    DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list);
+
+    /* Go through the list */
+    for (link = dev_list; link; link = next) {
+        next = link->next;
+        /* Check if in need of being removed */
+        if(link->state & DEV_STALE_LINK)
+           netwave_detach(link);
+    }
+} /* netwave_flush_stale_links */
+
+/*
+ * Function netwave_ioctl (dev, rq, cmd)
+ *
+ *     Perform ioctl : config & info stuff
+ *     This is the stuff that are treated the wireless extensions (iwconfig)
+ *
+ */
+static int netwave_ioctl(struct net_device *dev, /* ioctl device */
+                                                struct ifreq *rq,       /* Data passed */
+                                                int    cmd)         /* Ioctl number */
+{
+    unsigned long flags;
+    int                        ret = 0;
+#ifdef WIRELESS_EXT
+    ioaddr_t iobase = dev->base_addr;
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    struct iwreq *wrq = (struct iwreq *) rq;
+#endif
+       
+    DEBUG( 0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
+       
+    /* Disable interrupts & save flags */
+    save_flags(flags);
+    cli();
+
+    /* Look what is the request */
+    switch(cmd) {
+       /* --------------- WIRELESS EXTENSIONS --------------- */
+#ifdef WIRELESS_EXT
+    case SIOCGIWNAME:
+       /* Get name */
+       strcpy(wrq->u.name, "Netwave");
+       break;
+    case SIOCSIWNWID:
+       /* Set domain */
+#if WIRELESS_EXT > 8
+       if(!wrq->u.nwid.disabled) {
+           domain = wrq->u.nwid.value;
+#else  /* WIRELESS_EXT > 8 */
+       if(wrq->u.nwid.on) {
+           domain = wrq->u.nwid.nwid;
+#endif /* WIRELESS_EXT > 8 */
+           printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", 
+                   (domain >> 8) & 0x01, domain & 0xff);
+           wait_WOC(iobase);
+           writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+           writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+           writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2);
+           writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+       } break;
+    case SIOCGIWNWID:
+       /* Read domain*/
+#if WIRELESS_EXT > 8
+       wrq->u.nwid.value = domain;
+       wrq->u.nwid.disabled = 0;
+       wrq->u.nwid.fixed = 1;
+#else  /* WIRELESS_EXT > 8 */
+       wrq->u.nwid.nwid = domain;
+       wrq->u.nwid.on = 1;
+#endif /* WIRELESS_EXT > 8 */
+       break;
+#if WIRELESS_EXT > 8   /* Note : The API did change... */
+    case SIOCGIWENCODE:
+       /* Get scramble key */
+       if(wrq->u.encoding.pointer != (caddr_t) 0)
+         {
+           char        key[2];
+           key[1] = scramble_key & 0xff;
+           key[0] = (scramble_key>>8) & 0xff;
+           wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+           wrq->u.encoding.length = 2;
+           if(copy_to_user(wrq->u.encoding.pointer, key, 2))
+             ret = -EFAULT;
+         }
+       break;
+    case SIOCSIWENCODE:
+       /* Set  scramble key */
+       if(wrq->u.encoding.pointer != (caddr_t) 0)
+         {
+           char        key[2];
+           if(copy_from_user(key, wrq->u.encoding.pointer, 2))
+             {
+               ret = -EFAULT;
+               break;
+             }
+           scramble_key = (key[0] << 8) | key[1];
+           wait_WOC(iobase);
+           writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+           writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+           writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+           writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+         }
+       break;
+    case SIOCGIWMODE:
+      /* Mode of operation */
+       if(domain & 0x100)
+         wrq->u.mode = IW_MODE_INFRA;
+       else
+         wrq->u.mode = IW_MODE_ADHOC;
+      break;
+#else /* WIRELESS_EXT > 8 */
+    case SIOCGIWENCODE:
+       /* Get scramble key */
+       wrq->u.encoding.code = scramble_key;
+       wrq->u.encoding.method = 1;
+       break;
+    case SIOCSIWENCODE:
+       /* Set  scramble key */
+       scramble_key = wrq->u.encoding.code;
+       wait_WOC(iobase);
+       writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+       writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+       writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+       writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+       break;
+#endif /* WIRELESS_EXT > 8 */
+   case SIOCGIWRANGE:
+       /* Basic checking... */
+       if(wrq->u.data.pointer != (caddr_t) 0) {
+          struct iw_range      range;
+                  
+          /* Set the length (useless : its constant...) */
+          wrq->u.data.length = sizeof(struct iw_range);
+                  
+          /* Set information in the range struct */
+          range.throughput = 450 * 1000;       /* don't argue on this ! */
+          range.min_nwid = 0x0000;
+          range.max_nwid = 0x01FF;
+
+          range.num_channels = range.num_frequency = 0;
+                  
+          range.sensitivity = 0x3F;
+          range.max_qual.qual = 255;
+          range.max_qual.level = 255;
+          range.max_qual.noise = 0;
+                  
+#if WIRELESS_EXT > 7
+          range.num_bitrates = 1;
+          range.bitrate[0] = 1000000;  /* 1 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+          range.encoding_size[0] = 2;          /* 16 bits scrambling */
+          range.num_encoding_sizes = 1;
+          range.max_encoding_tokens = 1;       /* Only one key possible */
+#endif /* WIRELESS_EXT > 8 */
+
+          /* Copy structure to the user buffer */
+          if(copy_to_user(wrq->u.data.pointer, &range,
+                       sizeof(struct iw_range)))
+            ret = -EFAULT;
+       }
+       break;
+    case SIOCGIWPRIV:
+       /* Basic checking... */
+       if(wrq->u.data.pointer != (caddr_t) 0) {
+           struct iw_priv_args priv[] =
+           {   /* cmd,         set_args,       get_args,       name */
+               { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, 
+                 sizeof(struct site_survey), 
+                 "getsitesurvey" },
+           };
+                       
+           /* Set the number of ioctl available */
+           wrq->u.data.length = 1;
+                       
+           /* Copy structure to the user buffer */
+           if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+                        sizeof(priv)))
+             ret = -EFAULT;
+       } 
+       break;
+    case SIOCGIPSNAP:
+       if(wrq->u.data.pointer != (caddr_t) 0) {
+           /* Take snapshot of environment */
+           netwave_snapshot( priv, ramBase, iobase);
+           wrq->u.data.length = priv->nss.length;
+           /* Copy structure to the user buffer */
+           if(copy_to_user(wrq->u.data.pointer, 
+                        (u_char *) &priv->nss,
+                        sizeof( struct site_survey)))
+             {
+               printk(KERN_DEBUG "Bad buffer!\n");
+               break;
+             }
+
+           priv->lastExec = jiffies;
+       }
+       break;
+#endif
+    default:
+       ret = -EOPNOTSUPP;
+    }
+       
+    /* ReEnable interrupts & restore flags */
+    restore_flags(flags);
+    
+    return ret;
+}
+
+/*
+ * Function netwave_pcmcia_config (link)
+ *
+ *     netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION 
+ *     event is received, to configure the PCMCIA socket, and to make the
+ *     device available to the system. 
+ *
+ */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void netwave_pcmcia_config(dev_link_t *link) {
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    struct net_device *dev;
+    int i, j, last_ret, last_fn;
+    u_char buf[64];
+    win_req_t req;
+    memreq_t mem;
+    u_char *ramBase = NULL;
+    /*    modwin_t mod;
+         short iobase, *phys_addr;
+         */  
+    handle = link->handle;
+    dev = link->priv;
+
+    DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
+
+    /*
+      This reads the card's CONFIG tuple to find its configuration
+      registers.
+    */
+    tuple.Attributes = 0;
+    tuple.TupleData = (cisdata_t *) buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+       
+    /*
+     *  Try allocating IO ports.  This tries a few fixed addresses.
+     *  If you want, you can also read the card's config table to
+     *  pick addresses -- see the serial driver for an example.
+     */
+    for (j = 0x0; j < 0x400; j += 0x20) {
+       link->io.BasePort1 = j ^ 0x300;
+       i = CardServices(RequestIO, link->handle, &link->io);
+       if (i == CS_SUCCESS) break;
+    }
+    if (i != CS_SUCCESS) {
+       cs_error(link->handle, RequestIO, i);
+       goto failed;
+    }
+               
+    /*
+     *  Now allocate an interrupt line.  Note that this does not
+     *  actually assign a handler to the interrupt.
+     */
+    CS_CHECK(RequestIRQ, handle, &link->irq);
+       
+    /*
+     *  This actually configures the PCMCIA socket -- setting up
+     *  the I/O windows and the interrupt mapping.
+     */
+    CS_CHECK(RequestConfiguration, handle, &link->conf);
+    
+    /*
+     *  Allocate a 32K memory window.  Note that the dev_link_t
+     *  structure provides space for one window handle -- if your
+     *  device needs several windows, you'll need to keep track of
+     *  the handles in your private data structure, link->priv.
+     */
+    DEBUG(1, "Setting mem speed of %d\n", mem_speed);
+    
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    req.Base = 0; req.Size = 0x8000;
+    req.AccessSpeed = mem_speed;
+    link->win = (window_handle_t)link->handle;
+    CS_CHECK(RequestWindow, &link->win, &req);
+    mem.CardOffset = 0x20000; mem.Page = 0; 
+    CS_CHECK(MapMemPage, link->win, &mem);
+    
+    /* Store base address of the common window frame */
+    ramBase = ioremap(req.Base, 0x8000);
+    ((netwave_private*)dev->priv)->ramBase = ramBase;
+    
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    dev->tbusy = 0;
+    if (register_netdev(dev) != 0) {
+       printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
+       goto failed;
+    }
+       
+    link->state &= ~DEV_CONFIG_PENDING;
+       
+    link->dev = &((netwave_private *)dev->priv)->node;
+
+    /* Reset card before reading physical address */
+    netwave_doreset(dev->base_addr, ramBase);
+    
+    /* Read the ethernet address and fill in the Netwave registers. */
+    for (i = 0; i < 6; i++) 
+       dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
+
+    printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id "
+          "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq,
+          (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI),
+          (int) readb(ramBase+NETWAVE_EREG_NI+1));
+    for (i = 0; i < 6; i++)
+       printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    /* get revision words */
+    printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", 
+          get_uint16(ramBase + NETWAVE_EREG_ARW),
+          get_uint16(ramBase + NETWAVE_EREG_ARW+2));
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    netwave_release((u_long)link);
+    return;
+} /* netwave_pcmcia_config */
+
+/*
+ * Function netwave_release (arg)
+ *
+ *    After a card is removed, netwave_release() will unregister the net
+ *    device, and release the PCMCIA configuration.  If the device is
+ *    still open, this will be postponed until it is closed.
+ */
+static void netwave_release(u_long arg) {
+    dev_link_t *link = (dev_link_t *)arg;
+    struct net_device *dev = link->priv;
+
+    DEBUG(0, "netwave_release(0x%p)\n", link);
+
+    /*
+      If the device is currently in use, we won't release until it
+      is actually closed.
+      */
+    if (link->open) {
+       printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n",
+              link->dev->dev_name);
+       link->state |= DEV_STALE_CONFIG;
+       return;
+    }
+       
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win) {
+       iounmap(((netwave_private *)dev->priv)->ramBase);
+       CardServices(ReleaseWindow, link->win);
+    }
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING | DEV_STALE_CONFIG);
+    
+} /* netwave_release */
+
+/*
+ * Function netwave_event (event, priority, args)
+ *
+ *    The card status event handler.  Mostly, this schedules other
+ *    stuff to run after an event is received.  A CARD_REMOVAL event
+ *    also sets some flags to discourage the net drivers from trying
+ *    to talk to the card any more.
+ *
+ *    When a CARD_REMOVAL event is received, we immediately set a flag
+ *    to block future accesses to this device.  All the functions that
+ *    actually access the device should check this flag to make sure
+ *    the card is still present.
+ *
+ */
+static int netwave_event(event_t event, int priority,
+                        event_callback_args_t *args) {
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+       
+    DEBUG(1, "netwave_event(0x%06x)\n", event);
+  
+    switch (event) {
+    case CS_EVENT_REGISTRATION_COMPLETE:
+       DEBUG(0, "netwave_cs: registration complete\n");
+       break;
+
+    case CS_EVENT_CARD_REMOVAL:
+       link->state &= ~DEV_PRESENT;
+       if (link->state & DEV_CONFIG) {
+           dev->tbusy = 1; dev->start = 0;
+           /* ((netwave_private *)link->priv)->block = 1; */
+           link->release.expires = jiffies + 5;
+           add_timer(&link->release);
+       }
+       break;
+    case CS_EVENT_CARD_INSERTION:
+       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+       netwave_pcmcia_config( link);
+       break;
+    case CS_EVENT_PM_SUSPEND:
+       link->state |= DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+       if (link->state & DEV_CONFIG) {
+           if (link->open) {
+               dev->tbusy = 1; dev->start = 0;
+           }
+           CardServices(ReleaseConfiguration, link->handle);
+       }
+       break;
+    case CS_EVENT_PM_RESUME:
+       link->state &= ~DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+       if (link->state & DEV_CONFIG) {
+           CardServices(RequestConfiguration, link->handle, &link->conf);
+           if (link->open) {
+               netwave_reset(dev);
+               dev->tbusy = 0; dev->start = 1;
+           }
+       }
+       break;
+    }
+    return 0;
+} /* netwave_event */
+
+/*
+ * Function netwave_doreset (ioBase, ramBase)
+ *
+ *    Proper hardware reset of the card.
+ */
+static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) {
+    /* Reset card */
+    wait_WOC(ioBase);
+    outb(0x80, ioBase + NETWAVE_REG_PMR);
+    writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */
+    outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */
+}
+
+/*
+ * Function netwave_reset (dev)
+ *
+ *    Reset and restore all of the netwave registers 
+ */
+static void netwave_reset(struct net_device *dev) {
+    /* u_char state; */
+    netwave_private *priv = (netwave_private*) dev->priv;
+    u_char *ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+
+    DEBUG(0, "netwave_reset: Done with hardware reset\n");
+
+    priv->timeoutCounter = 0;
+       
+    /* If watchdog was activated, kill it ! */
+    del_timer(&priv->watchdog);
+       
+    /* Reset card */
+    netwave_doreset(iobase, ramBase);
+    printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n");
+       
+    /* Write a NOP to check the card */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+       
+    /* Set receive conf */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+    
+    /* Set transmit conf */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+    
+    /* Now set the MU Domain */
+    printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff);
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+       
+    /* Set scramble key */
+    printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key);
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+
+    /* Enable interrupts, bit 4 high to keep unused
+     * source from interrupting us, bit 2 high to 
+     * set interrupt enable, 567 to enable TxDN, 
+     * RxErr and RxRdy
+     */
+    wait_WOC(iobase);
+    outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR);
+
+    /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36
+     * waitWOC
+     * skriv 80 til d000:3688
+     * sjekk om det ble 80
+     */
+    
+    /* Enable Receiver */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+       
+    /* Set the IENA bit in COR */
+    wait_WOC(iobase);
+    outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR);
+}
+
+/*
+ * Function netwave_config (dev, map)
+ *
+ *    Configure device, this work is done by netwave_pcmcia_config when a
+ *    card is inserted
+ */
+static int netwave_config(struct net_device *dev, struct ifmap *map) {
+    return 0; 
+}
+
+/*
+ * Function netwave_hw_xmit (data, len, dev)    
+ */
+static int netwave_hw_xmit(unsigned char* data, int len,
+                          struct net_device* dev) {
+    unsigned long flags;
+    unsigned int TxFreeList,
+                curBuff,
+                MaxData, 
+                 DataOffset;
+    int tmpcount; 
+       
+    netwave_private *priv = (netwave_private *) dev->priv;
+    u_char* ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+       
+    /* Disable interrupts & save flags */
+    save_flags(flags);
+    cli();
+
+    /* Check if there are transmit buffers available */
+    wait_WOC(iobase);
+    if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) {
+       /* No buffers available */
+       printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n",
+              dev->name);
+       return 1;
+    }
+       
+    priv->stats.tx_bytes += len;
+
+    DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
+         readb(ramBase + NETWAVE_EREG_SPCQ),
+         readb(ramBase + NETWAVE_EREG_SPU),
+         readb(ramBase + NETWAVE_EREG_LIF),
+         readb(ramBase + NETWAVE_EREG_ISPLQ));
+
+    /* Now try to insert it into the adapters free memory */
+    wait_WOC(iobase);
+    TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP);
+    MaxData    = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
+    DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
+       
+    DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
+         TxFreeList, MaxData, DataOffset);
+
+    /* Copy packet to the adapter fragment buffers */
+    curBuff = TxFreeList; 
+    tmpcount = 0; 
+    while (tmpcount < len) {
+       int tmplen = len - tmpcount; 
+       copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, 
+                  (tmplen < MaxData) ? tmplen : MaxData);
+       tmpcount += MaxData;
+                       
+       /* Advance to next buffer */
+       curBuff = get_uint16(ramBase + curBuff);
+    }
+    
+    /* Now issue transmit list */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1);
+    writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3);
+       
+    /* If watchdog not already active, activate it... */
+    if(priv->watchdog.prev == (struct timer_list *) NULL) {
+
+       /* set timer to expire in WATCHDOG_JIFFIES */
+       priv->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+       add_timer(&priv->watchdog);
+    }
+    restore_flags( flags);
+    return 0;
+}
+
+static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+       /* This flag indicate that the hardware can't perform a transmission.
+        * Theoritically, NET3 check it before sending a packet to the driver,
+        * but in fact it never do that and pool continuously.
+        * As the watchdog will abort too long transmissions, we are quite safe...
+        */
+
+    if (dev->tbusy) {
+       /* Handled by watchdog */
+       return 1;
+               
+       /* If we get here, some higher level has decided we are broken.
+          There should really be a 'kick me' function call instead.
+          */
+       /*int tickssofar = jiffies - dev->trans_start;*/
+       /* printk("xmit called with busy. tickssofar %d\n", tickssofar); */
+       /*if (tickssofar < TX_TIMEOUT) 
+         return 1;
+         */
+       /* Should also detect if the kernel tries to xmit
+        * on a stopped card. 
+        */
+       
+       /*if (netwave_debug > 0)
+         printk(KERN_DEBUG "%s timed out.\n", dev->name);
+         netwave_reset(dev); 
+         dev->trans_start = jiffies;
+         dev->tbusy = 0;*/
+    }
+
+    /* Sending a NULL skb means some higher layer thinks we've missed an
+     * tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+     * itself. 
+     */
+
+    /* Block a timer-based transmit from overlapping. This could 
+     * better be done with atomic_swap(1, dev->tbusy, but set_bit()
+     * works as well 
+     */
+    if ( test_and_set_bit(0, (void*)&dev->tbusy) != 0) 
+       printk("%s: Transmitter access conflict.\n", dev->name);
+    else {
+       short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+       unsigned char* buf = skb->data;
+       
+       if (netwave_hw_xmit( buf, length, dev) == 1) {
+           /* Some error, let's make them call us another time? */
+           dev->tbusy = 0;
+       }
+       dev->trans_start = jiffies;
+    }
+    dev_kfree_skb(skb);
+    
+    return 0;
+} /* netwave_start_xmit */
+
+/*
+ * Function netwave_interrupt (irq, dev_id, regs)
+ *
+ *    This function is the interrupt handler for the Netwave card. This
+ *    routine will be called whenever: 
+ *       1. A packet is received.
+ *       2. A packet has successfully been transfered and the unit is
+ *          ready to transmit another packet.
+ *       3. A command has completed execution.
+ */
+static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) {
+    ioaddr_t iobase;
+    u_char *ramBase;
+    struct net_device *dev = (struct net_device *)dev_id;
+    struct netwave_private *priv;
+    int i;
+    dev_link_t *link;
+    
+       if ((dev == NULL) | (!dev->start))
+               return;
+    
+    priv = (netwave_private *)dev->priv;
+    
+    if (dev->interrupt) {
+       printk("%s: re-entering the interrupt handler.\n", dev->name);
+       return;
+    }
+    dev->interrupt = 1;
+       
+    /* Find the correct dev_link_t */
+    for (link = dev_list; NULL != link; link = link->next)
+       if (dev == link->priv) break;
+    
+    iobase = dev->base_addr;
+    ramBase = priv->ramBase;
+       
+    /* Now find what caused the interrupt, check while interrupts ready */
+    for (i = 0; i < 10; i++) {
+       u_char status;
+               
+       wait_WOC(iobase);       
+       if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02))
+           break; /* None of the interrupt sources asserted */
+       
+        status = inb(iobase + NETWAVE_REG_ASR);
+               
+       if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) {
+           DEBUG( 1, "netwave_interupt: Interrupt with status 0x%x "
+                  "from removed or suspended card!\n", status);
+           break;
+       }
+               
+       /* RxRdy */
+       if (status & 0x80) {
+           netwave_rx(dev);
+           /* wait_WOC(iobase); */
+           /* RxRdy cannot be reset directly by the host */
+       }
+       /* RxErr */
+       if (status & 0x40) {
+           u_char rser;
+                       
+           rser = readb(ramBase + NETWAVE_EREG_RSER);                  
+           
+           if (rser & 0x04) {
+               ++priv->stats.rx_dropped; 
+               ++priv->stats.rx_crc_errors;
+           }
+           if (rser & 0x02)
+               ++priv->stats.rx_frame_errors;
+                       
+           /* Clear the RxErr bit in RSER. RSER+4 is the
+            * write part. Also clear the RxCRC (0x04) and 
+            * RxBig (0x02) bits if present */
+           wait_WOC(iobase);
+           writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4);
+
+           /* Write bit 6 high to ASCC to clear RxErr in ASR,
+            * WOC must be set first! 
+            */
+           wait_WOC(iobase);
+           writeb(0x40, ramBase + NETWAVE_EREG_ASCC);
+
+           /* Remember to count up priv->stats on error packets */
+           ++priv->stats.rx_errors;
+       }
+       /* TxDN */
+       if (status & 0x20) {
+           int txStatus;
+
+           txStatus = readb(ramBase + NETWAVE_EREG_TSER);
+           DEBUG(3, "Transmit done. TSER = %x id %x\n", 
+                 txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
+           
+           if (txStatus & 0x20) {
+               /* Transmitting was okay, clear bits */
+               wait_WOC(iobase);
+               writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4);
+               ++priv->stats.tx_packets;
+           }
+                       
+           if (txStatus & 0xd0) {
+               if (txStatus & 0x80) {
+                   ++priv->stats.collisions; /* Because of /proc/net/dev*/
+                   /* ++priv->stats.tx_aborted_errors; */
+                   /* printk("Collision. %ld\n", jiffies - dev->trans_start); */
+               }
+               if (txStatus & 0x40) 
+                   ++priv->stats.tx_carrier_errors;
+               /* 0x80 TxGU Transmit giveup - nine times and no luck
+                * 0x40 TxNOAP No access point. Discarded packet.
+                * 0x10 TxErr Transmit error. Always set when 
+                *      TxGU and TxNOAP is set. (Those are the only ones
+                *      to set TxErr).
+                */
+               DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", 
+                     txStatus);
+               
+               /* Clear out TxGU, TxNOAP, TxErr and TxTrys */
+               wait_WOC(iobase);
+               writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
+               ++priv->stats.tx_errors;
+           }
+           DEBUG(3, "New status is TSER %x ASR %x\n",
+                 readb(ramBase + NETWAVE_EREG_TSER),
+                 inb(iobase + NETWAVE_REG_ASR));
+                       
+           /* If watchdog was activated, kill it ! */
+           del_timer(&priv->watchdog);
+
+           dev->tbusy = 0;
+           mark_bh(NET_BH);
+       }
+       /* TxBA, this would trigger on all error packets received */
+       /* if (status & 0x01) {
+          if (netwave_debug > 3) 
+          printk(KERN_DEBUG "Transmit buffers available, %x\n", status); 
+          } 
+          */
+    }
+    /* done.. */
+    dev->interrupt = 0;
+    return;
+} /* netwave_interrupt */
+
+/*
+ * Function netwave_watchdog (a)
+ *
+ *    Watchdog : when we start a transmission, we set a timer in the
+ *    kernel.  If the transmission complete, this timer is disabled. If
+ *    it expire, we reset the card.
+ *
+ */
+static void netwave_watchdog(u_long a) {
+    struct net_device *dev;
+    ioaddr_t iobase;
+       
+    dev = (struct net_device *) a;
+    iobase = dev->base_addr;
+    
+    DEBUG( 1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
+       
+    netwave_reset(dev); 
+       
+    /* We are not waiting anymore... */
+    dev->tbusy = 0;
+       
+} /* netwave_watchdog */
+
+static struct enet_statistics *netwave_get_stats(struct net_device *dev) {
+    netwave_private *priv = (netwave_private*)dev->priv;
+
+    update_stats(dev);
+
+    DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x"
+         " %x tx %x %x %x %x\n", 
+         readb(priv->ramBase + NETWAVE_EREG_SPCQ),
+         readb(priv->ramBase + NETWAVE_EREG_SPU),
+         readb(priv->ramBase + NETWAVE_EREG_LIF),
+         readb(priv->ramBase + NETWAVE_EREG_ISPLQ),
+         readb(priv->ramBase + NETWAVE_EREG_MHS),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0xe),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0xf),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0x18),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0x19),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a),
+         readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b));
+
+    return &priv->stats;
+}
+
+static void update_stats(struct net_device *dev) {
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+/*     netwave_private *priv = (netwave_private*) dev->priv;
+    priv->stats.rx_packets = readb(priv->ramBase + 0x18e); 
+    priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */
+
+    restore_flags(flags);
+}
+
+static int netwave_rx(struct net_device *dev) {
+    netwave_private *priv = (netwave_private*)(dev->priv);
+    u_char *ramBase = priv->ramBase;
+    ioaddr_t iobase = dev->base_addr;
+    u_char rxStatus;
+    struct sk_buff *skb = NULL;
+    unsigned int curBuffer,
+               rcvList;
+    int rcvLen;
+    int tmpcount = 0;
+    int dataCount, dataOffset;
+    int i;
+    u_char *ptr;
+       
+    DEBUG(3, "xinw_rx: Receiving ... \n");
+
+    /* Receive max 10 packets for now. */
+    for (i = 0; i < 10; i++) {
+       /* Any packets? */
+       wait_WOC(iobase);
+       rxStatus = readb(ramBase + NETWAVE_EREG_RSER);          
+       if ( !( rxStatus & 0x80)) /* No more packets */
+           break;
+               
+       /* Check if multicast/broadcast or other */
+       /* multicast = (rxStatus & 0x20);  */
+               
+       /* The receive list pointer and length of the packet */
+       wait_WOC(iobase);
+       rcvLen  = get_int16( ramBase + NETWAVE_EREG_RDP);
+       rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2);
+               
+       if (rcvLen < 0) {
+           printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", 
+                  rcvLen);
+           return 0;
+       }
+               
+       skb = dev_alloc_skb(rcvLen+5);
+       if (skb == NULL) {
+           DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
+                 "length %d\n", rcvLen);
+           ++priv->stats.rx_dropped; 
+           /* Tell the adapter to skip the packet */
+           wait_WOC(iobase);
+           writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+           writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+           return 0;
+       }
+
+       skb_reserve( skb, 2);  /* Align IP on 16 byte */
+       skb_put( skb, rcvLen);
+       skb->dev = dev;
+
+       /* Copy packet fragments to the skb data area */
+       ptr = (u_char*) skb->data;
+       curBuffer = rcvList;
+       tmpcount = 0; 
+       while ( tmpcount < rcvLen) {
+           /* Get length and offset of current buffer */
+           dataCount  = get_uint16( ramBase+curBuffer+2);
+           dataOffset = get_uint16( ramBase+curBuffer+4);
+               
+           copy_from_pc( ptr + tmpcount,
+                         ramBase+curBuffer+dataOffset, dataCount);
+
+           tmpcount += dataCount;
+               
+           /* Point to next buffer */
+           curBuffer = get_uint16(ramBase + curBuffer);
+       }
+       
+       skb->protocol = eth_type_trans(skb,dev);
+       /* Queue packet for network layer */
+       netif_rx(skb);
+               
+       /* Got the packet, tell the adapter to skip it */
+       wait_WOC(iobase);
+       writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
+       writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
+       DEBUG(3, "Packet reception ok\n");
+               
+       priv->stats.rx_packets++;
+
+       priv->stats.rx_bytes += skb->len;
+    }
+    return 0;
+}
+
+static int netwave_open(struct net_device *dev) {
+    dev_link_t *link;
+
+    DEBUG(1, "netwave_open: starting.\n");
+
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    
+    if (!DEV_OK(link))
+       return -ENODEV;
+
+    link->open++;
+    MOD_INC_USE_COUNT;
+       
+    dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+    netwave_reset(dev);
+       
+    return 0;
+}
+
+static int netwave_close(struct net_device *dev) {
+    dev_link_t *link;
+    netwave_private *priv = (netwave_private *) dev->priv;
+
+    DEBUG(1, "netwave_close: finishing.\n");
+
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    if (link == NULL)
+       return -ENODEV;
+       
+    /* If watchdog was activated, kill it ! */
+    del_timer(&priv->watchdog);
+       
+    link->open--;
+    dev->start = 0;
+    if (link->state & DEV_STALE_CONFIG) {
+       link->release.expires = jiffies + 5;
+       link->state |= DEV_RELEASE_PENDING;
+       add_timer(&link->release);
+    }  
+       
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int __init init_netwave_cs(void) {
+    servinfo_t serv;
+
+    DEBUG(0, "%s\n", version);
+
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+       printk("netwave_cs: Card Services release does not match!\n");
+       return -1;
+    }
+    register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach);
+       
+    return 0;
+}
+
+static void __exit exit_netwave_cs(void) {
+    DEBUG(1, "netwave_cs: unloading\n");
+
+    unregister_pccard_driver(&dev_info);
+
+    /* Do some cleanup of the device list */
+    netwave_flush_stale_links();
+    if(dev_list != NULL)       /* Critical situation */
+        printk("netwave_cs: devices remaining when removing module\n");
+}
+
+module_init(init_netwave_cs);
+module_exit(exit_netwave_cs);
+
+/* Set or clear the multicast filter for this adaptor.
+   num_addrs == -1     Promiscuous mode, receive all packets
+   num_addrs == 0      Normal mode, clear multicast list
+   num_addrs > 0       Multicast mode, receive normal and MC packets, and do
+   best-effort filtering.
+ */
+static void set_multicast_list(struct net_device *dev)
+{
+    ioaddr_t iobase = dev->base_addr;
+    u_char* ramBase = ((netwave_private*) dev->priv)->ramBase;
+    u_char  rcvMode = 0;
+   
+#ifdef PCMCIA_DEBUG
+    if (pc_debug > 2) {
+       static int old = 0;
+       if (old != dev->mc_count) {
+           old = dev->mc_count;
+           DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+                 dev->name, dev->mc_count);
+       }
+    }
+#endif
+       
+    if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
+       /* Multicast Mode */
+       rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast;
+    } else if (dev->flags & IFF_PROMISC) {
+       /* Promiscous mode */
+       rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast;
+    } else {
+       /* Normal mode */
+       rcvMode = rxConfRxEna + rxConfBcast;
+    }
+       
+    /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/
+    /* Now set receive mode */
+    wait_WOC(iobase);
+    writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0);
+    writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1);
+    writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2);
+}
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
new file mode 100644 (file)
index 0000000..45aab6d
--- /dev/null
@@ -0,0 +1,1778 @@
+/* ----------------------------------------------------------------------------
+Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
+  nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
+
+  The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
+  Access Controller for Ethernet (MACE).  It is essentially the Am2150
+  PCMCIA Ethernet card contained in the the Am2150 Demo Kit.
+
+Written by Roger C. Pao <rpao@paonet.org>
+  Copyright 1995 Roger C. Pao
+
+  This software may be used and distributed according to the terms of
+  the GNU Public License.
+
+Ported to Linux 1.3.* network driver environment by
+  Matti Aarnio <mea@utu.fi>
+
+References
+
+  Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
+  Am79C940 (MACE) Data Sheet, 1994
+  Am79C90 (C-LANCE) Data Sheet, 1994
+  Linux PCMCIA Programmer's Guide v1.17
+  /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
+
+  Eric Mears, New Media Corporation
+  Tom Pollard, New Media Corporation
+  Dean Siasoyco, New Media Corporation
+  Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
+  Donald Becker <becker@cesdis1.gsfc.nasa.gov>
+  David Hinds <dhinds@hyper.stanford.edu>
+
+  The Linux client driver is based on the 3c589_cs.c client driver by
+  David Hinds.
+
+  The Linux network driver outline is based on the 3c589_cs.c driver,
+  the 8390.c driver, and the example skeleton.c kernel code, which are
+  by Donald Becker.
+
+  The Am2150 network driver hardware interface code is based on the
+  OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
+
+  Special thanks for testing and help in debugging this driver goes
+  to Ken Lesniak.
+
+-------------------------------------------------------------------------------
+Driver Notes and Issues
+-------------------------------------------------------------------------------
+
+1. Developed on a Dell 320SLi
+   PCMCIA Card Services 2.6.2
+   Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
+
+2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
+   'insmod pcmcia_core.o io_speed=300'.
+   This will avoid problems with fast systems which causes rx_framecnt
+   to return random values.
+
+3. If hot extraction does not work for you, use 'ifconfig eth0 down'
+   before extraction.
+
+4. There is a bad slow-down problem in this driver.
+
+5. Future: Multicast processing.  In the meantime, do _not_ compile your
+   kernel with multicast ip enabled.
+
+-------------------------------------------------------------------------------
+History
+-------------------------------------------------------------------------------
+Log: nmclan_cs.c,v
+ * Revision 0.16  1995/07/01  06:42:17  rpao
+ * Bug fix: nmclan_reset() called CardServices incorrectly.
+ *
+ * Revision 0.15  1995/05/24  08:09:47  rpao
+ * Re-implement MULTI_TX dev->tbusy handling.
+ *
+ * Revision 0.14  1995/05/23  03:19:30  rpao
+ * Added, in nmclan_config(), "tuple.Attributes = 0;".
+ * Modified MACE ID check to ignore chip revision level.
+ * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
+ *
+ * Revision 0.13  1995/05/18  05:56:34  rpao
+ * Statistics changes.
+ * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
+ * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT.  Fixes driver lockup.
+ *
+ * Revision 0.12  1995/05/14  00:12:23  rpao
+ * Statistics overhaul.
+ *
+
+95/05/13 rpao  V0.10a
+               Bug fix: MACE statistics counters used wrong I/O ports.
+               Bug fix: mace_interrupt() needed to allow statistics to be
+               processed without RX or TX interrupts pending.
+95/05/11 rpao  V0.10
+               Multiple transmit request processing.
+               Modified statistics to use MACE counters where possible.
+95/05/10 rpao  V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
+               *Released
+95/05/10 rpao  V0.08
+               Bug fix: Make all non-exported functions private by using
+               static keyword.
+               Bug fix: Test IntrCnt _before_ reading MACE_IR.
+95/05/10 rpao  V0.07 Statistics.
+95/05/09 rpao  V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
+
+---------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------
+Conditional Compilation Options
+---------------------------------------------------------------------------- */
+
+#define MULTI_TX                       0
+#define TIMEOUT_TX                     1
+#define RESET_ON_TIMEOUT               1
+#define TX_INTERRUPTABLE               1
+#define RESET_XILINX                   0
+
+/* ----------------------------------------------------------------------------
+Include Files
+---------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/* ----------------------------------------------------------------------------
+Defines
+---------------------------------------------------------------------------- */
+
+#define ETHER_ADDR_LEN                 ETH_ALEN
+                                       /* 6 bytes in an Ethernet Address */
+#define MACE_LADRF_LEN                 8
+                                       /* 8 bytes in Logical Address Filter */
+
+/* Transmitter Busy Bit Index Defines */
+#define TBUSY_UNSPECIFIED              0
+#define TBUSY_PARTIAL_TX_FRAME         0
+#define TBUSY_NO_FREE_TX_FRAMES                1
+
+/* Loop Control Defines */
+#define MACE_MAX_IR_ITERATIONS         10
+#define MACE_MAX_RX_ITERATIONS         12
+       /*
+       TBD: Dean brought this up, and I assumed the hardware would
+       handle it:
+
+       If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
+       non-zero when the isr exits.  We may not get another interrupt
+       to process the remaining packets for some time.
+       */
+
+/*
+The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
+which manages the interface between the MACE and the PCMCIA bus.  It
+also includes buffer management for the 32K x 8 SRAM to control up to
+four transmit and 12 receive frames at a time.
+*/
+#define AM2150_MAX_TX_FRAMES           4
+#define AM2150_MAX_RX_FRAMES           12
+
+/* Am2150 Ethernet Card I/O Mapping */
+#define AM2150_RCV                     0x00
+#define AM2150_XMT                     0x04
+#define AM2150_XMT_SKIP                        0x09
+#define AM2150_RCV_NEXT                        0x0A
+#define AM2150_RCV_FRAME_COUNT         0x0B
+#define AM2150_MACE_BANK               0x0C
+#define AM2150_MACE_BASE               0x10
+
+/* MACE Registers */
+#define MACE_RCVFIFO                   0
+#define MACE_XMTFIFO                   1
+#define MACE_XMTFC                     2
+#define MACE_XMTFS                     3
+#define MACE_XMTRC                     4
+#define MACE_RCVFC                     5
+#define MACE_RCVFS                     6
+#define MACE_FIFOFC                    7
+#define MACE_IR                                8
+#define MACE_IMR                       9
+#define MACE_PR                                10
+#define MACE_BIUCC                     11
+#define MACE_FIFOCC                    12
+#define MACE_MACCC                     13
+#define MACE_PLSCC                     14
+#define MACE_PHYCC                     15
+#define MACE_CHIPIDL                   16
+#define MACE_CHIPIDH                   17
+#define MACE_IAC                       18
+/* Reserved */
+#define MACE_LADRF                     20
+#define MACE_PADR                      21
+/* Reserved */
+/* Reserved */
+#define MACE_MPC                       24
+/* Reserved */
+#define MACE_RNTPC                     26
+#define MACE_RCVCC                     27
+/* Reserved */
+#define MACE_UTR                       29
+#define MACE_RTR1                      30
+#define MACE_RTR2                      31
+
+/* MACE Bit Masks */
+#define MACE_XMTRC_EXDEF               0x80
+#define MACE_XMTRC_XMTRC               0x0F
+
+#define MACE_XMTFS_XMTSV               0x80
+#define MACE_XMTFS_UFLO                        0x40
+#define MACE_XMTFS_LCOL                        0x20
+#define MACE_XMTFS_MORE                        0x10
+#define MACE_XMTFS_ONE                 0x08
+#define MACE_XMTFS_DEFER               0x04
+#define MACE_XMTFS_LCAR                        0x02
+#define MACE_XMTFS_RTRY                        0x01
+
+#define MACE_RCVFS_RCVSTS              0xF000
+#define MACE_RCVFS_OFLO                        0x8000
+#define MACE_RCVFS_CLSN                        0x4000
+#define MACE_RCVFS_FRAM                        0x2000
+#define MACE_RCVFS_FCS                 0x1000
+
+#define MACE_FIFOFC_RCVFC              0xF0
+#define MACE_FIFOFC_XMTFC              0x0F
+
+#define MACE_IR_JAB                    0x80
+#define MACE_IR_BABL                   0x40
+#define MACE_IR_CERR                   0x20
+#define MACE_IR_RCVCCO                 0x10
+#define MACE_IR_RNTPCO                 0x08
+#define MACE_IR_MPCO                   0x04
+#define MACE_IR_RCVINT                 0x02
+#define MACE_IR_XMTINT                 0x01
+
+#define MACE_MACCC_PROM                        0x80
+#define MACE_MACCC_DXMT2PD             0x40
+#define MACE_MACCC_EMBA                        0x20
+#define MACE_MACCC_RESERVED            0x10
+#define MACE_MACCC_DRCVPA              0x08
+#define MACE_MACCC_DRCVBC              0x04
+#define MACE_MACCC_ENXMT               0x02
+#define MACE_MACCC_ENRCV               0x01
+
+#define MACE_PHYCC_LNKFL               0x80
+#define MACE_PHYCC_DLNKTST             0x40
+#define MACE_PHYCC_REVPOL              0x20
+#define MACE_PHYCC_DAPC                        0x10
+#define MACE_PHYCC_LRT                 0x08
+#define MACE_PHYCC_ASEL                        0x04
+#define MACE_PHYCC_RWAKE               0x02
+#define MACE_PHYCC_AWAKE               0x01
+
+#define MACE_IAC_ADDRCHG               0x80
+#define MACE_IAC_PHYADDR               0x04
+#define MACE_IAC_LOGADDR               0x02
+
+#define MACE_UTR_RTRE                  0x80
+#define MACE_UTR_RTRD                  0x40
+#define MACE_UTR_RPA                   0x20
+#define MACE_UTR_FCOLL                 0x10
+#define MACE_UTR_RCVFCSE               0x08
+#define MACE_UTR_LOOP_INCL_MENDEC      0x06
+#define MACE_UTR_LOOP_NO_MENDEC                0x04
+#define MACE_UTR_LOOP_EXTERNAL         0x02
+#define MACE_UTR_LOOP_NONE             0x00
+#define MACE_UTR_RESERVED              0x01
+
+/* Switch MACE register bank (only 0 and 1 are valid) */
+#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
+
+#define MACE_IMR_DEFAULT \
+  (0xFF - \
+    ( \
+      MACE_IR_CERR | \
+      MACE_IR_RCVCCO | \
+      MACE_IR_RNTPCO | \
+      MACE_IR_MPCO | \
+      MACE_IR_RCVINT | \
+      MACE_IR_XMTINT \
+    ) \
+  )
+#undef MACE_IMR_DEFAULT
+#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
+
+/* ----------------------------------------------------------------------------
+Type Definitions
+---------------------------------------------------------------------------- */
+
+typedef struct _mace_statistics {
+  /* MACE_XMTFS */
+  int xmtsv;
+  int uflo;
+  int lcol;
+  int more;
+  int one;
+  int defer;
+  int lcar;
+  int rtry;
+
+  /* MACE_XMTRC */
+  int exdef;
+  int xmtrc;
+
+  /* RFS1--Receive Status (RCVSTS) */
+  int oflo;
+  int clsn;
+  int fram;
+  int fcs;
+
+  /* RFS2--Runt Packet Count (RNTPC) */
+  int rfs_rntpc;
+
+  /* RFS3--Receive Collision Count (RCVCC) */
+  int rfs_rcvcc;
+
+  /* MACE_IR */
+  int jab;
+  int babl;
+  int cerr;
+  int rcvcco;
+  int rntpco;
+  int mpco;
+
+  /* MACE_MPC */
+  int mpc;
+
+  /* MACE_RNTPC */
+  int rntpc;
+
+  /* MACE_RCVCC */
+  int rcvcc;
+} mace_statistics;
+
+typedef struct _mace_private {
+  dev_node_t node;
+  struct net_device_stats linux_stats; /* Linux statistics counters */
+  mace_statistics mace_stats; /* MACE chip statistics counters */
+
+  /* restore_multicast_list() state variables */
+  int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
+  int multicast_num_addrs;
+
+  char tx_free_frames; /* Number of free transmit frame buffers */
+  char tx_irq_disabled; /* MACE TX interrupt disabled */
+} mace_private;
+
+/* ----------------------------------------------------------------------------
+Private Global Variables
+---------------------------------------------------------------------------- */
+
+#ifdef PCMCIA_DEBUG
+static char rcsid[] =
+"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
+static char *version =
+"nmclan_cs 0.16 (Roger C. Pao)";
+#endif
+
+static dev_info_t dev_info="nmclan_cs";
+static dev_link_t *dev_list=NULL;
+
+static char *if_names[]={
+  "Auto",
+  "10baseT",
+  "BNC",
+};
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ----------------------------------------------------------------------------
+Parameters
+       These are the parameters that can be set during loading with
+       'insmod'.
+---------------------------------------------------------------------------- */
+
+static int if_port=0; /* default=auto */
+  /*
+   * 0=auto
+   * 1=10base-T (twisted pair)
+   * 2=10base-2 (BNC)
+   */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(if_port, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/* ----------------------------------------------------------------------------
+Function Prototypes
+---------------------------------------------------------------------------- */
+
+static void nmclan_config(dev_link_t *link);
+static void nmclan_release(u_long arg);
+static int nmclan_event(event_t event, int priority,
+                      event_callback_args_t *args);
+
+static void nmclan_reset(struct net_device *dev);
+static int mace_config(struct net_device *dev, struct ifmap *map);
+static int mace_open(struct net_device *dev);
+static int mace_close(struct net_device *dev);
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct net_device_stats *mace_get_stats(struct net_device *dev);
+static int mace_rx(struct net_device *dev, unsigned char RxCnt);
+static void restore_multicast_list(struct net_device *dev);
+
+static void set_multicast_list(struct net_device *dev);
+
+static dev_link_t *nmclan_attach(void);
+static void nmclan_detach(dev_link_t *);
+
+/* ----------------------------------------------------------------------------
+flush_stale_links
+       Clean up stale device structures
+---------------------------------------------------------------------------- */
+
+static void flush_stale_links(void)
+{
+    dev_link_t *link, *next;
+    for (link = dev_list; link; link = next) {
+       next = link->next;
+       if (link->state & DEV_STALE_LINK)
+           nmclan_detach(link);
+    }
+}
+
+/* ----------------------------------------------------------------------------
+cs_error
+       Report a Card Services related error.
+---------------------------------------------------------------------------- */
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/* ----------------------------------------------------------------------------
+nmclan_init
+       We never need to do anything when a nmclan device is "initialized"
+       by the net software, because we only register already-found cards.
+---------------------------------------------------------------------------- */
+static int nmclan_init(struct net_device *dev)
+{
+  return 0;
+} /* nmclan_init */
+
+/* ----------------------------------------------------------------------------
+nmclan_attach
+       Creates an "instance" of the driver, allocating local data
+       structures for one device.  The device is registered with Card
+       Services.
+---------------------------------------------------------------------------- */
+static dev_link_t *nmclan_attach(void)
+{
+  client_reg_t client_reg;
+  dev_link_t *link;
+  struct net_device *dev;
+  int i, ret;
+
+  DEBUG(0, "nmclan_attach()\n");
+  DEBUG(1, "%s\n", rcsid);
+  flush_stale_links();
+
+  /* Create new ethernet device */
+  link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+  memset(link, 0, sizeof(struct dev_link_t));
+  link->release.function = &nmclan_release;
+  link->release.data = (u_long)link;
+  link->io.NumPorts1 = 32;
+  link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+  link->io.IOAddrLines = 5;
+  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+  link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+  if (irq_list[0] == -1)
+    link->irq.IRQInfo2 = irq_mask;
+  else
+    for (i = 0; i < 4; i++)
+      link->irq.IRQInfo2 |= 1 << irq_list[i];
+  link->irq.Handler = &mace_interrupt;
+  link->conf.Attributes = CONF_ENABLE_IRQ;
+  link->conf.Vcc = 50;
+  link->conf.IntType = INT_MEMORY_AND_IO;
+  link->conf.ConfigIndex = 1;
+  link->conf.Present = PRESENT_OPTION;
+
+  dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+  memset(dev, 0, sizeof(struct net_device));
+
+  /* Allocate private data area for this device. */
+  dev->priv = kmalloc(sizeof(mace_private), GFP_KERNEL);
+  memset(dev->priv, 0, sizeof(mace_private));
+  ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+  dev->hard_start_xmit = &mace_start_xmit;
+  dev->set_config = &mace_config;
+  dev->get_stats = &mace_get_stats;
+  dev->set_multicast_list = &set_multicast_list;
+  ether_setup(dev);
+  dev->name = ((mace_private *)dev->priv)->node.dev_name;
+  dev->init = &nmclan_init;
+  dev->open = &mace_open;
+  dev->stop = &mace_close;
+  dev->tbusy = 0xFF;
+  link->priv = link->irq.Instance = dev;
+
+  /* Register with Card Services */
+  link->next = dev_list;
+  dev_list = link;
+  client_reg.dev_info = &dev_info;
+  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+  client_reg.EventMask =
+    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+  client_reg.event_handler = &nmclan_event;
+  client_reg.Version = 0x0210;
+  client_reg.event_callback_args.client_data = link;
+  ret = CardServices(RegisterClient, &link->handle, &client_reg);
+  if (ret != 0) {
+    cs_error(link->handle, RegisterClient, ret);
+    nmclan_detach(link);
+    return NULL;
+  }
+
+  return link;
+} /* nmclan_attach */
+
+/* ----------------------------------------------------------------------------
+nmclan_detach
+       This deletes a driver "instance".  The device is de-registered
+       with Card Services.  If it has been released, all local data
+       structures are freed.  Otherwise, the structures will be freed
+       when the device is released.
+---------------------------------------------------------------------------- */
+static void nmclan_detach(dev_link_t *link)
+{
+  dev_link_t **linkp;
+
+  DEBUG(0, "nmclan_detach(0x%p)\n", link);
+
+  /* Locate device structure */
+  for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+    if (*linkp == link) break;
+  if (*linkp == NULL)
+    return;
+
+  if (link->state & DEV_CONFIG) {
+    nmclan_release((u_long)link);
+    if (link->state & DEV_STALE_CONFIG) {
+      link->state |= DEV_STALE_LINK;
+      return;
+    }
+  }
+
+  if (link->handle)
+    CardServices(DeregisterClient, link->handle);
+
+  /* Unlink device structure, free bits */
+  *linkp = link->next;
+  if (link->priv) {
+    struct net_device *dev = link->priv;
+    if (link->dev != NULL)
+      unregister_netdev(dev);
+    if (dev->priv)
+      kfree(dev->priv);
+    kfree(link->priv);
+  }
+  kfree(link);
+
+} /* nmclan_detach */
+
+/* ----------------------------------------------------------------------------
+mace_read
+       Reads a MACE register.  This is bank independent; however, the
+       caller must ensure that this call is not interruptable.  We are
+       assuming that during normal operation, the MACE is always in
+       bank 0.
+---------------------------------------------------------------------------- */
+static int mace_read(ioaddr_t ioaddr, int reg)
+{
+  int data = 0xFF;
+  unsigned long flags;
+
+  switch (reg >> 4) {
+    case 0: /* register 0-15 */
+      data = inb(ioaddr + AM2150_MACE_BASE + reg);
+      break;
+    case 1: /* register 16-31 */
+      save_flags(flags);
+      cli();
+      MACEBANK(1);
+      data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+      MACEBANK(0);
+      restore_flags(flags);
+      break;
+  }
+  return (data & 0xFF);
+} /* mace_read */
+
+/* ----------------------------------------------------------------------------
+mace_write
+       Writes to a MACE register.  This is bank independent; however,
+       the caller must ensure that this call is not interruptable.  We
+       are assuming that during normal operation, the MACE is always in
+       bank 0.
+---------------------------------------------------------------------------- */
+static void mace_write(ioaddr_t ioaddr, int reg, int data)
+{
+  unsigned long flags;
+
+  switch (reg >> 4) {
+    case 0: /* register 0-15 */
+      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
+      break;
+    case 1: /* register 16-31 */
+      save_flags(flags);
+      cli();
+      MACEBANK(1);
+      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+      MACEBANK(0);
+      restore_flags(flags);
+      break;
+  }
+} /* mace_write */
+
+/* ----------------------------------------------------------------------------
+mace_init
+       Resets the MACE chip.
+---------------------------------------------------------------------------- */
+static void mace_init(ioaddr_t ioaddr, char *enet_addr)
+{
+  int i;
+
+  /* MACE Software reset */
+  mace_write(ioaddr, MACE_BIUCC, 1);
+  while (mace_read(ioaddr, MACE_BIUCC) & 0x01) {
+    /* Wait for reset bit to be cleared automatically after <= 200ns */;
+  }
+  mace_write(ioaddr, MACE_BIUCC, 0);
+
+  /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
+  mace_write(ioaddr, MACE_FIFOCC, 0x0F);
+
+  mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
+  mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
+
+  /*
+   * Bit 2-1 PORTSEL[1-0] Port Select.
+   * 00 AUI/10Base-2
+   * 01 10Base-T
+   * 10 DAI Port (reserved in Am2150)
+   * 11 GPSI
+   * For this card, only the first two are valid.
+   * So, PLSCC should be set to
+   * 0x00 for 10Base-2
+   * 0x02 for 10Base-T
+   * Or just set ASEL in PHYCC below!
+   */
+  switch (if_port) {
+    case 1:
+      mace_write(ioaddr, MACE_PLSCC, 0x02);
+      break;
+    case 2:
+      mace_write(ioaddr, MACE_PLSCC, 0x00);
+      break;
+    default:
+      mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4);
+      /* ASEL Auto Select.  When set, the PORTSEL[1-0] bits are overridden,
+        and the MACE device will automatically select the operating media
+        interface port. */
+      break;
+  }
+
+  mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
+  /* Poll ADDRCHG bit */
+  while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+    ;
+  /* Set PADR register */
+  for (i = 0; i < ETHER_ADDR_LEN; i++)
+    mace_write(ioaddr, MACE_PADR, enet_addr[i]);
+
+  /* MAC Configuration Control Register should be written last */
+  /* Let set_multicast_list set this. */
+  /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
+  mace_write(ioaddr, MACE_MACCC, 0x00);
+} /* mace_init */
+
+/* ----------------------------------------------------------------------------
+nmclan_config
+       This routine is scheduled to run after a CARD_INSERTION event
+       is received, to configure the PCMCIA socket, and to make the
+       ethernet device available to the system.
+---------------------------------------------------------------------------- */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+static void nmclan_config(dev_link_t *link)
+{
+  client_handle_t handle;
+  struct net_device *dev;
+  tuple_t tuple;
+  cisparse_t parse;
+  u_char buf[64];
+  int i, last_ret, last_fn;
+  ioaddr_t ioaddr;
+  u_short *phys_addr;
+
+  handle = link->handle;
+  dev = link->priv;
+  phys_addr = (u_short *)dev->dev_addr;
+
+  DEBUG(0, "nmclan_config(0x%p)\n", link);
+
+  tuple.Attributes = 0;
+  tuple.TupleData = buf;
+  tuple.TupleDataMax = 64;
+  tuple.TupleOffset = 0;
+  tuple.DesiredTuple = CISTPL_CONFIG;
+  CS_CHECK(GetFirstTuple, handle, &tuple);
+  CS_CHECK(GetTupleData, handle, &tuple);
+  CS_CHECK(ParseTuple, handle, &tuple, &parse);
+  link->conf.ConfigBase = parse.config.base;
+
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+
+  CS_CHECK(RequestIO, handle, &link->io);
+  CS_CHECK(RequestIRQ, handle, &link->irq);
+  CS_CHECK(RequestConfiguration, handle, &link->conf);
+  dev->irq = link->irq.AssignedIRQ;
+  dev->base_addr = link->io.BasePort1;
+  dev->tbusy = 0;
+  i = register_netdev(dev);
+  if (i != 0) {
+    printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n");
+    goto failed;
+  }
+  
+  ioaddr = dev->base_addr;
+
+  /* Read the ethernet address from the CIS. */
+  tuple.DesiredTuple = 0x80 /* CISTPL_CFTABLE_ENTRY_MISC */;
+  tuple.TupleData = buf;
+  tuple.TupleDataMax = 64;
+  tuple.TupleOffset = 0;
+  CS_CHECK(GetFirstTuple, handle, &tuple);
+  CS_CHECK(GetTupleData, handle, &tuple);
+  memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN);
+
+  /* Verify configuration by reading the MACE ID. */
+  {
+    char sig[2];
+
+    sig[0] = mace_read(ioaddr, MACE_CHIPIDL);
+    sig[1] = mace_read(ioaddr, MACE_CHIPIDH);
+    if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
+      DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
+           sig[0], sig[1]);
+    } else {
+      printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should"
+            " be 0x40 0x?9\n", sig[0], sig[1]);
+      link->state &= ~DEV_CONFIG_PENDING;
+      return;
+    }
+  }
+
+  mace_init(ioaddr, dev->dev_addr);
+
+  /* The if_port symbol can be set when the module is loaded */
+  if (if_port <= 2)
+    dev->if_port = if_port;
+  else
+    printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n");
+
+  link->dev = &((mace_private *)dev->priv)->node;
+  link->state &= ~DEV_CONFIG_PENDING;
+  
+  printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port, hw_addr ",
+        dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]);
+  for (i = 0; i < 6; i++)
+      printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+  return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    nmclan_release((u_long)link);
+    return;
+    
+} /* nmclan_config */
+
+/* ----------------------------------------------------------------------------
+nmclan_release
+       After a card is removed, nmclan_release() will unregister the
+       net device, and release the PCMCIA configuration.  If the device
+       is still open, this will be postponed until it is closed.
+---------------------------------------------------------------------------- */
+static void nmclan_release(u_long arg)
+{
+  dev_link_t *link = (dev_link_t *)arg;
+
+  DEBUG(0, "nmclan_release(0x%p)\n", link);
+
+  if (link->open) {
+    DEBUG(1, "nmclan_cs: release postponed, '%s' "
+         "still open\n", link->dev->dev_name);
+    link->state |= DEV_STALE_CONFIG;
+    return;
+  }
+
+  CardServices(ReleaseConfiguration, link->handle);
+  CardServices(ReleaseIO, link->handle, &link->io);
+  CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+  link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* nmclan_release */
+
+/* ----------------------------------------------------------------------------
+nmclan_event
+       The card status event handler.  Mostly, this schedules other
+       stuff to run after an event is received.  A CARD_REMOVAL event
+       also sets some flags to discourage the net drivers from trying
+       to talk to the card any more.
+---------------------------------------------------------------------------- */
+static int nmclan_event(event_t event, int priority,
+                      event_callback_args_t *args)
+{
+  dev_link_t *link = args->client_data;
+  struct net_device *dev = link->priv;
+
+  DEBUG(1, "nmclan_event(0x%06x)\n", event);
+    
+  switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+      link->state &= ~DEV_PRESENT;
+      if (link->state & DEV_CONFIG) {
+       dev->tbusy = 0xFF; dev->start = 0;
+       link->release.expires = jiffies + HZ/20;
+       add_timer(&link->release);
+      }
+      break;
+    case CS_EVENT_CARD_INSERTION:
+      link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+      nmclan_config(link);
+      break;
+    case CS_EVENT_PM_SUSPEND:
+      link->state |= DEV_SUSPEND;
+      /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+      if (link->state & DEV_CONFIG) {
+       if (link->open) {
+         dev->tbusy = 0xFF; dev->start = 0;
+       }
+       CardServices(ReleaseConfiguration, link->handle);
+      }
+      break;
+    case CS_EVENT_PM_RESUME:
+      link->state &= ~DEV_SUSPEND;
+      /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+      if (link->state & DEV_CONFIG) {
+       CardServices(RequestConfiguration, link->handle, &link->conf);
+       if (link->open) {
+         dev->tbusy = 0;
+         dev->start = 1;
+         nmclan_reset(dev);
+       }
+      }
+      break;
+    case CS_EVENT_RESET_REQUEST:
+      return 1;
+      break;
+  }
+  return 0;
+} /* nmclan_event */
+
+/* ------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------------
+nmclan_reset
+       Reset and restore all of the Xilinx and MACE registers.
+---------------------------------------------------------------------------- */
+static void nmclan_reset(struct net_device *dev)
+{
+
+#if RESET_XILINX
+  dev_link_t *link;
+  conf_reg_t reg;
+  u_long OrigCorValue; 
+
+  /* Find our client handle. */
+  for (link = dev_list; link; link = link->next)
+    if (link->priv == dev) break;
+  if (link == NULL) {
+    printk(KERN_NOTICE "nmclan_cs: bad device pointer!\n");
+    return;
+  }
+
+  /* Save original COR value */
+  reg.Function = 0;
+  reg.Action = CS_READ;
+  reg.Offset = CISREG_COR;
+  reg.Value = 0;
+  CardServices(AccessConfigurationRegister, link->handle, &reg);
+  OrigCorValue = reg.Value;
+
+  /* Reset Xilinx */
+  reg.Action = CS_WRITE;
+  reg.Offset = CISREG_COR;
+  DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
+       OrigCorValue);
+  reg.Value = COR_SOFT_RESET;
+  CardServices(AccessConfigurationRegister, link->handle, &reg);
+  /* Need to wait for 20 ms for PCMCIA to finish reset. */
+
+  /* Restore original COR configuration index */
+  reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK);
+  CardServices(AccessConfigurationRegister, link->handle, &reg);
+  /* Xilinx is now completely reset along with the MACE chip. */
+  ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+#endif /* #if RESET_XILINX */
+
+  /* Xilinx is now completely reset along with the MACE chip. */
+  ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES;
+  
+  /* Reinitialize the MACE chip for operation. */
+  mace_init(dev->base_addr, dev->dev_addr);
+  mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
+
+  /* Restore the multicast list and enable TX and RX. */
+  restore_multicast_list(dev);
+} /* nmclan_reset */
+
+/* ----------------------------------------------------------------------------
+mace_config
+       [Someone tell me what this is supposed to do?  Is if_port a defined
+       standard?  If so, there should be defines to indicate 1=10Base-T,
+       2=10Base-2, etc. including limited automatic detection.]
+---------------------------------------------------------------------------- */
+static int mace_config(struct net_device *dev, struct ifmap *map)
+{
+  if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+    if (map->port <= 2) {
+      dev->if_port = map->port;
+      printk(KERN_INFO "%s: switched to %s port\n", dev->name,
+            if_names[dev->if_port]);
+    }
+  else
+    return -EINVAL;
+  }
+  return 0;
+} /* mace_config */
+
+/* ----------------------------------------------------------------------------
+mace_open
+       Open device driver.
+---------------------------------------------------------------------------- */
+static int mace_open(struct net_device *dev)
+{
+  ioaddr_t ioaddr = dev->base_addr;
+  dev_link_t *link;
+
+  for (link = dev_list; link; link = link->next)
+    if (link->priv == dev) break;
+  if (!DEV_OK(link))
+    return -ENODEV;
+
+  link->open++;
+  MOD_INC_USE_COUNT;
+
+  MACEBANK(0);
+
+  dev->interrupt = 0;
+  dev->tbusy = 0;
+  dev->start = 1;
+
+  nmclan_reset(dev);
+
+  return 0; /* Always succeed */
+} /* mace_open */
+
+/* ----------------------------------------------------------------------------
+mace_close
+       Closes device driver.
+---------------------------------------------------------------------------- */
+static int mace_close(struct net_device *dev)
+{
+  ioaddr_t ioaddr = dev->base_addr;
+  dev_link_t *link;
+
+  for (link = dev_list; link; link = link->next)
+    if (link->priv == dev) break;
+  if (link == NULL)
+    return -ENODEV;
+
+  DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+  
+  /* Mask off all interrupts from the MACE chip. */
+  outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+
+  link->open--;
+  dev->start = 0;
+  if (link->state & DEV_STALE_CONFIG) {
+    link->release.expires = jiffies + HZ/20;
+    link->state |= DEV_RELEASE_PENDING;
+    add_timer(&link->release);
+  }
+
+  MOD_DEC_USE_COUNT;
+
+  return 0;
+} /* mace_close */
+
+/* ----------------------------------------------------------------------------
+mace_start_xmit
+       This routine begins the packet transmit function.  When completed,
+       it will generate a transmit interrupt.
+
+       According to /usr/src/linux/net/inet/dev.c, if _start_xmit
+       returns 0, the "packet is now solely the responsibility of the
+       driver."  If _start_xmit returns non-zero, the "transmission
+       failed, put skb back into a list."
+---------------------------------------------------------------------------- */
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+  ioaddr_t ioaddr = dev->base_addr;
+  dev_link_t *link;
+
+#if TIMEOUT_TX
+  /* Transmitter timeout. */
+  if (dev->tbusy) {
+    int tickssofar = jiffies - dev->trans_start;
+
+    if (tickssofar < (HZ/5))
+      return 1;
+    printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name);
+#if RESET_ON_TIMEOUT
+    printk("resetting card\n");
+    for (link = dev_list; link; link = link->next)
+      if (link->priv == dev) break;
+    if (link)
+      CardServices(ResetCard, link->handle);
+#else /* #if RESET_ON_TIMEOUT */
+    printk("NOT resetting card\n");
+#endif /* #if RESET_ON_TIMEOUT */
+    dev->trans_start = jiffies;
+    return 1;
+  }
+#else /* #if TIMEOUT_TX */
+  if (dev->tbusy)
+    return 1;
+#endif /* #if TIMEOUT_TX */
+
+  DEBUG(3, "%s: mace_start_xmit(length = %ld) called.\n",
+       dev->name, (long)skb->len);
+  
+  /* Avoid timer-based retransmission conflicts. */
+  if (test_and_set_bit(TBUSY_UNSPECIFIED, (void*)&dev->tbusy) != 0) {
+    printk(KERN_NOTICE "%s: transmitter access conflict.\n",
+          dev->name);
+    return 1;
+  }
+
+#if (!TX_INTERRUPTABLE)
+  /* Disable MACE TX interrupts. */
+  outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
+    ioaddr + AM2150_MACE_BASE + MACE_IMR);
+  lp->tx_irq_disabled=1;
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+  {
+    /* This block must not be interrupted by another transmit request!
+       dev->tbusy will take care of timer-based retransmissions from
+       the upper layers.  The interrupt handler is guaranteed never to
+       service a transmit interrupt while we are in here.
+    */
+    set_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy);
+
+    lp->linux_stats.tx_bytes += skb->len;
+    lp->tx_free_frames--;
+    set_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+
+    /* WARNING: Write the _exact_ number of bytes written in the header! */
+    /* Put out the word header [must be an outw()] . . . */
+    outw(skb->len, ioaddr + AM2150_XMT);
+    /* . . . and the packet [may be any combination of outw() and outb()] */
+    outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
+    if (skb->len & 1) {
+      /* Odd byte transfer */
+      outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
+    }
+
+    dev->trans_start = jiffies;
+
+    if (lp->tx_free_frames > 0) {
+#if MULTI_TX
+      clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+#endif /* #if MULTI_TX */
+    }
+
+    clear_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy);
+  }
+
+#if (!TX_INTERRUPTABLE)
+  /* Re-enable MACE TX interrupts. */
+  lp->tx_irq_disabled=0;
+  outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+  dev_kfree_skb(skb);
+    
+  return 0;
+} /* mace_start_xmit */
+
+/* ----------------------------------------------------------------------------
+mace_interrupt
+       The interrupt handler.
+---------------------------------------------------------------------------- */
+static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct net_device *dev = (struct net_device *)dev_id;
+  mace_private *lp = (mace_private *)dev->priv;
+  ioaddr_t ioaddr = dev->base_addr;
+  int status;
+  int IntrCnt = MACE_MAX_IR_ITERATIONS;
+
+  if (dev == NULL) {
+    DEBUG(2, "mace_interrupt(): irq 0x%X for unknown device.\n",
+         irq);
+    return;
+  }
+
+  if (dev->interrupt || lp->tx_irq_disabled) {
+    sti();
+    printk(
+      (lp->tx_irq_disabled?
+       KERN_NOTICE "%s: Interrupt with tx_irq_disabled "
+       "[isr=%02X, imr=%02X]\n": 
+       KERN_NOTICE "%s: Re-entering the interrupt handler "
+       "[isr=%02X, imr=%02X]\n"),
+      dev->name,
+      inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
+      inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)
+    );
+    /* WARNING: MACE_IR has been read! */
+    return;
+  }
+  dev->interrupt = 1;
+  sti();
+
+  if (dev->start == 0) {
+    DEBUG(2, "%s: interrupt from dead card\n", dev->name);
+    goto exception;
+  }
+
+  do {
+    /* WARNING: MACE_IR is a READ/CLEAR port! */
+    status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
+    DEBUG(3, "mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
+
+    if (status & MACE_IR_RCVINT) {
+      mace_rx(dev, MACE_MAX_RX_ITERATIONS);
+    }
+
+    if (status & MACE_IR_XMTINT) {
+      unsigned char fifofc;
+      unsigned char xmtrc;
+      unsigned char xmtfs;
+
+      fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
+      if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
+       lp->linux_stats.tx_errors++;
+       outb(0xFF, ioaddr + AM2150_XMT_SKIP);
+      }
+
+      /* Transmit Retry Count (XMTRC, reg 4) */
+      xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
+      if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
+      lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
+
+      if (
+        (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
+        MACE_XMTFS_XMTSV /* Transmit Status Valid */
+      ) {
+       lp->mace_stats.xmtsv++;
+
+       if (xmtfs & ~MACE_XMTFS_XMTSV) {
+         if (xmtfs & MACE_XMTFS_UFLO) {
+           /* Underflow.  Indicates that the Transmit FIFO emptied before
+              the end of frame was reached. */
+           lp->mace_stats.uflo++;
+         }
+         if (xmtfs & MACE_XMTFS_LCOL) {
+           /* Late Collision */
+           lp->mace_stats.lcol++;
+         }
+         if (xmtfs & MACE_XMTFS_MORE) {
+           /* MORE than one retry was needed */
+           lp->mace_stats.more++;
+         }
+         if (xmtfs & MACE_XMTFS_ONE) {
+           /* Exactly ONE retry occurred */
+           lp->mace_stats.one++;
+         }
+         if (xmtfs & MACE_XMTFS_DEFER) {
+           /* Transmission was defered */
+           lp->mace_stats.defer++;
+         }
+         if (xmtfs & MACE_XMTFS_LCAR) {
+           /* Loss of carrier */
+           lp->mace_stats.lcar++;
+         }
+         if (xmtfs & MACE_XMTFS_RTRY) {
+           /* Retry error: transmit aborted after 16 attempts */
+           lp->mace_stats.rtry++;
+         }
+        } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
+
+      } /* if (xmtfs & MACE_XMTFS_XMTSV) */
+
+      lp->linux_stats.tx_packets++;
+      lp->tx_free_frames++;
+      clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy);
+      mark_bh(NET_BH);
+    } /* if (status & MACE_IR_XMTINT) */
+
+    if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
+      if (status & MACE_IR_JAB) {
+        /* Jabber Error.  Excessive transmit duration (20-150ms). */
+        lp->mace_stats.jab++;
+      }
+      if (status & MACE_IR_BABL) {
+        /* Babble Error.  >1518 bytes transmitted. */
+        lp->mace_stats.babl++;
+      }
+      if (status & MACE_IR_CERR) {
+       /* Collision Error.  CERR indicates the absence of the
+          Signal Quality Error Test message after a packet
+          transmission. */
+        lp->mace_stats.cerr++;
+      }
+      if (status & MACE_IR_RCVCCO) {
+        /* Receive Collision Count Overflow; */
+        lp->mace_stats.rcvcco++;
+      }
+      if (status & MACE_IR_RNTPCO) {
+        /* Runt Packet Count Overflow */
+        lp->mace_stats.rntpco++;
+      }
+      if (status & MACE_IR_MPCO) {
+        /* Missed Packet Count Overflow */
+        lp->mace_stats.mpco++;
+      }
+    } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
+
+  } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
+
+exception:
+  dev->interrupt = 0;
+  return;
+} /* mace_interrupt */
+
+/* ----------------------------------------------------------------------------
+mace_rx
+       Receives packets.
+---------------------------------------------------------------------------- */
+static int mace_rx(struct net_device *dev, unsigned char RxCnt)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+  ioaddr_t ioaddr = dev->base_addr;
+  unsigned char rx_framecnt;
+  unsigned short rx_status;
+
+  while (
+    ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
+    (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
+    (RxCnt--)
+  ) {
+    rx_status = inw(ioaddr + AM2150_RCV);
+
+    DEBUG(3, "%s: in mace_rx(), framecnt 0x%X, rx_status"
+         " 0x%X.\n", dev->name, rx_framecnt, rx_status);
+
+    if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
+      lp->linux_stats.rx_errors++;
+      if (rx_status & MACE_RCVFS_OFLO) {
+        lp->mace_stats.oflo++;
+      }
+      if (rx_status & MACE_RCVFS_CLSN) {
+        lp->mace_stats.clsn++;
+      }
+      if (rx_status & MACE_RCVFS_FRAM) {
+       lp->mace_stats.fram++;
+      }
+      if (rx_status & MACE_RCVFS_FCS) {
+        lp->mace_stats.fcs++;
+      }
+    } else {
+      short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
+        /* Auto Strip is off, always subtract 4 */
+      struct sk_buff *skb;
+
+      lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
+        /* runt packet count */
+      lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
+        /* rcv collision count */
+
+      DEBUG(3, "    receiving packet size 0x%X rx_status"
+           " 0x%X.\n", pkt_len, rx_status);
+      
+      skb = dev_alloc_skb(pkt_len+2);
+      
+      if (skb != NULL) {
+       skb->dev = dev;
+
+       skb_reserve(skb, 2);
+       insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
+       if (pkt_len & 1)
+           *(skb->tail-1) = inb(ioaddr + AM2150_RCV);
+       skb->protocol = eth_type_trans(skb, dev);
+       
+       netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
+
+       lp->linux_stats.rx_packets++;
+       lp->linux_stats.rx_bytes += skb->len;
+       outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+       continue;
+      } else {
+       DEBUG(1, "%s: couldn't allocate a sk_buff of size"
+             " %d.\n", dev->name, pkt_len);
+       lp->linux_stats.rx_dropped++;
+      }
+    }
+    outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+  } /* while */
+
+  return 0;
+} /* mace_rx */
+
+/* ----------------------------------------------------------------------------
+pr_linux_stats
+---------------------------------------------------------------------------- */
+static void pr_linux_stats(struct net_device_stats *pstats)
+{
+  DEBUG(2, "pr_linux_stats\n");
+  DEBUG(2, " rx_packets=%-7ld        tx_packets=%ld\n",
+       (long)pstats->rx_packets, (long)pstats->tx_packets);
+  DEBUG(2, " rx_errors=%-7ld         tx_errors=%ld\n",
+       (long)pstats->rx_errors, (long)pstats->tx_errors);
+  DEBUG(2, " rx_dropped=%-7ld        tx_dropped=%ld\n",
+       (long)pstats->rx_dropped, (long)pstats->tx_dropped);
+  DEBUG(2, " multicast=%-7ld         collisions=%ld\n",
+       (long)pstats->multicast, (long)pstats->collisions);
+
+  DEBUG(2, " rx_length_errors=%-7ld  rx_over_errors=%ld\n",
+       (long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
+  DEBUG(2, " rx_crc_errors=%-7ld     rx_frame_errors=%ld\n",
+       (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
+  DEBUG(2, " rx_fifo_errors=%-7ld    rx_missed_errors=%ld\n",
+       (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
+
+  DEBUG(2, " tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
+       (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
+  DEBUG(2, " tx_fifo_errors=%-7ld    tx_heartbeat_errors=%ld\n",
+       (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
+  DEBUG(2, " tx_window_errors=%ld\n",
+       (long)pstats->tx_window_errors);
+} /* pr_linux_stats */
+
+/* ----------------------------------------------------------------------------
+pr_mace_stats
+---------------------------------------------------------------------------- */
+static void pr_mace_stats(mace_statistics *pstats)
+{
+  DEBUG(2, "pr_mace_stats\n");
+
+  DEBUG(2, " xmtsv=%-7d             uflo=%d\n",
+       pstats->xmtsv, pstats->uflo);
+  DEBUG(2, " lcol=%-7d              more=%d\n",
+       pstats->lcol, pstats->more);
+  DEBUG(2, " one=%-7d               defer=%d\n",
+       pstats->one, pstats->defer);
+  DEBUG(2, " lcar=%-7d              rtry=%d\n",
+       pstats->lcar, pstats->rtry);
+
+  /* MACE_XMTRC */
+  DEBUG(2, " exdef=%-7d             xmtrc=%d\n",
+       pstats->exdef, pstats->xmtrc);
+  
+  /* RFS1--Receive Status (RCVSTS) */
+  DEBUG(2, " oflo=%-7d              clsn=%d\n",
+       pstats->oflo, pstats->clsn);
+  DEBUG(2, " fram=%-7d              fcs=%d\n",
+       pstats->fram, pstats->fcs);
+
+  /* RFS2--Runt Packet Count (RNTPC) */
+  /* RFS3--Receive Collision Count (RCVCC) */
+  DEBUG(2, " rfs_rntpc=%-7d         rfs_rcvcc=%d\n",
+       pstats->rfs_rntpc, pstats->rfs_rcvcc);
+
+  /* MACE_IR */
+  DEBUG(2, " jab=%-7d               babl=%d\n",
+       pstats->jab, pstats->babl);
+  DEBUG(2, " cerr=%-7d              rcvcco=%d\n",
+       pstats->cerr, pstats->rcvcco);
+  DEBUG(2, " rntpco=%-7d            mpco=%d\n",
+       pstats->rntpco, pstats->mpco);
+
+  /* MACE_MPC */
+  DEBUG(2, " mpc=%d\n", pstats->mpc);
+
+  /* MACE_RNTPC */
+  DEBUG(2, " rntpc=%d\n", pstats->rntpc);
+
+  /* MACE_RCVCC */
+  DEBUG(2, " rcvcc=%d\n", pstats->rcvcc);
+
+} /* pr_mace_stats */
+
+/* ----------------------------------------------------------------------------
+update_stats
+       Update statistics.  We change to register window 1, so this
+       should be run single-threaded if the device is active. This is
+       expected to be a rare operation, and it's simpler for the rest
+       of the driver to assume that window 0 is always valid rather
+       than use a special window-state variable.
+
+       oflo & uflo should _never_ occur since it would mean the Xilinx
+       was not able to transfer data between the MACE FIFO and the
+       card's SRAM fast enough.  If this happens, something is
+       seriously wrong with the hardware.
+---------------------------------------------------------------------------- */
+static void update_stats(ioaddr_t ioaddr, struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+    
+  lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC);
+  lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC);
+  lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC);
+  /* At this point, mace_stats is fully updated for this call.
+     We may now update the linux_stats. */
+
+  /* The MACE has no equivalent for linux_stats field which are commented
+     out. */
+
+  /* lp->linux_stats.multicast; */
+  lp->linux_stats.collisions = 
+    lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
+    /* Collision: The MACE may retry sending a packet 15 times
+       before giving up.  The retry count is in XMTRC.
+       Does each retry constitute a collision?
+       If so, why doesn't the RCVCC record these collisions? */
+
+  /* detailed rx_errors: */
+  lp->linux_stats.rx_length_errors = 
+    lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
+  /* lp->linux_stats.rx_over_errors */
+  lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs;
+  lp->linux_stats.rx_frame_errors = lp->mace_stats.fram;
+  lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo;
+  lp->linux_stats.rx_missed_errors = 
+    lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
+
+  /* detailed tx_errors */
+  lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry;
+  lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar;
+    /* LCAR usually results from bad cabling. */
+  lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo;
+  lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr;
+  /* lp->linux_stats.tx_window_errors; */
+
+  return;
+} /* update_stats */
+
+/* ----------------------------------------------------------------------------
+mace_get_stats
+       Gathers ethernet statistics from the MACE chip.
+---------------------------------------------------------------------------- */
+static struct net_device_stats *mace_get_stats(struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+
+  update_stats(dev->base_addr, dev);
+
+  DEBUG(1, "%s: updating the statistics.\n", dev->name);
+  pr_linux_stats(&lp->linux_stats);
+  pr_mace_stats(&lp->mace_stats);
+
+  return &lp->linux_stats;
+} /* net_device_stats */
+
+/* ----------------------------------------------------------------------------
+updateCRC
+       Modified from Am79C90 data sheet.
+---------------------------------------------------------------------------- */
+
+#if BROKEN_MULTICAST
+
+static void updateCRC(int *CRC, int bit)
+{
+  int poly[]={
+    1,1,1,0, 1,1,0,1,
+    1,0,1,1, 1,0,0,0,
+    1,0,0,0, 0,0,1,1,
+    0,0,1,0, 0,0,0,0
+  }; /* CRC polynomial.  poly[n] = coefficient of the x**n term of the
+       CRC generator polynomial. */
+
+  int j;
+
+  /* shift CRC and control bit (CRC[32]) */
+  for (j = 32; j > 0; j--)
+    CRC[j] = CRC[j-1];
+  CRC[0] = 0;
+
+  /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
+  if (bit ^ CRC[32])
+    for (j = 0; j < 32; j++)
+      CRC[j] ^= poly[j];
+} /* updateCRC */
+
+/* ----------------------------------------------------------------------------
+BuildLAF
+       Build logical address filter.
+       Modified from Am79C90 data sheet.
+
+Input
+       ladrf: logical address filter (contents initialized to 0)
+       adr: ethernet address
+---------------------------------------------------------------------------- */
+static void BuildLAF(int *ladrf, int *adr)
+{
+  int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
+
+  int i, byte; /* temporary array indices */
+  int hashcode; /* the output object */
+
+  CRC[32]=0;
+
+  for (byte = 0; byte < 6; byte++)
+    for (i = 0; i < 8; i++)
+      updateCRC(CRC, (adr[byte] >> i) & 1);
+
+  hashcode = 0;
+  for (i = 0; i < 6; i++)
+    hashcode = (hashcode << 1) + CRC[i];
+
+  byte = hashcode >> 3;
+  ladrf[byte] |= (1 << (hashcode & 7));
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 2) {
+    printk(KERN_DEBUG "    adr =");
+    for (i = 0; i < 6; i++)
+      printk(" %02X", adr[i]);
+    printk("\n" KERN_DEBUG "    hashcode = %d(decimal), ladrf[0:63]"
+          " =", hashcode);
+    for (i = 0; i < 8; i++)
+      printk(" %02X", ladrf[i]);
+    printk("\n");
+  }
+#endif
+} /* BuildLAF */
+
+/* ----------------------------------------------------------------------------
+restore_multicast_list
+       Restores the multicast filter for MACE chip to the last
+       set_multicast_list() call.
+
+Input
+       multicast_num_addrs
+       multicast_ladrf[]
+---------------------------------------------------------------------------- */
+static void restore_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+  int num_addrs = lp->multicast_num_addrs;
+  int *ladrf = lp->multicast_ladrf;
+  ioaddr_t ioaddr = dev->base_addr;
+  int i;
+
+  DEBUG(2, "%s: restoring Rx mode to %d addresses.\n",
+       dev->name, num_addrs);
+
+  if (num_addrs > 0) {
+
+    DEBUG(1, "Attempt to restore multicast list detected.\n");
+
+    mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
+    /* Poll ADDRCHG bit */
+    while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+      ;
+    /* Set LADRF register */
+    for (i = 0; i < MACE_LADRF_LEN; i++)
+      mace_write(ioaddr, MACE_LADRF, ladrf[i]);
+
+    mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
+    mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+  } else if (num_addrs < 0) {
+
+    /* Promiscuous mode: receive all packets */
+    mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(ioaddr, MACE_MACCC,
+      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+    );
+
+  } else {
+
+    /* Normal mode */
+    mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+  }
+} /* restore_multicast_list */
+
+/* ----------------------------------------------------------------------------
+set_multicast_list
+       Set or clear the multicast filter for this adaptor.
+
+Input
+       num_addrs == -1 Promiscuous mode, receive all packets
+       num_addrs == 0  Normal mode, clear multicast list
+       num_addrs > 0   Multicast mode, receive normal and MC packets, and do
+                       best-effort filtering.
+Output
+       multicast_num_addrs
+       multicast_ladrf[]
+---------------------------------------------------------------------------- */
+
+static void set_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+  int adr[ETHER_ADDR_LEN] = {0}; /* Ethernet address */
+  int i;
+  struct dev_mc_list *dmi = dev->mc_list;
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 1) {
+    static int old = 0;
+    if (dev->mc_count != old) {
+      old = dev->mc_count;
+      DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+           dev->name, old);
+    }
+  }
+#endif
+
+  /* Set multicast_num_addrs. */
+  lp->multicast_num_addrs = dev->mc_count;
+
+  /* Set multicast_ladrf. */
+  if (num_addrs > 0) {
+    /* Calculate multicast logical address filter */
+    memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
+    for (i = 0; i < dev->mc_count; i++) {
+      memcpy(adr, dmi->dmi_addr, ETHER_ADDR_LEN);
+      dmi = dmi->next;
+      BuildLAF(lp->multicast_ladrf, adr);
+    }
+  }
+
+  restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+#endif /* BROKEN_MULTICAST */
+
+static void restore_multicast_list(struct net_device *dev)
+{
+  ioaddr_t ioaddr = dev->base_addr;
+
+  DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
+       ((mace_private *)(dev->priv))->multicast_num_addrs);
+  
+  if (dev->flags & IFF_PROMISC) {
+    /* Promiscuous mode: receive all packets */
+    mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(ioaddr, MACE_MACCC,
+      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+    );
+  } else {
+    /* Normal mode */
+    mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+  }
+} /* restore_multicast_list */
+
+static void set_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = (mace_private *)dev->priv;
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 1) {
+    static int old = 0;
+    if (dev->mc_count != old) {
+      old = dev->mc_count;
+      DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+           dev->name, old);
+    }
+  }
+#endif
+
+  lp->multicast_num_addrs = dev->mc_count;
+  restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+/* ----------------------------------------------------------------------------
+init_nmclan_cs
+---------------------------------------------------------------------------- */
+
+static int __init init_nmclan_cs(void)
+{
+  servinfo_t serv;
+  DEBUG(0, "%s\n", version);
+  CardServices(GetCardServicesInfo, &serv);
+  if (serv.Revision != CS_RELEASE_CODE) {
+    printk(KERN_NOTICE "nmclan_cs: Card Services release does not match!\n");
+    return -1;
+  }
+  register_pccard_driver(&dev_info, &nmclan_attach, &nmclan_detach);
+  return 0;
+}
+
+/* ----------------------------------------------------------------------------
+exit_nmclan_cs
+---------------------------------------------------------------------------- */
+
+static void __exit exit_nmclan_cs(void)
+{
+  DEBUG(0, "nmclan_cs: unloading\n");
+  unregister_pccard_driver(&dev_info);
+  while (dev_list != NULL)
+    nmclan_detach(dev_list);
+}
+
+module_init(init_nmclan_cs);
+module_exit(exit_nmclan_cs);
diff --git a/drivers/net/pcmcia/ositech.h b/drivers/net/pcmcia/ositech.h
new file mode 100644 (file)
index 0000000..749bbaf
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+    This file contains the firmware of Seven of Diamonds from OSITECH.
+    (Special thanks to Kevin MacPherson of OSITECH)
+
+    This software may be used and distributed according to the terms of
+    the GNU Public License, incorporated herein by reference.
+*/
+
+    static const u_char __Xilinx7OD[] = {
+    0xFF, 0x04, 0xA0, 0x36, 0xF3, 0xEC, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF,
+    0xF3, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0x3F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F, 0xFE, 0xFF,
+    0xCE, 0xFE, 0xFE, 0xFE, 
+    0xFE, 0xDE, 0xBD, 0xDD, 0xFD, 0xFF, 0xFD, 0xCF, 0xF7, 0xBF, 0x7F, 0xFF,
+    0x7F, 0x3F, 0xFE, 0xBF, 
+    0xFF, 0xFF, 0xFF, 0xBC, 0xFF, 0xFF, 0xBD, 0xB5, 0x7F, 0x7F, 0xBF, 0xBF,
+    0x7F, 0xFF, 0xEF, 0xFF, 
+    0xFF, 0xFF, 0xFB, 0xFF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDE,
+    0xFE, 0xFE, 0xFA, 0xDE, 
+    0xBD, 0xFD, 0xED, 0xFD, 0xFD, 0xCF, 0xEF, 0xEF, 0xEF, 0xEF, 0xC7, 0xDF,
+    0xDF, 0xDF, 0xDF, 0xDF, 
+    0xFF, 0x7E, 0xFE, 0xFD, 0x7D, 0x6D, 0xEE, 0xFE, 0x7C, 0xFB, 0xF4, 0xFB,
+    0xCF, 0xDB, 0xDF, 0xFF, 
+    0xFF, 0xBB, 0x7F, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0x9E, 0xBF, 0x3B, 0xBF,
+    0xBF, 0x7F, 0x7F, 0x7F, 
+    0x7E, 0x6F, 0xDF, 0xEF, 0xF5, 0xF6, 0xFD, 0xF6, 0xF5, 0xED, 0xEB, 0xFF,
+    0xEF, 0xEF, 0xEF, 0x7E, 
+    0x7F, 0x7F, 0x6F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xEF, 0xBF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x1F, 0x1F, 0xEE, 0xFF, 0xBC,
+    0xB7, 0xFF, 0xDF, 0xFF, 
+    0xDF, 0xEF, 0x3B, 0xE3, 0xD3, 0xFF, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF,
+    0xFF, 0xBA, 0xBF, 0x2D, 
+    0xDB, 0xBD, 0xFD, 0xDB, 0xDF, 0xFA, 0xFB, 0xFF, 0xEF, 0xFB, 0xDB, 0xF3,
+    0xFF, 0xDF, 0xFD, 0x7F, 
+    0xEF, 0xFB, 0xFF, 0xFF, 0xBE, 0xBF, 0x27, 0xBA, 0xFE, 0xFB, 0xDF, 0xFF,
+    0xF6, 0xFF, 0xFF, 0xEF, 
+    0xFB, 0xDB, 0xF3, 0xD9, 0x9A, 0x3F, 0xFF, 0xAF, 0xBF, 0xFF, 0xFF, 0xBE,
+    0x3F, 0x37, 0xBD, 0x96, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, 0xFB, 0xF3, 0xF3, 0xEB, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xF7, 0xFA, 0xBC, 0xAE, 0xFE, 0xBE, 0xFE, 0xBB, 0x7F, 0xFD, 0xFF,
+    0x7F, 0xEF, 0xF7, 0xFB, 
+    0xBB, 0xD7, 0xF7, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xBC, 0xED, 0xFD,
+    0xBD, 0x9D, 0x7D, 0x7B, 
+    0xFB, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xF7, 
+    0xAA, 0xB9, 0xBF, 0x8F, 0xBF, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xCF,
+    0xFB, 0xEB, 0xCB, 0xEB, 
+    0xEE, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF, 0x3E, 0x33, 0x3F, 0x1C, 0x7C,
+    0xFC, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xCF, 0xD3, 0xF3, 0xE3, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEB, 0xFE, 0x35, 
+    0x3F, 0x3D, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0x6F, 0xE3,
+    0xE3, 0xE3, 0xEF, 0xFF, 
+    0xFF, 0xDF, 0xFF, 0xFF, 0xF7, 0xFE, 0x3E, 0x5E, 0xFE, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFD, 0xFF, 0xFF, 
+    0xAF, 0xCF, 0xF2, 0xCB, 0xCF, 0x8E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
+    0xFC, 0x3E, 0x1F, 0x9E, 
+    0xAD, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xB3, 0xF7, 0xE7,
+    0xF7, 0xFA, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEE, 0xEB, 0xAB, 0xAF, 0x9F, 0xE3, 0x7F, 0xFF, 0xDE,
+    0xFF, 0x7F, 0xEE, 0xFF, 
+    0xFF, 0xFB, 0x3A, 0xFA, 0xFF, 0xF2, 0x77, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF,
+    0xFE, 0xBD, 0xAE, 0xDE, 
+    0x7D, 0x7D, 0xFD, 0xFF, 0xBF, 0xEE, 0xFF, 0xFD, 0xFF, 0xDB, 0xFB, 0xFF,
+    0xF7, 0xEF, 0xFB, 0xFF, 
+    0xFF, 0xFE, 0xFF, 0x2D, 0xAF, 0xB9, 0xFD, 0x79, 0xFB, 0xFA, 0xFF, 0xBF,
+    0xEF, 0xFF, 0xFF, 0x91, 
+    0xFA, 0xFB, 0xDF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 0x37, 0xBF,
+    0xBF, 0xFF, 0x7F, 0x7F, 
+    0xFF, 0xFF, 0xFF, 0xAF, 0xFF, 0xFF, 0xF3, 0xFB, 0xFB, 0xFF, 0xF5, 0xEF,
+    0xFF, 0xFF, 0xF7, 0xFA, 
+    0xFF, 0xFF, 0xEE, 0xFA, 0xFE, 0xFB, 0x55, 0xDD, 0xFF, 0x7F, 0xAF, 0xFE,
+    0xFF, 0xFB, 0xFB, 0xF5, 
+    0xFF, 0xF7, 0xEF, 0xFF, 0xFF, 0xFF, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D,
+    0x7B, 0x7B, 0x7B, 0x7B, 
+    0xFB, 0xAE, 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xDA, 0xB7, 0x61, 
+    0xFF, 0xB9, 0x59, 0xF3, 0x73, 0xF3, 0xDF, 0x7F, 0x6F, 0xDF, 0xEF, 0xF7,
+    0xEB, 0xEB, 0xD7, 0xFF, 
+    0xD7, 0xFF, 0xFF, 0xF7, 0xFE, 0x7F, 0xFB, 0x3E, 0x38, 0x73, 0xF6, 0x7F,
+    0xFC, 0xFF, 0xFF, 0xCF, 
+    0xFF, 0xB7, 0xFB, 0xB3, 0xB3, 0x67, 0xFF, 0xE7, 0xFD, 0xFF, 0xEF, 0xF6,
+    0x7F, 0xB7, 0xBC, 0xF5, 
+    0x7B, 0xF6, 0xF7, 0xF5, 0xFF, 0xFF, 0xEF, 0xFF, 0xF7, 0xFF, 0xF7, 0xCE,
+    0xE7, 0xFF, 0x9F, 0xFF, 
+    0xFF, 0xF5, 0xFE, 0x7D, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEF, 0xFF, 0xF6, 
+    0xCB, 0xDB, 0xEE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xBE,
+    0x1E, 0x3E, 0xFE, 0xFF, 
+    0x7D, 0xFE, 0xFF, 0xFF, 0xEF, 0xBF, 0xE7, 0xFF, 0xE3, 0xE3, 0xFF, 0xDF,
+    0xE7, 0xFF, 0xFF, 0xFF, 
+    0xB8, 0xEF, 0xB7, 0x2F, 0xEE, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0x7F, 0xEF,
+    0xEB, 0xBF, 0xA3, 0xD3, 
+    0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xFD, 0x3F, 0xCF, 0xFD,
+    0xFB, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xAF, 0xFB, 0xBF, 0xBB, 0xBF, 0xDB, 0xFD, 0xFB, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x3E, 0xFE, 
+    0x3F, 0xBA, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xEF, 0xC3, 0x7F,
+    0xB2, 0x9B, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0x3C, 0xFF, 0x3F, 0x3C, 0xFF, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xAF, 0xF3, 0xFE, 0xF3, 0xE3, 0xEB, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xF7,
+    0x9A, 0xFE, 0xAF, 0x9E, 
+    0xBE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0x7B, 0xEF, 0xF7, 0xBF, 0xFB, 0xFB,
+    0xFB, 0xFF, 0xFF, 0x7F, 
+    0xFF, 0xFF, 0xFF, 0xBC, 0xBD, 0xFD, 0xBD, 0xDD, 0x7D, 0x7B, 0x7B, 0x7B,
+    0x7B, 0xFB, 0xAE, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x9A, 0xFF,
+    0x9F, 0xFF, 0xAF, 0xEF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xCF, 0xF3, 0xFF, 0xEB, 0xFF, 0xEB, 0xFF,
+    0xFF, 0xBF, 0xFF, 0xFF, 
+    0xEF, 0xFE, 0xFF, 0x37, 0xFC, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xCF, 0xEF, 0xFD, 0xF3, 
+    0xFF, 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0xFD, 0x2F, 0xFD,
+    0xFF, 0xFD, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEF, 0xCF, 0xFF, 0xF3, 0xBF, 0x69, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE, 
+    0xFB, 0x9F, 0xFF, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x87,
+    0xFE, 0xDA, 0xEF, 0xCF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xBF, 0xEF, 0xEF, 0xFD,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xFD, 0xFF, 0x7B, 0xFF, 0xEB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xEB, 0xF8, 0xFF, 0xEF, 
+    0xAF, 0xFF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xEE, 0x7F, 0xEF, 0xFF,
+    0xBB, 0xFF, 0xBF, 0xFB, 
+    0xFF, 0xFF, 0xFF, 0xF7, 0xF6, 0xFB, 0xBD, 0xFD, 0xDD, 0xF5, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xAF, 
+    0xFF, 0x5F, 0xF5, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6,
+    0xF3, 0xFF, 0xDE, 0xFE, 
+    0xEF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xDE, 0xDF, 0x5F, 0xDF,
+    0xFD, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xAF, 0xFF, 0xFF, 
+    0xEF, 0xED, 0xFF, 0xDF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDA, 0xBD, 0xBE,
+    0xAE, 0xFE, 0x7F, 0xFD, 
+    0xDF, 0xFF, 0xFF, 0x7F, 0xEF, 0xFF, 0xFB, 0xFB, 0xFB, 0x7F, 0xF7, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xF7, 
+    0xBC, 0xFD, 0xBD, 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE,
+    0xFF, 0xFF, 0xFD, 0xFF, 
+    0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x9F, 0xBF, 0xBF, 0xCF,
+    0x7F, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xAF, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xD7, 0xFE, 0xFF, 0xFF,
+    0xBF, 0xE7, 0xFE, 0xBF, 
+    0x7F, 0xFC, 0xFF, 0xFF, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFB,
+    0xFB, 0xFF, 0xFF, 0xDD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBD, 0xDF, 0x9D, 0xFD, 0xDF, 0xB9,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0xFF, 0xFB, 0xEF, 0xEB, 0xFF, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF6, 0x9F, 0xFF, 0xFC, 
+    0xFE, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xDF, 0xFA, 0xCD, 0xCF,
+    0xBF, 0x9F, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xF7, 0xFE, 0xBF, 0xFF, 0xDF, 0xEF, 0x5F, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x7F, 0x6F, 0xFF, 
+    0xBB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF,
+    0x5F, 0xFF, 0xBF, 0xBF, 
+    0xF9, 0xFF, 0xFF, 0xFF, 0x7F, 0x6E, 0x7B, 0xFF, 0xEF, 0xFD, 0xEB, 0xDF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xF7, 0xB6, 0x3E, 0xFC, 0xFD, 0xBF, 0x7E, 0xFB, 0xFF, 0xFF, 0xFF, 0xF7,
+    0xEF, 0xF7, 0xF3, 0xF7, 
+    0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x35, 0x79, 0xFF,
+    0xBF, 0xFC, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0x53, 0xDF, 0xFF, 0xEB, 0xBF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xBC, 
+    0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xF5,
+    0xFF, 0xF7, 0xFF, 0xFB, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xAA, 0xEE, 0xFE, 0x3F, 0x7D,
+    0xFD, 0xFF, 0xFF, 0xFF, 
+    0x7F, 0xAF, 0x77, 0xFB, 0xFB, 0xFF, 0xFB, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xBE, 0xBD, 0xBD, 
+    0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE, 0xFF, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFC, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0x9A, 0xD9, 0xB8, 0xFF, 0xFF, 0x79, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xCF, 
+    0xFB, 0xFF, 0xEB, 0xFF, 0xEB, 0xD7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xDE,
+    0xF8, 0xFB, 0xFE, 0x3F, 
+    0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xAD, 0xBF, 0xFA, 0xFF, 0x73,
+    0xDF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0x3A, 0xF5, 0xB7, 0xFC, 0x3F, 0xF9, 0xFD, 0xFF, 0xFF, 0xFF,
+    0x7F, 0xEF, 0xF3, 0xFF, 
+    0xBF, 0xFE, 0xF3, 0x9F, 0xFE, 0xFF, 0xFF, 0xFF, 0xF7, 0x3E, 0xFF, 0xFF,
+    0xFF, 0xBF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xD3, 0xFE, 0xDB, 0xFF, 0xDB, 0xDF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0x3E, 0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F,
+    0xF3, 0xFF, 0xED, 0xFF, 
+    0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF6, 0x3C, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x9F, 0xEF, 0xEF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x7E, 0xBF, 
+    0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBB, 0xEF, 0xDF, 0xF1,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x3E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xBF, 
+    0xEF, 0xFD, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+    0xFC, 0x3E, 0xFE, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2E, 0xEF, 0xF3, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xF7, 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x7F, 0xAF, 0xFB, 
+    0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xF2, 0xD6, 0xED,
+    0xBD, 0xBD, 0xBD, 0x7D, 
+    0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x92, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
+    0xAF, 0xEB, 0xEB, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFE, 0x2E, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0x4F, 0xEF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE, 
+    0x3C, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE,
+    0xC3, 0xFD, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xCF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xEE, 0x3E, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xDF, 0xE2, 0xFF,
+    0xFF, 0xFF, 0xFB, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xBE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x7F, 0xEE, 
+    0x5F, 0xE6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E,
+    0x7D, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBF, 0xF7, 0x36, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEF, 0xD3, 0xF6, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xEE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xEF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE,
+    0xFB, 0xFA, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xD6, 0xFD, 0xBD, 0xBD, 0xBD,
+    0x7D, 0x7B, 0x7B, 0x7B, 
+    0x7B, 0xFB, 0xAE, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xF7, 0xBA, 0xBF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xEB, 0x6B,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0x4F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0x3E, 0x6E, 0xFC, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC3, 0xC9, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0x3E, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xEF, 0xFB, 
+    0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE,
+    0xFE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xF6, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE,
+    0xEF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFE, 0xFF, 0xF7, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x7F, 0xFA, 0xEF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE7, 0xFF, 0xFE, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xEF, 0xBF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0xFF, 0xFC, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x7F, 
+    0xFE, 0xAE, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7,
+    0xF7, 0xFA, 0xFF, 0xFD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B,
+    0x7B, 0x7B, 0xFB, 0xAF, 
+    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xE7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xCF, 0xFE, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE7, 0xF2, 0xFC, 
+    0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAE, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7E, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+    0xFE, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xEF, 0xDD, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xAF, 0xEF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xF6, 0x9C, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB,
+    0xAE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7A, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xDF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0x6F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xF7, 0xFE, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xEB,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x9E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xCB, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xEF, 
+    0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFB, 0xAF, 0x7F, 0xFF, 
+    0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xBF, 0xFF, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xF7, 0xBC, 0xBD, 
+    0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x7F, 
+    0xAF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0xFE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF,
+    0xFF, 0xFF, 0xEF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xBF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xEF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFE, 0xFF, 0x9F, 0x9F,
+    0x9F, 0x3F, 0x3F, 0x3F, 
+    0x3F, 0x3F, 0xFF, 0xEF, 0xDF, 0xDF, 0xDF, 0xDF, 0xCF, 0xB7, 0xBF, 0xBF,
+    0xBF, 0xBF, 0xFF, 0xBC, 
+    0xB9, 0x9D, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xEF, 0xD7,
+    0xF5, 0xF3, 0xF1, 0xD1, 
+    0x65, 0xE3, 0xE3, 0xE3, 0xA3, 0xFF, 0xFE, 0x7F, 0xFE, 0xDE, 0xDE, 0xFF,
+    0xBD, 0xBD, 0xBD, 0xBD, 
+    0xDF, 0xEF, 0xFB, 0xF7, 0xF3, 0xF3, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+    0xFB, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+    
+    };
index f5d4b3c49c7ea88a873ce0b8288d8ff96299f146..26fa47ee96bcafeb92b9a59eace4d204ba265701 100644 (file)
@@ -11,7 +11,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
 
-    pcnet_cs.c 1.99 1999/09/15 15:33:09
+    pcnet_cs.c 1.101 1999/10/21 00:56:19
     
     The network driver code is based on Donald Becker's NE2000 code:
 
@@ -72,7 +72,7 @@ static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"pcnet_cs.c 1.99 1999/09/15 15:33:09 (David Hinds)";
+"pcnet_cs.c 1.101 1999/10/21 00:56:19 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -477,7 +477,8 @@ static hw_info_t *get_prom(dev_link_t *link)
 {
     struct net_device *dev = link->priv;
     unsigned char prom[32];
-    int i, j, ioaddr;
+    ioaddr_t ioaddr;
+    int i, j;
 
     /* This is lifted straight from drivers/net/ne.c */
     struct {
@@ -851,7 +852,7 @@ static int pcnet_event(event_t event, int priority,
 
 static void set_misc_reg(struct net_device *dev)
 {
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
     pcnet_dev_t *info = (pcnet_dev_t *)dev;
     u_char tmp;
     
@@ -889,7 +890,7 @@ static int pcnet_open(struct net_device *dev)
        int i;
        for (i = 0; i < 20; i++) {
            if ((inb(dev->base_addr+0x1c) & 0x01) == 0) break;
-           current->state = TASK_INTERRUPTIBLE;
+           __set_current_state(TASK_UNINTERRUPTIBLE);
            schedule_timeout(HZ/10);
        }
     }
@@ -941,7 +942,7 @@ static int pcnet_close(struct net_device *dev)
 
 static void pcnet_reset_8390(struct net_device *dev)
 {
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
     int i;
 
     ei_status.txing = ei_status.dmaing = 0;
@@ -996,7 +997,7 @@ static void ei_watchdog(u_long arg)
 {
     pcnet_dev_t *info = (pcnet_dev_t *)(arg);
     struct net_device *dev = &info->dev;
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
 
     if (dev->start == 0) goto reschedule;
 
@@ -1027,7 +1028,7 @@ static void dma_get_8390_hdr(struct net_device *dev,
                             struct e8390_pkt_hdr *hdr,
                             int ring_page)
 {
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
 
     if (ei_status.dmaing) {
        printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input."
@@ -1059,7 +1060,7 @@ static void dma_get_8390_hdr(struct net_device *dev,
 static void dma_block_input(struct net_device *dev, int count,
                            struct sk_buff *skb, int ring_offset)
 {
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
     int xfer_count = count;
     char *buf = skb->data;
 
@@ -1116,7 +1117,7 @@ static void dma_block_output(struct net_device *dev, int count,
                             const unsigned char *buf,
                             const int start_page)
 {
-    int nic_base = dev->base_addr;
+    ioaddr_t nic_base = dev->base_addr;
     pcnet_dev_t *info = (pcnet_dev_t *)dev;
 #ifdef PCMCIA_DEBUG
     int retries = 0;
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
new file mode 100644 (file)
index 0000000..3abdf7d
--- /dev/null
@@ -0,0 +1,2013 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for SMC91c92-based cards.
+
+    This driver supports Megahertz PCMCIA ethernet cards; and
+    Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
+    multifunction cards.
+
+    Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu
+
+    smc91c92_cs.c 1.79 1999/10/19 00:38:29
+    
+    This driver contains code written by Donald Becker
+    (becker@cesdis.gsfc.nasa.gov), Rowan Hughes (x-csrdh@jcu.edu.au),
+    David Hinds (dhinds@hyper.stanford.edu), and Erik Stahlman
+    (erik@vt.edu).  Donald wrote the SMC 91c92 code using parts of
+    Erik's SMC 91c94 driver.  Rowan wrote a similar driver, and I've
+    incorporated some parts of his driver here.  I (Dave) wrote most
+    of the PCMCIA glue code, and the Ositech support code.  Kelly
+    Stephens (kstephen@holli.com) added support for the Motorola
+    Mariner, with help from Allen Brost. 
+
+    This software may be used and distributed according to the terms of
+    the GNU Public License, incorporated herein by reference.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+/* Ositech Seven of Diamonds firmware */
+#include "ositech.h"
+
+/*====================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+static const char *version =
+"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+static char *if_names[] = { "auto", "10baseT", "10base2"};
+
+/* Parameters that can be set with 'insmod' */
+
+/*
+  Transceiver/media type.
+   0 = auto
+   1 = 10baseT (and autoselect if #define AUTOSELECT),
+   2 = AUI/10base2,
+*/
+static int if_port = 0;
+
+/* Bit map of interrupts to choose from. */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(if_port, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/* Operational parameter that usually are not changed. */
+
+/* Do you want to use 32 bit xfers?  This should work on all chips,
+   but could cause trouble with some PCMCIA controllers... */
+#define USE_32_BIT             1
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT             ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK              4
+
+/* Times to check the check the chip before concluding that it doesn't
+   currently have room for another Tx packet. */
+#define MEMORY_WAIT_TIME               8
+
+/* Values that should be specific lengths */
+typedef unsigned short uint16;
+
+static dev_info_t dev_info = "smc91c92_cs";
+
+static dev_link_t *dev_list = NULL;
+
+struct smc_private {
+    u_short                    manfid;
+    u_short                    cardid;
+    struct net_device_stats    stats;
+    dev_node_t                 node;
+    struct sk_buff             *saved_skb;
+    int                                packets_waiting;
+    caddr_t                    base;
+    u_short                    cfg;
+    struct timer_list          media;
+    int                                watchdog, tx_err;
+    u_short                    media_status;
+    u_short                    fast_poll;
+    u_long                     last_rx;
+};
+
+/* Special definitions for Megahertz multifunction cards */
+#define MEGAHERTZ_ISR          0x0380
+
+/* Special function registers for Motorola Mariner */
+#define MOT_LAN                        0x0000
+#define MOT_UART               0x0020
+#define MOT_EEPROM             0x20
+
+#define MOT_NORMAL \
+(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
+
+/* Special function registers for Ositech cards */
+#define OSITECH_AUI_CTL                0x0c
+#define OSITECH_PWRDOWN                0x0d
+#define OSITECH_RESET          0x0e
+#define OSITECH_ISR            0x0f
+#define OSITECH_AUI_PWR                0x0c
+#define OSITECH_RESET_ISR      0x0e
+
+#define OSI_AUI_PWR            0x40
+#define OSI_LAN_PWRDOWN                0x02
+#define OSI_MODEM_PWRDOWN      0x01
+#define OSI_LAN_RESET          0x02
+#define OSI_MODEM_RESET                0x01
+
+/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
+#define        BANK_SELECT             14              /* Window select register. */
+#define SMC_SELECT_BANK(x)  { outw( x, ioaddr + BANK_SELECT); }
+
+/* Bank 0 registers. */
+#define        TCR             0       /* transmit control register */
+#define         TCR_CLEAR      0       /* do NOTHING */
+#define  TCR_ENABLE    0x0001  /* if this is 1, we can transmit */
+#define         TCR_PAD_EN     0x0080  /* pads short packets to 64 bytes */
+#define  TCR_MONCSN    0x0400  /* Monitor Carrier. */
+#define  TCR_FDUPLX    0x0800  /* Full duplex mode. */
+#define         TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
+
+#define EPH            2       /* Ethernet Protocol Handler report. */
+#define  EPH_TX_SUC    0x0001
+#define  EPH_SNGLCOL   0x0002
+#define  EPH_MULCOL    0x0004
+#define  EPH_LTX_MULT  0x0008
+#define  EPH_16COL     0x0010
+#define  EPH_SQET      0x0020
+#define  EPH_LTX_BRD   0x0040
+#define  EPH_TX_DEFR   0x0080
+#define  EPH_LAT_COL   0x0200
+#define  EPH_LOST_CAR  0x0400
+#define  EPH_EXC_DEF   0x0800
+#define  EPH_CTR_ROL   0x1000
+#define  EPH_RX_OVRN   0x2000
+#define  EPH_LINK_OK   0x4000
+#define  EPH_TX_UNRN   0x8000
+#define MEMINFO                8       /* Memory Information Register */
+#define MEMCFG         10      /* Memory Configuration Register */
+
+/* Bank 1 registers. */
+#define CONFIG                 0
+#define  CFG_MII_SELECT                0x8000  /* 91C100 only */
+#define  CFG_NO_WAIT           0x1000
+#define  CFG_FULL_STEP         0x0400
+#define  CFG_SET_SQLCH         0x0200
+#define  CFG_AUI_SELECT                0x0100
+#define  CFG_16BIT             0x0080
+#define  CFG_DIS_LINK          0x0040
+#define  CFG_STATIC            0x0030
+#define  CFG_IRQ_SEL_1         0x0004
+#define  CFG_IRQ_SEL_0         0x0002
+#define BASE_ADDR              2
+#define        ADDR0                   4
+#define        GENERAL                 10
+#define        CONTROL                 12
+#define  CTL_STORE             0x0001
+#define  CTL_RELOAD            0x0002
+#define  CTL_EE_SELECT         0x0004
+#define  CTL_TE_ENABLE         0x0020
+#define  CTL_CR_ENABLE         0x0040
+#define  CTL_LE_ENABLE         0x0080
+#define  CTL_AUTO_RELEASE      0x0800
+#define         CTL_POWERDOWN          0x2000
+
+/* Bank 2 registers. */
+#define MMU_CMD                0
+#define         MC_ALLOC       0x20    /* or with number of 256 byte packets */
+#define         MC_RESET       0x40
+#define  MC_RELEASE    0x80    /* remove and release the current rx packet */
+#define  MC_FREEPKT    0xA0    /* Release packet in PNR register */
+#define  MC_ENQUEUE    0xC0    /* Enqueue the packet for transmit */
+#define        PNR_ARR         2
+#define FIFO_PORTS     4
+#define  FP_RXEMPTY    0x8000
+#define        POINTER         6
+#define  PTR_AUTO_INC  0x0040
+#define  PTR_READ      0x2000
+#define         PTR_AUTOINC    0x4000
+#define         PTR_RCV        0x8000
+#define        DATA_1          8
+#define        INTERRUPT       12
+#define  IM_RCV_INT            0x1
+#define         IM_TX_INT              0x2
+#define         IM_TX_EMPTY_INT        0x4
+#define         IM_ALLOC_INT           0x8
+#define         IM_RX_OVRN_INT         0x10
+#define         IM_EPH_INT             0x20
+
+#define        RCR             4
+enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
+            RxEnable = 0x0100, RxStripCRC = 0x0200};
+#define  RCR_SOFTRESET 0x8000  /* resets the chip */
+#define         RCR_STRIP_CRC  0x200   /* strips CRC */
+#define  RCR_ENABLE    0x100   /* IFF this is set, we can recieve packets */
+#define  RCR_ALMUL     0x4     /* receive all multicast packets */
+#define         RCR_PROMISC    0x2     /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define         RCR_NORMAL     (RCR_STRIP_CRC | RCR_ENABLE)
+#define  RCR_CLEAR     0x0             /* set it to a base state */
+#define        COUNTER         6
+
+/* BANK 3 -- not the same values as in smc9194! */
+#define        MULTICAST0      0
+#define        MULTICAST2      2
+#define        MULTICAST4      4
+#define        MULTICAST6      6
+#define REVISION       0x0a
+
+/* Transmit status bits. */
+#define TS_SUCCESS 0x0001
+#define TS_16COL   0x0010
+#define TS_LATCOL  0x0200
+#define TS_LOSTCAR 0x0400
+
+/* Receive status bits. */
+#define RS_ALGNERR     0x8000
+#define RS_BADCRC      0x2000
+#define RS_ODDFRAME    0x1000
+#define RS_TOOLONG     0x0800
+#define RS_TOOSHORT    0x0400
+#define RS_MULTICAST   0x0001
+#define RS_ERRORS      (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+#define set_bits(v, p) outw(inw(p)|(v), (p))
+#define mask_bits(v, p) outw(inw(p)&(v), (p))
+
+/*====================================================================*/
+
+static dev_link_t *smc91c92_attach(void);
+static void smc91c92_detach(dev_link_t *);
+static void smc91c92_config(dev_link_t *link);
+static void smc91c92_release(u_long arg);
+static int smc91c92_event(event_t event, int priority,
+                         event_callback_args_t *args);
+
+static int smc91c92_open(struct net_device *dev);
+static int smc91c92_close(struct net_device *dev);
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void smc_rx(struct net_device *dev);
+static struct net_device_stats *smc91c92_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static int s9k_config(struct net_device *dev, struct ifmap *map);
+static void smc_set_xcvr(struct net_device *dev, int if_port);
+static void smc_reset(struct net_device *dev);
+static void media_check(u_long arg);
+
+/*======================================================================
+
+    This bit of code is used to avoid unregistering network devices
+    at inappropriate times.  2.2 and later kernels are fairly picky
+    about when this can happen.
+    
+======================================================================*/
+
+static void flush_stale_links(void)
+{
+    dev_link_t *link, *next;
+    for (link = dev_list; link; link = next) {
+       next = link->next;
+       if (link->state & DEV_STALE_LINK)
+           smc91c92_detach(link);
+    }
+}
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+static int smc91c92_init(struct net_device *dev)
+{
+    DEBUG(0, "%s: smc91c92_init called!\n", dev->name);
+    return 0;
+}
+
+/*======================================================================
+
+  smc91c92_attach() creates an "instance" of the driver, allocating
+  local data structures for one device.  The device is registered
+  with Card Services.
+
+======================================================================*/
+
+static dev_link_t *smc91c92_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    int i, ret;
+
+    DEBUG(0, "smc91c92_attach()\n");
+    flush_stale_links();
+    
+    /* Create new ethernet device */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    memset(link, 0, sizeof(struct dev_link_t));
+    link->release.function = &smc91c92_release;
+    link->release.data = (u_long)link;
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 4;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+       link->irq.IRQInfo2 = irq_mask;
+    else
+       for (i = 0; i < 4; i++)
+           link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->irq.Handler = &smc_interrupt;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    
+    dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+    memset(dev, 0, sizeof(struct net_device));
+    
+    /* Make up a SMC91-specific-data structure. */
+    dev->priv = kmalloc(sizeof(struct smc_private), GFP_KERNEL);
+    memset(dev->priv, 0, sizeof(struct smc_private));
+    
+    /* The SMC91c92-specific entries in the device structure. */
+    dev->hard_start_xmit = &smc_start_xmit;
+    dev->get_stats = &smc91c92_get_stats;
+    dev->set_config = &s9k_config;
+    dev->set_multicast_list = &set_rx_mode;
+    ether_setup(dev);
+    dev->name = ((struct smc_private *)dev->priv)->node.dev_name;
+    dev->init = &smc91c92_init;
+    dev->open = &smc91c92_open;
+    dev->stop = &smc91c92_close;
+    dev->tbusy = 1;
+    link->priv = link->irq.Instance = dev;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+       CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &smc91c92_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != 0) {
+       cs_error(link->handle, RegisterClient, ret);
+       smc91c92_detach(link);
+       return NULL;
+    }
+    
+    return link;
+} /* smc91c92_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void smc91c92_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    long flags;
+
+    DEBUG(0, "smc91c92_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+       if (*linkp == link) break;
+    if (*linkp == NULL)
+       return;
+    
+    save_flags(flags);
+    cli();
+    if (link->state & DEV_RELEASE_PENDING) {
+       del_timer(&link->release);
+       link->state &= ~DEV_RELEASE_PENDING;
+    }
+    restore_flags(flags);
+    
+    if (link->state & DEV_CONFIG) {
+       smc91c92_release((u_long)link);
+       if (link->state & DEV_STALE_CONFIG) {
+           link->state |= DEV_STALE_LINK;
+           return;
+       }
+    }
+    
+    if (link->handle)
+       CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    if (link->priv) {
+       struct net_device *dev = link->priv;
+       if (link->dev != NULL)
+           unregister_netdev(dev);
+       if (dev->priv)
+           kfree(dev->priv);
+       kfree(link->priv);
+    }
+    kfree(link);
+    
+} /* smc91c92_detach */
+
+/*====================================================================*/
+
+static int cvt_ascii_address(struct net_device *dev, char *s)
+{
+    int i, j, da, c;
+
+    if (strlen(s) != 12)
+       return -1;
+    for (i = 0; i < 6; i++) {
+       da = 0;
+       for (j = 0; j < 2; j++) {
+           c = *s++;
+           da <<= 4;
+           da += ((c >= '0') && (c <= '9')) ?
+               (c - '0') : ((c & 0x0f) + 9);
+       }
+       dev->dev_addr[i] = da;
+    }
+    return 0;
+}
+
+/*====================================================================*/
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
+                    cisparse_t *parse)
+{
+    int i;
+    i = CardServices(fn, handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    i = CardServices(GetTupleData, handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+/*======================================================================
+
+    Configuration stuff for Megahertz cards
+
+    mhz_3288_power() is used to power up a 3288's ethernet chip.
+    mhz_mfc_config() handles socket setup for multifunction (1144
+    and 3288) cards.  mhz_setup() gets a card's hardware ethernet
+    address.
+    
+======================================================================*/
+
+static int mhz_3288_power(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *lp = dev->priv;
+    u_char tmp;
+    
+    /* Read the ISR twice... */
+    readb(lp->base+MEGAHERTZ_ISR);
+    udelay(5);
+    readb(lp->base+MEGAHERTZ_ISR);
+
+    /* Pause 200ms... */
+    mdelay(200);
+    
+    /* Now read and write the COR... */
+    tmp = readb(lp->base + link->conf.ConfigBase + CISREG_COR);
+    udelay(5);
+    writeb(tmp, lp->base + link->conf.ConfigBase + CISREG_COR);
+
+    return 0;
+}
+
+static int mhz_mfc_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *lp = dev->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    win_req_t req;
+    memreq_t mem;
+    int i, k;
+
+    link->conf.Attributes |= CONF_ENABLE_SPKR;
+    link->conf.Status = CCSR_AUDIO_ENA;
+    link->irq.Attributes =
+       IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 8;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+    i = first_tuple(link->handle, &tuple, &parse);
+    /* The Megahertz combo cards have modem-like CIS entries, so
+       we have to explicitly try a bunch of port combinations. */
+    while (i == CS_SUCCESS) {
+       link->conf.ConfigIndex = cf->index;
+       link->io.BasePort2 = cf->io.win[0].base;
+       for (k = 0; k < 0x400; k += 0x10) {
+           if (k & 0x80) continue;
+           link->io.BasePort1 = k ^ 0x300;
+           i = CardServices(RequestIO, link->handle, &link->io);
+           if (i == CS_SUCCESS) break;
+       }
+       if (i == CS_SUCCESS) break;
+       i = next_tuple(link->handle, &tuple, &parse);
+    }
+    if (i != CS_SUCCESS)
+       return i;
+    dev->base_addr = link->io.BasePort1;
+    
+    /* Allocate a memory window, for accessing the ISR */
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    req.Base = 0;
+    req.Size = 0x1000;
+    req.AccessSpeed = 0;
+    link->win = (window_handle_t)link->handle;
+    i = CardServices(RequestWindow, &link->win, &req);
+    if (i != CS_SUCCESS)
+       return i;
+    lp->base = ioremap(req.Base, 0x1000);
+    mem.CardOffset = mem.Page = 0;
+    if (lp->manfid == MANFID_MOTOROLA)
+       mem.CardOffset = link->conf.ConfigBase;
+    i = CardServices(MapMemPage, link->win, &mem);
+    
+    if ((i == CS_SUCCESS)
+       && (lp->manfid == MANFID_MEGAHERTZ)
+       && (lp->cardid == PRODID_MEGAHERTZ_EM3288))
+       mhz_3288_power(link);
+    
+    return i;
+}
+
+static int mhz_setup(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255], *station_addr;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+
+    /* Read the station address from the CIS.  It is stored as the last
+       (fourth) string in the Version 1 Version/ID tuple. */
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+       return -1;
+    /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
+    if (next_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+       first_tuple(handle, &tuple, &parse);
+    if (parse.version_1.ns > 3) {
+       station_addr = parse.version_1.str + parse.version_1.ofs[3];
+       if (cvt_ascii_address(dev, station_addr) == 0)
+           return 0;
+    }
+
+    /* Another possibility: for the EM3288, in a special tuple */
+    tuple.DesiredTuple = 0x81;
+    if (CardServices(GetFirstTuple, handle, &tuple) != CS_SUCCESS)
+       return -1;
+    if (CardServices(GetTupleData, handle, &tuple) != CS_SUCCESS)
+       return -1;
+    buf[12] = '\0';
+    if (cvt_ascii_address(dev, buf) == 0)
+       return 0;
+    
+    return -1;
+}
+
+/*======================================================================
+
+    Configuration stuff for the Motorola Mariner
+
+    mot_config() writes directly to the Mariner configuration
+    registers because the CIS is just bogus.
+    
+======================================================================*/
+
+static void mot_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *lp = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    ioaddr_t iouart = link->io.BasePort2;
+    
+    /* Set UART base address and force map with COR bit 1 */
+    writeb(iouart & 0xff,        lp->base + MOT_UART + CISREG_IOBASE_0);
+    writeb((iouart >> 8) & 0xff, lp->base + MOT_UART + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           lp->base + MOT_UART + CISREG_COR);
+    
+    /* Set SMC base address and force map with COR bit 1 */
+    writeb(ioaddr & 0xff,        lp->base + MOT_LAN + CISREG_IOBASE_0);
+    writeb((ioaddr >> 8) & 0xff, lp->base + MOT_LAN + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           lp->base + MOT_LAN + CISREG_COR);
+
+    /* Wait for things to settle down */
+    mdelay(100);
+}
+
+static int mot_setup(dev_link_t *link) {
+    struct net_device *dev = link->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int i, wait, loop;
+    unsigned int addr;
+
+    /* Read Ethernet address from Serial EEPROM */
+    
+    for (i = 0; i < 3; i++) {
+       SMC_SELECT_BANK( 2 );
+       outw(MOT_EEPROM + i, ioaddr + POINTER);
+       SMC_SELECT_BANK( 1 );
+       outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
+
+       for (loop = 0; loop < 200; loop++) {
+           udelay(10);
+           wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
+           if (wait == 0) break;
+       }
+       
+       if (wait)
+           return -1;
+       
+       addr = inw(ioaddr + GENERAL);
+       dev->dev_addr[2*i]   = addr & 0xff;
+       dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
+    }
+    
+    return 0;
+}
+
+/*====================================================================*/
+
+static int smc_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    int i;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+    link->io.NumPorts1 = 16;
+    i = first_tuple(link->handle, &tuple, &parse);
+    while (i != CS_NO_MORE_ITEMS) {
+       if (i == CS_SUCCESS) {
+           link->conf.ConfigIndex = cf->index;
+           link->io.BasePort1 = cf->io.win[0].base;
+           i = CardServices(RequestIO, link->handle, &link->io);
+           if (i == CS_SUCCESS) break;
+       }
+       i = next_tuple(link->handle, &tuple, &parse);
+    }
+    if (i == CS_SUCCESS)
+       dev->base_addr = link->io.BasePort1;
+    return i;
+}
+
+static int smc_setup(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_lan_node_id_t *node_id;
+    u_char buf[255], *station_addr;
+    int i;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    
+    /* Check for a LAN function extension tuple */
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+       if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID)
+           break;
+       i = next_tuple(handle, &tuple, &parse);
+    }
+    if (i == CS_SUCCESS) {
+       node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+       if (node_id->nb == 6) {
+           for (i = 0; i < 6; i++)
+               dev->dev_addr[i] = node_id->id[i];
+           return 0;
+       }
+    }
+    
+    /* Try the third string in the Version 1 Version/ID tuple. */
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+       return -1;
+    station_addr = parse.version_1.str + parse.version_1.ofs[2];
+    if (cvt_ascii_address(dev, station_addr) == 0)
+       return 0;
+
+    return -1;
+}
+
+/*====================================================================*/
+
+static int osi_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    static ioaddr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+    int i, j;
+    
+    link->conf.Attributes |= CONF_ENABLE_SPKR;
+    link->conf.Status = CCSR_AUDIO_ENA;
+    link->irq.Attributes =
+       IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+    link->io.NumPorts1 = 64;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 8;
+    
+    /* Enable Hard Decode, LAN, Modem */
+    link->conf.ConfigIndex = 0x23;
+    
+    for (j = 0; j < 4; j++) {
+       link->io.BasePort2 = com[j];
+       i = CardServices(RequestIO, link->handle, &link->io);
+       if (i == CS_SUCCESS) break;
+    }
+    if (i != CS_SUCCESS) {
+       /* Fallback: turn off hard decode */
+       link->conf.ConfigIndex = 0x03;
+       link->io.NumPorts2 = 0;
+       i = CardServices(RequestIO, link->handle, &link->io);
+    }
+    dev->base_addr = link->io.BasePort1 + 0x10;
+    return i;
+}
+
+static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    u_char buf[255];
+    int i;
+    
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    
+    /* Read the station address from tuple 0x90, subtuple 0x04 */
+    tuple.DesiredTuple = 0x90;
+    i = CardServices(GetFirstTuple, handle, &tuple);
+    while (i == CS_SUCCESS) {
+       i = CardServices(GetTupleData, handle, &tuple);
+       if ((i != CS_SUCCESS) || (buf[0] == 0x04))
+           break;
+       i = CardServices(GetNextTuple, handle, &tuple);
+    }
+    if (i != CS_SUCCESS)
+       return -1;
+    for (i = 0; i < 6; i++)
+       dev->dev_addr[i] = buf[i+2];
+
+    if (manfid != MANFID_OSITECH) return 0;
+
+    if (cardid == PRODID_OSITECH_SEVEN) {
+       /* Download the Seven of Diamonds firmware */
+       for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+           outb(__Xilinx7OD[i], link->io.BasePort1+2);
+           udelay(50);
+       }
+    } else {
+       /* Make sure both functions are powered up */
+       set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR);
+       /* Now, turn on the interrupt for both card functions */
+       set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR);
+       DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+             inw(link->io.BasePort1 + OSITECH_AUI_PWR),
+             inw(link->io.BasePort1 + OSITECH_RESET_ISR));
+    }
+
+    return 0;
+}
+
+/*======================================================================
+
+    This verifies that the chip is some SMC91cXX variant, and returns
+    the revision code if successful.  Otherwise, it returns -ENODEV.
+    
+======================================================================*/
+
+static int check_sig(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int width;
+    u_short s;
+
+    SMC_SELECT_BANK(1);
+    if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
+       /* Try powering up the chip */
+       outw(0, ioaddr + CONTROL);
+       mdelay(55);
+    }
+
+    /* Try setting bus width */
+    width = (link->io.Attributes1 == IO_DATA_PATH_WIDTH_AUTO);
+    s = inb(ioaddr + CONFIG);
+    if (width)
+       s |= CFG_16BIT;
+    else
+       s &= ~CFG_16BIT;
+    outb(s, ioaddr + CONFIG);
+    
+    /* Check Base Address Register to make sure bus width is OK */
+    s = inw(ioaddr + BASE_ADDR);
+    if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
+       ((s >> 8) != (s & 0xff))) {
+       SMC_SELECT_BANK(3);
+       s = inw(ioaddr + REVISION);
+       return (s & 0xff);
+    }
+
+    if (width) {
+       event_callback_args_t args;
+       printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n");
+       args.client_data = link;
+       smc91c92_event(CS_EVENT_RESET_PHYSICAL, 0, &args);
+       CardServices(ReleaseIO, link->handle, &link->io);
+       link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+       CardServices(RequestIO, link->handle, &link->io);
+       smc91c92_event(CS_EVENT_CARD_RESET, 0, &args);
+       return check_sig(link);
+    }
+    return -ENODEV;
+}
+
+/*======================================================================
+
+    smc91c92_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+
+======================================================================*/
+
+#define CS_EXIT_TEST(ret, svc, label) \
+if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; }
+
+static void smc91c92_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    struct smc_private *lp = dev->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_short buf[32];
+    char *name;
+    int i, rev;
+
+    DEBUG(0, "smc91c92_config(0x%p)\n", link);
+    
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    i = first_tuple(handle, &tuple, &parse);
+    CS_EXIT_TEST(i, ParseTuple, config_failed);
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    tuple.DesiredTuple = CISTPL_MANFID;
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+       lp->manfid = parse.manfid.manf;
+       lp->cardid = parse.manfid.card;
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    if (lp->manfid == MANFID_OSITECH) {
+       i = osi_config(link);
+    } else if (lp->manfid == MANFID_MOTOROLA
+            || ((lp->manfid == MANFID_MEGAHERTZ)
+                && ((lp->cardid == PRODID_MEGAHERTZ_VARIOUS)
+                    || (lp->cardid == PRODID_MEGAHERTZ_EM3288)))) {
+       i = mhz_mfc_config(link);
+    } else {
+       i = smc_config(link);
+    }
+    CS_EXIT_TEST(i, RequestIO, config_failed);
+    
+    i = CardServices(RequestIRQ, link->handle, &link->irq);
+    CS_EXIT_TEST(i, RequestIRQ, config_failed);
+    i = CardServices(RequestConfiguration, link->handle, &link->conf);
+    CS_EXIT_TEST(i, RequestConfiguration, config_failed);
+    
+    if (lp->manfid == MANFID_MOTOROLA)
+       mot_config(link);
+
+    dev->irq = link->irq.AssignedIRQ;
+
+    if ((if_port >= 0) && (if_port <= 2))
+       dev->if_port = if_port;
+    else
+       printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n");
+    dev->tbusy = 0;
+    
+    if (register_netdev(dev) != 0) {
+       printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
+       goto config_undo;
+    }
+
+    switch (lp->manfid) {
+    case MANFID_OSITECH:
+    case MANFID_PSION:
+       i = osi_setup(link, lp->manfid, lp->cardid); break;
+    case MANFID_SMC:
+    case MANFID_NEW_MEDIA:
+       i = smc_setup(link); break;
+    case 0x128: /* For broken Megahertz cards */
+    case MANFID_MEGAHERTZ:
+       i = mhz_setup(link); break;
+    case MANFID_MOTOROLA:
+    default: /* get the hw address from EEPROM */
+       i = mot_setup(link); break;
+    }
+    
+    if (i != 0) {
+       printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n");
+       link->state &= ~DEV_CONFIG_PENDING;
+       goto config_undo;
+    }
+
+    link->dev = &lp->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    rev = check_sig(link);
+    name = "???";
+    if (rev > 0)
+       switch (rev >> 4) {
+       case 3: name = "92"; break;
+       case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
+       case 5: name = "95"; break;
+       case 7: name = "100"; break;
+       case 8: name = "100-FD"; break;
+       }
+    printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, "
+          "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr,
+          dev->irq);
+    for (i = 0; i < 6; i++)
+       printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+    if (rev > 0) {
+       u_long mir, mcr, mii;
+       ioaddr_t ioaddr = dev->base_addr;
+       SMC_SELECT_BANK(0);
+       mir = inw(ioaddr + MEMINFO) & 0xff;
+       if (mir == 0xff) mir++;
+       /* Get scale factor for memory size */
+       mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
+       mir *= 128 * (1<<((mcr >> 9) & 7));
+       if (mir & 0x3ff)
+           printk(KERN_INFO "  %lu byte", mir);
+       else
+           printk(KERN_INFO "  %lu kb", mir>>10);
+       SMC_SELECT_BANK(1);
+       mii = inw(ioaddr + CONFIG) & CFG_MII_SELECT;
+       printk(" buffer, %s xcvr\n", mii ? "MII" : if_names[dev->if_port]);
+    }
+    
+    return;
+    
+config_undo:
+    unregister_netdev(dev);
+config_failed:                 /* CS_EXIT_TEST() calls jump to here... */
+    smc91c92_release((u_long)link);
+    
+} /* smc91c92_config */
+
+/*======================================================================
+
+    After a card is removed, smc91c92_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void smc91c92_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+    struct net_device *dev = link->priv;
+
+    DEBUG(0, "smc91c92_release(0x%p)\n", link);
+    
+    if (link->open) {
+       DEBUG(1, "smc91c92_cs: release postponed, '%s' still open\n",
+             dev->name);
+       link->state |= DEV_STALE_CONFIG;
+       return;
+    }
+    
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    if (link->win) {
+       struct smc_private *lp = dev->priv;
+       iounmap(lp->base);
+       CardServices(ReleaseWindow, link->win);
+    }
+    
+    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* smc91c92_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int smc91c92_event(event_t event, int priority,
+                         event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+    
+    DEBUG(1, "smc91c92_event(0x%06x)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+       link->state &= ~DEV_PRESENT;
+       if (link->state & DEV_CONFIG) {
+           dev->tbusy = 1;
+           dev->start = 0;
+           link->release.expires = jiffies + HZ/20;
+           link->state |= DEV_RELEASE_PENDING;
+           add_timer(&link->release);
+       }
+       break;
+    case CS_EVENT_CARD_INSERTION:
+       link->state |= DEV_PRESENT;
+       smc91c92_config(link);
+       break;
+    case CS_EVENT_PM_SUSPEND:
+       link->state |= DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+       if (link->state & DEV_CONFIG) {
+           if (link->open) {
+               dev->tbusy = 1; dev->start = 0;
+           }
+           CardServices(ReleaseConfiguration, link->handle);
+       }
+       break;
+    case CS_EVENT_PM_RESUME:
+       link->state &= ~DEV_SUSPEND;
+       /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+       if (link->state & DEV_CONFIG) {
+           struct smc_private *lp = dev->priv;
+           if ((lp->manfid == MANFID_MEGAHERTZ) &&
+               (lp->cardid == PRODID_MEGAHERTZ_EM3288))
+               mhz_3288_power(link);
+           CardServices(RequestConfiguration, link->handle, &link->conf);
+           if (lp->manfid == MANFID_MOTOROLA)
+               mot_config(link);
+           if ((lp->manfid == MANFID_OSITECH) &&
+               (lp->cardid != PRODID_OSITECH_SEVEN)) {
+               /* Power up the card and enable interrupts */
+               set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+               set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+           }
+           if (link->open) {
+               smc_reset(dev);
+               dev->tbusy = 0; dev->start = 1;
+           }
+       }
+       break;
+    }
+    return 0;
+} /* smc91c92_event */
+
+/*======================================================================
+  
+    The driver core code, most of which should be common with a
+    non-PCMCIA implementation.
+    
+======================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static void smc_dump(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    u_short i, w, save;
+    save = inw(ioaddr + BANK_SELECT);
+    for (w = 0; w < 4; w++) {
+       SMC_SELECT_BANK(w);
+       printk(KERN_DEBUG "bank %d: ", w);
+       for (i = 0; i < 14; i += 2)
+           printk(" %04x", inw(ioaddr + i));
+       printk("\n");
+    }
+    outw(save, ioaddr + BANK_SELECT);
+}
+#endif
+
+static int smc91c92_open(struct net_device *dev)
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    dev_link_t *link;
+
+#ifdef PCMCIA_DEBUG
+    DEBUG(0, "%s: smc91c92_open(%p), ID/Window %4.4x.\n",
+         dev->name, dev, inw(dev->base_addr + BANK_SELECT));
+    if (pc_debug > 1) smc_dump(dev);
+#endif
+    
+    /* Check that the PCMCIA card is still here. */
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    /* Physical device present signature. */
+    if (!DEV_OK(link))
+       return -ENODEV;
+    if (check_sig(link) < 0) {
+       printk("smc91c92_cs: Yikes!  Bad chip signature!\n");
+       return -ENODEV;
+    }
+    link->open++;
+    MOD_INC_USE_COUNT;
+    
+    dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+    lp->saved_skb = 0;
+    lp->packets_waiting = 0;
+    
+    smc_reset(dev);
+    lp->media.function = &media_check;
+    lp->media.data = (u_long)dev;
+    lp->media.expires = jiffies + HZ;
+    add_timer(&lp->media);
+    
+    return 0;
+} /* smc91c92_open */
+
+/*====================================================================*/
+
+static int smc91c92_close(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    dev_link_t *link;
+
+    DEBUG(0, "%s: smc91c92_close(), status %4.4x.\n",
+         dev->name, inw(ioaddr + BANK_SELECT));
+    
+    dev->tbusy = 1;
+    dev->start = 0;
+    
+    /* Shut off all interrupts, and turn off the Tx and Rx sections.
+       Don't bother to check for chip present. */
+    SMC_SELECT_BANK( 2 );      /* Nominally paranoia, but do no assume... */
+    outw(0, ioaddr + INTERRUPT);
+    SMC_SELECT_BANK( 0 );
+    mask_bits(0xff00, ioaddr + RCR);
+    mask_bits(0xff00, ioaddr + TCR);
+    
+    /* Put the chip into power-down mode. */
+    SMC_SELECT_BANK( 1 );
+    outw(CTL_POWERDOWN, ioaddr + CONTROL  );
+    
+    for (link = dev_list; link; link = link->next)
+       if (link->priv == dev) break;
+    if (link == NULL)
+       return -ENODEV;
+    
+    link->open--; dev->start = 0;
+    del_timer(&((struct smc_private *)dev->priv)->media);
+    if (link->state & DEV_STALE_CONFIG) {
+       link->release.expires = jiffies + HZ/20;
+       link->state |= DEV_RELEASE_PENDING;
+       add_timer(&link->release);
+    }
+    
+    MOD_DEC_USE_COUNT;
+    
+    return 0;
+} /* smc91c92_close */
+
+/*======================================================================
+
+   Transfer a packet to the hardware and trigger the packet send.
+   This may be called at either from either the Tx queue code
+   or the interrupt handler.
+   
+======================================================================*/
+
+static void smc_hardware_send_packet( struct net_device * dev )
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    struct sk_buff *skb = lp->saved_skb;
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned char packet_no;
+    
+    if ( !skb ) {
+       printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name);
+       return;
+    }
+    
+    /* There should be a packet slot waiting. */
+    packet_no = inw(ioaddr + PNR_ARR) >> 8;
+    if ( packet_no & 0x80 ) {
+       /* If not, there is a hardware problem!  Likely an ejected card. */
+       printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation"
+              " failed, status %#2.2x.\n", dev->name, packet_no);
+       dev_kfree_skb (skb);
+       lp->saved_skb = NULL;
+       dev->tbusy = 0;
+       return;
+    }
+    
+    lp->stats.tx_bytes += skb->len;
+    /* The card should use the just-allocated buffer. */
+    outw( packet_no, ioaddr + PNR_ARR );
+    /* point to the beginning of the packet */
+    outw( PTR_AUTOINC , ioaddr + POINTER );
+    
+    /* Send the packet length ( +6 for status, length and ctl byte )
+       and the status word ( set to zeros ). */
+    {
+       unsigned char *buf = skb->data;
+       int length = skb->len;  /* The chip will pad to ethernet min length. */
+
+       DEBUG(2, "%s: Trying to xmit packet of length %d.\n",
+             dev->name, length);
+       
+#ifdef USE_32_BIT
+       outl((length+6) << 16, ioaddr + DATA_1);
+       if (length & 0x2) {
+           outsl(ioaddr + DATA_1, buf,  length >> 2 );
+           outw( *((uint16 *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+       } else
+           outsl(ioaddr + DATA_1, buf,  length >> 2 );
+#else
+       /* send the packet length: +6 for status words, length, and ctl */
+       outw( 0, ioaddr + DATA_1 );
+       outw(length + 6, ioaddr + DATA_1 );
+       outsw(ioaddr + DATA_1 , buf, length >> 1);
+#endif
+       
+       /* The odd last byte, if there is one, goes in the control word. */
+       outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1 );
+    }
+    
+    /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
+    outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
+        (inw(ioaddr + INTERRUPT) & 0xff00),
+        ioaddr + INTERRUPT);
+    
+    /* The chip does the rest of the work. */
+    outw( MC_ENQUEUE , ioaddr + MMU_CMD );
+    
+    lp->saved_skb = NULL;
+    dev_kfree_skb (skb);
+    dev->trans_start = jiffies;
+    dev->tbusy = 0;
+    return;
+}
+
+/*====================================================================*/
+
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned short num_pages;
+    short time_out, ir;
+    
+    /* Transmitter timeout, serious problems. */
+    if (dev->tbusy) {
+       int tickssofar = jiffies - dev->trans_start;
+       if (tickssofar < TX_TIMEOUT)
+           return 1;
+       printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, "
+              "Tx_status %2.2x status %4.4x.\n",
+              dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2));
+       lp->stats.tx_errors++;
+       smc_reset(dev);
+       dev->trans_start = jiffies;
+       dev->tbusy = 0;
+       lp->saved_skb = NULL;
+    }
+
+    DEBUG(2, "%s: smc91c92_start_xmit(length = %d) called,"
+         " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2));
+    
+    /* Avoid timer-based retransmission conflicts. */
+    if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+       printk(KERN_ERR "%s: transmitter access conflict.\n", dev->name);
+       return 1;
+    }
+    
+    if ( lp->saved_skb) {
+       /* THIS SHOULD NEVER HAPPEN. */
+       lp->stats.tx_aborted_errors++;
+       printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n",
+              dev->name);
+       return 1;
+    }
+    lp->saved_skb = skb;
+    
+    num_pages = skb->len >> 8;
+    
+    if (num_pages > 7) {
+       printk(KERN_ERR "%s: Far too big packet error.\n", dev->name);
+       dev_kfree_skb (skb);
+       lp->saved_skb = NULL;
+       lp->stats.tx_dropped++;
+       return 0;               /* Do not re-queue this packet. */
+    }
+    /* A packet is now waiting. */
+    lp->packets_waiting++;
+    
+    SMC_SELECT_BANK( 2 );      /* Paranoia, we should always be in window 2 */
+    
+    /* Allocate the memory; send the packet now if we win. */
+    outw( MC_ALLOC | num_pages, ioaddr + MMU_CMD );
+    for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
+       ir = inw(ioaddr+INTERRUPT);
+       if (ir & IM_ALLOC_INT) {
+           /* Acknowledge the interrupt, send the packet. */
+           outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
+           smc_hardware_send_packet(dev);      /* Send the packet now.. */
+           return 0;
+       }
+    }
+    
+    /* Otherwise defer until the Tx-space-allocated interrupt. */
+    DEBUG(2, "%s: memory allocation deferred.\n", dev->name);
+    outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
+    
+    return 0;
+}
+
+/*======================================================================
+
+    Handle a Tx anomolous event.  Entered while in Window 2.
+    
+======================================================================*/
+
+static void smc_tx_err( struct net_device * dev )
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
+    int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
+    int tx_status;
+    
+    /* select this as the packet to read from */
+    outw( packet_no, ioaddr + PNR_ARR );
+    
+    /* read the first word from this packet */
+    outw( PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER );
+    
+    tx_status = inw(ioaddr + DATA_1);
+
+    lp->stats.tx_errors++;
+    if (tx_status & TS_LOSTCAR) lp->stats.tx_carrier_errors++;
+    if (tx_status & TS_LATCOL)  lp->stats.tx_window_errors++;
+    if (tx_status & TS_16COL) {
+       lp->stats.tx_aborted_errors++;
+       lp->tx_err++;
+    }
+    
+    if ( tx_status & TS_SUCCESS ) {
+       printk(KERN_NOTICE "%s: Successful packet caused error "
+              "interrupt?\n", dev->name);
+    }
+    /* re-enable transmit */
+    SMC_SELECT_BANK( 0 );
+    outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+    SMC_SELECT_BANK( 2 );
+    
+    outw( MC_FREEPKT, ioaddr + MMU_CMD );      /* Free the packet memory. */
+    
+    /* one less packet waiting for me */
+    lp->packets_waiting--;
+    
+    outw( saved_packet, ioaddr + PNR_ARR );
+    return;
+}
+
+/*====================================================================*/
+
+static void smc_eph_irq(struct net_device *dev)
+{
+    struct smc_private *lp = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned short card_stats, ephs;
+    
+    SMC_SELECT_BANK(0);
+    ephs = inw(ioaddr + EPH);
+    DEBUG(2, "%s: Ethernet protocol handler interrupt, status"
+         " %4.4x.\n", dev->name, ephs);
+    /* Could be a counter roll-over warning: update stats. */
+    card_stats = inw( ioaddr + COUNTER );
+    /* single collisions */
+    lp->stats.collisions += card_stats & 0xF;
+    card_stats >>= 4;
+    /* multiple collisions */
+    lp->stats.collisions += card_stats & 0xF;
+#if 0          /* These are for when linux supports these statistics */
+    card_stats >>= 4;                  /* deferred */
+    card_stats >>= 4;                  /* excess deferred */
+#endif
+    /* If we had a transmit error we must re-enable the transmitter. */
+    outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR);
+
+    /* Clear a link error interrupt. */
+    SMC_SELECT_BANK(1);
+    outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+        ioaddr + CONTROL);
+    SMC_SELECT_BANK(2);
+}
+
+/*====================================================================*/
+    
+static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = dev_id;
+    struct smc_private *lp;
+    ioaddr_t ioaddr;
+    u_short saved_bank, saved_pointer, mask, status;
+    char bogus_cnt = INTR_WORK;                /* Work we are willing to do. */
+
+    if ((dev == NULL) || !dev->start)
+       return;
+    ioaddr = dev->base_addr;
+    
+#ifdef PCMCIA_DEBUG
+    if (dev->interrupt) {
+       printk(KERN_ERR "%s: re-entering the interrupt handler.\n",
+              dev->name);
+       return;
+    }
+    dev->interrupt = 1;
+
+    DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+         irq, ioaddr);
+#endif
+    
+    lp = (struct smc_private *)dev->priv;
+    lp->watchdog = 0;
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    if ((saved_bank & 0xff00) != 0x3300) {
+       /* The device does not exist -- the card could be off-line, or
+          maybe it has been ejected. */
+#ifdef PCMCIA_DEBUG
+       if (dev->start)
+           DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent"
+                 "/ejected device.\n", dev->name, irq);
+       dev->interrupt = 0;
+#endif
+       goto irq_done;
+    }
+    
+    SMC_SELECT_BANK(2);
+    saved_pointer = inw(ioaddr + POINTER);
+    mask = inw(ioaddr + INTERRUPT) >> 8;
+    /* clear all interrupts */
+    outw( 0, ioaddr + INTERRUPT );
+    
+    do { /* read the status flag, and mask it */
+       status = inw(ioaddr + INTERRUPT) & 0xff;
+       DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+             status, mask);
+       if ((status & mask) == 0)
+           break;
+       
+       if (status & IM_RCV_INT) {
+           /* Got a packet(s). */
+           smc_rx(dev);
+           lp->last_rx = jiffies;
+       }
+       if (status & IM_TX_INT) {
+           smc_tx_err(dev);
+           outw(IM_TX_INT, ioaddr + INTERRUPT);
+       }
+       status &= mask;
+       if (status & IM_TX_EMPTY_INT) {
+           outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
+           mask &= ~IM_TX_EMPTY_INT;
+           lp->stats.tx_packets += lp->packets_waiting;
+           lp->packets_waiting = 0;
+       }
+       if (status & IM_ALLOC_INT) {
+           /* Clear this interrupt so it doesn't happen again */
+           mask &= ~IM_ALLOC_INT;
+           
+           smc_hardware_send_packet(dev);
+           
+           /* enable xmit interrupts based on this */
+           mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
+           
+           /* and let the card send more packets to me */
+           mark_bh( NET_BH );
+       }
+       if (status & IM_RX_OVRN_INT) {
+           lp->stats.rx_errors++;
+           lp->stats.rx_fifo_errors++;         
+           outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
+       }
+       if (status & IM_EPH_INT)
+           smc_eph_irq(dev);
+    } while ( --bogus_cnt);
+
+    DEBUG(3, "  Restoring saved registers mask %2.2x bank %4.4x"
+         " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+    
+    /* restore state register */
+    outw((mask<<8), ioaddr + INTERRUPT);
+    outw(saved_pointer, ioaddr + POINTER);
+    SMC_SELECT_BANK( saved_bank );
+
+#ifdef PCMCIA_DEBUG
+    dev->interrupt = 0;
+    DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+#endif
+
+irq_done:
+    
+    if ((lp->manfid == MANFID_OSITECH) &&
+       (lp->cardid != PRODID_OSITECH_SEVEN)) {
+       /* Retrigger interrupt if needed */
+       mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
+       set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
+    }
+    if (lp->manfid == MANFID_MOTOROLA) {
+       u_char cor;
+       cor = readb(lp->base + MOT_UART + CISREG_COR);
+       writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_UART + CISREG_COR);
+       writeb(cor, lp->base + MOT_UART + CISREG_COR);
+       cor = readb(lp->base + MOT_LAN + CISREG_COR);
+       writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_LAN + CISREG_COR);
+       writeb(cor, lp->base + MOT_LAN + CISREG_COR);
+    }
+#ifdef DOES_NOT_WORK
+    if (lp->base != NULL) { /* Megahertz MFC's */
+       readb(lp->base+MEGAHERTZ_ISR);
+       readb(lp->base+MEGAHERTZ_ISR);
+    }
+#endif
+}
+
+/*====================================================================*/
+
+static void smc_rx(struct net_device *dev)
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int rx_status;
+    int packet_length; /* Caution: not frame length, rather words
+                          to transfer from the chip. */
+    
+    /* Assertion: we are in Window 2. */
+    
+    if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
+       printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n",
+              dev->name);
+       return;
+    }
+    
+    /*  Reset the read pointer, and read the status and packet length. */
+    outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
+    rx_status = inw(ioaddr + DATA_1);
+    packet_length = inw(ioaddr + DATA_1) & 0x07ff;
+
+    DEBUG(2, "%s: Receive status %4.4x length %d.\n",
+         dev->name, rx_status, packet_length);
+    
+    if ( !(rx_status & RS_ERRORS )) {          
+       /* do stuff to make a new packet */
+       struct sk_buff *skb;
+       
+       /* Note: packet_length adds 5 or 6 extra bytes here! */
+       skb = dev_alloc_skb(packet_length+2);
+       
+       if ( skb == NULL ) {
+           DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name);
+           lp->stats.rx_dropped++;
+           outw( MC_RELEASE, ioaddr + MMU_CMD );
+           return;
+       }
+       
+       packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
+       skb_reserve(skb, 2);
+       insw(ioaddr+DATA_1, skb_put(skb, packet_length),
+               (packet_length+1)>>1);
+       skb->protocol = eth_type_trans(skb, dev);
+       
+       skb->dev = dev;
+       netif_rx(skb);
+       lp->stats.rx_packets++;
+       lp->stats.rx_bytes += skb->len;
+       if (rx_status & RS_MULTICAST)
+           lp->stats.multicast++;
+    } else {
+       /* error ... */
+       lp->stats.rx_errors++;
+       
+       if (rx_status & RS_ALGNERR)  lp->stats.rx_frame_errors++;
+       if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
+           lp->stats.rx_length_errors++;
+       if (rx_status & RS_BADCRC)      lp->stats.rx_crc_errors++;
+    }
+    /* Let the MMU free the memory of this packet. */
+    outw(MC_RELEASE, ioaddr + MMU_CMD);
+    
+    return;
+}
+
+/*====================================================================*/
+
+static struct net_device_stats *smc91c92_get_stats(struct net_device *dev)
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    /* Nothing to update - the 91c92 is a pretty primative chip. */
+    return &lp->stats;
+}
+
+/*======================================================================
+
+    Compute the AUTODIN polynomial "CRC32" for ethernet packets.
+
+======================================================================*/
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+
+static unsigned ether_crc(int length, unsigned char *data)
+{
+    int crc = 0xffffffff;      /* Initial value. */
+    
+    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);
+       }
+    }
+    /* The hash index is the either the upper or lower bits of the CRC, so
+     * we return the entire CRC.
+     */
+    return crc;
+}
+
+/*======================================================================
+  
+    Calculate values for the hardware multicast filter hash table.
+    
+======================================================================*/
+
+static void fill_multicast_tbl(int count, struct dev_mc_list *addrs,
+                              unsigned char *multicast_table)
+{
+    struct dev_mc_list *mc_addr;
+    
+    for (mc_addr = addrs;  mc_addr && --count > 0;  mc_addr = mc_addr->next) {
+       unsigned int position = ether_crc(6, mc_addr->dmi_addr);
+#ifndef final_version          /* Verify multicast address. */
+       if ( (mc_addr->dmi_addr[0] & 1) == 0)
+           continue;
+#endif
+       multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
+    }
+}
+
+/*======================================================================
+  
+    Set the receive mode.
+    
+    This routine is used by both the protocol level to notify us of
+    promiscuous/multicast mode changes, and by the open/reset code to
+    initialize the Rx registers.  We always set the multicast list and
+    leave the receiver running.
+    
+======================================================================*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned int multicast_table[ 2 ] = { 0, };
+    long flags;
+    uint16 rx_cfg_setting;
+    
+    if (dev->flags & IFF_PROMISC) {
+       printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name);
+       rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
+    } else if (dev->flags & IFF_ALLMULTI)
+       rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
+    else {
+       if (dev->mc_count)  {
+           fill_multicast_tbl(dev->mc_count, dev->mc_list,
+                              (unsigned char *)multicast_table);
+       }
+       rx_cfg_setting = RxStripCRC | RxEnable;
+    }
+    
+    /* Load MC table and Rx setting into the chip without interrupts. */
+    save_flags(flags);
+    cli();
+    SMC_SELECT_BANK( 3 );
+    outl(multicast_table[0], ioaddr + MULTICAST0);
+    outl(multicast_table[1], ioaddr + MULTICAST4);
+    SMC_SELECT_BANK(0);
+    outw(rx_cfg_setting, ioaddr + RCR);
+    SMC_SELECT_BANK(2);
+    restore_flags(flags);
+    
+    return;
+}
+
+/*======================================================================
+
+    Senses when a card's config changes. Here, it's coax or TP.
+======================================================================*/
+
+static int s9k_config(struct net_device *dev, struct ifmap *map)
+{
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+       if (map->port <= 2) {
+           dev->if_port = map->port;
+           printk(KERN_INFO "%s: switched to %s port\n",
+                  dev->name, if_names[dev->if_port]);
+       } else
+           return -EINVAL;
+    }
+    smc_reset(dev);
+    return 0;
+}
+
+/*======================================================================
+
+    Reset the chip, reloading every register that might be corrupted.
+
+======================================================================*/
+
+/*
+  Set transceiver type, perhaps to something other than what the user
+  specified in dev->if_port.
+*/
+static void smc_set_xcvr(struct net_device *dev, int if_port)
+{
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    u_short saved_bank;
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    SMC_SELECT_BANK(1);
+    if (if_port == 2) {
+       outw(lp->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
+       if ((lp->manfid == MANFID_OSITECH) &&
+           (lp->cardid != PRODID_OSITECH_SEVEN))
+           set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+       lp->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
+    } else {
+       outw(lp->cfg, ioaddr + CONFIG);
+       if ((lp->manfid == MANFID_OSITECH) &&
+           (lp->cardid != PRODID_OSITECH_SEVEN))
+           mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+       lp->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
+    }
+    SMC_SELECT_BANK(saved_bank);
+}
+
+static void smc_reset(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    struct smc_private *lp = dev->priv;
+    int i;
+
+    DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);
+    
+    /* The first interaction must be a write to bring the chip out
+       of sleep mode. */
+    SMC_SELECT_BANK( 0 );
+    /* Reset the chip. */
+    outw( RCR_SOFTRESET, ioaddr + RCR );
+    udelay(10);
+    
+    /* Clear the transmit and receive configuration registers. */
+    outw( RCR_CLEAR, ioaddr + RCR );
+    outw( TCR_CLEAR, ioaddr + TCR );
+    
+    /* Set the Window 1 control, configuration and station addr registers.
+       No point in writing the I/O base register ;-> */
+    SMC_SELECT_BANK(1);
+    /* Automatically release succesfully transmitted packets,
+       Accept link errors, counter and Tx error interrupts. */
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+        ioaddr + CONTROL);
+    lp->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
+    lp->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC |
+       (lp->manfid == MANFID_OSITECH ? (CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0) : 0);
+    smc_set_xcvr(dev, dev->if_port);
+    if ((lp->manfid == MANFID_OSITECH) &&
+       (lp->cardid != PRODID_OSITECH_SEVEN))
+       outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
+            (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
+            ioaddr - 0x10 + OSITECH_AUI_PWR);
+    
+    /* Fill in the physical address.  The databook is wrong about the order! */
+    for (i = 0; i < 6; i += 2)
+       outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
+            ioaddr + ADDR0 + i);
+    
+    /* Reset the MMU */
+    SMC_SELECT_BANK(2);
+    outw(MC_RESET, ioaddr + MMU_CMD );
+    outw(0, ioaddr + INTERRUPT );
+    
+    /* Re-enable the chip. */
+    SMC_SELECT_BANK(0);
+    outw(((lp->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
+        TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);
+    set_rx_mode(dev);
+
+    /* Enable interrupts. */
+    SMC_SELECT_BANK( 2 );
+    outw( (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
+         ioaddr + INTERRUPT );
+}
+
+/*======================================================================
+
+    Media selection timer routine
+    
+======================================================================*/
+
+static void media_check(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)(arg);
+    struct smc_private *lp = (struct smc_private *)dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    u_short i, media, saved_bank;
+
+    if (dev->start == 0) goto reschedule;
+    
+    lp = (struct smc_private *)dev->priv;
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    SMC_SELECT_BANK(2);
+    i = inw(ioaddr + INTERRUPT);
+    SMC_SELECT_BANK(0);
+    media = inw(ioaddr + EPH) & EPH_LINK_OK;
+    SMC_SELECT_BANK(1);
+    media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
+    SMC_SELECT_BANK(saved_bank);
+    
+    /* Check for pending interrupt with watchdog flag set: with
+       this, we can limp along even if the interrupt is blocked */
+    if (lp->watchdog++ && ((i>>8) & i)) {
+       if (!lp->fast_poll)
+           printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+       smc_interrupt(dev->irq, dev, NULL);
+       lp->fast_poll = HZ;
+    }
+    if (lp->fast_poll) {
+       lp->fast_poll--;
+       lp->media.expires = jiffies + 1;
+       add_timer(&lp->media);
+       return;
+    }
+
+    if (lp->cfg & CFG_MII_SELECT)
+       goto reschedule;
+
+    /* Ignore collisions unless we've had no rx's recently */
+    if (jiffies - lp->last_rx > HZ) {
+       if (lp->tx_err || (lp->media_status & EPH_16COL))
+           media |= EPH_16COL;
+    }
+    lp->tx_err = 0;
+
+    if (media != lp->media_status) {
+       if ((media & lp->media_status & 1) &&
+           ((lp->media_status ^ media) & EPH_LINK_OK))
+           printk(KERN_INFO "%s: %s link beat\n", dev->name,
+                  (lp->media_status & EPH_LINK_OK ? "lost" : "found"));
+       else if ((media & lp->media_status & 2) &&
+                ((lp->media_status ^ media) & EPH_16COL))
+           printk(KERN_INFO "%s: coax cable %s\n", dev->name,
+                  (media & EPH_16COL ? "problem" : "ok"));
+       if (dev->if_port == 0) {
+           if (media & 1) {
+               if (media & EPH_LINK_OK)
+                   printk(KERN_INFO "%s: flipped to 10baseT\n",
+                          dev->name);
+               else
+                   smc_set_xcvr(dev, 2);
+           } else {
+               if (media & EPH_16COL)
+                   smc_set_xcvr(dev, 1);
+               else
+                   printk(KERN_INFO "%s: flipped to 10base2\n",
+                          dev->name);
+           }
+       }
+       lp->media_status = media;
+    }
+    
+reschedule:
+    lp->media.expires = jiffies + HZ;
+    add_timer(&lp->media);
+}
+
+/*====================================================================*/
+
+static int __init init_smc91c92_cs(void)
+{
+    servinfo_t serv;
+    DEBUG(0, "%s\n", version);
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+       printk(KERN_ERR
+              "smc91c92_cs: Card Services release does not match!\n");
+       return -1;
+    }
+    register_pccard_driver(&dev_info, &smc91c92_attach, &smc91c92_detach);
+    return 0;
+}
+
+static void __exit exit_smc91c92_cs(void)
+{
+    DEBUG(0, "smc91c92_cs: unloading\n");
+    unregister_pccard_driver(&dev_info);
+    while (dev_list != NULL)
+       smc91c92_detach(dev_list);
+}
+
+module_init(init_smc91c92_cs);
+module_exit(exit_smc91c92_cs);
diff --git a/drivers/net/pcmcia/wavelan.h b/drivers/net/pcmcia/wavelan.h
new file mode 100644 (file)
index 0000000..0e1ee22
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *     Wavelan Pcmcia driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganization and extension of the driver.
+ * Original copyright follow. See wavelan_cs.h for details.
+ *
+ * This file contain the declarations of the Wavelan hardware. Note that
+ * the Pcmcia Wavelan include a i82593 controler (see definitions in
+ * file i82593.h).
+ *
+ * The main difference between the pcmcia hardware and the ISA one is
+ * the Ethernet Controler (i82593 instead of i82586). The i82593 allow
+ * only one send buffer. The PSA (Parameter Storage Area : EEprom for
+ * permanent storage of various info) is memory mapped, but not the
+ * MMI (Modem Management Interface).
+ */
+
+/*
+ * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: 
+ *   An Ethernet-like radio transceiver controlled by an Intel 82593
+ *   coprocessor.
+ *
+ *
+ ****************************************************************************
+ *   Copyright 1995
+ *   Anthony D. Joseph
+ *   Massachusetts Institute of Technology
+ *
+ *   Permission to use, copy, modify, and distribute this program
+ *   for any purpose and without fee is hereby granted, provided
+ *   that this copyright and permission notice appear on all copies
+ *   and supporting documentation, the name of M.I.T. not be used
+ *   in advertising or publicity pertaining to distribution of the
+ *   program without specific prior permission, and notice be given
+ *   in supporting documentation that copying and distribution is
+ *   by permission of M.I.T.  M.I.T. makes no representations about
+ *   the suitability of this software for any purpose.  It is pro-
+ *   vided "as is" without express or implied warranty.         
+ ****************************************************************************
+ *
+ *
+ * Credits:
+ *     Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for
+ *       providing extremely useful information about WaveLAN PCMCIA hardware
+ *
+ *     This driver is based upon several other drivers, in particular:
+ *       David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ *       Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ *      Anders Klemets' PCMCIA WaveLAN adapter driver
+ *       Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ */
+
+#ifndef _WAVELAN_H
+#define        _WAVELAN_H
+
+/************************** MAGIC NUMBERS ***************************/
+
+/* The detection of the wavelan card is made by reading the MAC address
+ * from the card and checking it. If you have a non AT&T product (OEM,
+ * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
+ * part to accomodate your hardware...
+ */
+const unsigned char    MAC_ADDRESSES[][3] =
+{
+  { 0x08, 0x00, 0x0E },                /* AT&T Wavelan (standard) & DEC RoamAbout */
+  { 0x08, 0x00, 0x6A },                /* AT&T Wavelan (alternate) */
+  { 0x00, 0x00, 0xE1 },                /* Hitachi Wavelan */
+  { 0x00, 0x60, 0x1D }         /* Lucent Wavelan (another one) */
+  /* Add your card here and send me the patch ! */
+};
+
+/*
+ * Constants used to convert channels to frequencies
+ */
+
+/* Frequency available in the 2.0 modem, in units of 250 kHz
+ * (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,
+                                   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 };
+
+/*************************** PC INTERFACE ****************************/
+
+/* WaveLAN host interface definitions */
+
+#define        LCCR(base)      (base)          /* LAN Controller Command Register */
+#define        LCSR(base)      (base)          /* LAN Controller Status Register */
+#define        HACR(base)      (base+0x1)      /* Host Adapter Command Register */
+#define        HASR(base)      (base+0x1)      /* Host Adapter Status Register */
+#define PIORL(base)    (base+0x2)      /* Program I/O Register Low */
+#define RPLL(base)     (base+0x2)      /* Receive Pointer Latched Low */
+#define PIORH(base)    (base+0x3)      /* Program I/O Register High */
+#define RPLH(base)     (base+0x3)      /* Receive Pointer Latched High */
+#define PIOP(base)     (base+0x4)      /* Program I/O Port */
+#define MMR(base)      (base+0x6)      /* MMI Address Register */
+#define MMD(base)      (base+0x7)      /* MMI Data Register */
+
+/* Host Adaptor Command Register bit definitions */
+
+#define HACR_LOF         (1 << 3)      /* Lock Out Flag, toggle every 250ms */
+#define HACR_PWR_STAT    (1 << 4)      /* Power State, 1=active, 0=sleep */
+#define HACR_TX_DMA_RESET (1 << 5)     /* Reset transmit DMA ptr on high */
+#define HACR_RX_DMA_RESET (1 << 6)     /* Reset receive DMA ptr on high */
+#define HACR_ROM_WEN     (1 << 7)      /* EEPROM write enabled when true */
+
+#define HACR_RESET              (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET)
+#define        HACR_DEFAULT            (HACR_PWR_STAT)
+
+/* Host Adapter Status Register bit definitions */
+
+#define HASR_MMI_BUSY  (1 << 2)        /* MMI is busy when true */
+#define HASR_LOF       (1 << 3)        /* Lock out flag status */
+#define HASR_NO_CLK    (1 << 4)        /* active when modem not connected */
+
+/* Miscellaneous bit definitions */
+
+#define PIORH_SEL_TX   (1 << 5)        /* PIOR points to 0=rx/1=tx buffer */
+#define MMR_MMI_WR     (1 << 0)        /* Next MMI cycle is 0=read, 1=write */
+#define PIORH_MASK     0x1f            /* only low 5 bits are significant */
+#define RPLH_MASK      0x1f            /* only low 5 bits are significant */
+#define MMI_ADDR_MASK  0x7e            /* Bits 1-6 of MMR are significant */
+
+/* Attribute Memory map */
+
+#define CIS_ADDR       0x0000          /* Card Information Status Register */
+#define PSA_ADDR       0x0e00          /* Parameter Storage Area address */
+#define EEPROM_ADDR    0x1000          /* EEPROM address (unused ?) */
+#define COR_ADDR       0x4000          /* Configuration Option Register */
+
+/* Configuration Option Register bit definitions */
+
+#define COR_CONFIG     (1 << 0)        /* Config Index, 0 when unconfigured */
+#define COR_SW_RESET   (1 << 7)        /* Software Reset on true */
+#define COR_LEVEL_IRQ  (1 << 6)        /* Level IRQ */
+
+/* Local Memory map */
+
+#define RX_BASE                0x0000          /* Receive memory, 8 kB */
+#define TX_BASE                0x2000          /* Transmit memory, 2 kB */
+#define UNUSED_BASE    0x2800          /* Unused, 22 kB */
+#define RX_SIZE                (TX_BASE-RX_BASE)       /* Size of receive area */
+#define RX_SIZE_SHIFT  6               /* Bits to shift in stop register */
+
+#define TRUE  1
+#define FALSE 0
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
+
+/* Size of a MAC address */
+#define WAVELAN_ADDR_SIZE      6
+
+/* Maximum size of Wavelan packet */
+#define WAVELAN_MTU    1500
+
+#define        MAXDATAZ                (6 + 6 + 2 + WAVELAN_MTU)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t   psa_t;
+struct psa_t
+{
+  /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */
+  unsigned char        psa_io_base_addr_1;     /* [0x00] Base address 1 ??? */
+  unsigned char        psa_io_base_addr_2;     /* [0x01] Base address 2 */
+  unsigned char        psa_io_base_addr_3;     /* [0x02] Base address 3 */
+  unsigned char        psa_io_base_addr_4;     /* [0x03] Base address 4 */
+  unsigned char        psa_rem_boot_addr_1;    /* [0x04] Remote Boot Address 1 */
+  unsigned char        psa_rem_boot_addr_2;    /* [0x05] Remote Boot Address 2 */
+  unsigned char        psa_rem_boot_addr_3;    /* [0x06] Remote Boot Address 3 */
+  unsigned char        psa_holi_params;        /* [0x07] HOst Lan Interface (HOLI) Parameters */
+  unsigned char        psa_int_req_no;         /* [0x08] Interrupt Request Line */
+  unsigned char        psa_unused0[7];         /* [0x09-0x0F] unused */
+
+  unsigned char        psa_univ_mac_addr[WAVELAN_ADDR_SIZE];   /* [0x10-0x15] Universal (factory) MAC Address */
+  unsigned char        psa_local_mac_addr[WAVELAN_ADDR_SIZE];  /* [0x16-1B] Local MAC Address */
+  unsigned char        psa_univ_local_sel;     /* [0x1C] Universal Local Selection */
+#define                PSA_UNIVERSAL   0               /* Universal (factory) */
+#define                PSA_LOCAL       1               /* Local */
+  unsigned char        psa_comp_number;        /* [0x1D] Compatability Number: */
+#define                PSA_COMP_PC_AT_915      0       /* PC-AT 915 MHz        */
+#define                PSA_COMP_PC_MC_915      1       /* PC-MC 915 MHz        */
+#define                PSA_COMP_PC_AT_2400     2       /* PC-AT 2.4 GHz        */
+#define                PSA_COMP_PC_MC_2400     3       /* PC-MC 2.4 GHz        */
+#define                PSA_COMP_PCMCIA_915     4       /* PCMCIA 915 MHz or 2.0 */
+  unsigned char        psa_thr_pre_set;        /* [0x1E] Modem Threshold Preset */
+  unsigned char        psa_feature_select;     /* [0x1F] Call code required (1=on) */
+#define                PSA_FEATURE_CALL_CODE   0x01    /* Call code required (Japan) */
+  unsigned char        psa_subband;            /* [0x20] Subband       */
+#define                PSA_SUBBAND_915         0       /* 915 MHz or 2.0 */
+#define                PSA_SUBBAND_2425        1       /* 2425 MHz     */
+#define                PSA_SUBBAND_2460        2       /* 2460 MHz     */
+#define                PSA_SUBBAND_2484        3       /* 2484 MHz     */
+#define                PSA_SUBBAND_2430_5      4       /* 2430.5 MHz   */
+  unsigned char        psa_quality_thr;        /* [0x21] Modem Quality Threshold */
+  unsigned char        psa_mod_delay;          /* [0x22] Modem Delay ??? (reserved) */
+  unsigned char        psa_nwid[2];            /* [0x23-0x24] Network ID */
+  unsigned char        psa_nwid_select;        /* [0x25] Network ID Select On Off */
+  unsigned char        psa_encryption_select;  /* [0x26] Encryption On Off */
+  unsigned char        psa_encryption_key[8];  /* [0x27-0x2E] Encryption Key */
+  unsigned char        psa_databus_width;      /* [0x2F] AT bus width select 8/16 */
+  unsigned char        psa_call_code[8];       /* [0x30-0x37] (Japan) Call Code */
+  unsigned char        psa_nwid_prefix[2];     /* [0x38-0x39] Roaming domain */
+  unsigned char        psa_reserved[2];        /* [0x3A-0x3B] Reserved - fixed 00 */
+  unsigned char        psa_conf_status;        /* [0x3C] Conf Status, bit 0=1:config*/
+  unsigned char        psa_crc[2];             /* [0x3D] CRC-16 over PSA */
+  unsigned char        psa_crc_status;         /* [0x3F] CRC Valid Flag */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define        PSA_SIZE        64
+
+/* Calculate offset of a field in the above structure
+ * Warning : only even addresses are used */
+#define        psaoff(p,f)     ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t   mmw_t;
+struct mmw_t
+{
+  unsigned char        mmw_encr_key[8];        /* encryption key */
+  unsigned char        mmw_encr_enable;        /* enable/disable encryption */
+#define        MMW_ENCR_ENABLE_MODE    0x02    /* Mode of security option */
+#define        MMW_ENCR_ENABLE_EN      0x01    /* Enable security option */
+  unsigned char        mmw_unused0[1];         /* unused */
+  unsigned char        mmw_des_io_invert;      /* Encryption option */
+#define        MMW_DES_IO_INVERT_RES   0x0F    /* Reserved */
+#define        MMW_DES_IO_INVERT_CTRL  0xF0    /* Control ??? (set to 0) */
+  unsigned char        mmw_unused1[5];         /* unused */
+  unsigned char        mmw_loopt_sel;          /* looptest selection */
+#define        MMW_LOOPT_SEL_DIS_NWID  0x40    /* disable NWID filtering */
+#define        MMW_LOOPT_SEL_INT       0x20    /* activate Attention Request */
+#define        MMW_LOOPT_SEL_LS        0x10    /* looptest w/o collision avoidance */
+#define MMW_LOOPT_SEL_LT3A     0x08    /* looptest 3a */
+#define        MMW_LOOPT_SEL_LT3B      0x04    /* looptest 3b */
+#define        MMW_LOOPT_SEL_LT3C      0x02    /* looptest 3c */
+#define        MMW_LOOPT_SEL_LT3D      0x01    /* looptest 3d */
+  unsigned char        mmw_jabber_enable;      /* jabber timer enable */
+  /* Abort transmissions > 200 ms */
+  unsigned char        mmw_freeze;             /* freeze / unfreeeze signal level */
+  /* 0 : signal level & qual updated for every new message, 1 : frozen */
+  unsigned char        mmw_anten_sel;          /* antenna selection */
+#define MMW_ANTEN_SEL_SEL      0x01    /* direct antenna selection */
+#define        MMW_ANTEN_SEL_ALG_EN    0x02    /* antenna selection algo. enable */
+  unsigned char        mmw_ifs;                /* inter frame spacing */
+  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+  unsigned char        mmw_mod_delay;          /* modem delay (synchro) */
+  unsigned char        mmw_jam_time;           /* jamming time (after collision) */
+  unsigned char        mmw_unused2[1];         /* unused */
+  unsigned char        mmw_thr_pre_set;        /* level threshold preset */
+  /* Discard all packet with signal < this value (4) */
+  unsigned char        mmw_decay_prm;          /* decay parameters */
+  unsigned char        mmw_decay_updat_prm;    /* decay update parameterz */
+  unsigned char        mmw_quality_thr;        /* quality (z-quotient) threshold */
+  /* Discard all packet with quality < this value (3) */
+  unsigned char        mmw_netw_id_l;          /* NWID low order byte */
+  unsigned char        mmw_netw_id_h;          /* NWID high order byte */
+  /* Network ID or Domain : create virtual net on the air */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char        mmw_mode_select;        /* for analog tests (set to 0) */
+  unsigned char        mmw_unused3[1];         /* unused */
+  unsigned char        mmw_fee_ctrl;           /* frequency eeprom control */
+#define        MMW_FEE_CTRL_PRE        0x10    /* Enable protected instructions */
+#define        MMW_FEE_CTRL_DWLD       0x08    /* Download eeprom to mmc */
+#define        MMW_FEE_CTRL_CMD        0x07    /* EEprom commands : */
+#define        MMW_FEE_CTRL_READ       0x06    /* Read */
+#define        MMW_FEE_CTRL_WREN       0x04    /* Write enable */
+#define        MMW_FEE_CTRL_WRITE      0x05    /* Write data to address */
+#define        MMW_FEE_CTRL_WRALL      0x04    /* Write data to all addresses */
+#define        MMW_FEE_CTRL_WDS        0x04    /* Write disable */
+#define        MMW_FEE_CTRL_PRREAD     0x16    /* Read addr from protect register */
+#define        MMW_FEE_CTRL_PREN       0x14    /* Protect register enable */
+#define        MMW_FEE_CTRL_PRCLEAR    0x17    /* Unprotect all registers */
+#define        MMW_FEE_CTRL_PRWRITE    0x15    /* Write addr in protect register */
+#define        MMW_FEE_CTRL_PRDS       0x14    /* Protect register disable */
+  /* Never issue this command (PRDS) : it's irreversible !!! */
+
+  unsigned char        mmw_fee_addr;           /* EEprom address */
+#define        MMW_FEE_ADDR_CHANNEL    0xF0    /* Select the channel */
+#define        MMW_FEE_ADDR_OFFSET     0x0F    /* Offset in channel data */
+#define        MMW_FEE_ADDR_EN         0xC0    /* FEE_CTRL enable operations */
+#define        MMW_FEE_ADDR_DS         0x00    /* FEE_CTRL disable operations */
+#define        MMW_FEE_ADDR_ALL        0x40    /* FEE_CTRL all operations */
+#define        MMW_FEE_ADDR_CLEAR      0xFF    /* FEE_CTRL clear operations */
+
+  unsigned char        mmw_fee_data_l;         /* Write data to EEprom */
+  unsigned char        mmw_fee_data_h;         /* high octet */
+  unsigned char        mmw_ext_ant;            /* Setting for external antenna */
+#define        MMW_EXT_ANT_EXTANT      0x01    /* Select external antenna */
+#define        MMW_EXT_ANT_POL         0x02    /* Polarity of the antenna */
+#define        MMW_EXT_ANT_INTERNAL    0x00    /* Internal antenna */
+#define        MMW_EXT_ANT_EXTERNAL    0x03    /* External antenna */
+#define        MMW_EXT_ANT_IQ_TEST     0x1C    /* IQ test pattern (set to 0) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define        MMW_SIZE        37
+
+/* Calculate offset of a field in the above structure */
+#define        mmwoff(p,f)     (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t   mmr_t;
+struct mmr_t
+{
+  unsigned char        mmr_unused0[8];         /* unused */
+  unsigned char        mmr_des_status;         /* encryption status */
+  unsigned char        mmr_des_avail;          /* encryption available (0x55 read) */
+#define        MMR_DES_AVAIL_DES       0x55            /* DES available */
+#define        MMR_DES_AVAIL_AES       0x33            /* AES (AT&T) available */
+  unsigned char        mmr_des_io_invert;      /* des I/O invert register */
+  unsigned char        mmr_unused1[5];         /* unused */
+  unsigned char        mmr_dce_status;         /* DCE status */
+#define        MMR_DCE_STATUS_RX_BUSY          0x01    /* receiver busy */
+#define        MMR_DCE_STATUS_LOOPT_IND        0x02    /* loop test indicated */
+#define        MMR_DCE_STATUS_TX_BUSY          0x04    /* transmitter on */
+#define        MMR_DCE_STATUS_JBR_EXPIRED      0x08    /* jabber timer expired */
+#define MMR_DCE_STATUS                 0x0F    /* mask to get the bits */
+  unsigned char        mmr_dsp_id;             /* DSP id (AA = Daedalus rev A) */
+  unsigned char        mmr_unused2[2];         /* unused */
+  unsigned char        mmr_correct_nwid_l;     /* # of correct NWID's rxd (low) */
+  unsigned char        mmr_correct_nwid_h;     /* # of correct NWID's rxd (high) */
+  /* Warning : Read high order octet first !!! */
+  unsigned char        mmr_wrong_nwid_l;       /* # of wrong NWID's rxd (low) */
+  unsigned char        mmr_wrong_nwid_h;       /* # of wrong NWID's rxd (high) */
+  unsigned char        mmr_thr_pre_set;        /* level threshold preset */
+#define        MMR_THR_PRE_SET         0x3F            /* level threshold preset */
+#define        MMR_THR_PRE_SET_CUR     0x80            /* Current signal above it */
+  unsigned char        mmr_signal_lvl;         /* signal level */
+#define        MMR_SIGNAL_LVL          0x3F            /* signal level */
+#define        MMR_SIGNAL_LVL_VALID    0x80            /* Updated since last read */
+  unsigned char        mmr_silence_lvl;        /* silence level (noise) */
+#define        MMR_SILENCE_LVL         0x3F            /* silence level */
+#define        MMR_SILENCE_LVL_VALID   0x80            /* Updated since last read */
+  unsigned char        mmr_sgnl_qual;          /* signal quality */
+#define        MMR_SGNL_QUAL           0x0F            /* signal quality */
+#define        MMR_SGNL_QUAL_ANT       0x80            /* current antenna used */
+  unsigned char        mmr_netw_id_l;          /* NWID low order byte ??? */
+  unsigned char        mmr_unused3[3];         /* unused */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char        mmr_fee_status;         /* Status of frequency eeprom */
+#define        MMR_FEE_STATUS_ID       0xF0            /* Modem revision id */
+#define        MMR_FEE_STATUS_DWLD     0x08            /* Download in progress */
+#define        MMR_FEE_STATUS_BUSY     0x04            /* EEprom busy */
+  unsigned char        mmr_unused4[1];         /* unused */
+  unsigned char        mmr_fee_data_l;         /* Read data from eeprom (low) */
+  unsigned char        mmr_fee_data_h;         /* Read data from eeprom (high) */
+};
+
+/* Size for structure checking (if padding is correct) */
+#define        MMR_SIZE        36
+
+/* Calculate offset of a field in the above structure */
+#define        mmroff(p,f)     (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+  struct mmw_t w;      /* Write to the mmc */
+  struct mmr_t r;      /* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_H */
diff --git a/drivers/net/pcmcia/wavelan_cs.c b/drivers/net/pcmcia/wavelan_cs.c
new file mode 100644 (file)
index 0000000..5e9c9e6
--- /dev/null
@@ -0,0 +1,4856 @@
+/*
+ *     Wavelan Pcmcia driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follow. See wavelan_cs.h for details.
+ *
+ * This code is derived from Anthony D. Joseph's code and all the changes here
+ * are also under the original copyright below.
+ *
+ * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and
+ * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services
+ *
+ * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added
+ * critical code in the routine to initialize the Modem Management Controller.
+ *
+ * Thanks to Alan Cox and Bruce Janson for their advice.
+ *
+ *     -- Yunzhou Li (scip4166@nus.sg)
+ *
+#ifdef WAVELAN_ROAMING 
+ * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu)
+ * based on patch by Joe Finney from Lancaster University.
+#endif :-)
+ *
+ * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An
+ * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor.
+ *
+ *   A non-shared memory PCMCIA ethernet driver for linux
+ *
+ * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu)
+ *
+ *
+ * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu)
+ *
+ * Apr 2 '98  made changes to bring the i82593 control/int handling in line
+ *             with offical specs...
+ *
+ ****************************************************************************
+ *   Copyright 1995
+ *   Anthony D. Joseph
+ *   Massachusetts Institute of Technology
+ *
+ *   Permission to use, copy, modify, and distribute this program
+ *   for any purpose and without fee is hereby granted, provided
+ *   that this copyright and permission notice appear on all copies
+ *   and supporting documentation, the name of M.I.T. not be used
+ *   in advertising or publicity pertaining to distribution of the
+ *   program without specific prior permission, and notice be given
+ *   in supporting documentation that copying and distribution is
+ *   by permission of M.I.T.  M.I.T. makes no representations about
+ *   the suitability of this software for any purpose.  It is pro-
+ *   vided "as is" without express or implied warranty.         
+ ****************************************************************************
+ *
+ */
+
+#include "wavelan_cs.h"                /* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (wavelan modem or i82593)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts.
+ */
+static inline unsigned long
+wv_splhi(void)
+{
+  unsigned long flags;
+
+  save_flags(flags);
+  cli();
+
+  return(flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(unsigned long  flags)
+{
+  restore_flags(flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for reporting error to cardservices
+ */
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *
+wv_structuct_check(void)
+{
+#define        SC(t,s,n)       if (sizeof(t) != s) return(n);
+
+  SC(psa_t, PSA_SIZE, "psa_t");
+  SC(mmw_t, MMW_SIZE, "mmw_t");
+  SC(mmr_t, MMR_SIZE, "mmr_t");
+
+#undef SC
+
+  return((char *) NULL);
+} /* wv_structuct_check */
+#endif /* STRUCT_CHECK */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Usefull subroutines to manage the modem of the wavelan
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u_char
+hasr_read(u_long       base)
+{
+  return(inb(HASR(base)));
+} /* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void
+hacr_write(u_long      base,
+          u_char       hacr)
+{
+  outb(hacr, HACR(base));
+} /* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void
+hacr_write_slow(u_long base,
+               u_char  hacr)
+{
+  hacr_write(base, hacr);
+  /* delay might only be needed sometimes */
+  udelay(1000L);
+} /* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+static void
+psa_read(device *      dev,
+        int            o,      /* offset in PSA */
+        u_char *       b,      /* buffer to fill */
+        int            n)      /* size to read */
+{
+  u_char *     ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1);
+
+  while(n-- > 0)
+    {
+      *b++ = readb(ptr);
+      /* Due to a lack of address decode pins, the WaveLAN PCMCIA card
+       * only supports reading even memory addresses. That means the
+       * increment here MUST be two.
+       * Because of that, we can't use memcpy_fromio()...
+       */
+      ptr += 2;
+    }
+} /* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Paramter Storage Area to the WaveLAN card's memory
+ */
+static void
+psa_write(device *     dev,
+         int           o,      /* Offset in psa */
+         u_char *      b,      /* Buffer in memory */
+         int           n)      /* Length of buffer */
+{
+  u_char *     ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1);
+  int          count = 0;
+  ioaddr_t     base = dev->base_addr;
+  /* As there seem to have no flag PSA_BUSY as in the ISA model, we are
+   * oblige to verify this address to know when the PSA is ready... */
+  volatile u_char *    verify = ((u_char *) dev->mem_start) + PSA_ADDR +
+    (psaoff(0, psa_comp_number) << 1);
+
+  /* Authorize writting to PSA */
+  hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN);
+
+  while(n-- > 0)
+    {
+      /* write to PSA */
+      writeb(*b++, ptr);
+      ptr += 2;
+
+      /* I don't have the spec, so I don't know what the correct
+       * sequence to write is. This hack seem to work for me... */
+      count = 0;
+      while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100))
+       udelay(1000);
+    }
+
+  /* Put the host interface back in standard state */
+  hacr_write(base, HACR_DEFAULT);
+} /* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static u_short
+psa_crc(unsigned char *        psa,    /* The PSA */
+       int             size)   /* Number of short for CRC */
+{
+  int          byte_cnt;       /* Loop on the PSA */
+  u_short      crc_bytes = 0;  /* Data in the PSA */
+  int          bit_cnt;        /* Loop on the bits of the short */
+
+  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
+    {
+      crc_bytes ^= psa[byte_cnt];      /* Its an xor */
+
+      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+       {
+         if(crc_bytes & 0x0001)
+           crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+         else
+           crc_bytes >>= 1 ;
+        }
+    }
+
+  return crc_bytes;
+} /* psa_crc */
+#endif /* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device *   dev)
+{
+#ifdef SET_PSA_CRC
+  psa_t                psa;
+  u_short      crc;
+
+  /* read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /* update the checksum */
+  crc = psa_crc((unsigned char *) &psa,
+               sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+               - sizeof(psa.psa_crc_status));
+
+  psa.psa_crc[0] = crc & 0xFF;
+  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+  /* Write it ! */
+  psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
+           (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+  /* Check again (luxury !) */
+  crc = psa_crc((unsigned char *) &psa,
+                sizeof(psa) - sizeof(psa.psa_crc_status));
+
+  if(crc != 0)
+    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif /* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void
+mmc_out(u_long         base,
+       u_short         o,
+       u_char          d)
+{
+  /* Wait for MMC to go idle */
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+
+  outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
+  outb(d, MMD(base));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_write(u_long       base,
+         u_char        o,
+         u_char *      b,
+         int           n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0 )
+    mmc_out(base, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_long  base,
+       u_short o)
+{
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  outb(o << 1, MMR(base));             /* Set the read address */
+
+  outb(0, MMD(base));                  /* Required dummy write */
+
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  return (u_char) (inb(MMD(base)));    /* Now do the actual read */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_read(u_long                base,
+        u_char         o,
+        u_char *       b,
+        int            n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0)
+    *(--b) = mmc_in(base, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long                base)   /* i/o port of the card */
+{
+  int  temp;
+
+  temp = mmc_in(base, mmroff(0, mmr_des_avail));
+  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+    return 0;
+  else
+    return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
+ */
+static inline void
+fee_wait(u_long                base,   /* i/o port of the card */
+        int            delay,  /* Base delay to wait for */
+        int            number) /* Number of time to wait */
+{
+  int          count = 0;      /* Wait only a limited time */
+
+  while((count++ < number) &&
+       (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+    udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEprom (frequency select cards).
+ */
+static void
+fee_read(u_long                base,   /* i/o port of the card */
+        u_short        o,      /* destination offset */
+        u_short *      b,      /* data buffer */
+        int            n)      /* number of registers */
+{
+  b += n;              /* Position at the end of the area */
+
+  /* Write the address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the read command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
+
+      /* Wait until EEprom is ready (should be quick !) */
+      fee_wait(base, 10, 100);
+
+      /* Read the value */
+      *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
+             mmc_in(base, mmroff(0, mmr_fee_data_l)));
+    }
+}
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void
+fee_write(u_long       base,   /* i/o port of the card */
+         u_short       o,      /* destination offset */
+         u_short *     b,      /* data buffer */
+         int           n)      /* number of registers */
+{
+  b += n;              /* Position at the end of the area */
+
+#ifdef EEPROM_IS_PROTECTED     /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Ask to read the protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+  fee_wait(base, 10, 100);
+
+  /* Read the protected register */
+  printk("Protected 2 : %02X-%02X\n",
+        mmc_in(base, mmroff(0, mmr_fee_data_h)),
+        mmc_in(base, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
+
+  /* Enable protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Unprotect area */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Or use : */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
+
+  fee_wait(base, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+
+  /* Write enable */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Write the EEprom address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the value */
+      mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+      mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+      /* Write the write command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
+
+      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+      udelay(10000);
+      fee_wait(base, 10, 100);
+    }
+
+  /* Write disable */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+  fee_wait(base, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED     /* disabled */
+  /* Reprotect EEprom */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+  fee_wait(base, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+}
+#endif /* WIRELESS_EXT */
+
+/******************* WaveLAN Roaming routines... ********************/
+
+#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
+
+unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
+  
+void wv_roam_init(struct net_device *dev)
+{
+  net_local  *lp= (net_local *)dev->priv;
+
+  /* Do not remove this unless you have a good reason */
+  printk(KERN_NOTICE "%s: Warning, you have enabled roaming on"
+        " device %s !\n", dev->name, dev->name);
+  printk(KERN_NOTICE "Roaming is currently an experimental unsuported feature"
+        " of the Wavelan driver.\n");
+  printk(KERN_NOTICE "It may work, but may also make the driver behave in"
+        " erratic ways or crash.\n");
+
+  lp->wavepoint_table.head=NULL;           /* Initialise WavePoint table */
+  lp->wavepoint_table.num_wavepoints=0;
+  lp->wavepoint_table.locked=0;
+  lp->curr_point=NULL;                        /* No default WavePoint */
+  lp->cell_search=0;
+  
+  lp->cell_timer.data=(int)lp;                /* Start cell expiry timer */
+  lp->cell_timer.function=wl_cell_expiry;
+  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+  add_timer(&lp->cell_timer);
+  
+  wv_nwid_filter(NWID_PROMISC,lp) ;    /* Enter NWID promiscuous mode */
+  /* to build up a good WavePoint */
+                                           /* table... */
+  printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
+}
+void wv_roam_cleanup(struct net_device *dev)
+{
+  wavepoint_history *ptr,*old_ptr;
+  net_local *lp= (net_local *)dev->priv;
+  
+  printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name);
+  
+  /* Fixme : maybe we should check that the timer exist before deleting it */
+  del_timer(&lp->cell_timer);          /* Remove cell expiry timer       */
+  ptr=lp->wavepoint_table.head;        /* Clear device's WavePoint table */
+  while(ptr!=NULL)
+    {
+      old_ptr=ptr;
+      ptr=ptr->next;   
+      wl_del_wavepoint(old_ptr,lp);    
+    }
+}
+
+/* Enable/Disable NWID promiscuous mode on a given device */
+void wv_nwid_filter(unsigned char mode, net_local *lp)
+{
+  mm_t                  m;
+  unsigned long         x;
+  
+#ifdef WAVELAN_ROAMING_DEBUG
+  printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name);
+#endif
+  
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+  
+  m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
+  mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);
+  
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+  
+  if(mode==NWID_PROMISC)
+    lp->cell_search=1;
+  else
+       lp->cell_search=0;
+}
+
+/* Find a record in the WavePoint table matching a given NWID */
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
+{
+  wavepoint_history    *ptr=lp->wavepoint_table.head;
+  
+  while(ptr!=NULL){
+    if(ptr->nwid==nwid)
+      return ptr;      
+    ptr=ptr->next;
+  }
+  return NULL;
+}
+
+/* Create a new wavepoint table entry */
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
+{
+  wavepoint_history *new_wavepoint;
+
+#ifdef WAVELAN_ROAMING_DEBUG   
+  printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid);
+#endif
+  
+  if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS)
+    return NULL;
+  
+  new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC);
+  if(new_wavepoint==NULL)
+    return NULL;
+  
+  new_wavepoint->nwid=nwid;                       /* New WavePoints NWID */
+  new_wavepoint->average_fast=0;                    /* Running Averages..*/
+  new_wavepoint->average_slow=0;
+  new_wavepoint->qualptr=0;                       /* Start of ringbuffer */
+  new_wavepoint->last_seq=seq-1;                /* Last sequence no.seen */
+  memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */
+  
+  new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */
+  new_wavepoint->prev=NULL;
+  
+  if(lp->wavepoint_table.head!=NULL)
+    lp->wavepoint_table.head->prev=new_wavepoint;
+  
+  lp->wavepoint_table.head=new_wavepoint;
+  
+  lp->wavepoint_table.num_wavepoints++;     /* no. of visible wavepoints */
+  
+  return new_wavepoint;
+}
+
+/* Remove a wavepoint entry from WavePoint table */
+void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
+{
+  if(wavepoint==NULL)
+    return;
+  
+  if(lp->curr_point==wavepoint)
+    lp->curr_point=NULL;
+  
+  if(wavepoint->prev!=NULL)
+    wavepoint->prev->next=wavepoint->next;
+  
+  if(wavepoint->next!=NULL)
+    wavepoint->next->prev=wavepoint->prev;
+  
+  if(lp->wavepoint_table.head==wavepoint)
+    lp->wavepoint_table.head=wavepoint->next;
+  
+  lp->wavepoint_table.num_wavepoints--;
+  kfree(wavepoint);
+}
+
+/* Timer callback function - checks WavePoint table for stale entries */ 
+void wl_cell_expiry(unsigned long data)
+{
+  net_local *lp=(net_local *)data;
+  wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
+  
+#if WAVELAN_ROAMING_DEBUG > 1
+  printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name);
+#endif
+  
+  if(lp->wavepoint_table.locked)
+    {
+#if WAVELAN_ROAMING_DEBUG > 1
+      printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n");
+#endif
+      
+      lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */
+      add_timer(&lp->cell_timer);
+      return;
+    }
+  
+  while(wavepoint!=NULL)
+    {
+      if(wavepoint->last_seen < jiffies-CELL_TIMEOUT)
+       {
+#ifdef WAVELAN_ROAMING_DEBUG
+         printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid);
+#endif
+         
+         old_point=wavepoint;
+         wavepoint=wavepoint->next;
+         wl_del_wavepoint(old_point,lp);
+       }
+      else
+       wavepoint=wavepoint->next;
+    }
+  lp->cell_timer.expires=jiffies+CELL_TIMEOUT;
+  add_timer(&lp->cell_timer);
+}
+
+/* Update SNR history of a wavepoint */
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq) 
+{
+  int i=0,num_missed=0,ptr=0;
+  int average_fast=0,average_slow=0;
+  
+  num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed
+                                                           any beacons? */
+  if(num_missed)
+    for(i=0;i<num_missed;i++)
+      {
+       wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
+       wavepoint->qualptr %=WAVEPOINT_HISTORY;    /* in the ringbuffer. */
+      }
+  wavepoint->last_seen=jiffies;                 /* Add beacon to history */
+  wavepoint->last_seq=seq;     
+  wavepoint->sigqual[wavepoint->qualptr++]=sigqual;          
+  wavepoint->qualptr %=WAVEPOINT_HISTORY;
+  ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
+  
+  for(i=0;i<WAVEPOINT_FAST_HISTORY;i++)       /* Update running averages */
+    {
+      average_fast+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  average_slow=average_fast;
+  for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
+    {
+      average_slow+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
+  wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;      
+}
+
+/* Perform a handover to a new WavePoint */
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
+{
+  ioaddr_t              base = lp->dev->base_addr;  
+  mm_t                  m;
+  unsigned long         x;
+  
+  if(wavepoint==lp->curr_point)          /* Sanity check... */
+    {
+      wv_nwid_filter(!NWID_PROMISC,lp);
+      return;
+    }
+  
+#ifdef WAVELAN_ROAMING_DEBUG
+  printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name);
+#endif
+       
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+  
+  m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
+  m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;
+  
+  mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);
+  
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+  
+  wv_nwid_filter(!NWID_PROMISC,lp);
+  lp->curr_point=wavepoint;
+}
+
+/* Called when a WavePoint beacon is received */
+static inline void wl_roam_gather(device *  dev,
+                                 u_char *  hdr,   /* Beacon header */
+                                 u_char *  stats) /* SNR, Signal quality 
+                                                     of packet */
+{
+  wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */
+  unsigned short nwid=ntohs(beacon->nwid);  
+  unsigned short sigqual=stats[2] & MMR_SGNL_QUAL;   /* SNR of beacon */
+  wavepoint_history *wavepoint=NULL;                /* WavePoint table entry */
+  net_local *lp=(net_local *)dev->priv;              /* Device info */
+
+#if WAVELAN_ROAMING_DEBUG > 1
+  printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
+  printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
+#endif
+  
+  lp->wavepoint_table.locked=1;                            /* <Mutex> */
+  
+  wavepoint=wl_roam_check(nwid,lp);            /* Find WavePoint table entry */
+  if(wavepoint==NULL)                    /* If no entry, Create a new one... */
+    {
+      wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
+      if(wavepoint==NULL)
+       goto out;
+    }
+  if(lp->curr_point==NULL)             /* If this is the only WavePoint, */
+    wv_roam_handover(wavepoint, lp);            /* Jump on it! */
+  
+  wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
+                                                        stats. */
+  
+  if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
+    if(!lp->cell_search)                  /* WavePoint is getting faint, */
+      wv_nwid_filter(NWID_PROMISC,lp);    /* start looking for a new one */
+  
+  if(wavepoint->average_slow > 
+     lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
+    wv_roam_handover(wavepoint, lp);   /* Handover to a better WavePoint */
+  
+  if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
+    if(lp->cell_search)  /* getting better, drop out of cell search mode */
+      wv_nwid_filter(!NWID_PROMISC,lp);
+  
+out:
+  lp->wavepoint_table.locked=0;                        /* </MUTEX>   :-) */
+}
+
+/* Test this MAC frame a WavePoint beacon */
+static inline int WAVELAN_BEACON(unsigned char *data)
+{
+  wavepoint_beacon *beacon= (wavepoint_beacon *)data;
+  static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
+  
+  if(memcmp(beacon,&beacon_template,9)==0)
+    return 1;
+  else
+    return 0;
+}
+#endif /* WAVELAN_ROAMING */
+
+/************************ I82593 SUBROUTINES *************************/
+/*
+ * Usefull subroutines to manage the Ethernet controler
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to synchronously send a command to the i82593 chip. 
+ * Should be called with interrupts enabled.
+ */
+static int
+wv_82593_cmd(device *  dev,
+            char *     str,
+            int        cmd,
+            int        result)
+{
+  ioaddr_t     base = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  int          status;
+  long         spin;
+  u_long       opri;
+
+  /* Spin until the chip finishes executing its current command (if any) */
+  do
+    {
+      opri = wv_splhi();
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+      wv_splx(opri);
+    }
+  while((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
+
+  /* We are waiting for command completion */
+  wv_wait_completed = TRUE;
+
+  /* Issue the command to the controler */
+  outb(cmd, LCCR(base));
+
+  /* If we don't have to check the result of the command */
+  if(result == SR0_NO_RESULT)
+    {
+      wv_wait_completed = FALSE;
+      return(TRUE);
+    }
+
+  /* Busy wait while the LAN controller executes the command.
+   * Note : wv_wait_completed should be volatile */
+  spin = 0;
+  while(wv_wait_completed && (spin++ < 1000))
+    udelay(10);
+
+  /* If the interrupt handler hasn't be called */
+  if(wv_wait_completed)
+    {
+      outb(OP0_NOP, LCCR(base));
+      status = inb(LCSR(base));
+      if(status & SR0_INTERRUPT)
+       {
+         /* There was an interrupt : call the interrupt handler */
+#ifdef DEBUG_INTERRUPT_ERROR
+         printk(KERN_WARNING "wv_82593_cmd: interrupt handler not installed or interrupt disabled\n");
+#endif
+
+         wavelan_interrupt(dev->irq, (void *) dev,
+                           (struct pt_regs *) NULL);
+       }
+      else
+       {
+         wv_wait_completed = 0; /* XXX */
+#ifdef DEBUG_INTERRUPT_ERROR
+         printk(KERN_INFO "wv_82593_cmd: %s timeout, status0 0x%02x\n",
+                str, status);
+#endif
+         /* We probably should reset the controller here */
+         return(FALSE);
+       }
+    }
+
+  /* Check the return code provided by the interrupt handler against
+   * the expected return code provided by the caller */
+  if((lp->status & SR0_EVENT_MASK) != result)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "wv_82593_cmd: %s failed, status0 = 0x%x\n",
+            str, lp->status);
+#endif
+      return(FALSE);
+    }
+
+  return(TRUE);
+} /* wv_82593_cmd */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a 593 op-code number 7, and obtains the diagnose
+ * status for the WaveLAN.
+ */
+static inline int
+wv_diag(device *       dev)
+{
+  if(wv_82593_cmd(dev, "wv_diag(): diagnose",
+                 OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
+    return(TRUE);
+
+#ifdef DEBUG_CONFIG_ERROR
+  printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
+#endif
+  return(FALSE);
+} /* wv_diag */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read len bytes from the i82593's ring buffer, starting at
+ * chip address addr. The results read from the chip are stored in buf.
+ * The return value is the address to use for next the call.
+ */
+static int
+read_ringbuf(device *  dev,
+            int        addr,
+            char *     buf,
+            int        len)
+{
+  ioaddr_t     base = dev->base_addr;
+  int          ring_ptr = addr;
+  int          chunk_len;
+  char *       buf_ptr = buf;
+
+#ifdef OLDIES
+  /* After having check skb_put (net/core/skbuff.c) in the kernel, it seem
+   * quite safe to remove this... */
+
+  /* If buf is NULL, just increment the ring buffer pointer */
+  if(buf == NULL)
+    return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE);
+#endif
+
+  /* Get all the buffer */
+  while(len > 0)
+    {
+      /* Position the Program I/O Register at the ring buffer pointer */
+      outb(ring_ptr & 0xff, PIORL(base));
+      outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base));
+
+      /* First, determine how much we can read without wrapping around the
+        ring buffer */
+      if((addr + len) < (RX_BASE + RX_SIZE))
+       chunk_len = len;
+      else
+       chunk_len = RX_BASE + RX_SIZE - addr;
+      insb(PIOP(base), buf_ptr, chunk_len);
+      buf_ptr += chunk_len;
+      len -= chunk_len;
+      ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
+    }
+  return(ring_ptr);
+} /* read_ringbuf */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82593, or at least ask for it...
+ * Because wv_82593_config use the transmission buffer, we must do it
+ * when we are sure that there is no transmission, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option...), so you may experience
+ * some delay sometime...
+ */
+static inline void
+wv_82593_reconfig(device *     dev)
+{
+  net_local *  lp = (net_local *)dev->priv;
+  dev_link_t * link = ((net_local *) dev->priv)->link;
+
+  /* Check if we can do it now ! */
+  if(!(link->open) || (test_and_set_bit(0, (void *)&dev->tbusy) != 0))
+    {
+      lp->reconfig_82593 = TRUE;
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "%s: wv_82593_reconfig(): delayed (busy = %ld, link = %d)\n",
+            dev->name, dev->tbusy, link->open);
+#endif
+    }
+  else
+    {
+      lp->reconfig_82593 = FALSE;
+      wv_82593_config(dev);
+      dev->tbusy = 0;
+    }
+}
+
+#ifdef OLDIES
+/*------------------------------------------------------------------*/
+/*
+ * Dumps the current i82593 receive buffer to the console.
+ */
+static void wavelan_dump(device *dev)
+{
+  ioaddr_t base = dev->base_addr;
+  int i, c;
+
+  /* disable receiver so we can use channel 1 */
+  outb(OP0_RCV_DISABLE, LCCR(base));
+
+  /* reset receive DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_RX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+
+  /* dump into receive buffer */
+  wv_82593_cmd(dev, "wavelan_dump(): dump", CR0_CHNL|OP0_DUMP, SR0_DUMP_DONE);
+
+  /* set read pointer to start of receive buffer */
+  outb(0, PIORL(base));
+  outb(0, PIORH(base));
+
+  printk(KERN_DEBUG "wavelan_cs: dump:\n");
+  printk(KERN_DEBUG "     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
+  for(i = 0; i < 73; i++){
+    if((i % 16) == 0) {
+      printk("\n0x%02x:", i);
+      if (!i) {
+       printk("   ");
+       continue;
+      }
+    }
+    c = inb(PIOP(base));
+    printk("%02x ", c);
+  }
+  printk("\n");
+
+  /* enable the receiver again */
+  wv_ru_start(dev);
+}
+#endif
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routines are used in the code to show debug informations.
+ * Most of the time, it dump the content of hardware structures...
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void
+wv_psa_show(psa_t *    p)
+{
+  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+        p->psa_io_base_addr_1,
+        p->psa_io_base_addr_2,
+        p->psa_io_base_addr_3,
+        p->psa_io_base_addr_4);
+  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+        p->psa_rem_boot_addr_1,
+        p->psa_rem_boot_addr_2,
+        p->psa_rem_boot_addr_3);
+  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+  printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        p->psa_unused0[0],
+        p->psa_unused0[1],
+        p->psa_unused0[2],
+        p->psa_unused0[3],
+        p->psa_unused0[4],
+        p->psa_unused0[5],
+        p->psa_unused0[6]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_univ_mac_addr[0],
+        p->psa_univ_mac_addr[1],
+        p->psa_univ_mac_addr[2],
+        p->psa_univ_mac_addr[3],
+        p->psa_univ_mac_addr[4],
+        p->psa_univ_mac_addr[5]);
+  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_local_mac_addr[0],
+        p->psa_local_mac_addr[1],
+        p->psa_local_mac_addr[2],
+        p->psa_local_mac_addr[3],
+        p->psa_local_mac_addr[4],
+        p->psa_local_mac_addr[5]);
+  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
+  printk("psa_comp_number: %d, ", p->psa_comp_number);
+  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+        p->psa_feature_select);
+  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
+  printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
+  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_encryption_key[0],
+        p->psa_encryption_key[1],
+        p->psa_encryption_key[2],
+        p->psa_encryption_key[3],
+        p->psa_encryption_key[4],
+        p->psa_encryption_key[5],
+        p->psa_encryption_key[6],
+        p->psa_encryption_key[7]);
+  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+        p->psa_call_code[0]);
+  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        p->psa_call_code[0],
+        p->psa_call_code[1],
+        p->psa_call_code[2],
+        p->psa_call_code[3],
+        p->psa_call_code[4],
+        p->psa_call_code[5],
+        p->psa_call_code[6],
+        p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+        p->psa_reserved[0],
+        p->psa_reserved[1],
+        p->psa_reserved[2],
+        p->psa_reserved[3]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif /* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function need to be completed...
+ */
+static void
+wv_mmc_show(device *   dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  mmr_t                m;
+
+  /* Basic check */
+  if(hasr_read(base) & HASR_NO_CLK)
+    {
+      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
+            dev->name);
+      return;
+    }
+
+  /* Read the mmc */
+  mmc_out(base, mmwoff(0, mmw_freeze), 1);
+  mmc_read(base, 0, (u_char *)&m, sizeof(m));
+  mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+  /* Don't forget to update statistics */
+  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif /* WIRELESS_EXT */
+
+  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        m.mmr_unused0[0],
+        m.mmr_unused0[1],
+        m.mmr_unused0[2],
+        m.mmr_unused0[3],
+        m.mmr_unused0[4],
+        m.mmr_unused0[5],
+        m.mmr_unused0[6],
+        m.mmr_unused0[7]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
+        m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+        m.mmr_unused1[0],
+        m.mmr_unused1[1],
+        m.mmr_unused1[2],
+        m.mmr_unused1[3],
+        m.mmr_unused1[4]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+        m.mmr_dce_status,
+        (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
+        (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+        "loop test indicated," : "",
+        (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
+        (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+        "jabber timer expired," : "");
+  printk(KERN_DEBUG "Dsp ID: %02X\n",
+        m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+        m.mmr_unused2[0],
+        m.mmr_unused2[1]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+        (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+        (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+        m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+        (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
+  printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+        m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+        (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
+  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
+        (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
+  printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+        (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif /* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif /* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82593_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82593's receive unit.
+ */
+static void
+wv_ru_show(device *    dev)
+{
+  net_local *lp = (net_local *) dev->priv;
+
+  printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n");
+  printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop);
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_ru_show */
+#endif /* DEBUG_I82593_SHOW */
+
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void
+wv_dev_show(device *   dev)
+{
+  printk(KERN_DEBUG "dev:");
+  printk(" start=%d,", dev->start);
+  printk(" tbusy=%ld,", dev->tbusy);
+  printk(" interrupt=%d,", dev->interrupt);
+  printk(" trans_start=%ld,", dev->trans_start);
+  printk(" flags=0x%x,", dev->flags);
+  printk("\n");
+} /* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void
+wv_local_show(device * dev)
+{
+  net_local *lp;
+
+  lp = (net_local *)dev->priv;
+
+  printk(KERN_DEBUG "local:");
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_local_show */
+#endif /* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void
+wv_packet_info(u_char *                p,              /* Packet to dump */
+              int              length,         /* Length of the packet */
+              char *           msg1,           /* Name of the device */
+              char *           msg2)           /* Name of the function */
+{
+  int          i;
+  int          maxi;
+
+  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+        msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+        msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
+
+#ifdef DEBUG_PACKET_DUMP
+
+  printk(KERN_DEBUG "data=\"");
+
+  if((maxi = length) > DEBUG_PACKET_DUMP)
+    maxi = DEBUG_PACKET_DUMP;
+  for(i = 14; i < maxi; i++)
+    if(p[i] >= ' ' && p[i] <= '~')
+      printk(" %c", p[i]);
+    else
+      printk("%02X", p[i]);
+  if(maxi < length)
+    printk("..");
+  printk("\"\n");
+  printk(KERN_DEBUG "\n");
+#endif /* DEBUG_PACKET_DUMP */
+}
+#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
+
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup
+ * There  is a lot of flag to configure it at your will...
+ */
+static inline void
+wv_init_info(device *  dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  psa_t                psa;
+  int          i;
+
+  /* Read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+  wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+  wv_ru_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+  /* Now, let's go for the basic stuff */
+  printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr",
+        dev->name, base, dev->irq);
+  for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
+    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+  /* Print current network id */
+  if(psa.psa_nwid_select)
+    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
+  else
+    printk(", nwid off");
+
+  /* If 2.00 card */
+  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      unsigned short   freq;
+
+      /* Ask the EEprom to read the frequency from the first area */
+      fee_read(base, 0x00 /* 1st area - frequency... */,
+              &freq, 1);
+
+      /* Print frequency */
+      printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+      /* Hack !!! */
+      if(freq & 0x20)
+       printk(".5");
+    }
+  else
+    {
+      printk(", PCMCIA, ");
+      switch (psa.psa_subband)
+       {
+       case PSA_SUBBAND_915:
+         printk("915");
+         break;
+       case PSA_SUBBAND_2425:
+         printk("2425");
+         break;
+       case PSA_SUBBAND_2460:
+         printk("2460");
+         break;
+       case PSA_SUBBAND_2484:
+         printk("2484");
+         break;
+       case PSA_SUBBAND_2430_5:
+         printk("2430.5");
+         break;
+       default:
+         printk("???");
+       }
+    }
+
+  printk(" MHz\n");
+#endif /* DEBUG_BASIC_SHOW */
+
+#ifdef DEBUG_VERSION_SHOW
+  /* Print version information */
+  printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
+
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on differents
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the current ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats        *
+wavelan_get_stats(device *     dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
+
+  return(&((net_local *) dev->priv)->stats);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1     Promiscuous mode, receive all packets
+ * num_addrs == 0      Normal mode, clear multicast list
+ * num_addrs > 0       Multicast mode, receive normal and MC packets,
+ *                     and do best-effort filtering.
+ */
+
+static void
+wavelan_set_multicast_list(device *    dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+        dev->name, dev->flags, dev->mc_count);
+#endif
+
+  if(dev->flags & IFF_PROMISC)
+    {
+      /*
+       * Enable promiscuous mode: receive all packets.
+       */
+      if(!lp->promiscuous)
+       {
+         lp->promiscuous = 1;
+         lp->allmulticast = 0;
+         lp->mc_count = 0;
+
+         wv_82593_reconfig(dev);
+
+         /* Tell the kernel that we are doing a really bad job... */
+         dev->flags |= IFF_PROMISC;
+       }
+    }
+  else
+    /* If all multicast addresses
+     * or too much multicast addresses for the hardware filter */
+    if((dev->flags & IFF_ALLMULTI) ||
+       (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES))
+      {
+       /*
+        * Disable promiscuous mode, but active the all multicast mode
+        */
+       if(!lp->allmulticast)
+         {
+           lp->promiscuous = 0;
+           lp->allmulticast = 1;
+           lp->mc_count = 0;
+
+           wv_82593_reconfig(dev);
+
+           /* Tell the kernel that we are doing a really bad job... */
+           dev->flags |= IFF_ALLMULTI;
+         }
+      }
+    else
+      /* If there is some multicast addresses to send */
+      if(dev->mc_list != (struct dev_mc_list *) NULL)
+       {
+         /*
+          * Disable promiscuous mode, but receive all packets
+          * in multicast list
+          */
+#ifdef MULTICAST_AVOID
+         if(lp->promiscuous || lp->allmulticast ||
+            (dev->mc_count != lp->mc_count))
+#endif
+           {
+             lp->promiscuous = 0;
+             lp->allmulticast = 0;
+             lp->mc_count = dev->mc_count;
+
+             wv_82593_reconfig(dev);
+           }
+       }
+      else
+       {
+         /*
+          * Switch to normal mode: disable promiscuous mode and 
+          * clear the multicast list.
+          */
+         if(lp->promiscuous || lp->mc_count == 0)
+           {
+             lp->promiscuous = 0;
+             lp->allmulticast = 0;
+             lp->mc_count = 0;
+
+             wv_82593_reconfig(dev);
+           }
+       }
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This function doesn't exist...
+ * (Note : it was a nice way to test the reconfigure stuff...)
+ */
+#ifdef SET_MAC_ADDRESS
+static int
+wavelan_set_mac_address(device *       dev,
+                       void *          addr)
+{
+  struct sockaddr *    mac = addr;
+
+  /* Copy the address */
+  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
+
+  /* Reconfig the beast */
+  wv_82593_reconfig(dev);
+
+  return 0;
+}
+#endif /* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
+ */
+static inline int
+wv_set_frequency(u_long                base,   /* i/o port of the card */
+                iw_freq *      frequency)
+{
+  const int    BAND_NUM = 10;  /* Number of bands */
+  long         freq = 0L;      /* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+  int          i;
+#endif
+
+  /* Setting by frequency */
+  /* Theoritically, you may set any frequency between
+   * the two limits with a 0.5 MHz precision. In practice,
+   * I don't want you to have trouble with local
+   * regulations... */
+  if((frequency->e == 1) &&
+     (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
+    {
+      freq = ((frequency->m / 10000) - 24000L) / 5;
+    }
+
+  /* Setting by channel (same as wfreqsel) */
+  /* Warning : each channel is 22MHz wide, so some of the channels
+   * will interfere... */
+  if((frequency->e == 0) &&
+     (frequency->m >= 0) && (frequency->m < BAND_NUM))
+    {
+      /* Get frequency offset. */
+      freq = channel_bands[frequency->m] >> 1;
+    }
+
+  /* Verify if the frequency is allowed */
+  if(freq != 0L)
+    {
+      u_short  table[10];      /* Authorized frequency table */
+
+      /* Read the frequency table */
+      fee_read(base, 0x71 /* frequency table */,
+              table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Frequency table :");
+      for(i = 0; i < 10; i++)
+       {
+         printk(" %04X",
+                table[i]);
+       }
+      printk("\n");
+#endif
+
+      /* Look in the table if the frequency is allowed */
+      if(!(table[9 - ((freq - 24) / 16)] &
+          (1 << ((freq - 24) % 16))))
+       return -EINVAL;         /* not allowed */
+    }
+  else
+    return -EINVAL;
+
+  /* If we get a usable frequency */
+  if(freq != 0L)
+    {
+      unsigned short   area[16];
+      unsigned short   dac[2];
+      unsigned short   area_verify[16];
+      unsigned short   dac_verify[2];
+      /* Corresponding gain (in the power adjust value table)
+       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+       * & WCIN062D.DOC, page 6.2.9 */
+      unsigned short   power_limit[] = { 40, 80, 120, 160, 0 };
+      int              power_band = 0;         /* Selected band */
+      unsigned short   power_adjust;           /* Correct value */
+
+      /* Search for the gain */
+      power_band = 0;
+      while((freq > power_limit[power_band]) &&
+           (power_limit[++power_band] != 0))
+       ;
+
+      /* Read the first area */
+      fee_read(base, 0x00,
+              area, 16);
+
+      /* Read the DAC */
+      fee_read(base, 0x60,
+              dac, 2);
+
+      /* Read the new power adjust value */
+      fee_read(base, 0x6B - (power_band >> 1),
+              &power_adjust, 1);
+      if(power_band & 0x1)
+       power_adjust >>= 8;
+      else
+       power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+       {
+         printk(" %04X",
+                area[i]);
+       }
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+            dac[0], dac[1]);
+#endif
+
+      /* Frequency offset (for info only...) */
+      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+      /* Receiver Principle main divider coefficient */
+      area[3] = (freq >> 1) + 2400L - 352L;
+      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Transmitter Main divider coefficient */
+      area[13] = (freq >> 1) + 2400L;
+      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Others part of the area are flags, bit streams or unused... */
+
+      /* Set the value in the DAC */
+      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+      /* Write the first area */
+      fee_write(base, 0x00,
+               area, 16);
+
+      /* Write the DAC */
+      fee_write(base, 0x60,
+               dac, 2);
+
+      /* We now should verify here that the EEprom writting was ok */
+
+      /* ReRead the first area */
+      fee_read(base, 0x00,
+              area_verify, 16);
+
+      /* ReRead the DAC */
+      fee_read(base, 0x60,
+              dac_verify, 2);
+
+      /* Compare */
+      if(memcmp(area, area_verify, 16 * 2) ||
+        memcmp(dac, dac_verify, 2 * 2))
+       {
+#ifdef DEBUG_IOCTL_ERROR
+         printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+#endif
+         return -EOPNOTSUPP;
+       }
+
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F);
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+             MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61);
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl),
+             MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+      /* Verification of what we have done... */
+
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+       {
+         printk(" %04X",
+                area_verify[i]);
+       }
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+            dac_verify[0], dac_verify[1]);
+#endif
+
+      return 0;
+    }
+  else
+    return -EINVAL;            /* Bah, never get there... */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies
+ */
+static inline int
+wv_frequency_list(u_long       base,   /* i/o port of the card */
+                 iw_freq *     list,   /* List of frequency to fill */
+                 int           max)    /* Maximum number of frequencies */
+{
+  u_short      table[10];      /* Authorized frequency table */
+  long         freq = 0L;      /* offset to 2.4 GHz in .5 MHz + 12 MHz */
+  int          i;              /* index in the table */
+#if WIRELESS_EXT > 7
+  const int    BAND_NUM = 10;  /* Number of bands */
+  int          c = 0;          /* Channel number */
+#endif WIRELESS_EXT
+
+  /* Read the frequency table */
+  fee_read(base, 0x71 /* frequency table */,
+          table, 10);
+
+  /* Look all frequencies */
+  i = 0;
+  for(freq = 0; freq < 150; freq++)
+    /* Look in the table if the frequency is allowed */
+    if(table[9 - (freq / 16)] & (1 << (freq % 16)))
+      {
+#if WIRELESS_EXT > 7
+       /* Compute approximate channel number */
+       while((((channel_bands[c] >> 1) - 24) < freq) &&
+             (c < BAND_NUM))
+         c++;
+       list[i].i = c;  /* Set the list index */
+#endif WIRELESS_EXT
+
+       /* put in the list */
+       list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+       list[i++].e = 1;
+
+       /* Check number */
+       if(i >= max)
+         return(i);
+      }
+
+  return(i);
+}
+
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics : for each packet, compare the source
+ * address with out list, and if match, get the stats...
+ * Sorry, but this function really need wireless extensions...
+ */
+static inline void
+wl_spy_gather(device * dev,
+             u_char *  mac,            /* MAC address */
+             u_char *  stats)          /* Statistics to gather */
+{
+  net_local *  lp = (net_local *) dev->priv;
+  int          i;
+
+  /* Look all addresses */
+  for(i = 0; i < lp->spy_number; i++)
+    /* If match */
+    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
+      {
+       /* Update statistics */
+       lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+       lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+       lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+       lp->spy_stat[i].updated = 0x7;
+      }
+}
+#endif /* WIRELESS_SPY */
+
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculate an histogram on the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one wavelan is really weak,
+ * or you may also calculate the mean and standard deviation of the level...
+ */
+static inline void
+wl_his_gather(device * dev,
+             u_char *  stats)          /* Statistics to gather */
+{
+  net_local *  lp = (net_local *) dev->priv;
+  u_char       level = stats[0] & MMR_SIGNAL_LVL;
+  int          i;
+
+  /* Find the correct interval */
+  i = 0;
+  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
+    ;
+
+  /* Increment interval counter */
+  (lp->his_sum[i])++;
+}
+#endif /* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl : config & info stuff
+ * This is here that are treated the wireless extensions (iwconfig)
+ */
+static int
+wavelan_ioctl(struct net_device *      dev,    /* Device on wich the ioctl apply */
+             struct ifreq *    rq,     /* Data passed */
+             int               cmd)    /* Ioctl number */
+{
+  ioaddr_t             base = dev->base_addr;
+  net_local *          lp = (net_local *)dev->priv;    /* lp is not unused */
+  struct iwreq *       wrq = (struct iwreq *) rq;
+  psa_t                        psa;
+  mm_t                 m;
+  unsigned long                x;
+  int                  ret = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  /* Look what is the request */
+  switch(cmd)
+    {
+      /* --------------- WIRELESS EXTENSIONS --------------- */
+
+    case SIOCGIWNAME:
+      strcpy(wrq->u.name, "Wavelan");
+      break;
+
+    case SIOCSIWNWID:
+      /* Set NWID in wavelan */
+#if WIRELESS_EXT > 8
+      if(!wrq->u.nwid.disabled)
+       {
+         /* Set NWID in psa */
+         psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
+         psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
+#else  /* WIRELESS_EXT > 8 */
+      if(wrq->u.nwid.on)
+       {
+         /* Set NWID in psa */
+         psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+         psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+#endif /* WIRELESS_EXT > 8 */
+         psa.psa_nwid_select = 0x01;
+         psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+                   (unsigned char *)psa.psa_nwid, 3);
+
+         /* Set NWID in mmc */
+         m.w.mmw_netw_id_l = psa.psa_nwid[1];
+         m.w.mmw_netw_id_h = psa.psa_nwid[0];
+         mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m,
+                   (unsigned char *)&m.w.mmw_netw_id_l, 2);
+         mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00);
+       }
+      else
+       {
+         /* Disable nwid in the psa */
+         psa.psa_nwid_select = 0x00;
+         psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa,
+                   (unsigned char *)&psa.psa_nwid_select, 1);
+
+         /* Disable nwid in the mmc (no filtering) */
+         mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
+       }
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      break;
+
+    case SIOCGIWNWID:
+      /* Read the NWID */
+      psa_read(dev, (char *)psa.psa_nwid - (char *)&psa,
+              (unsigned char *)psa.psa_nwid, 3);
+#if WIRELESS_EXT > 8
+      wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+      wrq->u.nwid.fixed = 1;   /* Superfluous */
+#else  /* WIRELESS_EXT > 8 */
+      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.on = psa.psa_nwid_select;
+#endif /* WIRELESS_EXT > 8 */
+      break;
+
+    case SIOCSIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+          (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+       ret = wv_set_frequency(base, &(wrq->u.freq));
+      else
+       ret = -EOPNOTSUPP;
+      break;
+
+    case SIOCGIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+       * (does it work for everybody ??? - especially old cards...) */
+      if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+          (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+       {
+         unsigned short        freq;
+
+         /* Ask the EEprom to read the frequency from the first area */
+         fee_read(base, 0x00 /* 1st area - frequency... */,
+                  &freq, 1);
+         wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+         wrq->u.freq.e = 1;
+       }
+      else
+       {
+         psa_read(dev, (char *)&psa.psa_subband - (char *)&psa,
+                  (unsigned char *)&psa.psa_subband, 1);
+
+         if(psa.psa_subband <= 4)
+           {
+             wrq->u.freq.m = fixed_bands[psa.psa_subband];
+             wrq->u.freq.e = (psa.psa_subband != 0);
+           }
+         else
+           ret = -EOPNOTSUPP;
+       }
+      break;
+
+    case SIOCSIWSENS:
+      /* Set the level threshold */
+#if WIRELESS_EXT > 7
+      /* We should complain loudly if wrq->u.sens.fixed = 0, because we
+       * can't set auto mode... */
+      psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F;
+#else  /* WIRELESS_EXT > 7 */
+      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
+#endif /* WIRELESS_EXT > 7 */
+      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+              (unsigned char *)&psa.psa_thr_pre_set, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+      break;
+
+    case SIOCGIWSENS:
+      /* Read the level threshold */
+      psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+              (unsigned char *)&psa.psa_thr_pre_set, 1);
+#if WIRELESS_EXT > 7
+      wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F;
+      wrq->u.sens.fixed = 1;
+#else  /* WIRELESS_EXT > 7 */
+      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
+#endif /* WIRELESS_EXT > 7 */
+      break;
+
+#if WIRELESS_EXT > 8
+    case SIOCSIWENCODE:
+      /* Set encryption key */
+      if(!mmc_encr(base))
+       {
+         ret = -EOPNOTSUPP;
+         break;
+       }
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+       {
+         /* Check the size of the key */
+         if(wrq->u.encoding.length != 8)
+           {
+             ret = -EINVAL;
+             break;
+           }
+
+         /* Copy the key in the driver */
+         if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
+                           wrq->u.encoding.length))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         psa.psa_encryption_select = 1;
+         psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+                   (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+         mmc_out(base, mmwoff(0, mmw_encr_enable),
+                 MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+         mmc_write(base, mmwoff(0, mmw_encr_key),
+                   (unsigned char *) &psa.psa_encryption_key, 8);
+       }
+
+      if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
+       {       /* disable encryption */
+         psa.psa_encryption_select = 0;
+         psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+                   (unsigned char *) &psa.psa_encryption_select, 1);
+
+         mmc_out(base, mmwoff(0, mmw_encr_enable), 0);
+       }
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      break;
+
+    case SIOCGIWENCODE:
+      /* Read the encryption key */
+      if(!mmc_encr(base))
+       {
+         ret = -EOPNOTSUPP;
+         break;
+       }
+
+      /* only super-user can see encryption key */
+      if(!suser())
+       {
+         ret = -EPERM;
+         break;
+       }
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+       {
+         psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa,
+                  (unsigned char *) &psa.psa_encryption_select, 1+8);
+
+         /* encryption is enabled ? */
+         if(psa.psa_encryption_select)
+           wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+         else
+           wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+         wrq->u.encoding.flags |= mmc_encr(base);
+
+         /* Copy the key to the user buffer */
+         wrq->u.encoding.length = 8;
+         if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
+           ret = -EFAULT;
+       }
+      break;
+#endif /* WIRELESS_EXT > 8 */
+
+#ifdef WAVELAN_ROAMING_EXT
+#if WIRELESS_EXT > 5
+    case SIOCSIWESSID:
+      /* Check if disable */
+      if(wrq->u.data.flags == 0)
+       lp->filter_domains = 0;
+      else
+       /* Basic checking... */
+       if(wrq->u.data.pointer != (caddr_t) 0)
+         {
+           char        essid[IW_ESSID_MAX_SIZE + 1];
+           char *      endp;
+
+           /* Check the size of the string */
+           if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1)
+             {
+               ret = -E2BIG;
+               break;
+             }
+
+           /* Copy the string in the driver */
+           if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length))
+             {
+               ret = -EFAULT;
+               break;
+             }
+           essid[IW_ESSID_MAX_SIZE] = '\0';
+
+#ifdef DEBUG_IOCTL_INFO
+           printk(KERN_DEBUG "SetEssid : ``%s''\n", essid);
+#endif /* DEBUG_IOCTL_INFO */
+
+           /* Convert to a number (note : Wavelan specific) */
+           lp->domain_id = simple_strtoul(essid, &endp, 16);
+           /* Has it worked  ? */
+           if(endp > essid)
+             lp->filter_domains = 1;
+           else
+             {
+               lp->filter_domains = 0;
+               ret = -EINVAL;
+             }
+         }
+      break;
+
+    case SIOCGIWESSID:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+       {
+         char          essid[IW_ESSID_MAX_SIZE + 1];
+
+         /* Is the domain ID active ? */
+         wrq->u.data.flags = lp->filter_domains;
+
+         /* Copy Domain ID into a string (Wavelan specific) */
+         /* Sound crazy, be we can't have a snprintf in the kernel !!! */
+         sprintf(essid, "%lX", lp->domain_id);
+         essid[IW_ESSID_MAX_SIZE] = '\0';
+
+         /* Set the length */
+         wrq->u.data.length = strlen(essid) + 1;
+
+         /* Copy structure to the user buffer */
+         if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length))
+           ret = -EFAULT;
+       }
+      break;
+
+    case SIOCSIWAP:
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
+            wrq->u.ap_addr.sa_data[0],
+            wrq->u.ap_addr.sa_data[1],
+            wrq->u.ap_addr.sa_data[2],
+            wrq->u.ap_addr.sa_data[3],
+            wrq->u.ap_addr.sa_data[4],
+            wrq->u.ap_addr.sa_data[5]);
+#endif /* DEBUG_IOCTL_INFO */
+
+      ret = -EOPNOTSUPP;       /* Not supported yet */
+      break;
+
+    case SIOCGIWAP:
+      /* Should get the real McCoy instead of own Ethernet address */
+      memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE);
+      wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
+
+      ret = -EOPNOTSUPP;       /* Not supported yet */
+      break;
+#endif /* WIRELESS_EXT > 5 */
+#endif /* WAVELAN_ROAMING_EXT */
+
+#if WIRELESS_EXT > 8
+#ifdef WAVELAN_ROAMING
+    case SIOCSIWMODE:
+      switch(wrq->u.mode)
+       {
+       case IW_MODE_ADHOC:
+         if(do_roaming)
+           {
+             wv_roam_cleanup(dev);
+             do_roaming = 0;
+           }
+         break;
+       case IW_MODE_INFRA:
+         if(!do_roaming)
+           {
+             wv_roam_init(dev);
+             do_roaming = 1;
+           }
+         break;
+       default:
+         ret = -EINVAL;
+       }
+      break;
+
+    case SIOCGIWMODE:
+      if(do_roaming)
+       wrq->u.mode = IW_MODE_INFRA;
+      else
+       wrq->u.mode = IW_MODE_ADHOC;
+      break;
+#endif /* WAVELAN_ROAMING */
+#endif /* WIRELESS_EXT > 8 */
+
+    case SIOCGIWRANGE:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+       {
+         struct iw_range       range;
+
+         /* Set the length (useless : its constant...) */
+         wrq->u.data.length = sizeof(struct iw_range);
+
+         /* Set information in the range struct */
+         range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */
+         range.min_nwid = 0x0000;
+         range.max_nwid = 0xFFFF;
+
+         /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+         if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+              (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+           {
+             range.num_channels = 10;
+             range.num_frequency = wv_frequency_list(base, range.freq,
+                                                     IW_MAX_FREQUENCIES);
+           }
+         else
+           range.num_channels = range.num_frequency = 0;
+
+         range.sensitivity = 0x3F;
+         range.max_qual.qual = MMR_SGNL_QUAL;
+         range.max_qual.level = MMR_SIGNAL_LVL;
+         range.max_qual.noise = MMR_SILENCE_LVL;
+
+#if WIRELESS_EXT > 7
+         range.num_bitrates = 1;
+         range.bitrate[0] = 2000000;   /* 2 Mb/s */
+#endif /* WIRELESS_EXT > 7 */
+
+#if WIRELESS_EXT > 8
+         /* Encryption supported ? */
+         if(mmc_encr(base))
+           {
+             range.encoding_size[0] = 8;       /* DES = 64 bits key */
+             range.num_encoding_sizes = 1;
+             range.max_encoding_tokens = 1;    /* Only one key possible */
+           }
+         else
+           {
+             range.num_encoding_sizes = 0;
+             range.max_encoding_tokens = 0;
+           }
+#endif /* WIRELESS_EXT > 8 */
+
+         /* Copy structure to the user buffer */
+         if(copy_to_user(wrq->u.data.pointer, &range,
+                         sizeof(struct iw_range)))
+           ret = -EFAULT;
+       }
+      break;
+
+    case SIOCGIWPRIV:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+       {
+         struct iw_priv_args   priv[] =
+         {     /* cmd,         set_args,       get_args,       name */
+           { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
+           { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
+           { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,     0, "sethisto" },
+           { SIOCGIPHISTO, 0,      IW_PRIV_TYPE_INT | 16, "gethisto" },
+           { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" },
+           { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" },
+         };
+
+         /* Set the number of ioctl available */
+         wrq->u.data.length = 6;
+
+         /* Copy structure to the user buffer */
+         if(copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+                      sizeof(priv)))
+           ret = -EFAULT;
+       }
+      break;
+
+#ifdef WIRELESS_SPY
+    case SIOCSIWSPY:
+      /* Set the spy list */
+
+      /* Check the number of addresses */
+      if(wrq->u.data.length > IW_MAX_SPY)
+       {
+         ret = -E2BIG;
+         break;
+       }
+      lp->spy_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->spy_number > 0)
+       {
+         struct sockaddr       address[IW_MAX_SPY];
+         int                   i;
+
+         /* Copy addresses to the driver */
+         if(copy_from_user(address, wrq->u.data.pointer,
+                           sizeof(struct sockaddr) * lp->spy_number))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         /* Copy addresses to the lp structure */
+         for(i = 0; i < lp->spy_number; i++)
+           {
+             memcpy(lp->spy_address[i], address[i].sa_data,
+                    WAVELAN_ADDR_SIZE);
+           }
+
+         /* Reset structure... */
+         memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+         printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
+         for(i = 0; i < wrq->u.data.length; i++)
+           printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n",
+                  lp->spy_address[i][0],
+                  lp->spy_address[i][1],
+                  lp->spy_address[i][2],
+                  lp->spy_address[i][3],
+                  lp->spy_address[i][4],
+                  lp->spy_address[i][5]);
+#endif /* DEBUG_IOCTL_INFO */
+       }
+
+      break;
+
+    case SIOCGIWSPY:
+      /* Get the spy list and spy stats */
+
+      /* Set the number of addresses */
+      wrq->u.data.length = lp->spy_number;
+
+      /* If the user want to have the addresses back... */
+      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+       {
+         struct sockaddr       address[IW_MAX_SPY];
+         int                   i;
+
+         /* Copy addresses from the lp structure */
+         for(i = 0; i < lp->spy_number; i++)
+           {
+             memcpy(address[i].sa_data, lp->spy_address[i],
+                    WAVELAN_ADDR_SIZE);
+             address[i].sa_family = ARPHRD_ETHER;
+           }
+
+         /* Copy addresses to the user buffer */
+         if(copy_to_user(wrq->u.data.pointer, address,
+                      sizeof(struct sockaddr) * lp->spy_number))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         /* Copy stats to the user buffer (just after) */
+         if(copy_to_user(wrq->u.data.pointer +
+                      (sizeof(struct sockaddr) * lp->spy_number),
+                      lp->spy_stat, sizeof(iw_qual) * lp->spy_number))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         /* Reset updated flags */
+         for(i = 0; i < lp->spy_number; i++)
+           lp->spy_stat[i].updated = 0x0;
+       }       /* if(pointer != NULL) */
+
+      break;
+#endif /* WIRELESS_SPY */
+
+      /* ------------------ PRIVATE IOCTL ------------------ */
+
+    case SIOCSIPQTHR:
+      if(!suser())
+       {
+         ret = -EPERM;
+         break;
+       }
+      psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+              (unsigned char *)&psa.psa_quality_thr, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+      mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
+      break;
+
+    case SIOCGIPQTHR:
+      psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+              (unsigned char *)&psa.psa_quality_thr, 1);
+      *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+      break;
+
+#ifdef WAVELAN_ROAMING
+    case SIOCSIPROAM:
+      /* Note : should check if user == root */
+      if(do_roaming && (*wrq->u.name)==0)
+       wv_roam_cleanup(dev);
+      else if(do_roaming==0 && (*wrq->u.name)!=0)
+       wv_roam_init(dev);
+
+      do_roaming = (*wrq->u.name);
+         
+      break;
+
+    case SIOCGIPROAM:
+      *(wrq->u.name) = do_roaming;
+      break;
+#endif /* WAVELAN_ROAMING */
+
+#ifdef HISTOGRAM
+    case SIOCSIPHISTO:
+      /* Verif if the user is root */
+      if(!suser())
+       {
+         ret = -EPERM;
+       }
+
+      /* Check the number of intervals */
+      if(wrq->u.data.length > 16)
+       {
+         ret = -E2BIG;
+         break;
+       }
+      lp->his_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->his_number > 0)
+       {
+         /* Copy interval ranges to the driver */
+         if(copy_from_user(lp->his_range, wrq->u.data.pointer,
+                        sizeof(char) * lp->his_number))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         /* Reset structure... */
+         memset(lp->his_sum, 0x00, sizeof(long) * 16);
+       }
+      break;
+
+    case SIOCGIPHISTO:
+      /* Set the number of intervals */
+      wrq->u.data.length = lp->his_number;
+
+      /* Give back the distribution statistics */
+      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+       {
+         /* Copy data to the user buffer */
+         if(copy_to_user(wrq->u.data.pointer, lp->his_sum,
+                      sizeof(long) * lp->his_number))
+           ret = -EFAULT;
+
+       }       /* if(pointer != NULL) */
+      break;
+#endif /* HISTOGRAM */
+
+      /* ------------------- OTHER IOCTL ------------------- */
+
+    default:
+      ret = -EOPNOTSUPP;
+    }
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+  return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics
+ * Called by /proc/net/wireless...
+ */
+static iw_stats *
+wavelan_get_wireless_stats(device *    dev)
+{
+  ioaddr_t             base = dev->base_addr;
+  net_local *          lp = (net_local *) dev->priv;
+  mmr_t                        m;
+  iw_stats *           wstats;
+  unsigned long                x;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  if(lp == (net_local *) NULL)
+    return (iw_stats *) NULL;
+  wstats = &lp->wstats;
+
+  /* Get data from the mmc */
+  mmc_out(base, mmwoff(0, mmw_freeze), 1);
+
+  mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+  mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
+  mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
+
+  mmc_out(base, mmwoff(0, mmw_freeze), 0);
+
+  /* Copy data to wireless stuff */
+  wstats->status = m.mmr_dce_status & MMR_DCE_STATUS;
+  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
+                         ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
+                         ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+  wstats->discard.code = 0L;
+  wstats->discard.misc = 0L;
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
+#endif
+  return &lp->wstats;
+}
+#endif /* WIRELESS_EXT */
+
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deal with receiving the packets.
+ * The interrupt handler get an interrupt when a packet has been
+ * successfully received and called this part...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the starting address of the frame pointed to by the receive
+ * frame pointer and verify that the frame seem correct
+ * (called by wv_packet_rcv())
+ */
+static inline int
+wv_start_of_frame(device *     dev,
+                 int           rfp,    /* end of frame */
+                 int           wrap)   /* start of buffer */
+{
+  ioaddr_t     base = dev->base_addr;
+  int          rp;
+  int          len;
+
+  rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
+  outb(rp & 0xff, PIORL(base));
+  outb(((rp >> 8) & PIORH_MASK), PIORH(base));
+  len = inb(PIOP(base));
+  len |= inb(PIOP(base)) << 8;
+
+  /* Sanity checks on size */
+  /* Frame too big */
+  if(len > MAXDATAZ + 100)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n",
+            dev->name, rfp, len);
+#endif
+      return(-1);
+    }
+  
+  /* Frame too short */
+  if(len < 7)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n",
+            dev->name, rfp, len);
+#endif
+      return(-1);
+    }
+  
+  /* Wrap around buffer */
+  if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",
+            dev->name, wrap, rfp, len);
+#endif
+      return(-1);
+    }
+
+  return((rp - len + RX_SIZE) % RX_SIZE);
+} /* wv_start_of_frame */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copy of data (including the ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: We
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here.  The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor"
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device *                dev,
+              int              fd_p,
+              int              sksize)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  struct sk_buff *     skb;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+        dev->name, fd_p, sksize);
+#endif
+
+  /* Allocate some buffer for the new packet */
+  if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n",
+            dev->name, sksize);
+#endif
+      lp->stats.rx_dropped++;
+      /*
+       * Not only do we want to return here, but we also need to drop the
+       * packet on the floor to clear the interrupt.
+       */
+      return;
+    }
+
+  skb->dev = dev;
+
+  skb_reserve(skb, 2);
+  fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize);
+  skb->protocol = eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+  /* Another glitch : Due to the way the GET_PACKET macro is written,
+   * we are not sure to have the same thing in skb->data. On the other
+   * hand, skb->mac.raw is not defined everywhere...
+   * For versions between 1.2.13 and those where skb->mac.raw appear,
+   * I don't have a clue...
+   */
+  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif /* DEBUG_RX_INFO */
+     
+  /* Statistics gathering & stuff associated.
+   * It seem a bit messy with all the define, but it's really simple... */
+  if(
+#ifdef WIRELESS_SPY
+     (lp->spy_number > 0) ||
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+     (lp->his_number > 0) ||
+#endif /* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+     (do_roaming) ||
+#endif /* WAVELAN_ROAMING */
+     0)
+    {
+      u_char   stats[3];       /* Signal level, Noise level, Signal quality */
+
+      /* read signal level, silence level and signal quality bytes */
+      fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE,
+                         stats, 3);
+#ifdef DEBUG_RX_INFO
+      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+            dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
+#endif
+
+#ifdef WAVELAN_ROAMING
+      if(do_roaming)
+       if(WAVELAN_BEACON(skb->data))
+         wl_roam_gather(dev, skb->data, stats);
+#endif /* WAVELAN_ROAMING */
+         
+      /* Spying stuff */
+#ifdef WIRELESS_SPY
+      /* Same as above */
+      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+      wl_his_gather(dev, stats);
+#endif /* HISTOGRAM */
+    }
+
+  /*
+   * Hand the packet to the Network Module
+   */
+  netif_rx(skb);
+
+  /* Keep stats up to date */
+  lp->stats.rx_packets++;
+  lp->stats.rx_bytes += skb->len;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
+  return;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the card to the network interface layer above
+ * this driver.  This routine checks if a buffer has been successfully
+ * received by the WaveLAN card.  If so, the routine wv_packet_read is
+ * called to do the actual transfer of the card's data including the
+ * ethernet header into a packet consisting of an sk_buff chain.
+ * (called by wavelan_interrupt())
+ */
+static inline void
+wv_packet_rcv(device * dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  net_local *  lp = (net_local *) dev->priv;
+  int          newrfp;
+  int          rp;
+  int          len;
+  int          f_start;
+  int          status;
+  int          i593_rfp;
+  int          stat_ptr;
+  u_char       c[4];
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name);
+#endif
+
+  /* Get the new receive frame pointer from the i82593 chip */
+  outb(CR0_STATUS_2 | OP0_NOP, LCCR(base));
+  i593_rfp = inb(LCSR(base));
+  i593_rfp |= inb(LCSR(base)) << 8;
+  i593_rfp %= RX_SIZE;
+
+  /* Get the new receive frame pointer from the WaveLAN card.
+   * It is 3 bytes more than the increment of the i82593 receive
+   * frame pointer, for each packet. This is because it includes the
+   * 3 roaming bytes added by the mmc.
+   */
+  newrfp = inb(RPLL(base));
+  newrfp |= inb(RPLH(base)) << 8;
+  newrfp %= RX_SIZE;
+
+#ifdef DEBUG_RX_INFO
+  printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+        dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+#ifdef DEBUG_RX_ERROR
+  /* If no new frame pointer... */
+  if(lp->overrunning || newrfp == lp->rfp)
+    printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+          dev->name, i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+
+  /* Read all frames (packets) received */
+  while(newrfp != lp->rfp)
+    {
+      /* A frame is composed of the packet, followed by a status word,
+       * the length of the frame (word) and the mmc info (SNR & qual).
+       * It's because the length is at the end that we can only scan
+       * frames backward. */
+
+      /* Find the first frame by skipping backwards over the frames */
+      rp = newrfp;     /* End of last frame */
+      while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) &&
+           (f_start != -1))
+         rp = f_start;
+
+      /* If we had a problem */
+      if(f_start == -1)
+       {
+#ifdef DEBUG_RX_ERROR
+         printk(KERN_INFO "wavelan_cs: cannot find start of frame ");
+         printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n",
+                i593_rfp, lp->stop, newrfp, lp->rfp);
+#endif
+         lp->rfp = rp;         /* Get to the last usable frame */
+         continue;
+       }
+
+      /* f_start point to the beggining of the first frame received
+       * and rp to the beggining of the next one */
+
+      /* Read status & length of the frame */
+      stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
+      stat_ptr = read_ringbuf(dev, stat_ptr, c, 4);
+      status = c[0] | (c[1] << 8);
+      len = c[2] | (c[3] << 8);
+
+      /* Check status */
+      if((status & RX_RCV_OK) != RX_RCV_OK)
+       {
+         lp->stats.rx_errors++;
+         if(status & RX_NO_SFD)
+           lp->stats.rx_frame_errors++;
+         if(status & RX_CRC_ERR)
+           lp->stats.rx_crc_errors++;
+         if(status & RX_OVRRUN)
+           lp->stats.rx_over_errors++;
+
+#ifdef DEBUG_RX_FAIL
+         printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n",
+                dev->name, status);
+#endif
+       }
+      else
+       /* Read the packet and transmit to Linux */
+       wv_packet_read(dev, f_start, len - 2);
+
+      /* One frame has been processed, skip it */
+      lp->rfp = rp;
+    }
+
+  /*
+   * Update the frame stop register, but set it to less than
+   * the full 8K to allow space for 3 bytes of signal strength
+   * per packet.
+   */
+  lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name);
+#endif
+}
+
+/*********************** PACKET TRANSMISSION ***********************/
+/*
+ * This part deal with sending packet through the wavelan
+ * We copy the packet to the send buffer and then issue the send
+ * command to the i82593. The result of this operation will be
+ * checked in wavelan_interrupt()
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ * (called in wavelan_packet_xmit())
+ */
+static inline void
+wv_packet_write(device *       dev,
+               void *          buf,
+               short           length)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  ioaddr_t             base = dev->base_addr;
+  unsigned long                x;
+  int                  clen = length;
+  register u_short     xmtdata_base = TX_BASE;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
+#endif
+
+  x = wv_splhi();
+
+  /* Check if we need some padding */
+  if(clen < ETH_ZLEN)
+    clen = ETH_ZLEN;
+
+  /* Write the length of data buffer followed by the buffer */
+  outb(xmtdata_base & 0xff, PIORL(base));
+  outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(clen & 0xff, PIOP(base));       /* lsb */
+  outb(clen >> 8, PIOP(base));         /* msb */
+
+  /* Send the data */
+  outsb(PIOP(base), buf, clen);
+
+  /* Indicate end of transmit chain */
+  outb(OP0_NOP, PIOP(base));
+  /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */
+  outb(OP0_NOP, PIOP(base));
+
+  /* Reset the transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  /* Send the transmit command */
+  wv_82593_cmd(dev, "wv_packet_write(): transmit",
+              OP0_TRANSMIT, SR0_NO_RESULT);
+
+  /* Keep stats up to date */
+  lp->stats.tx_bytes += length;
+
+  /* If watchdog not already active, activate it... */
+  if(lp->watchdog.prev == (timer_list *) NULL)
+    {
+      /* set timer to expire in WATCHDOG_JIFFIES */
+      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+      add_timer(&lp->watchdog);
+    }
+
+  wv_splx(x); 
+
+#ifdef DEBUG_TX_INFO
+  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
+#endif /* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the the harware is ready to accept
+ * the packet. We also prevent reentrance. Then, we call the function
+ * to send the packet...
+ */
+static int
+wavelan_packet_xmit(struct sk_buff *   skb,
+                   device *            dev)
+{
+  net_local *  lp = (net_local *)dev->priv;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+        (unsigned) skb);
+#endif
+
+  /* This flag indicate that the hardware can't perform a transmission.
+   * Theoritically, NET3 check it before sending a packet to the driver,
+   * but in fact it never do that and pool continuously.
+   * As the watchdog will abort too long transmissions, we are quite safe...
+   */
+  if(dev->tbusy)
+    return(1);
+
+  /*
+   * For ethernet, fill in the header.
+   */
+
+  /*
+   * Block a timer-based transmit from overlapping a previous transmit.
+   * In other words, prevent reentering this routine.
+   */
+  if(test_and_set_bit(0, (void *)&dev->tbusy) != 0)
+#ifdef DEBUG_TX_ERROR
+    printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name);
+#endif
+  else
+    {
+      /* If somebody has asked to reconfigure the controler, we can do it now */
+      if(lp->reconfig_82593)
+       {
+         lp->reconfig_82593 = FALSE;
+         wv_82593_config(dev);
+       }
+
+#ifdef DEBUG_TX_ERROR
+      if(skb->next)
+       printk(KERN_INFO "skb has next\n");
+#endif
+      wv_packet_write(dev, skb->data, skb->len);
+    }
+
+  dev_kfree_skb(skb);
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+  return(0);
+}
+
+/********************** HARDWARE CONFIGURATION **********************/
+/*
+ * This part do the real job of starting and configuring the hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_config())
+ */
+static inline int
+wv_mmc_init(device *   dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  psa_t                psa;
+  mmw_t                m;
+  int          configured;
+  int          i;              /* Loop counter */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+  /* Read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /*
+   * Check the first three octets of the MAC addr for the manufacturer's code.
+   * Note: If you get the error message below, you've got a
+   * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on
+   * how to configure your card...
+   */
+  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+    if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) &&
+       (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) &&
+       (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2]))
+      break;
+
+  /* If we have not found it... */
+  if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3))
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n",
+            dev->name, psa.psa_univ_mac_addr[0],
+            psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]);
+#endif
+      return FALSE;
+    }
+
+  /* Get the MAC address */
+  memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+#ifdef USE_PSA_CONFIG
+  configured = psa.psa_conf_status & 1;
+#else
+  configured = 0;
+#endif
+
+  /* Is the PSA is not configured */
+  if(!configured)
+    {
+      /* User will be able to configure NWID after (with iwconfig) */
+      psa.psa_nwid[0] = 0;
+      psa.psa_nwid[1] = 0;
+
+      /* As NWID is not set : no NWID checking */
+      psa.psa_nwid_select = 0;
+
+      /* Disable encryption */
+      psa.psa_encryption_select = 0;
+
+      /* Set to standard values
+       * 0x04 for AT,
+       * 0x01 for MCA,
+       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+       */
+      if (psa.psa_comp_number & 1)
+       psa.psa_thr_pre_set = 0x01;
+      else
+       psa.psa_thr_pre_set = 0x04;
+      psa.psa_quality_thr = 0x03;
+
+      /* It is configured */
+      psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+      /* Write the psa */
+      psa_write(dev, (char *)psa.psa_nwid - (char *)&psa,
+               (unsigned char *)psa.psa_nwid, 4);
+      psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+               (unsigned char *)&psa.psa_thr_pre_set, 1);
+      psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa,
+               (unsigned char *)&psa.psa_quality_thr, 1);
+      psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa,
+               (unsigned char *)&psa.psa_conf_status, 1);
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev);
+#endif /* USE_PSA_CONFIG */
+    }
+
+  /* Zero the mmc structure */
+  memset(&m, 0x00, sizeof(m));
+
+  /* Copy PSA info to the mmc */
+  m.mmw_netw_id_l = psa.psa_nwid[1];
+  m.mmw_netw_id_h = psa.psa_nwid[0];
+  
+  if(psa.psa_nwid_select & 1)
+    m.mmw_loopt_sel = 0x00;
+  else
+    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, 
+        sizeof(m.mmw_encr_key));
+
+  if(psa.psa_encryption_select)
+    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+  else
+    m.mmw_encr_enable = 0;
+
+  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+  /*
+   * Set default modem control parameters.
+   * See NCR document 407-0024326 Rev. A.
+   */
+  m.mmw_jabber_enable = 0x01;
+  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+  m.mmw_ifs = 0x20;
+  m.mmw_mod_delay = 0x04;
+  m.mmw_jam_time = 0x38;
+
+  m.mmw_des_io_invert = 0;
+  m.mmw_freeze = 0;
+  m.mmw_decay_prm = 0;
+  m.mmw_decay_updat_prm = 0;
+
+  /* Write all info to mmc */
+  mmc_write(base, 0, (u_char *)&m, sizeof(m));
+
+  /* The following code start the modem of the 2.00 frequency
+   * selectable cards at power on. It's not strictly needed for the
+   * following boots...
+   * The original patch was by Joe Finney for the PCMCIA driver, but
+   * I've cleaned it a bit and add documentation.
+   * Thanks to Loeke Brederveld from Lucent for the info.
+   */
+
+  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+   * (does it work for everybody ??? - especially old cards...) */
+  /* Note : WFREQSEL verify that it is able to read from EEprom
+   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * My test is more crude but do work... */
+  if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      m.mmw_fee_addr = 0x0F;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+               (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+      fee_wait(base, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+      /* The frequency was in the last word downloaded... */
+      mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m,
+              (unsigned char *)&m.mmw_fee_data_l, 2);
+
+      /* Print some info for the user */
+      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+            dev->name,
+            ((m.mmw_fee_data_h << 4) |
+             (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
+#endif
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      m.mmw_fee_addr = 0x61;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m,
+               (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+    }  /* if 2.00 card */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to gracefully turn off reception, and wait for any commands
+ * to complete.
+ * (called in wv_ru_start() and wavelan_close() and wavelan_event())
+ */
+static int
+wv_ru_stop(device *    dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  unsigned long        opri;
+  int          status;
+  int          spin;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
+#endif
+
+  /* First, send the LAN controller a stop receive command */
+  wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
+              OP0_STOP_RCV, SR0_NO_RESULT);
+
+  /* Then, spin until the receive unit goes idle */
+  spin = 0;
+  do
+    {
+      udelay(10);
+      opri = wv_splhi();
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+      wv_splx(opri);
+    }
+  while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin++ < 300));
+
+  /* Now, spin until the chip finishes executing its current command */
+  do
+    {
+      udelay(10);
+      opri = wv_splhi();
+      outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+      status = inb(LCSR(base));
+      wv_splx(opri);
+    }
+  while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin++ < 300));
+
+  /* If there was a problem */
+  if(spin > 300)
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
+            dev->name);
+#endif
+      return FALSE;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name);
+#endif
+  return TRUE;
+} /* wv_ru_stop */
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine starts the receive unit running.  First, it checks if
+ * the card is actually ready. Then the card is instructed to receive
+ * packets again.
+ * (called in wv_hw_reset() & wavelan_open())
+ */
+static int
+wv_ru_start(device *   dev)
+{
+  ioaddr_t     base = dev->base_addr;
+  net_local *  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+  /*
+   * We need to start from a quiescent state. To do so, we could check
+   * if the card is already running, but instead we just try to shut
+   * it down. First, we disable reception (in case it was already enabled).
+   */
+  if(!wv_ru_stop(dev))
+    return FALSE;
+
+  /* Now we know that no command is being executed. */
+
+  /* Set the receive frame pointer and stop pointer */
+  lp->rfp = 0;
+  outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base));
+
+  /* Reset ring management.  This sets the receive frame pointer to 1 */
+  outb(OP1_RESET_RING_MNGMT, LCCR(base));
+
+  /* but I set it to 3 bytes per packet less than 8K */
+  lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+  outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
+  outb(OP1_INT_ENABLE, LCCR(base));
+  outb(OP1_SWIT_TO_PORT_0, LCCR(base));
+
+  /* Reset receive DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write_slow(base, HACR_DEFAULT);
+
+  /* Receive DMA on channel 1 */
+  wv_82593_cmd(dev, "wv_ru_start(): rcv-enable",
+              CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT);
+
+#ifdef DEBUG_I82593_SHOW
+  {
+    int        status;
+    int        opri;
+    int        i = 0;
+
+    /* spin until the chip starts receiving */
+    do
+      {
+       opri = wv_splhi();
+       outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
+       status = inb(LCSR(base));
+       wv_splx(opri);
+       if(i++ > 10000)
+         break;
+      }
+    while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
+         ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY));
+    printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n",
+          (status & SR3_RCV_STATE_MASK), i);
+  }
+#endif
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controler (i82593).
+ * In the ISA driver, this is integrated in wavelan_hardware_reset()
+ * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit())
+ */
+static int
+wv_82593_config(device *       dev)
+{
+  ioaddr_t                     base = dev->base_addr;
+  net_local *                  lp = (net_local *) dev->priv;
+  struct i82593_conf_block     cfblk;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
+#endif
+
+  /* Create & fill i82593 config block
+   *
+   * Now conform to Wavelan document WCIN085B
+   */
+  memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
+  cfblk.d6mod = FALSE;         /* Run in i82593 advanced mode */
+  cfblk.fifo_limit = 5;         /* = 56 B rx and 40 B tx fifo thresholds */
+  cfblk.forgnesi = FALSE;       /* 0=82C501, 1=AMD7992B compatibility */
+  cfblk.fifo_32 = 1;
+  cfblk.throttle_enb = FALSE;
+  cfblk.contin = TRUE;          /* enable continuous mode */
+  cfblk.cntrxint = FALSE;       /* enable continuous mode receive interrupts */
+  cfblk.addr_len = WAVELAN_ADDR_SIZE;
+  cfblk.acloc = TRUE;           /* Disable source addr insertion by i82593 */
+  cfblk.preamb_len = 0;         /* 2 bytes preamble (SFD) */
+  cfblk.loopback = FALSE;
+  cfblk.lin_prio = 0;          /* conform to 802.3 backoff algoritm */
+  cfblk.exp_prio = 5;          /* conform to 802.3 backoff algoritm */
+  cfblk.bof_met = 1;           /* conform to 802.3 backoff algoritm */
+  cfblk.ifrm_spc = 0x20;       /* 32 bit times interframe spacing */
+  cfblk.slottim_low = 0x20;    /* 32 bit times slot time */
+  cfblk.slottim_hi = 0x0;
+  cfblk.max_retr = 15;
+  cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE);    /* Promiscuous mode */
+  cfblk.bc_dis = FALSE;         /* Enable broadcast reception */
+  cfblk.crs_1 = TRUE;          /* Transmit without carrier sense */
+  cfblk.nocrc_ins = FALSE;     /* i82593 generates CRC */      
+  cfblk.crc_1632 = FALSE;      /* 32-bit Autodin-II CRC */
+  cfblk.crs_cdt = FALSE;       /* CD not to be interpreted as CS */
+  cfblk.cs_filter = 0;         /* CS is recognized immediately */
+  cfblk.crs_src = FALSE;       /* External carrier sense */
+  cfblk.cd_filter = 0;         /* CD is recognized immediately */
+  cfblk.min_fr_len = ETH_ZLEN >> 2;     /* Minimum frame length 64 bytes */
+  cfblk.lng_typ = FALSE;       /* Length field > 1500 = type field */
+  cfblk.lng_fld = TRUE;        /* Disable 802.3 length field check */
+  cfblk.rxcrc_xf = TRUE;       /* Don't transfer CRC to memory */
+  cfblk.artx = TRUE;           /* Disable automatic retransmission */
+  cfblk.sarec = TRUE;          /* Disable source addr trig of CD */
+  cfblk.tx_jabber = TRUE;      /* Disable jabber jam sequence */
+  cfblk.hash_1 = FALSE;        /* Use bits 0-5 in mc address hash */
+  cfblk.lbpkpol = TRUE;        /* Loopback pin active high */
+  cfblk.fdx = FALSE;           /* Disable full duplex operation */
+  cfblk.dummy_6 = 0x3f;        /* all ones */
+  cfblk.mult_ia = FALSE;       /* No multiple individual addresses */
+  cfblk.dis_bof = FALSE;       /* Disable the backoff algorithm ?! */
+  cfblk.dummy_1 = TRUE;        /* set to 1 */
+  cfblk.tx_ifs_retrig = 3;     /* Hmm... Disabled */
+#ifdef MULTICAST_ALL
+  cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE);     /* Allow all multicasts */
+#else
+  cfblk.mc_all = FALSE;                /* No multicast all mode */
+#endif
+  cfblk.rcv_mon = 0;           /* Monitor mode disabled */
+  cfblk.frag_acpt = TRUE;      /* Do not accept fragments */
+  cfblk.tstrttrs = FALSE;      /* No start transmission threshold */
+  cfblk.fretx = TRUE;          /* FIFO automatic retransmission */
+  cfblk.syncrqs = FALSE;       /* Synchronous DRQ deassertion... */
+  cfblk.sttlen = TRUE;         /* 6 byte status registers */
+  cfblk.rx_eop = TRUE;         /* Signal EOP on packet reception */
+  cfblk.tx_eop = TRUE;         /* Signal EOP on packet transmission */
+  cfblk.rbuf_size = RX_SIZE>>11;       /* Set receive buffer size */
+  cfblk.rcvstop = TRUE;        /* Enable Receive Stop Register */
+
+#ifdef DEBUG_I82593_SHOW
+  {
+    u_char *c = (u_char *) &cfblk;
+    int i;
+    printk(KERN_DEBUG "wavelan_cs: config block:");
+    for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++)
+      {
+       if((i % 16) == 0) printk("\n" KERN_DEBUG);
+       printk("%02x ", *c);
+      }
+    printk("\n");
+  }
+#endif
+
+  /* Copy the config block to the i82593 */
+  outb(TX_BASE & 0xff, PIORL(base));
+  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base));    /* lsb */
+  outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base));     /* msb */
+  outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block));
+
+  /* reset transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
+                  OP0_CONFIGURE, SR0_CONFIGURE_DONE))
+    return(FALSE);
+
+  /* Initialize adapter's ethernet MAC address */
+  outb(TX_BASE & 0xff, PIORL(base));
+  outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+  outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */
+  outb(0, PIOP(base));                 /* byte count msb */
+  outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE);
+
+  /* reset transmit DMA pointer */
+  hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+  hacr_write(base, HACR_DEFAULT);
+  if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
+                  OP0_IA_SETUP, SR0_IA_SETUP_DONE))
+    return(FALSE);
+
+#ifdef WAVELAN_ROAMING
+    /* If roaming is enabled, join the "Beacon Request" multicast group... */
+    /* But only if it's not in there already! */
+  if(do_roaming)
+    dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1);
+#endif /* WAVELAN_ROAMING */
+
+  /* If any multicast address to set */
+  if(lp->mc_count)
+    {
+      struct dev_mc_list *     dmi;
+      int                      addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
+            dev->name, lp->mc_count);
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+       printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
+              dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
+              dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
+#endif
+
+      /* Initialize adapter's ethernet multicast addresses */
+      outb(TX_BASE & 0xff, PIORL(base));
+      outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base));
+      outb(addrs_len & 0xff, PIOP(base));      /* byte count lsb */
+      outb((addrs_len >> 8), PIOP(base));      /* byte count msb */
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+       outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen);
+
+      /* reset transmit DMA pointer */
+      hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
+      hacr_write(base, HACR_DEFAULT);
+      if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
+                      OP0_MC_SETUP, SR0_MC_SETUP_DONE))
+       return(FALSE);
+      lp->mc_count = dev->mc_count;    /* remember to avoid repeated reset */
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
+#endif
+  return(TRUE);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Access Configuration Register, perform a software reset,
+ * and then re-enable the card's software.
+ *
+ * If I understand correctly : reset the pcmcia interface of the
+ * wavelan.
+ * (called by wv_config())
+ */
+static inline int
+wv_pcmcia_reset(device *       dev)
+{
+  int          i;
+  conf_reg_t   reg = { 0, CS_READ, CISREG_COR, 0 };
+  dev_link_t * link = ((net_local *) dev->priv)->link;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name);
+#endif
+
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
+        dev->name, (u_int) reg.Value);
+#endif
+
+  reg.Action = CS_WRITE;
+  reg.Value = reg.Value | COR_SW_RESET;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+  reg.Action = CS_WRITE;
+  reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_hw_config() is called after a CARD_INSERTION event is
+ * received, to configure the wavelan hardware.
+ * Note that the reception will be enabled in wavelan->open(), so the
+ * device is configured but idle...
+ * Performs the following actions:
+ *     1. A pcmcia software reset (using wv_pcmcia_reset())
+ *     2. A power reset (reset DMA)
+ *     3. Reset the LAN controller
+ *     4. Initialize the radio modem (using wv_mmc_init)
+ *     5. Configure LAN controller (using wv_82593_config)
+ *     6. Perform a diagnostic on the LAN controller
+ * (called by wavelan_event() & wv_hw_reset())
+ */
+static int
+wv_hw_config(device *  dev)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  ioaddr_t             base = dev->base_addr;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
+#endif
+
+#ifdef STRUCT_CHECK
+  if(wv_structuct_check() != (char *) NULL)
+    {
+      printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n",
+            dev->name, wv_structuct_check());
+      return FALSE;
+    }
+#endif /* STRUCT_CHECK == 1 */
+
+  /* Reset the pcmcia interface */
+  if(wv_pcmcia_reset(dev) == FALSE)
+    return FALSE;
+
+  /* Power UP the module + reset the modem + reset host adapter
+   * (in fact, reset DMA channels) */
+  hacr_write_slow(base, HACR_RESET);
+  hacr_write(base, HACR_DEFAULT);
+
+  /* Check if the the module has been powered up... */
+  if(hasr_read(base) & HASR_NO_CLK)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
+            dev->name);
+#endif
+      return FALSE;
+    }
+
+  /* initialize the modem */
+  if(wv_mmc_init(dev) == FALSE)
+    return FALSE;
+
+  /* reset the LAN controller (i82593) */
+  outb(OP0_RESET, LCCR(base));
+  udelay(1000L);       /* A bit crude ! */
+
+  /* Initialize the LAN controler */
+  if((wv_82593_config(dev) == FALSE) ||
+     (wv_diag(dev) == FALSE))
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", dev->name);
+#endif
+      return FALSE;
+    }
+
+  /* 
+   * insert code for loopback test here
+   */
+
+  /* The device is now configured */
+  lp->configured = 1;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the wavelan and restart it.
+ * Performs the following actions:
+ *     1. Call wv_hw_config()
+ *     2. Start the LAN controller's receive unit
+ * (called by wavelan_event(), wavelan_watchdog() and wavelan_open())
+ */
+static inline void
+wv_hw_reset(device *   dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
+#endif
+
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
+
+  lp->nresets++;
+  lp->configured = 0;
+  
+  /* Call wv_hw_config() for most of the reset & init stuff */
+  if(wv_hw_config(dev) == FALSE)
+    return;
+
+  /* start receive unit */
+  wv_ru_start(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wv_pcmcia_config() is called after a CARD_INSERTION event is
+ * received, to configure the PCMCIA socket, and to make the ethernet
+ * device available to the system.
+ * (called by wavelan_event())
+ */
+static inline int
+wv_pcmcia_config(dev_link_t *  link)
+{
+  client_handle_t      handle;
+  tuple_t              tuple;
+  cisparse_t           parse;
+  struct net_device *  dev;
+  int                  i;
+  u_char               buf[64];
+  win_req_t            req;
+  memreq_t             mem;
+
+  handle = link->handle;
+  dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link);
+#endif
+
+  /*
+   * This reads the card's CONFIG tuple to find its configuration
+   * registers.
+   */
+  do
+    {
+      tuple.Attributes = 0;
+      tuple.DesiredTuple = CISTPL_CONFIG;
+      i = CardServices(GetFirstTuple, handle, &tuple);
+      if(i != CS_SUCCESS)
+       break;
+      tuple.TupleData = (cisdata_t *)buf;
+      tuple.TupleDataMax = 64;
+      tuple.TupleOffset = 0;
+      i = CardServices(GetTupleData, handle, &tuple);
+      if(i != CS_SUCCESS)
+       break;
+      i = CardServices(ParseTuple, handle, &tuple, &parse);
+      if(i != CS_SUCCESS)
+       break;
+      link->conf.ConfigBase = parse.config.base;
+      link->conf.Present = parse.config.rmask[0];
+    }
+  while(0);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, ParseTuple, i);
+      link->state &= ~DEV_CONFIG_PENDING;
+      return FALSE;
+    }
+    
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+  do
+    {
+      i = CardServices(RequestIO, link->handle, &link->io);
+      if(i != CS_SUCCESS)
+       {
+         cs_error(link->handle, RequestIO, i);
+         break;
+       }
+
+      /*
+       * Now allocate an interrupt line.  Note that this does not
+       * actually assign a handler to the interrupt.
+       */
+      i = CardServices(RequestIRQ, link->handle, &link->irq);
+      if(i != CS_SUCCESS)
+       {
+         cs_error(link->handle, RequestIRQ, i);
+         break;
+       }
+
+      /*
+       * This actually configures the PCMCIA socket -- setting up
+       * the I/O windows and the interrupt mapping.
+       */
+      link->conf.ConfigIndex = 1;
+      i = CardServices(RequestConfiguration, link->handle, &link->conf);
+      if(i != CS_SUCCESS)
+       {
+         cs_error(link->handle, RequestConfiguration, i);
+         break;
+       }
+
+      /*
+       * Allocate a 4K memory window.  Note that the dev_link_t
+       * structure provides space for one window handle -- if your
+       * device needs several windows, you'll need to keep track of
+       * the handles in your private data structure, link->priv.
+       */
+      req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+      req.Base = 0; req.Size = 0x1000;
+      req.AccessSpeed = mem_speed;
+      link->win = (window_handle_t)link->handle;
+      i = CardServices(RequestWindow, &link->win, &req);
+      if(i != CS_SUCCESS)
+       {
+         cs_error(link->handle, RequestWindow, i);
+         break;
+       }
+
+      dev->rmem_start = dev->mem_start =
+         (u_long)ioremap(req.Base, 0x1000);
+      dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;
+
+      mem.CardOffset = 0; mem.Page = 0;
+      i = CardServices(MapMemPage, link->win, &mem);
+      if(i != CS_SUCCESS)
+       {
+         cs_error(link->handle, MapMemPage, i);
+         break;
+       }
+
+      /* Feed device with this info... */
+      dev->irq = link->irq.AssignedIRQ;
+      dev->base_addr = link->io.BasePort1;
+      dev->tbusy = 0;
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
+            (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr);
+#endif
+
+      i = register_netdev(dev);
+      if(i != 0)
+       {
+#ifdef DEBUG_CONFIG_ERRORS
+         printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n");
+#endif
+         break;
+       }
+    }
+  while(0);            /* Humm... Disguised goto !!! */
+
+  link->state &= ~DEV_CONFIG_PENDING;
+  /* If any step failed, release any partially configured state */
+  if(i != 0)
+    {
+      wv_pcmcia_release((u_long) link);
+      return FALSE;
+    }
+
+  /* ???? Could you explain me this, Dave ? */
+  link->dev = &((net_local *) dev->priv)->node;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "<-wv_pcmcia_config()\n");
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * After a card is removed, wv_pcmcia_release() will unregister the net
+ * device, and release the PCMCIA configuration.  If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+wv_pcmcia_release(u_long       arg)    /* Address of the interface struct */
+{
+  dev_link_t * link = (dev_link_t *) arg;
+  device *     dev = (device *) link->priv;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link);
+#endif
+
+  /* If the device is currently in use, we won't release until it is
+   * actually closed. */
+  if(link->open)
+    {
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n",
+            dev->name);
+#endif
+      link->state |= DEV_STALE_CONFIG;
+      return;
+    }
+
+  /* Don't bother checking to see if these succeed or not */
+  iounmap((u_char *)dev->mem_start);
+  CardServices(ReleaseWindow, link->win);
+  CardServices(ReleaseConfiguration, link->handle);
+  CardServices(ReleaseIO, link->handle, &link->io);
+  CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+  link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING | DEV_STALE_CONFIG);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
+#endif
+} /* wv_pcmcia_release */
+
+/*------------------------------------------------------------------*/
+/*
+ * Sometimes, netwave_detach can't be performed following a call from
+ * cardmgr (device still open, pcmcia_release not done) and the device
+ * is put in a STALE_LINK state and remains in memory.
+ *
+ * This function run through our current list of device and attempt
+ * another time to remove them. We hope that since last time the
+ * device has properly been closed.
+ *
+ * (called by wavelan_attach() & cleanup_module())
+ */
+static void
+wv_flush_stale_links(void)
+{
+  dev_link_t * link;           /* Current node in linked list */
+  dev_link_t * next;           /* Next node in linked list */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list);
+#endif
+
+  /* Go through the list */
+  for (link = dev_list; link; link = next)
+    {
+      next = link->next;
+
+      /* Check if in need of being removed */
+      if((link->state & DEV_STALE_LINK) ||
+        (! (link->state & DEV_PRESENT)))
+       wavelan_detach(link);
+
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "<- wv_flush_stale_links()\n");
+#endif
+}
+
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever: 
+ *     1. A packet is received.
+ *     2. A packet has successfully been transfered and the unit is
+ *        ready to transmit another packet.
+ *     3. A command has completed execution.
+ */
+static void
+wavelan_interrupt(int          irq,
+                 void *        dev_id,
+                 struct pt_regs * regs)
+{
+  device *     dev;
+  net_local *  lp;
+  ioaddr_t     base;
+  int          status0;
+  u_int                tx_status;
+
+  if((dev = (device *)dev_id) == (device *) NULL)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
+            irq);
+#endif
+      return;
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+  lp = (net_local *) dev->priv;
+  base = dev->base_addr;
+
+  /* Prevent reentrance. What should we do here ? */
+#ifdef DEBUG_INTERRUPT_ERROR
+  if(dev->interrupt)
+    printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n",
+          dev->name);
+#endif
+  dev->interrupt = 1;
+
+  /* Treat all pending interrupts */
+  while(1)
+    {
+      /* ---------------- INTERRUPT CHECKING ---------------- */
+      /*
+       * Look for the interrupt and verify the validity
+       */
+      outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
+      status0 = inb(LCSR(base));
+
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, 
+            (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT);
+      if(status0&SR0_INTERRUPT)
+       {
+         printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" :
+                ((status0 & SR0_EXECUTION) ? "cmd" :
+                 ((status0 & SR0_RECEPTION) ? "recv" : "unknown")),
+                (status0 & SR0_EVENT_MASK));
+       }
+      else
+       printk("\n");
+#endif
+
+      /* Return if no actual interrupt from i82593 (normal exit) */
+      if(!(status0 & SR0_INTERRUPT))
+       break;
+
+      /* If interrupt is both Rx and Tx or none...
+       * This code in fact is there to catch the spurious interrupt
+       * when you remove the wavelan pcmcia card from the socket */
+      if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) ||
+        ((status0 & SR0_BOTH_RX_TX) == 0x0))
+       {
+#ifdef DEBUG_INTERRUPT_INFO
+         printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n",
+                dev->name, status0);
+#endif
+         /* Acknowledge the interrupt */
+         outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+         break;
+       }
+
+      lp->status = status0;    /* Save current status (for commands) */
+
+      /* ----------------- RECEIVING PACKET ----------------- */
+      /*
+       * When the wavelan signal the reception of a new packet,
+       * we call wv_packet_rcv() to copy if from the buffer and
+       * send it to NET3
+       */
+      if(status0 & SR0_RECEPTION)
+       {
+#ifdef DEBUG_INTERRUPT_INFO
+         printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name);
+#endif
+
+         if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT)
+           {
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n",
+                    dev->name);
+#endif
+             lp->stats.rx_over_errors++;
+             lp->overrunning = 1;
+           }
+
+         /* Get the packet */
+         wv_packet_rcv(dev);
+         lp->overrunning = 0;
+
+         /* Acknowledge the interrupt */
+         outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+         continue;
+       }
+
+      /* ---------------- COMMAND COMPLETION ---------------- */
+      /*
+       * Interrupts issued when the i82593 has completed a command.
+       * Most likely : transmission done
+       */
+
+      /* If we are already waiting elsewhere for the command to complete */
+      if(wv_wait_completed)
+       {
+#ifdef DEBUG_INTERRUPT_INFO
+         printk(KERN_DEBUG "%s: wv_interrupt(): command completed\n",
+                dev->name);
+#endif
+
+         /* Signal command completion */
+         wv_wait_completed = 0;
+
+         /* Acknowledge the interrupt */
+         outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
+         continue;
+       }
+
+      /* If a transmission has been done */
+      if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
+        (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
+        (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+       {
+#ifdef DEBUG_TX_ERROR
+         if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE)
+           printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n",
+                  dev->name);
+#endif
+
+         /* If watchdog was activated, kill it ! */
+         if(lp->watchdog.prev != (timer_list *) NULL)
+           del_timer(&lp->watchdog);
+
+         /* Get transmission status */
+         tx_status = inb(LCSR(base));
+         tx_status |= (inb(LCSR(base)) << 8);
+#ifdef DEBUG_INTERRUPT_INFO
+         printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n",
+                dev->name);
+         {
+           u_int       rcv_bytes;
+           u_char      status3;
+           rcv_bytes = inb(LCSR(base));
+           rcv_bytes |= (inb(LCSR(base)) << 8);
+           status3 = inb(LCSR(base));
+           printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n",
+                  tx_status, rcv_bytes, (u_int) status3);
+         }
+#endif
+         /* Check for possible errors */
+         if((tx_status & TX_OK) != TX_OK)
+           {
+             lp->stats.tx_errors++;
+
+             if(tx_status & TX_FRTL)
+               {
+#ifdef DEBUG_TX_ERROR
+                 printk(KERN_INFO "%s: wv_interrupt(): frame too long\n",
+                        dev->name);
+#endif
+               }
+             if(tx_status & TX_UND_RUN)
+               {
+#ifdef DEBUG_TX_FAIL
+                 printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n",
+                        dev->name);
+#endif
+                 lp->stats.tx_aborted_errors++;
+               }
+             if(tx_status & TX_LOST_CTS)
+               {
+#ifdef DEBUG_TX_FAIL
+                 printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name);
+#endif
+                 lp->stats.tx_carrier_errors++;
+               }
+             if(tx_status & TX_LOST_CRS)
+               {
+#ifdef DEBUG_TX_FAIL
+                 printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n",
+                        dev->name);
+#endif
+                 lp->stats.tx_carrier_errors++;
+               }
+             if(tx_status & TX_HRT_BEAT)
+               {
+#ifdef DEBUG_TX_FAIL
+                 printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name);
+#endif
+                 lp->stats.tx_heartbeat_errors++;
+               }
+             if(tx_status & TX_DEFER)
+               {
+#ifdef DEBUG_TX_FAIL
+                 printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n",
+                        dev->name);
+#endif
+               }
+             /* Ignore late collisions since they're more likely to happen
+              * here (the WaveLAN design prevents the LAN controller from
+              * receiving while it is transmitting). We take action only when
+              * the maximum retransmit attempts is exceeded.
+              */
+             if(tx_status & TX_COLL)
+               {
+                 if(tx_status & TX_MAX_COL)
+                   {
+#ifdef DEBUG_TX_FAIL
+                     printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n",
+                            dev->name);
+#endif
+                     if(!(tx_status & TX_NCOL_MASK))
+                       {
+                         lp->stats.collisions += 0x10;
+                       }
+                   }
+               }
+           }   /* if(!(tx_status & TX_OK)) */
+
+         lp->stats.collisions += (tx_status & TX_NCOL_MASK);
+         lp->stats.tx_packets++;
+
+         dev->tbusy = FALSE;
+         mark_bh(NET_BH);
+         outb(CR0_INT_ACK | OP0_NOP, LCCR(base));      /* Acknowledge the interrupt */
+       } 
+      else     /* if interrupt = transmit done or retransmit done */
+       {
+#ifdef DEBUG_INTERRUPT_ERROR
+         printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n",
+                status0);
+#endif
+         outb(CR0_INT_ACK | OP0_NOP, LCCR(base));      /* Acknowledge the interrupt */
+       }
+    }
+  dev->interrupt = FALSE;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
+} /* wv_interrupt */
+
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog : when we start a transmission, we set a timer in the
+ * kernel.  If the transmission complete, this timer is disabled. If
+ * it expire, it try to unlock the hardware.
+ *
+ * Note : this watchdog doesn't work on the same principle as the
+ * watchdog in the ISA driver. I make it this way because the overhead
+ * of add_timer() and del_timer() is nothing and that it avoid calling
+ * the watchdog, saving some CPU... If you want to apply the same
+ * watchdog to the ISA driver, you should be a bit carefull, because
+ * of the many transmit buffers...
+ * This watchdog is also move clever, it try to abort the current
+ * command before reseting everything...
+ */
+static void
+wavelan_watchdog(u_long                a)
+{
+  device *             dev;
+  net_local *          lp;
+  ioaddr_t             base;
+  int                  spin;
+
+  dev = (device *) a;
+  base = dev->base_addr;
+  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+        dev->name);
+#endif
+
+  /* We are waiting for command completion */
+  wv_wait_completed = TRUE;
+
+  /* Ask to abort the current command */
+  outb(OP0_ABORT, LCCR(base));
+
+  /* Busy wait while the LAN controller executes the command.
+   * Note : wv_wait_completed should be volatile */
+  spin = 0;
+  while(wv_wait_completed && (spin++ < 250))
+    udelay(10);
+
+  /* If the interrupt handler hasn't be called or invalid status */
+  if((wv_wait_completed) ||
+     ((lp->status & SR0_EVENT_MASK) != SR0_EXECUTION_ABORTED))
+    {
+      /* It seem that it wasn't enough */
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n",
+            dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+#ifdef DEBUG_PSA_SHOW
+  {
+    psa_t              psa;
+    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+    wv_psa_show(&psa);
+  }
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82593_SHOW
+  wv_ru_show(dev);
+#endif
+
+  /* We are no more waiting for something... */
+  dev->tbusy = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
+
+/********************* CONFIGURATION CALLBACKS *********************/
+/*
+ * Here are the functions called by the pcmcia package (cardmgr) and
+ * linux networking (NET3) for initialization, configuration and
+ * deinstallations of the Wavelan Pcmcia Hardware.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "open" the device.
+ */
+static int
+wavelan_open(device *  dev)
+{
+  dev_link_t * link = ((net_local *) dev->priv)->link;
+  net_local *  lp = (net_local *)dev->priv;
+  ioaddr_t     base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+        (unsigned int) dev);
+#endif
+
+  /* Check if the modem is powered up (wavelan_close() power it down */
+  if(hasr_read(base) & HASR_NO_CLK)
+    {
+      /* Power up (power up time is 250us) */
+      hacr_write(base, HACR_DEFAULT);
+
+      /* Check if the the module has been powered up... */
+      if(hasr_read(base) & HASR_NO_CLK)
+       {
+#ifdef DEBUG_CONFIG_ERRORS
+         printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n",
+                dev->name);
+#endif
+         return FALSE;
+       }
+    }
+
+  /* Start reception and declare the driver ready */
+  if(!lp->configured)
+    return FALSE;
+  if(!wv_ru_start(dev))
+    wv_hw_reset(dev);          /* If problem : reset */
+  dev->interrupt = 0;
+  dev->start = 1;
+  dev->tbusy = 0;   
+
+  /* Mark the device as used */
+  link->open++;
+  MOD_INC_USE_COUNT;
+
+#ifdef WAVELAN_ROAMING
+  if(do_roaming)
+    wv_roam_init(dev);
+#endif /* WAVELAN_ROAMING */
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Shutdown the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "close" the device.
+ */
+static int
+wavelan_close(device * dev)
+{
+  dev_link_t * link = ((net_local *) dev->priv)->link;
+  net_local *  lp = (net_local *)dev->priv;
+  ioaddr_t     base = dev->base_addr;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+        (unsigned int) dev);
+#endif
+
+  /* If the device isn't open, then nothing to do */
+  if(!link->open)
+    {
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name);
+#endif
+      return 0;
+    }
+
+#ifdef WAVELAN_ROAMING
+  /* Cleanup of roaming stuff... */
+  if(do_roaming)
+    wv_roam_cleanup(dev);
+#endif /* WAVELAN_ROAMING */
+
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
+
+  link->open--;
+  MOD_DEC_USE_COUNT;
+
+  /* If the card is still present */
+  if(dev->start)
+    {
+      dev->tbusy = 1;
+      dev->start = 0;
+
+      /* Stop receiving new messages and wait end of transmission */
+      wv_ru_stop(dev);
+
+      /* Power down the module */
+      hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT));
+    }
+  else
+    /* The card is no more there (flag is activated in wv_pcmcia_release) */
+    if(link->state & DEV_STALE_CONFIG)
+      wv_pcmcia_release((u_long)link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * We never need to do anything when a wavelan device is "initialized"
+ * by the net software, because we only register already-found cards.
+ */
+static int
+wavelan_init(device *  dev)
+{
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<>wavelan_init()\n");
+#endif
+
+  return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device (one interface).  The device
+ * is registered with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+static dev_link_t *
+wavelan_attach(void)
+{
+  client_reg_t client_reg;     /* Register with cardmgr */
+  dev_link_t * link;           /* Info for cardmgr */
+  device *     dev;            /* Interface generic data */
+  net_local *  lp;             /* Interface specific data */
+  int          i, ret;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "-> wavelan_attach()\n");
+#endif
+
+  /* Perform some cleanup */
+  wv_flush_stale_links();
+
+  /* Initialize the dev_link_t structure */
+  link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+  memset(link, 0, sizeof(struct dev_link_t));
+
+  /* Unused for the Wavelan */
+  link->release.function = &wv_pcmcia_release;
+  link->release.data = (u_long) link;
+
+  /* The io structure describes IO port mapping */
+  link->io.NumPorts1 = 8;
+  link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+  link->io.IOAddrLines = 3;
+
+  /* Interrupt setup */
+  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+  link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+  if (irq_list[0] == -1)
+    link->irq.IRQInfo2 = irq_mask;
+  else
+    for (i = 0; i < 4; i++)
+      link->irq.IRQInfo2 |= 1 << irq_list[i];
+  link->irq.Handler = wavelan_interrupt;
+
+  /* General socket configuration */
+  link->conf.Attributes = CONF_ENABLE_IRQ;
+  link->conf.Vcc = 50;
+  link->conf.IntType = INT_MEMORY_AND_IO;
+
+  /* Chain drivers */
+  link->next = dev_list;
+  dev_list = link;
+
+  /* Allocate the generic data structure */
+  dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+  memset(dev, 0x00, sizeof(struct net_device));
+  link->priv = link->irq.Instance = dev;
+
+  /* Allocate the wavelan-specific data structure. */
+  dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
+  memset(lp, 0x00, sizeof(net_local));
+
+  /* Init specific data */
+  wv_wait_completed = 0;
+  lp->status = FALSE;
+  lp->configured = 0;
+  lp->reconfig_82593 = FALSE;
+  lp->nresets = 0;
+
+  /* Set the watchdog timer */
+  lp->watchdog.function = wavelan_watchdog;
+  lp->watchdog.data = (unsigned long) dev;
+
+  /* back links */
+  lp->link = link;
+  lp->dev = dev;
+
+  /* Standard setup for generic data */
+  ether_setup(dev);
+
+  /* wavelan NET3 callbacks */
+  dev->init = &wavelan_init;
+  dev->open = &wavelan_open;
+  dev->stop = &wavelan_close;
+  dev->hard_start_xmit = &wavelan_packet_xmit;
+  dev->get_stats = &wavelan_get_stats;
+  dev->set_multicast_list = &wavelan_set_multicast_list;
+#ifdef SET_MAC_ADDRESS
+  dev->set_mac_address = &wavelan_set_mac_address;
+#endif /* SET_MAC_ADDRESS */
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+  dev->do_ioctl = wavelan_ioctl;       /* wireless extensions */
+  dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+  /* Other specific data */
+  /* Provide storage area for device name */
+  dev->name = ((net_local *)dev->priv)->node.dev_name;
+  dev->tbusy = 1;
+  dev->mtu = WAVELAN_MTU;
+
+  /* Register with Card Services */
+  client_reg.dev_info = &dev_info;
+  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+  client_reg.EventMask = 
+    CS_EVENT_REGISTRATION_COMPLETE |
+    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+  client_reg.event_handler = &wavelan_event;
+  client_reg.Version = 0x0210;
+  client_reg.event_callback_args.client_data = link;
+
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n");
+#endif
+
+  ret = CardServices(RegisterClient, &link->handle, &client_reg);
+  if(ret != 0)
+    {
+      cs_error(link->handle, RegisterClient, ret);
+      wavelan_detach(link);
+      return NULL;
+    }
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<- wavelan_attach()\n");
+#endif
+
+  return link;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * This deletes a driver "instance".  The device is de-registered with
+ * Card Services.  If it has been released, all local data structures
+ * are freed.  Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void
+wavelan_detach(dev_link_t *    link)
+{
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link);
+#endif
+
+  /*
+   * If the device is currently configured and active, we won't
+   * actually delete it yet.  Instead, it is marked so that when the
+   * release() function is called, that will trigger a proper
+   * detach().
+   */
+  if(link->state & DEV_CONFIG)
+    {
+      /* Some others haven't done their job : give them another chance */
+      wv_pcmcia_release((u_long) link);
+      if(link->state & DEV_STALE_CONFIG)
+       {
+#ifdef DEBUG_CONFIG_INFO
+         printk(KERN_DEBUG "wavelan_detach: detach postponed,"
+                " '%s' still locked\n", link->dev->dev_name);
+#endif
+         link->state |= DEV_STALE_LINK;
+         return;
+       }
+    }
+
+  /* Break the link with Card Services */
+  if(link->handle)
+    CardServices(DeregisterClient, link->handle);
+    
+  /* Remove the interface data from the linked list */
+  if(dev_list == link)
+    dev_list = link->next;
+  else
+    {
+      dev_link_t *     prev = dev_list;
+
+      while((prev != (dev_link_t *) NULL) && (prev->next != link))
+       prev = prev->next;
+
+      if(prev == (dev_link_t *) NULL)
+       {
+#ifdef DEBUG_CONFIG_ERRORS
+         printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n");
+#endif
+         return;
+       }
+
+      prev->next = link->next;
+    }
+
+  /* Free pieces */
+  if(link->priv)
+    {
+      device * dev = (device *) link->priv;
+
+      /* Remove ourselves from the kernel list of ethernet devices */
+      /* Warning : can't be called from interrupt, timer or wavelan_close() */
+      if(link->dev != NULL)
+       unregister_netdev(dev);
+      link->dev = NULL;
+
+      if(dev->priv)
+       {
+         /* Sound strange, but safe... */
+         ((net_local *) dev->priv)->link = (dev_link_t *) NULL;
+         ((net_local *) dev->priv)->dev = (device *) NULL;
+         kfree(dev->priv);
+       }
+      kfree(link->priv);
+    }
+  kfree(link);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<- wavelan_detach()\n");
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * The card status event handler. Mostly, this schedules other stuff
+ * to run after an event is received. A CARD_REMOVAL event also sets
+ * some flags to discourage the net drivers from trying to talk to the
+ * card any more.
+ */
+static int
+wavelan_event(event_t          event,          /* The event received */
+             int               priority,
+             event_callback_args_t *   args)
+{
+  dev_link_t * link = (dev_link_t *) args->client_data;
+  device *     dev = (device *) link->priv;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "->wavelan_event(): %s\n",
+        ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" :
+         ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" :
+          ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" :
+           ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" :
+            ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" :
+             ((event == CS_EVENT_PM_RESUME) ? "pm resume" :
+              ((event == CS_EVENT_CARD_RESET) ? "card reset" :
+               "unknown"))))))));
+#endif
+
+    switch(event)
+      {
+      case CS_EVENT_REGISTRATION_COMPLETE:
+#ifdef DEBUG_CONFIG_INFO
+       printk(KERN_DEBUG "wavelan_cs: registration complete\n");
+#endif
+       break;
+
+      case CS_EVENT_CARD_REMOVAL:
+       /* Oups ! The card is no more there */
+       link->state &= ~DEV_PRESENT;
+       if(link->state & DEV_CONFIG)
+         {
+           /* Accept no more transmissions */
+           dev->tbusy = 1; dev->start = 0;
+
+           /* Release the card */
+           wv_pcmcia_release((u_long) link);
+         }
+       break;
+
+      case CS_EVENT_CARD_INSERTION:
+       /* Reset and configure the card */
+       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+       if(wv_pcmcia_config(link) &&
+          wv_hw_config(dev))
+         wv_init_info(dev);
+       else
+         dev->irq = 0;
+       break;
+
+      case CS_EVENT_PM_SUSPEND:
+       /* NB: wavelan_close will be called, but too late, so we are
+        * obliged to close nicely the wavelan here. David, could you
+        * close the device before suspending them ? And, by the way,
+        * could you, on resume, add a "route add -net ..." after the
+        * ifconfig up ??? Thanks... */
+
+       /* Stop receiving new messages and wait end of transmission */
+       wv_ru_stop(dev);
+
+       /* Power down the module */
+       hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT));
+
+       /* The card is now suspended */
+       link->state |= DEV_SUSPEND;
+       /* Fall through... */
+      case CS_EVENT_RESET_PHYSICAL:
+       if(link->state & DEV_CONFIG)
+         {
+           if(link->open)
+             {
+               dev->tbusy = 1;
+               dev->start = 0;
+             }
+           CardServices(ReleaseConfiguration, link->handle);
+         }
+       break;
+
+      case CS_EVENT_PM_RESUME:
+       link->state &= ~DEV_SUSPEND;
+       /* Fall through... */
+      case CS_EVENT_CARD_RESET:
+       if(link->state & DEV_CONFIG)
+         {
+           CardServices(RequestConfiguration, link->handle, &link->conf);
+           if(link->open)      /* If RESET -> True, If RESUME -> False ??? */
+             {
+               wv_hw_reset(dev);
+               dev->tbusy = 0;
+               dev->start = 1;
+             }
+         }
+       break;
+    }
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "<-wavelan_event()\n");
+#endif
+  return 0;
+}
+
+/****************************** MODULE ******************************/
+/*
+ * Module entry points : insertion & removal
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Module insertion : initialisation of the module.
+ * Register the card with cardmgr...
+ */
+static int __init
+init_wavelan_cs(void)
+{
+  servinfo_t   serv;
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> init_wavelan_cs()\n");
+#ifdef DEBUG_VERSION_SHOW
+  printk(KERN_DEBUG "%s", version);
+#endif
+#endif
+
+  CardServices(GetCardServicesInfo, &serv);
+  if(serv.Revision != CS_RELEASE_CODE)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n");
+#endif
+      return -1;
+    }
+
+  register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach);
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- init_wavelan_cs()\n");
+#endif
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Module removal
+ */
+static void __exit
+exit_wavelan_cs(void)
+{
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+#ifdef DEBUG_BASIC_SHOW
+  printk(KERN_NOTICE "wavelan_cs: unloading\n");
+#endif
+
+  /* Do some cleanup of the device list */
+  wv_flush_stale_links();
+
+  /* If there remain some devices... */
+#ifdef DEBUG_CONFIG_ERRORS
+  if(dev_list != NULL)
+    {
+      /* Honestly, if this happen we are in a deep s**t */
+      printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n");
+      printk(KERN_INFO "Please flush your disks and reboot NOW !\n");
+    }
+#endif
+
+  unregister_pccard_driver(&dev_info);
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
+}
+
+module_init(init_wavelan_cs);
+module_exit(exit_wavelan_cs);
diff --git a/drivers/net/pcmcia/wavelan_cs.h b/drivers/net/pcmcia/wavelan_cs.h
new file mode 100644 (file)
index 0000000..84dc211
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ *     Wavelan Pcmcia driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contain all definition and declarations necessary for the
+ * wavelan pcmcia driver. This file is a private header, so it should
+ * be included only on wavelan_cs.c !!!
+ */
+
+#ifndef WAVELAN_CS_H
+#define WAVELAN_CS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * This driver provide a Linux interface to the Wavelan Pcmcia hardware
+ * The Wavelan is a product of Lucent (http://www.wavelan.com/).
+ * This division was formerly part of NCR and then AT&T.
+ * Wavelan are also distributed by DEC (RoamAbout DS)...
+ *
+ * To know how to use this driver, read the PCMCIA HOWTO.
+ * If you want to exploit the many other fonctionalities, look comments
+ * in the code...
+ *
+ * This driver is the result of the effort of many peoples (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * Web page
+ * --------
+ *     I try to maintain a web page with the Wireless LAN Howto at :
+ *         http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
+ *
+ * Debugging and options
+ * ---------------------
+ *     You will find below a set of '#define" allowing a very fine control
+ *     on the driver behaviour and the debug messages printed.
+ *     The main options are :
+ *     o WAVELAN_ROAMING, for the experimental roaming support.
+ *     o SET_PSA_CRC, to have your card correctly recognised by
+ *       an access point and the Point-to-Point diagnostic tool.
+ *     o USE_PSA_CONFIG, to read configuration from the PSA (EEprom)
+ *       (otherwise we always start afresh with some defaults)
+ *
+ * wavelan_cs.o is darn too big
+ * -------------------------
+ *     That's true ! There is a very simple way to reduce the driver
+ *     object by 33% (yes !). Comment out the following line :
+ *             #include <linux/wireless.h>
+ *     Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection :
+ * ----------------------------------
+ *     The detection code of the wavelan chech that the first 3
+ *     octets of the MAC address fit the company code. This type of
+ *     detection work well for AT&T cards (because the AT&T code is
+ *     hardcoded in wavelan.h), but of course will fail for other
+ *     manufacturer.
+ *
+ *     If you are sure that your card is derived from the wavelan,
+ *     here is the way to configure it :
+ *     1) Get your MAC address
+ *             a) With your card utilities (wfreqsel, instconf, ...)
+ *             b) With the driver :
+ *                     o compile the kernel with DEBUG_CONFIG_INFO enabled
+ *                     o Boot and look the card messages
+ *     2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
+ *     3) Compile & verify
+ *     4) Send me the MAC code - I will include it in the next version...
+ *
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first one to support "wireless extensions".
+ * This set of extensions provide you some way to control the wireless
+ * caracteristics of the hardware in a standard way and support for
+ * applications for taking advantage of it (like Mobile IP).
+ *
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
+ *
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan_cs.c :      The actual code for the driver - C functions
+ *
+ * wavelan_cs.h :      Private header : local types / vars for the driver
+ *
+ * wavelan.h :         Description of the hardware interface & structs
+ *
+ * i82593.h :          Description if the Ethernet controler
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * The history of the Wavelan drivers is as complicated as history of
+ * the Wavelan itself (NCR -> AT&T -> Lucent).
+ *
+ * All started with Anders Klemets <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Now, I'm doing the same to the Pcmcia driver + some
+ * reorganisation.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * Credits:
+ *    Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
+ *     Loeke Brederveld of Lucent for providing extremely useful
+ *     information about WaveLAN PCMCIA hardware
+ *
+ *    This driver is based upon several other drivers, in particular:
+ *     David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ *     Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ *     Anders Klemets' PCMCIA WaveLAN adapter driver
+ *     Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ *
+ * Additional Credits:
+ *
+ *    This software was originally developed under Linux 1.2.3
+ *     (Slackware 2.0 distribution).
+ *    And then under Linux 2.0.x (Debian 1.1 - pcmcia 2.8.18-23) with
+ *     HP OmniBook 4000 & 5500.
+ *
+ *    It is based on other device drivers and information either written
+ *    or supplied by:
+ *     James Ashton (jaa101@syseng.anu.edu.au),
+ *     Ajay Bakre (bakre@paul.rutgers.edu),
+ *     Donald Becker (becker@super.org),
+ *     Jim Binkley <jrb@cs.pdx.edu>,
+ *     Loeke Brederveld <lbrederv@wavelan.com>,
+ *     Allan Creighton (allanc@cs.su.oz.au),
+ *     Brent Elphick <belphick@uwaterloo.ca>,
+ *     Joe Finney <joe@comp.lancs.ac.uk>,
+ *     Matthew Geier (matthew@cs.su.oz.au),
+ *     Remo di Giovanni (remo@cs.su.oz.au),
+ *     Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *     David Hinds <dhinds@hyper.stanford.edu>,
+ *     Jan Hoogendoorn (c/o marteijn@lucent.com),
+ *      Bruce Janson <bruce@cs.usyd.edu.au>,
+ *     Anthony D. Joseph <adj@lcs.mit.edu>,
+ *     Anders Klemets (klemets@paul.rutgers.edu),
+ *     Yunzhou Li <yunzhou@strat.iol.unh.edu>,
+ *     Marc Meertens (mmeertens@lucent.com),
+ *     Keith Moore,
+ *     Robert Morris (rtm@das.harvard.edu),
+ *     Ian Parkin (ian@cs.su.oz.au),
+ *     John Rosenberg (johnr@cs.su.oz.au),
+ *     George Rossi (george@phm.gov.au),
+ *     Arthur Scott (arthur@cs.su.oz.au),
+ *     Stanislav Sinyagin <stas@isf.ru>
+ *     Peter Storey,
+ *     Jean Tourrilhes <jt@hpl.hp.com>,
+ *     Girish Welling (welling@paul.rutgers.edu)
+ *     Clark Woodworth <clark@hiway1.exit109.com>
+ *     Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes made in 2.8.22 :
+ * ----------------------
+ *     - improved wv_set_multicast_list
+ *     - catch spurious interrupt
+ *     - correct release of the device
+ *
+ * Changes mades in release :
+ * ------------------------
+ *     - Reorganisation of the code, function name change
+ *     - Creation of private header (wavelan_cs.h)
+ *     - Reorganised debug messages
+ *     - More comments, history, ...
+ *     - Configure earlier (in "insert" instead of "open")
+ *        and do things only once
+ *     - mmc_init : configure the PSA if not done
+ *     - mmc_init : 2.00 detection better code for 2.00 init
+ *     - better info at startup
+ *     - Correct a HUGE bug (volatile & uncalibrated busy loop)
+ *       in wv_82593_cmd => config speedup
+ *     - Stop receiving & power down on close (and power up on open)
+ *       use "ifconfig down" & "ifconfig up ; route add -net ..."
+ *     - Send packets : add watchdog instead of pooling
+ *     - Receive : check frame wrap around & try to recover some frames
+ *     - wavelan_set_multicast_list : avoid reset
+ *     - add wireless extensions (ioctl & get_wireless_stats)
+ *       get/set nwid/frequency on fly, info for /proc/net/wireless
+ *     - Supress useless stuff from lp (net_local), but add link
+ *     - More inlines
+ *     - Lot of others minor details & cleanups
+ *
+ * Changes made in second release :
+ * ------------------------------
+ *     - Optimise wv_85893_reconfig stuff, fix potential problems
+ *     - Change error values for ioctl
+ *     - Non blocking wv_ru_stop() + call wv_reset() in case of problems
+ *     - Remove development printk from wavelan_watchdog()
+ *     - Remove of the watchdog to wavelan_close instead of wavelan_release
+ *       fix potential problems...
+ *     - Start debugging suspend stuff (but it's still a bit weird)
+ *     - Debug & optimize dump header/packet in Rx & Tx (debug)
+ *     - Use "readb" and "writeb" to be kernel 2.1 compliant
+ *     - Better handling of bogus interrupts
+ *     - Wireless extension : SETSPY and GETSPY
+ *     - Remove old stuff (stats - for those needing it, just ask me...)
+ *     - Make wireless extensions optional
+ *
+ * Changes made in third release :
+ * -----------------------------
+ *     - cleanups & typos
+ *     - modif wireless ext (spy -> only one pointer)
+ *     - new private ioctl to set/get quality & level threshold
+ *     - Init : correct default value of level threshold for pcmcia
+ *     - kill watchdog in hw_reset
+ *     - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ *     - Add message level (debug stuff in /var/adm/debug & errors not
+ *       displayed at console and still in /var/adm/messages)
+ *
+ * Changes made in fourth release :
+ * ------------------------------
+ *     - multicast support (yes !) thanks to Yongguang Zhang.
+ *
+ * Changes made in fifth release (2.9.0) :
+ * -------------------------------------
+ *     - Revisited multicast code (it was mostly wrong).
+ *     - protect code in wv_82593_reconfig with dev->tbusy (oups !)
+ *
+ * Changes made in sixth release (2.9.1a) :
+ * --------------------------------------
+ *     - Change the detection code for multi manufacturer code support
+ *     - Correct bug (hang kernel) in init when we were "rejecting" a card 
+ *
+ * Changes made in seventh release (2.9.1b) :
+ * ----------------------------------------
+ *     - Update to wireless extensions changes
+ *     - Silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made in eigth release :
+ * -----------------------------
+ *     - Small bug in debug code (probably not the last one...)
+ *     - 1.2.13 support (thanks to Clark Woodworth)
+ *
+ * Changes made for release in 2.9.2b :
+ * ----------------------------------
+ *     - Level threshold is now a standard wireless extension (version 4 !)
+ *     - modules parameters types for kernel > 2.1.17
+ *     - updated man page
+ *     - Others cleanup from David Hinds
+ *
+ * Changes made for release in 2.9.5 :
+ * ---------------------------------
+ *     - byte count stats (courtesy of David Hinds)
+ *     - Remove dev_tint stuff (courtesy of David Hinds)
+ *     - Others cleanup from David Hinds
+ *     - Encryption setting from Brent Elphick (thanks a lot !)
+ *     - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
+ * Changes made for release in 2.9.6 :
+ * ---------------------------------
+ *     - fix bug : no longuer disable watchdog in case of bogus interrupt
+ *     - increase timeout in config code for picky hardware
+ *     - mask unused bits in status (Wireless Extensions)
+ *
+ * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds :
+ * -----------------------------------------------------------------
+ *     - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
+ *     - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
+ *     - Better initialisation of the i82593 controller
+ *       from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
+ *
+ * Changes made for release in 3.0.10 :
+ * ----------------------------------
+ *     - Fix eject "hang" of the driver under 2.2.X :
+ *             o create wv_flush_stale_links()
+ *             o Rename wavelan_release to wv_pcmcia_release & move up
+ *             o move unregister_netdev to wavelan_detach()
+ *             o wavelan_release() no longer call wavelan_detach()
+ *             o Supress "release" timer
+ *             o Other cleanups & fixes
+ *     - New MAC address in the probe
+ *     - Reorg PSA_CRC code (endian neutral & cleaner)
+ *     - Correct initialisation of the i82593 from Lucent manual
+ *     - Put back the watchdog, with larger timeout
+ *     - TRANSMIT_NO_CRC is a "normal" error, so recover from it
+ *       from Derrick J Brashear <shadow@dementia.org>
+ *     - Better handling of TX and RX normal failure conditions
+ *     - #ifdef out all the roaming code
+ *     - Add ESSID & "AP current address" ioctl stubs
+ *     - General cleanup of the code
+ *
+ * Changes made for release in 3.0.13 :
+ * ----------------------------------
+ *     - Re-enable compilation of roaming code by default, but with
+ *       do_roaming = 0
+ *     - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
+ *       at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
+ *     - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
+ *
+ * Changes made for release in 3.0.15 :
+ * ----------------------------------
+ *     - Change e-mail and web page addresses
+ *     - Watchdog timer is now correctly expressed in HZ, not in jiffies
+ *     - Add channel number to the list of frequencies in range
+ *     - Add the (short) list of bit-rates in range
+ *     - Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 3.1.2 :
+ * ---------------------------------
+ *     - Fix check for root permission (break instead of exit)
+ *     - New nwid & encoding setting (Wireless Extension 9)
+ *
+ * Wishes & dreams:
+ * ----------------
+ *     - Cleanup and integrate the roaming code
+ *       (std debug, set DomainID, decay avg and co...)
+ */
+
+/***************************** INCLUDES *****************************/
+
+/* Linux headers that we need */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>            /* Wireless extensions */
+#endif
+
+/* Pcmcia headers that we need */
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/version.h>
+
+/* Wavelan declarations */
+#include "i82593.h"    /* Definitions for the Intel chip */
+
+#include "wavelan.h"   /* Others bits of the hardware */
+
+/************************** DRIVER OPTIONS **************************/
+/*
+ * `#define' or `#undef' the following constant to change the behaviour
+ * of the driver...
+ */
+#define WAVELAN_ROAMING                /* Include experimental roaming code */
+#undef WAVELAN_ROAMING_EXT     /* Enable roaming wireless extensions */
+#undef SET_PSA_CRC             /* Set the CRC in PSA (slower) */
+#define USE_PSA_CONFIG         /* Use info from the PSA */
+#undef STRUCT_CHECK            /* Verify padding of structures */
+#undef EEPROM_IS_PROTECTED     /* Doesn't seem to be necessary */
+#define MULTICAST_AVOID                /* Avoid extra multicast (I'm sceptical) */
+#undef SET_MAC_ADDRESS         /* Experimental */
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY           /* Enable spying addresses */
+#undef HISTOGRAM               /* Enable histogram of sig level... */
+#endif
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE      /* Module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE    /* Calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE   /* Calls to handler */
+#undef DEBUG_INTERRUPT_INFO    /* type of interrupt & so on */
+#define DEBUG_INTERRUPT_ERROR  /* problems */
+#undef DEBUG_CONFIG_TRACE      /* Trace the config functions */
+#undef DEBUG_CONFIG_INFO       /* What's going on... */
+#define DEBUG_CONFIG_ERRORS    /* Errors on configuration */
+#undef DEBUG_TX_TRACE          /* Transmission calls */
+#undef DEBUG_TX_INFO           /* Header of the transmited packet */
+#undef DEBUG_TX_FAIL           /* Normal failure conditions */
+#define DEBUG_TX_ERROR         /* Unexpected conditions */
+#undef DEBUG_RX_TRACE          /* Transmission calls */
+#undef DEBUG_RX_INFO           /* Header of the transmited packet */
+#undef DEBUG_RX_FAIL           /* Normal failure conditions */
+#define DEBUG_RX_ERROR         /* Unexpected conditions */
+#undef DEBUG_PACKET_DUMP       32      /* Dump packet on the screen */
+#undef DEBUG_IOCTL_TRACE       /* Misc call by Linux */
+#undef DEBUG_IOCTL_INFO                /* Various debug info */
+#define DEBUG_IOCTL_ERROR      /* What's going wrong */
+#define DEBUG_BASIC_SHOW       /* Show basic startup info */
+#undef DEBUG_VERSION_SHOW      /* Print version info */
+#undef DEBUG_PSA_SHOW          /* Dump psa to screen */
+#undef DEBUG_MMC_SHOW          /* Dump mmc to screen */
+#undef DEBUG_SHOW_UNUSED       /* Show also unused fields */
+#undef DEBUG_I82593_SHOW       /* Show i82593 status */
+#undef DEBUG_DEVICE_SHOW       /* Show device parameters */
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char *version = "wavelan_cs.c : v21 (wireless extensions) 18/10/99\n";
+#endif
+
+/* Watchdog temporisation */
+#define        WATCHDOG_JIFFIES        (256*HZ/100)
+
+/* Fix a bug in some old wireless extension definitions */
+#ifndef IW_ESSID_MAX_SIZE
+#define IW_ESSID_MAX_SIZE      32
+#endif
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+#define SIOCSIPQTHR    SIOCDEVPRIVATE          /* Set quality threshold */
+#define SIOCGIPQTHR    SIOCDEVPRIVATE + 1      /* Get quality threshold */
+#define SIOCSIPROAM     SIOCDEVPRIVATE + 2      /* Set roaming state */
+#define SIOCGIPROAM     SIOCDEVPRIVATE + 3      /* Get roaming state */
+
+#define SIOCSIPHISTO   SIOCDEVPRIVATE + 6      /* Set histogram ranges */
+#define SIOCGIPHISTO   SIOCDEVPRIVATE + 7      /* Get histogram values */
+
+/*************************** WaveLAN Roaming  **************************/
+#ifdef WAVELAN_ROAMING         /* Conditional compile, see above in options */
+
+#define WAVELAN_ROAMING_DEBUG   0      /* 1 = Trace of handover decisions */
+                                       /* 2 = Info on each beacon rcvd... */
+#define MAX_WAVEPOINTS         7       /* Max visible at one time */
+#define WAVEPOINT_HISTORY      5       /* SNR sample history slow search */
+#define WAVEPOINT_FAST_HISTORY 2       /* SNR sample history fast search */
+#define SEARCH_THRESH_LOW      10      /* SNR to enter cell search */
+#define SEARCH_THRESH_HIGH     13      /* SNR to leave cell search */
+#define WAVELAN_ROAMING_DELTA  1       /* Hysteresis value (+/- SNR) */
+#define CELL_TIMEOUT           2*HZ    /* in jiffies */
+
+#define FAST_CELL_SEARCH       1       /* Boolean values... */
+#define NWID_PROMISC           1       /* for code clarity. */
+
+typedef struct wavepoint_beacon
+{
+  unsigned char                dsap,           /* Unused */
+                       ssap,           /* Unused */
+                       ctrl,           /* Unused */
+                       O,U,I,          /* Unused */
+                       spec_id1,       /* Unused */
+                       spec_id2,       /* Unused */
+                       pdu_type,       /* Unused */
+                       seq;            /* WavePoint beacon sequence number */
+  unsigned short       domain_id,      /* WavePoint Domain ID */
+                       nwid;           /* WavePoint NWID */
+} wavepoint_beacon;
+
+typedef struct wavepoint_history
+{
+  unsigned short       nwid;           /* WavePoint's NWID */
+  int                  average_slow;   /* SNR running average */
+  int                  average_fast;   /* SNR running average */
+  unsigned char          sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */
+  unsigned char                qualptr;        /* Index into ringbuffer */
+  unsigned char                last_seq;       /* Last seq. no seen for WavePoint */
+  struct wavepoint_history *next;      /* Next WavePoint in table */
+  struct wavepoint_history *prev;      /* Previous WavePoint in table */
+  unsigned long                last_seen;      /* Time of last beacon recvd, jiffies */
+} wavepoint_history;
+
+struct wavepoint_table
+{
+  wavepoint_history    *head;          /* Start of ringbuffer */
+  int                  num_wavepoints; /* No. of WavePoints visible */
+  unsigned char                locked;         /* Table lock */
+};
+
+#endif /* WAVELAN_ROAMING */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct net_device      device;
+typedef struct net_device_stats        en_stats;
+typedef struct iw_statistics   iw_stats;
+typedef struct iw_quality      iw_qual;
+typedef struct iw_freq         iw_freq;
+typedef struct net_local       net_local;
+typedef struct timer_list      timer_list;
+
+/* Basic types */
+typedef u_char         mac_addr[WAVELAN_ADDR_SIZE];    /* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keep data in two structure. "device"
+ * keep the generic data (same format for everybody) and "net_local" keep
+ * the additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+  dev_node_t   node;           /* ???? What is this stuff ???? */
+  device *     dev;            /* Reverse link... */
+  dev_link_t * link;           /* pcmcia structure */
+  en_stats     stats;          /* Ethernet interface statistics */
+  int          nresets;        /* Number of hw resets */
+  u_char       configured;     /* If it is configured */
+  u_char       reconfig_82593; /* Need to reconfigure the controler */
+  u_char       promiscuous;    /* Promiscuous mode */
+  u_char       allmulticast;   /* All Multicast mode */
+  int          mc_count;       /* Number of multicast addresses */
+  timer_list   watchdog;       /* To avoid blocking state */
+
+  u_char        status;                /* Current i82593 status */
+  int          stop;           /* Current i82593 Stop Hit Register */
+  int          rfp;            /* Last DMA machine receive pointer */
+  int          overrunning;    /* Receiver overrun flag */
+
+#ifdef WIRELESS_EXT
+  iw_stats     wstats;         /* Wireless specific stats */
+#endif
+
+#ifdef WIRELESS_SPY
+  int          spy_number;             /* Number of addresses to spy */
+  mac_addr     spy_address[IW_MAX_SPY];        /* The addresses to spy */
+  iw_qual      spy_stat[IW_MAX_SPY];           /* Statistics gathered */
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+  int          his_number;             /* Number of intervals */
+  u_char       his_range[16];          /* Boundaries of interval ]n-1; n] */
+  u_long       his_sum[16];            /* Sum in interval */
+#endif /* HISTOGRAM */
+#ifdef WAVELAN_ROAMING
+  u_long       domain_id;      /* Domain ID we lock on for roaming */
+  int          filter_domains; /* Check Domain ID of beacon found */
+ struct wavepoint_table        wavepoint_table;        /* Table of visible WavePoints*/
+  wavepoint_history *  curr_point;             /* Current wavepoint */
+  int                  cell_search;            /* Searching for new cell? */
+  struct timer_list    cell_timer;             /* Garbage collection */
+#endif /* WAVELAN_ROAMING */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+#ifdef WAVELAN_ROAMING
+/* ---------------------- ROAMING SUBROUTINES -----------------------*/
+
+wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
+wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
+void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
+void wl_cell_expiry(unsigned long data);
+wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
+void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
+void wv_nwid_filter(unsigned char mode, net_local *lp);
+void wv_roam_init(struct net_device *dev);
+void wv_roam_cleanup(struct net_device *dev);
+#endif /* WAVELAN_ROAMING */
+
+/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline unsigned long    /* flags */
+       wv_splhi(void);         /* Disable interrupts */
+static inline void
+       wv_splx(unsigned long); /* ReEnable interrupts : flags */
+static void
+       cs_error(client_handle_t, /* Report error to cardmgr */
+                int,
+                int);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static inline u_char           /* data */
+       hasr_read(u_long);      /* Read the host interface : base address */
+static inline void
+       hacr_write(u_long,      /* Write to host interface : base address */
+                  u_char),     /* data */
+       hacr_write_slow(u_long,
+                  u_char);
+static void
+       psa_read(device *,      /* Read the Parameter Storage Area */
+                int,           /* offset in PSA */
+                u_char *,      /* buffer to fill */
+                int),          /* size to read */
+       psa_write(device *,     /* Write to the PSA */
+                 int,          /* Offset in psa */
+                 u_char *,     /* Buffer in memory */
+                 int);         /* Length of buffer */
+static inline void
+       mmc_out(u_long,         /* Write 1 byte to the Modem Manag Control */
+               u_short,
+               u_char),
+       mmc_write(u_long,       /* Write n bytes to the MMC */
+                 u_char,
+                 u_char *,
+                 int);
+static inline u_char           /* Read 1 byte from the MMC */
+       mmc_in(u_long,
+              u_short);
+static inline void
+       mmc_read(u_long,        /* Read n bytes from the MMC */
+                u_char,
+                u_char *,
+                int),
+       fee_wait(u_long,        /* Wait for frequency EEprom : base address */
+                int,           /* Base delay to wait for */
+                int);          /* Number of time to wait */
+static void
+       fee_read(u_long,        /* Read the frequency EEprom : base address */
+                u_short,       /* destination offset */
+                u_short *,     /* data buffer */
+                int);          /* number of registers */
+/* ---------------------- I82593 SUBROUTINES ----------------------- */
+static int
+       wv_82593_cmd(device *,  /* synchronously send a command to i82593 */ 
+                    char *,
+                    int,
+                    int);
+static inline int
+       wv_diag(device *);      /* Diagnostique the i82593 */
+static int
+       read_ringbuf(device *,  /* Read a receive buffer */
+                    int,
+                    char *,
+                    int);
+static inline void
+       wv_82593_reconfig(device *);    /* Reconfigure the controler */
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+static inline void
+       wv_init_info(device *); /* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats        *
+       wavelan_get_stats(device *);    /* Give stats /proc/net/dev */
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline int
+       wv_start_of_frame(device *,     /* Seek beggining of current frame */
+                         int,  /* end of frame */
+                         int); /* start of buffer */
+static inline void
+       wv_packet_read(device *,        /* Read a packet from a frame */
+                      int,
+                      int),
+       wv_packet_rcv(device *);        /* Read all packets waiting */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline void
+       wv_packet_write(device *,       /* Write a packet to the Tx buffer */
+                       void *,
+                       short);
+static int
+       wavelan_packet_xmit(struct sk_buff *,   /* Send a packet */
+                           device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+       wv_mmc_init(device *);  /* Initialize the modem */
+static int
+       wv_ru_stop(device *),   /* Stop the i82593 receiver unit */
+       wv_ru_start(device *);  /* Start the i82593 receiver unit */
+static int
+       wv_82593_config(device *);      /* Configure the i82593 */
+static inline int
+       wv_pcmcia_reset(device *);      /* Reset the pcmcia interface */
+static int
+       wv_hw_config(device *); /* Reset & configure the whole hardware */
+static inline void
+       wv_hw_reset(device *);  /* Same, + start receiver unit */
+static inline int
+       wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */
+static void
+       wv_pcmcia_release(u_long),      /* Remove a device */
+       wv_flush_stale_links(void);     /* "detach" all possible devices */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+wavelan_interrupt(int, /* Interrupt handler */
+                 void *,
+                 struct pt_regs *);
+static void
+       wavelan_watchdog(u_long);       /* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+       wavelan_open(device *),         /* Open the device */
+       wavelan_close(device *),        /* Close the device */
+       wavelan_init(device *);         /* Do nothing */
+static dev_link_t *
+       wavelan_attach(void);           /* Create a new device */
+static void
+       wavelan_detach(dev_link_t *);   /* Destroy a removed device */
+static int
+       wavelan_event(event_t,          /* Manage pcmcia events */
+                     int,
+                     event_callback_args_t *);
+
+/**************************** VARIABLES ****************************/
+
+static dev_info_t dev_info = "wavelan_cs";
+static dev_link_t *dev_list = NULL;    /* Linked list of devices */
+
+/* WARNING : the following variable MUST be volatile
+ * It is used by wv_82593_cmd to syncronise with wavelan_interrupt */ 
+static volatile int    wv_wait_completed = 0;
+
+/*
+ * Parameters that can be set with 'insmod'
+ * The exact syntax is 'insmod wavelan_cs.o <var>=<value>'
+ */
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
+static int     irq_mask = 0xdeb8;
+static int     irq_list[4] = { -1 };
+
+/* Shared memory speed, in ns */
+static int     mem_speed = 0;
+
+/* New module interface */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(mem_speed, "i");
+
+#ifdef WAVELAN_ROAMING         /* Conditional compile, see above in options */
+/* Enable roaming mode ? No ! Please keep this to 0 */
+static int     do_roaming = 0;
+MODULE_PARM(do_roaming, "i");
+#endif /* WAVELAN_ROAMING */
+
+#endif /* WAVELAN_CS_H */
+
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
new file mode 100644 (file)
index 0000000..d092306
--- /dev/null
@@ -0,0 +1,2364 @@
+/* [xirc2ps_cs.c wk 14.04.97] (1.31 1998/12/09 19:32:55)
+ * Xircom Creditcard Ethernet Adapter IIps driver
+ *
+ * This driver works for the CE2, CEM28, CEM33, CE3 and CEM56 cards.
+ * The CEM56 has some problems, but it works.
+ * The CEII card with the 14k4 modem and other old cards do not work.
+ *
+ * Written by Werner Koch (werner.koch@guug.de),
+ * based on David Hinds skeleton driver.
+ *
+ * You can get the latest driver revision from
+ *     "http://www.d.shuttle.de/isil/xircom/xirc2ps.html"
+ *
+ * Please report bugs to: "xircom-bugs@isil.d.shuttle.de"
+ *
+ * A bug fix for the CEM56 to use modem and ethernet simultaneously
+ * was provided by Koen Van Herck (Koen.Van.Herck@xircom.com).
+ *
+ * If your card locks up you should use the option "lockup_hack=1";
+ * this may solve the problem but violates a kernel timing convention
+ * (Thanks to David Luyer).
+ *
+ * Thanks to David Hinds for the PCMCIA package, Donald Becker for some
+ * advice, Xircom for providing specs and help, 4PC GmbH Duesseldorf for
+ * providing some hardware and last not least to all folks who helped to
+ * develop this driver.
+ *
+ * For those, who are willing to do alpha testing of drivers, I have setup
+ * the mailing list "xircom-devel@isil.d.shuttle.de" (To subscribe send a
+ * message containing the word "subscribe" in the subject or somewhere at
+ * the beginning of a line to "xircom-devel-request@isil.d.shuttle.de").
+ *
+ ************************************************************************
+ * Copyright (c) 1997,1998 Werner Koch (dd9jn)
+ *
+ * This driver 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.
+ *
+ * It 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.
+ *
+ * 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
+ *
+ *
+ * ALTERNATIVELY, this driver may be distributed under the terms of
+ * the following license, in which case the provisions of this license
+ * are required INSTEAD OF the GNU General Public License.  (This clause
+ * is necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Enable the bug fix for CEM56 to use modem and ethernet simultaneously */
+#define CEM56_FIX
+
+#if !defined(PCMCIA_DEBUG) && 0
+  #define PCMCIA_DEBUG 4
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+
+#ifndef MANFID_XIRCOM
+  #define MANFID_XIRCOM           0x0105
+#endif
+#ifndef MANFID_COMPAQ
+  #define MANFID_COMPAQ           0x0138
+  #define MANFID_COMPAQ2          0x0183  /* is this correct? */
+#endif
+#ifndef MANFID_INTEL
+  #define MANFID_INTEL            0x0089
+#endif
+
+#include <pcmcia/ds.h>
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT     ((400*HZ)/1000)
+
+/****************
+ * Some constants used to access the hardware
+ */
+
+/* Register offsets and value constans */
+#define XIRCREG_CR  0  /* Command register (wr) */
+enum xirc_cr {
+    TransmitPacket = 0x01,
+    SoftReset = 0x02,
+    EnableIntr = 0x04,
+    ForceIntr  = 0x08,
+    ClearTxFIFO = 0x10,
+    ClearRxOvrun = 0x20,
+    RestartTx   = 0x40
+};
+#define XIRCREG_ESR 0  /* Ethernet status register (rd) */
+enum xirc_esr {
+    FullPktRcvd = 0x01, /* full packet in receive buffer */
+    PktRejected = 0x04, /* a packet has been rejected */
+    TxPktPend = 0x08,  /* TX Packet Pending */
+    IncorPolarity = 0x10,
+    MediaSelect = 0x20 /* set if TP, clear if AUI */
+};
+#define XIRCREG_PR  1  /* Page Register select */
+#define XIRCREG_EDP 4  /* Ethernet Data Port Register */
+#define XIRCREG_ISR 6  /* Ethernet Interrupt Status Register */
+enum xirc_isr {
+    TxBufOvr = 0x01,   /* TX Buffer Overflow */
+    PktTxed  = 0x02,   /* Packet Transmitted */
+    MACIntr  = 0x04,   /* MAC Interrupt occured */
+    TxResGrant = 0x08, /* Tx Reservation Granted */
+    RxFullPkt = 0x20,  /* Rx Full Packet */
+    RxPktRej  = 0x40,  /* Rx Packet Rejected */
+    ForcedIntr= 0x80   /* Forced Interrupt */
+};
+#define XIRCREG1_IMR0 12 /* Ethernet Interrupt Mask Register (on page 1)*/
+#define XIRCREG1_IMR1 13
+#define XIRCREG0_TSO  8  /* Transmit Space Open Register (on page 0)*/
+#define XIRCREG0_TRS  10 /* Transmit reservation Size Register (page 0)*/
+#define XIRCREG0_DO   12 /* Data Offset Register (page 0) (wr) */
+#define XIRCREG0_RSR  12 /* Receive Status Register (page 0) (rd) */
+enum xirc_rsr {
+    PhyPkt = 0x01,     /* set:physical packet, clear: multicast packet */
+    BrdcstPkt = 0x02,  /* set if it is a broadcast packet */
+    PktTooLong = 0x04, /* set if packet length > 1518 */
+    AlignErr = 0x10,   /* incorrect CRC and last octet not complete */
+    CRCErr = 0x20,     /* incorrect CRC and last octet is complete */
+    PktRxOk = 0x80     /* received ok */
+};
+#define XIRCREG0_PTR 13 /* packets transmitted register (rd) */
+#define XIRCREG0_RBC 14 /* receive byte count regsister (rd) */
+#define XIRCREG1_ECR 14 /* ethernet configurationn register */
+enum xirc_ecr {
+    FullDuplex = 0x04, /* enable full duplex mode */
+    LongTPMode = 0x08, /* adjust for longer lengths of TP cable */
+    DisablePolCor = 0x10,/* disable auto polarity correction */
+    DisableLinkPulse = 0x20, /* disable link pulse generation */
+    DisableAutoTx = 0x40, /* disable auto-transmit */
+};
+#define XIRCREG2_RBS 8 /* receive buffer start register */
+#define XIRCREG2_LED 10 /* LED Configuration register */
+/* values for the leds:    Bits 2-0 for led 1
+ *  0 disabled            Bits 5-3 for led 2
+ *  1 collision
+ *  2 noncollision
+ *  3 link_detected
+ *  4 incor_polarity
+ *  5 jabber
+ *  6 auto_assertion
+ *  7 rx_tx_activity
+ */
+#define XIRCREG2_MSR 12 /* Mohawk specific register */
+
+#define XIRCREG4_GPR0 8 /* General Purpose Register 0 */
+#define XIRCREG4_GPR1 9 /* General Purpose Register 1 */
+#define XIRCREG2_GPR2 13 /* General Purpose Register 2 (page2!)*/
+#define XIRCREG4_BOV 10 /* Bonding Version Register */
+#define XIRCREG4_LMA 12 /* Local Memory Address Register */
+#define XIRCREG4_LMD 14 /* Local Memory Data Port */
+/* MAC register can only by accessed with 8 bit operations */
+#define XIRCREG40_CMD0 8    /* Command Register (wr) */
+enum xirc_cmd {            /* Commands */
+    Transmit = 0x01,
+    EnableRecv = 0x04,
+    DisableRecv = 0x08,
+    Abort = 0x10,
+    Online = 0x20,
+    IntrAck = 0x40,
+    Offline = 0x80
+};
+#define XIRCREG5_RHSA0 10  /* Rx Host Start Address */
+#define XIRCREG40_RXST0 9   /* Receive Status Register */
+#define XIRCREG40_TXST0 11  /* Transmit Status Register 0 */
+#define XIRCREG40_TXST1 12  /* Transmit Status Register 10 */
+#define XIRCREG40_RMASK0 13  /* Receive Mask Register */
+#define XIRCREG40_TMASK0 14  /* Transmit Mask Register 0 */
+#define XIRCREG40_TMASK1 15  /* Transmit Mask Register 0 */
+#define XIRCREG42_SWC0 8   /* Software Configuration 0 */
+#define XIRCREG42_SWC1 9   /* Software Configuration 1 */
+#define XIRCREG42_BOC  10  /* Back-Off Configuration */
+#define XIRCREG44_TDR0 8   /* Time Domain Reflectometry 0 */
+#define XIRCREG44_TDR1 9   /* Time Domain Reflectometry 1 */
+#define XIRCREG44_RXBC_LO 10 /* Rx Byte Count 0 (rd) */
+#define XIRCREG44_RXBC_HI 11 /* Rx Byte Count 1 (rd) */
+#define XIRCREG45_REV   15 /* Revision Register (rd) */
+#define XIRCREG50_IA   8   /* Individual Address (8-13) */
+
+static char *if_names[] = { "Auto", "10BaseT", "10Base2", "AUI", "100BaseT" };
+
+/****************
+ * All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+ * you do not define PCMCIA_DEBUG at all, all the debug code will be
+ * left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+ * be present but disabled -- but it can then be enabled for specific
+ * modules at load time with a 'pc_debug=#' option to insmod.
+ */
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#endif
+static char *version =
+"xirc2ps_cs.c 1.31 1998/12/09 19:32:55 (dd9jn+kvh)";
+           /* !--- CVS revision */
+#define KDBG_XIRC KERN_DEBUG   "xirc2ps_cs: "
+#define KERR_XIRC KERN_ERR     "xirc2ps_cs: "
+#define KWRN_XIRC KERN_WARNING "xirc2ps_cs: "
+#define KNOT_XIRC KERN_NOTICE  "xirc2ps_cs: "
+#define KINF_XIRC KERN_INFO    "xirc2ps_cs: "
+
+/* card types */
+#define XIR_UNKNOWN  0 /* unknown: not supported */
+#define XIR_CE      1  /* (prodid 1) different hardware: not supported */
+#define XIR_CE2      2 /* (prodid 2) */
+#define XIR_CE3      3 /* (prodid 3) */
+#define XIR_CEM      4 /* (prodid 1) different hardware: not supported */
+#define XIR_CEM2     5 /* (prodid 2) */
+#define XIR_CEM3     6 /* (prodid 3) */
+#define XIR_CEM33    7 /* (prodid 4) */
+#define XIR_CEM56M   8 /* (prodid 5) */
+#define XIR_CEM56    9 /* (prodid 6) */
+#define XIR_CM28    10 /* (prodid 3) modem only: not supported here */
+#define XIR_CM33    11 /* (prodid 4) modem only: not supported here */
+#define XIR_CM56    12 /* (prodid 5) modem only: not supported here */
+#define XIR_CG     13  /* (prodid 1) GSM modem only: not supported */
+#define XIR_CBE     14 /* (prodid 1) cardbus ethernet: not supported */
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+static int if_port = 0;
+MODULE_PARM(if_port, "i");
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_long irq_mask = 0xdeb8;
+MODULE_PARM(irq_mask, "i");
+
+static int irq_list[4] = { -1 };
+MODULE_PARM(irq_list, "1-4i");
+
+static int do_sound = 1;
+MODULE_PARM(do_sound, "i");
+
+static int card_type = 0;
+MODULE_PARM(card_type, "i");  /* dummy, not used anymore */
+
+static int lockup_hack = 0;
+MODULE_PARM(lockup_hack, "i");  /* anti lockup hack */
+
+/*====================================================================*/
+
+/* We do not process more than these number of bytes during one
+ * interrupt. (Of course we receive complete packets, so this is not
+ * an exact value).
+ * Something between 2000..22000; first value gives best interrupt latency,
+ * the second enables the usage of the complete on-chip buffer. We use the
+ * high value as the initial value.
+ */
+static unsigned maxrx_bytes = 22000;
+
+/* MII management prototypes */
+static void mii_idle(ioaddr_t ioaddr);
+static void mii_putbit(ioaddr_t ioaddr, unsigned data);
+static int  mii_getbit( ioaddr_t ioaddr );
+static void mii_wbits(ioaddr_t ioaddr, unsigned data, int len);
+static unsigned mii_rd(ioaddr_t ioaddr,        u_char phyaddr, u_char phyreg);
+static void mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg,
+                                  unsigned data, int len);
+
+/*
+ * The event() function is this driver's Card Services event handler.
+ * It will be called by Card Services when an appropriate card status
+ * event is received.  The config() and release() entry points are
+ * used to configure or release a socket, in response to card insertion
+ * and ejection events.  They are invoked from the event handler.
+ */
+
+static int has_ce2_string(dev_link_t * link);
+static void xirc2ps_config(dev_link_t * link);
+static void xirc2ps_release(u_long arg);
+static int xirc2ps_event(event_t event, int priority,
+                        event_callback_args_t * args);
+
+/****************
+ * The attach() and detach() entry points are used to create and destroy
+ * "instances" of the driver, where each instance represents everything
+ * needed to manage one actual PCMCIA card.
+ */
+
+static dev_link_t *xirc2ps_attach(void);
+static void xirc2ps_detach(dev_link_t *);
+
+/****************
+ * You'll also need to prototype all the functions that will actually
+ * be used to talk to your device.  See 'pcmem_cs' for a good example
+ * of a fully self-sufficient driver; the other drivers rely more or
+ * less on other parts of the kernel.
+ */
+
+static void xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+
+static dev_info_t dev_info = "xirc2ps_cs";
+
+/****************
+ * A linked list of "instances" of the device.  Each actual
+ * PCMCIA card corresponds to one device instance, and is described
+ * by one dev_link_t structure (defined in ds.h).
+ *
+ * You may not want to use a linked list for this -- for example, the
+ * memory card driver uses an array of dev_link_t pointers, where minor
+ * device numbers are used to derive the corresponding array index.
+ */
+
+static dev_link_t *dev_list = NULL;
+
+/****************
+ * A dev_link_t structure has fields for most things that are needed
+ * to keep track of a socket, but there will usually be some device
+ * specific information that also needs to be kept track of.  The
+ * 'priv' pointer in a dev_link_t structure can be used to point to
+ * a device-specific private data structure, like this.
+ *
+ * A driver needs to provide a dev_node_t structure for each device
+ * on a card.  In some cases, there is only one device per card (for
+ * example, ethernet cards, modems).  In other cases, there may be
+ * many actual or logical devices (SCSI adapters, memory cards with
+ * multiple partitions).  The dev_node_t structures need to be kept
+ * in a linked list starting at the 'dev' field of a dev_link_t
+ * structure.  We allocate them in the card's private data structure,
+ * because they generally can't be allocated dynamically.
+ */
+
+typedef struct local_info_t {
+    dev_node_t node;
+    struct enet_statistics stats;
+    int card_type;
+    int probe_port;
+    int silicon; /* silicon revision. 0=old CE2, 1=Scipper, 4=Mohawk */
+    int mohawk;  /* a CE3 type card */
+    int dingo;  /* a CEM56 type card */
+    int modem;  /* is a multi function card (i.e with a modem) */
+    caddr_t dingo_ccr; /* only used for CEM56 cards */
+    int suspended;
+    unsigned last_ptr_value; /* last packets transmitted value */
+    const char *manf_str;
+} local_info_t;
+
+/****************
+ * Some more prototypes
+ */
+static int do_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct enet_statistics *do_get_stats(struct net_device *dev);
+static void set_addresses(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static int do_init(struct net_device *dev);
+static int set_card_type( dev_link_t *link, const void *s );
+static int do_config(struct net_device *dev, struct ifmap *map);
+static int do_open(struct net_device *dev);
+static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void hardreset(struct net_device *dev);
+static void do_reset(struct net_device *dev, int full);
+static int init_mii(struct net_device *dev);
+static void do_powerdown(struct net_device *dev);
+static int do_stop(struct net_device *dev);
+
+/*=============== Helper functions =========================*/
+static void
+flush_stale_links(void)
+{
+    dev_link_t *link, *next;
+    for (link = dev_list; link; link = next) {
+       next = link->next;
+       if (link->state & DEV_STALE_LINK)
+           xirc2ps_detach(link);
+    }
+}
+
+static void
+cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+static int
+get_tuple_data(int fn, client_handle_t handle, tuple_t *tuple )
+{
+    int err;
+
+    if( (err=CardServices(fn, handle, tuple)) )
+       return err;
+    return CardServices(GetTupleData, handle, tuple);
+}
+
+static int
+get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+    int err;
+
+    if( (err=get_tuple_data(fn, handle, tuple)) )
+       return err;
+    return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c)  get_tuple(GetNextTuple, a, b, c)
+
+#define SelectPage(pgnr)   outb((pgnr), ioaddr + XIRCREG_PR)
+#define GetByte(reg)      ((unsigned)inb(ioaddr + (reg)))
+#define GetWord(reg)      ((unsigned)inw(ioaddr + (reg)))
+#define PutByte(reg,value) outb((value), ioaddr+(reg))
+#define PutWord(reg,value) outw((value), ioaddr+(reg))
+
+static void
+busy_loop(u_long len)
+{
+    u_long timeout = jiffies + len;
+    u_long flags;
+
+    save_flags(flags);
+    sti();
+    while(timeout >= jiffies)
+       ;
+    restore_flags(flags);
+}
+
+/*====== Functions used for debugging =================================*/
+#if defined(PCMCIA_DEBUG) && 0 /* reading regs may change system status */
+static void
+PrintRegisters(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+
+    if(pc_debug > 1) {
+       int i, page;
+
+       printk(KDBG_XIRC "Register  common: ");
+       for(i = 0; i < 8; i++ )
+           printk(" %2.2x", GetByte(i) );
+       printk("\n");
+       for(page = 0; page <= 8; page++) {
+           printk(KDBG_XIRC "Register page %2x: ", page);
+           SelectPage(page);
+           for(i = 8; i < 16; i++)
+               printk(" %2.2x", GetByte(i));
+           printk("\n");
+       }
+       for(page=0x40 ; page <= 0x5f; page++) {
+           if( page == 0x43 || (page >= 0x46 && page <= 0x4f)
+               || (page >= 0x51 && page <=0x5e) )
+               continue;
+           printk(KDBG_XIRC "Register page %2x: ", page);
+           SelectPage(page);
+           for(i = 8; i < 16; i++)
+               printk(" %2.2x", GetByte(i));
+           printk("\n");
+       }
+    }
+}
+#endif /* PCMCIA_DEBUG */
+
+/*============== MII Management functions ===============*/
+
+/****************
+ * Turn around for read
+ */
+static void
+mii_idle(ioaddr_t ioaddr)
+{
+    PutByte(XIRCREG2_GPR2, 0x04|0 ); /* drive MDCK low */
+    udelay(1);
+    PutByte(XIRCREG2_GPR2, 0x04|1 ); /* and drive MDCK high */
+    udelay(1);
+}
+
+/****************
+ * Write a bit to MDI/O
+ */
+static void
+mii_putbit(ioaddr_t ioaddr, unsigned data)
+{
+  #if 1
+    if( data ) {
+       PutByte(XIRCREG2_GPR2, 0x0c|2|0 ); /* set MDIO */
+       udelay(1);
+       PutByte(XIRCREG2_GPR2, 0x0c|2|1 ); /* and drive MDCK high */
+       udelay(1);
+    }
+    else {
+       PutByte(XIRCREG2_GPR2, 0x0c|0|0 ); /* clear MDIO */
+       udelay(1);
+       PutByte(XIRCREG2_GPR2, 0x0c|0|1 ); /* and drive MDCK high */
+       udelay(1);
+    }
+  #else
+    if( data ) {
+       PutWord(XIRCREG2_GPR2-1, 0x0e0e );
+       udelay(1);
+       PutWord(XIRCREG2_GPR2-1, 0x0f0f );
+       udelay(1);
+    }
+    else {
+       PutWord(XIRCREG2_GPR2-1, 0x0c0c );
+       udelay(1);
+       PutWord(XIRCREG2_GPR2-1, 0x0d0d );
+       udelay(1);
+    }
+  #endif
+}
+
+/****************
+ * Get a bit from MDI/O
+ */
+static int
+mii_getbit( ioaddr_t ioaddr )
+{
+    unsigned d;
+
+    PutByte(XIRCREG2_GPR2, 4|0 ); /* drive MDCK low */
+    udelay(1);
+    d = GetByte(XIRCREG2_GPR2); /* read MDIO */
+    PutByte(XIRCREG2_GPR2, 4|1 ); /* drive MDCK high again */
+    udelay(1);
+    return d & 0x20; /* read MDIO */
+}
+
+static void
+mii_wbits(ioaddr_t ioaddr, unsigned data, int len)
+{
+    unsigned m = 1 << (len-1);
+    for( ; m; m >>= 1)
+       mii_putbit( ioaddr, data & m );
+}
+
+static unsigned
+mii_rd(ioaddr_t ioaddr,        u_char phyaddr, u_char phyreg)
+{
+    int i;
+    unsigned data=0, m;
+
+    SelectPage(2);
+    for( i=0; i < 32; i++ )            /* 32 bit preamble */
+       mii_putbit(ioaddr, 1);
+    mii_wbits(ioaddr, 0x06, 4);        /* Start and opcode for read */
+    mii_wbits(ioaddr, phyaddr, 5);     /* PHY address to be accessed */
+    mii_wbits(ioaddr, phyreg, 5);      /* PHY register to read */
+    mii_idle(ioaddr);                  /* turn around */
+    mii_getbit( ioaddr);
+
+    for( m = 1<<15; m; m >>= 1 )
+       if( mii_getbit( ioaddr ) )
+           data |= m;
+    mii_idle(ioaddr);
+    return data;
+}
+
+static void
+mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len)
+{
+    int i;
+
+    SelectPage(2);
+    for( i=0; i < 32; i++ )            /* 32 bit preamble */
+       mii_putbit(ioaddr, 1);
+    mii_wbits(ioaddr, 0x05, 4);        /* Start and opcode for write */
+    mii_wbits(ioaddr, phyaddr, 5);     /* PHY address to be accessed */
+    mii_wbits(ioaddr, phyreg, 5);      /* PHY Register to write */
+    mii_putbit(ioaddr, 1);             /* turn around */
+    mii_putbit(ioaddr, 0);
+    mii_wbits(ioaddr, data, len);      /* And write the data */
+    mii_idle(ioaddr);
+}
+
+#ifdef PCMCIA_DEBUG
+static void
+mii_dump(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    int i;
+
+    /* Note that registers 14, 1d,1e and 1f are reserved and should
+     * not be read according to the DP83840A specs.
+     */
+    printk(KERN_DEBUG "%s: MII register dump:\n", dev->name);
+    for(i=0; i < 32; i++ ) {
+       if( !(i % 8) ) {
+           if( i )
+               printk("\n");
+           printk(KERN_DEBUG "%s:", dev->name );
+       }
+       printk(" %04x", mii_rd(ioaddr, 0, i) );
+    }
+    printk("\n");
+}
+#endif
+
+/*============= Main bulk of functions =========================*/
+
+/****************
+ * xirc2ps_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device.  The device is registered
+ * with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+
+static dev_link_t *
+xirc2ps_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    local_info_t *local;
+    int err;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "attach()\n");
+  #endif
+    flush_stale_links();
+
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    memset(link, 0, sizeof(struct dev_link_t));
+    link->release.function = &xirc2ps_release;
+    link->release.data = (u_long) link;
+
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Allocate space for a device structure */
+    dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+    memset(dev, 0, sizeof(struct net_device));
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    memset(local, 0, sizeof(local_info_t));
+    dev->priv = local;
+
+    /* Fill in card specific entries */
+    dev->hard_start_xmit = &do_start_xmit;
+    dev->set_config = &do_config;
+    dev->get_stats = &do_get_stats;
+    dev->do_ioctl = &do_ioctl;
+    dev->set_multicast_list = &set_multicast_list;
+    ether_setup(dev);
+    dev->name = local->node.dev_name;
+    dev->init = &do_init;
+    dev->open = &do_open;
+    dev->stop = &do_stop;
+    dev->tbusy = 1;
+    link->priv = dev;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+       CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+       CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &xirc2ps_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    if( (err = CardServices(RegisterClient, &link->handle, &client_reg)) ) {
+       cs_error(link->handle, RegisterClient, err);
+       xirc2ps_detach(link);
+       return NULL;
+    }
+
+    return link;
+} /* xirc2ps_attach */
+
+/****************
+ *  This deletes a driver "instance".  The device is de-registered
+ *  with Card Services.  If it has been released, all local data
+ *  structures are freed.  Otherwise, the structures will be freed
+ *  when the device is released.
+ */
+
+static void
+xirc2ps_detach(dev_link_t * link)
+{
+    dev_link_t **linkp;
+    long flags;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "detach(0x%p)\n", link);
+  #endif
+
+    /* Locate device structure */
+    for( linkp = &dev_list; *linkp; linkp = &(*linkp)->next )
+       if( *linkp == link)
+           break;
+    if( !*linkp ) {
+      #ifdef PCMCIA_DEBUG
+       printk(KDBG_XIRC "detach(0x%p): dev_link lost\n", link );
+      #endif
+       return;
+    }
+
+    save_flags(flags);
+    cli();
+    if( link->state & DEV_RELEASE_PENDING ) {
+       del_timer(&link->release);
+       link->state &= ~DEV_RELEASE_PENDING;
+    }
+    restore_flags(flags);
+
+    /*
+     * If the device is currently configured and active, we won't
+     * actually delete it yet. Instead, it is marked so that when
+     * the release() function is called, that will trigger a proper
+     * detach().
+     */
+    if(link->state & DEV_CONFIG) {
+      #ifdef PCMCIA_DEBUG
+       printk(KDBG_XIRC "detach postponed, '%s' "
+                         "still locked\n", link->dev->dev_name);
+      #endif
+       link->state |= DEV_STALE_LINK;
+       return;
+    }
+
+    /* Break the link with Card Services */
+    if(link->handle)
+       CardServices(DeregisterClient, link->handle);
+
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if(link->priv) {
+       struct net_device *dev = link->priv;
+       if (link->dev != NULL)
+           unregister_netdev(dev);
+       if( dev->priv )
+           kfree(dev->priv);
+       kfree(link->priv);
+    }
+    kfree(link);
+
+} /* xirc2ps_detach */
+
+/****************
+ * Detect the type of the card. s is the buffer with the data of tuple 0x20
+ * Returns: 0 := not supported
+ *                    mediaid=11 and prodid=47
+ * Media-Id bits:
+ *  Ethernet       0x01
+ *  Tokenring      0x02
+ *  Arcnet         0x04
+ *  Wireless       0x08
+ *  Modem          0x10
+ *  GSM only       0x20
+ * Prod-Id bits:
+ *  Pocket         0x10
+ *  External       0x20
+ *  Creditcard     0x40
+ *  Cardbus        0x80
+ *
+ */
+static int
+set_card_type( dev_link_t *link, const void *s )
+{
+    struct net_device *dev = link->priv;
+    local_info_t *local = dev->priv;
+  #ifdef PCMCIA_DEBUG
+    unsigned cisrev = ((const unsigned char *)s)[2];
+  #endif
+    unsigned mediaid= ((const unsigned char *)s)[3];
+    unsigned prodid = ((const unsigned char *)s)[4];
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "cisrev=%02x mediaid=%02x prodid=%02x\n",
+                         cisrev, mediaid, prodid );
+  #endif
+
+    local->mohawk = 0;
+    local->dingo = 0;
+    local->modem = 0;
+    local->card_type = XIR_UNKNOWN;
+    if( !(prodid & 0x40) ) {
+       printk(KNOT_XIRC "Ooops: Not a creditcard\n");
+       return 0;
+    }
+    if( !(mediaid & 0x01) ) {
+       printk(KNOT_XIRC "Not an Ethernet card\n");
+       return 0;
+    }
+    if( mediaid & 0x10 ) {
+       local->modem = 1;
+       switch( prodid & 15 ) {
+         case 1: local->card_type = XIR_CEM   ; break;
+         case 2: local->card_type = XIR_CEM2  ; break;
+         case 3: local->card_type = XIR_CEM3  ; break;
+         case 4: local->card_type = XIR_CEM33 ; break;
+         case 5: local->card_type = XIR_CEM56M;
+                 local->mohawk = 1;
+                 break;
+         case 6:
+         case 7: /* 7 is the RealPort 10/56 */
+                 local->card_type = XIR_CEM56 ;
+                 local->mohawk = 1;
+                 local->dingo = 1;
+                 break;
+       }
+    }
+    else {
+       switch( prodid & 15 ) {
+         case 1: local->card_type = has_ce2_string(link)? XIR_CE2 : XIR_CE ;
+                 break;
+         case 2: local->card_type = XIR_CE2; break;
+         case 3: local->card_type = XIR_CE3;
+                 local->mohawk = 1;
+                 break;
+       }
+    }
+    if( local->card_type == XIR_CE || local->card_type == XIR_CEM ) {
+       printk(KNOT_XIRC "Sorry, this is an old CE card\n");
+       return 0;
+    }
+    if( local->card_type == XIR_UNKNOWN )
+       printk(KNOT_XIRC "Warning: Unknown card (mediaid=%02x prodid=%02x)\n",
+                                 mediaid, prodid );
+
+    return 1;
+}
+
+/****************
+ * There are some CE2 cards out which claim to be a CE card.
+ * This function looks for a "CE2" in the 3rd version field.
+ * Returns: true if this is a CE2
+ */
+static int
+has_ce2_string(dev_link_t * link)
+{
+    client_handle_t handle = link->handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[256];
+
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 254;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2 ) {
+       if( strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2") )
+           return 1;
+    }
+    return 0;
+}
+
+/****************
+ * xirc2ps_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * ethernet device available to the system.
+ */
+static void
+xirc2ps_config(dev_link_t * link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    struct net_device *dev;
+    local_info_t *local;
+    ioaddr_t ioaddr;
+    int err, i;
+    u_char buf[64];
+    cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+
+    handle = link->handle;
+    dev = link->priv;
+    local = dev->priv;
+    local->dingo_ccr = 0;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "config(0x%p)\n", link);
+  #endif
+
+    /*
+     * This reads the card's CONFIG tuple to find its configuration
+     * registers.
+     */
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+
+    /* Is this a valid card */
+    tuple.DesiredTuple = CISTPL_MANFID;
+    if( (err=first_tuple(handle, &tuple, &parse))) {
+       printk(KNOT_XIRC "manfid not found in CIS\n");
+       goto failure;
+    }
+
+    switch( parse.manfid.manf ) {
+      case MANFID_XIRCOM:
+       local->manf_str = "Xircom";
+       #ifdef PCMCIA_DEBUG
+         if(pc_debug)
+           printk(KDBG_XIRC "found xircom card\n");
+       #endif
+       break;
+      case MANFID_ACCTON:
+       local->manf_str = "Accton";
+       #ifdef PCMCIA_DEBUG
+         if(pc_debug)
+           printk(KDBG_XIRC "found Accton card\n");
+       #endif
+       break;
+      case MANFID_COMPAQ:
+      case MANFID_COMPAQ2:
+       local->manf_str = "Compaq";
+       #ifdef PCMCIA_DEBUG
+         if(pc_debug)
+           printk(KDBG_XIRC "found Compaq card\n");
+       #endif
+       break;
+      case MANFID_INTEL:
+       local->manf_str = "Intel";
+       #ifdef PCMCIA_DEBUG
+         if(pc_debug)
+           printk(KDBG_XIRC "found Intel card\n");
+       #endif
+       break;
+      default:
+       printk(KNOT_XIRC "Unknown Card Manufacturer ID: 0x%04x\n",
+                                                (unsigned)parse.manfid.manf);
+       goto failure;
+    }
+
+    if( !set_card_type(link, buf ) ) {
+       printk(KNOT_XIRC "this card is not supported\n");
+       goto failure;
+    }
+
+    /* get configuration stuff */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    if( (err=first_tuple(handle, &tuple, &parse)))
+       goto cis_error;
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present =    parse.config.rmask[0];
+
+    /* get the ethernet address from the CIS */
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    for( err = first_tuple(handle, &tuple, &parse); !err;
+                            err = next_tuple(handle, &tuple, &parse) ) {
+       /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries:
+        * the first one with a length of zero the second correct -
+        * so I skip all entries with length 0 */
+       if( parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID
+           && ((cistpl_lan_node_id_t *)parse.funce.data)->nb )
+           break;
+    }
+    if( err ) { /* not found: try to get the node-id from tuple 0x89 */
+       tuple.DesiredTuple = 0x89;  /* data layout looks like tuple 0x22 */
+       if( !(err = get_tuple_data(GetFirstTuple, handle, &tuple )) ) {
+           if( tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID )
+               memcpy( &parse, buf, 8 );
+           else
+               err = -1;
+       }
+    }
+    if( err ) { /* another try (James Lehmer's CE2 version 4.1)*/
+       tuple.DesiredTuple = CISTPL_FUNCE;
+       for( err = first_tuple(handle, &tuple, &parse); !err;
+                                err = next_tuple(handle, &tuple, &parse) ) {
+           if( parse.funce.type == 0x02 && parse.funce.data[0] == 1
+               && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13 ) {
+               buf[1] = 4;
+               memcpy( &parse, buf+1, 8 );
+               break;
+           }
+       }
+    }
+    if( err ) {
+       printk(KNOT_XIRC "node-id not found in CIS\n");
+       goto failure;
+    }
+    node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+    if( node_id->nb != 6 ) {
+       printk(KNOT_XIRC "malformed node-id in CIS\n");
+       goto failure;
+    }
+    for( i=0; i < 6; i++ )
+       dev->dev_addr[i] = node_id->id[i];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    link->io.IOAddrLines =10;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+    link->irq.Attributes = IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+    if( irq_list[0] == -1 )
+       link->irq.IRQInfo2 = irq_mask;
+    else {
+       for( i = 0; i < 4; i++)
+           link->irq.IRQInfo2 |= 1 << irq_list[i];
+    }
+    link->irq.Handler = xirc2ps_interrupt;
+    link->irq.Instance = dev;
+    if( local->modem ) {
+       int pass;
+
+       if( do_sound ) {
+           link->conf.Attributes |= CONF_ENABLE_SPKR;
+           link->conf.Status |= CCSR_AUDIO_ENA;
+       }
+       link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED ;
+       link->io.NumPorts2 = 8;
+       link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+       if( local->dingo ) {
+           /* Take the Modem IO port from the CIS and scan for a free
+            * Ethernet port */
+           link->io.NumPorts1 = 16; /* no Mako stuff anymore */
+           tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+           for( err = first_tuple(handle, &tuple, &parse); !err;
+                                err = next_tuple(handle, &tuple, &parse) ) {
+               if( cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8 ) {
+                   for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+                       link->conf.ConfigIndex = cf->index ;
+                       link->io.BasePort2 = cf->io.win[0].base;
+                       link->io.BasePort1 = ioaddr;
+                       if( !(err=CardServices(RequestIO, link->handle,
+                                                               &link->io)) )
+                           goto port_found;
+                   }
+               }
+           }
+       }
+       else {
+           link->io.NumPorts1 = 18;
+           /* We do 2 passes here: The first one uses the regular mapping and
+            * the second tries again, thereby considering that the 32 ports are
+            * mirrored every 32 bytes. Actually we use a mirrored port for
+            * the Mako if (on the first pass) the COR bit 5 is set.
+            */
+           for( pass=0; pass < 2; pass++ ) {
+               tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+               for( err = first_tuple(handle, &tuple, &parse); !err;
+                                    err = next_tuple(handle, &tuple, &parse) ){
+                   if( cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8 ){
+                       link->conf.ConfigIndex = cf->index ;
+                       link->io.BasePort2 = cf->io.win[0].base;
+                       link->io.BasePort1 = link->io.BasePort2
+                                   + (pass ? ( cf->index & 0x20 ? -24:8 )
+                                           : ( cf->index & 0x20 ?   8:-24) );
+                       if( !(err=CardServices(RequestIO, link->handle,
+                                                               &link->io)))
+                           goto port_found;
+                   }
+               }
+           }
+           /* if special option:
+            * try to configure as Ethernet only.
+            * .... */
+       }
+       printk(KNOT_XIRC "no ports available\n");
+    }
+    else {
+       link->irq.Attributes |= IRQ_TYPE_EXCLUSIVE;
+       link->io.NumPorts1 = 16;
+       for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+           link->io.BasePort1 = ioaddr;
+           if( !(err=CardServices(RequestIO, link->handle, &link->io)) )
+               goto port_found;
+       }
+       link->io.BasePort1 = 0; /* let CS decide */
+       if( (err=CardServices(RequestIO, link->handle, &link->io)) ) {
+           cs_error(link->handle, RequestIO, err);
+           goto config_error;
+       }
+    }
+  port_found:
+    if( err )
+        goto config_error;
+
+    /****************
+     * Now allocate an interrupt line. Note that this does not
+     * actually assign a handler to the interrupt.
+     */
+    if( (err=CardServices(RequestIRQ, link->handle, &link->irq))) {
+       cs_error(link->handle, RequestIRQ, err);
+       goto config_error;
+    }
+
+    /****************
+     * This actually configures the PCMCIA socket -- setting up
+     * the I/O windows and the interrupt mapping.
+     */
+    if( (err=CardServices(RequestConfiguration,
+                                       link->handle, &link->conf)) ) {
+       cs_error(link->handle, RequestConfiguration, err);
+       goto config_error;
+    }
+
+    if( local->dingo ) {
+      #ifdef CEM56_FIX
+       conf_reg_t reg;
+      #endif
+       win_req_t req;
+       memreq_t mem;
+
+      #ifdef CEM56_FIX
+       /* Reset the modem's BAR to the correct value
+        * This is necessary because in the RequestConfiguration call,
+        * the base address of the ethernet port (BasePort1) is written
+        * to the BAR registers of the modem.
+        */
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_IOBASE_0;
+       reg.Value = link->io.BasePort2 & 0xff;
+       if( (err = CardServices(AccessConfigurationRegister, link->handle,
+                                                            &reg )) ) {
+           cs_error(link->handle, AccessConfigurationRegister, err);
+           goto config_error;
+       }
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_IOBASE_1;
+       reg.Value = (link->io.BasePort2 >> 8) & 0xff;
+       if( (err = CardServices(AccessConfigurationRegister, link->handle,
+                                                             &reg )) ) {
+           cs_error(link->handle, AccessConfigurationRegister, err);
+           goto config_error;
+       }
+     #endif
+
+       /* There is no config entry for the Ethernet part which
+        * is at 0x0800. So we allocate a window into the attribute
+        * memory and write direct to the CIS registers
+        */
+       req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+       req.Base = 0;
+       req.Size = 0x1000; /* 4k window */
+       req.AccessSpeed = 0;
+       link->win = (window_handle_t)link->handle;
+       if( (err = CardServices(RequestWindow, &link->win, &req )) ) {
+           cs_error(link->handle, RequestWindow, err);
+           goto config_error;
+       }
+       local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800;
+       mem.CardOffset = 0x0;
+       mem.Page = 0;
+       if( (err = CardServices(MapMemPage, link->win, &mem) ) ) {
+           cs_error(link->handle, MapMemPage, err);
+           goto config_error;
+       }
+
+       /* Setup the CCRs; there are no infos in the CIS about the Ethernet
+        * part.
+        */
+       writeb(0x47, local->dingo_ccr + CISREG_COR );
+       ioaddr = link->io.BasePort1;
+       writeb( ioaddr & 0xff     , local->dingo_ccr + CISREG_IOBASE_0 );
+       writeb((ioaddr >> 8)&0xff , local->dingo_ccr + CISREG_IOBASE_1 );
+
+      #if 0
+       {
+           u_char tmp;
+           printk(KERN_INFO "ECOR:" );
+           for(i=0; i < 7; i++ ) {
+               tmp = readb(local->dingo_ccr + i*2 );
+               printk(" %02x", tmp );
+           }
+           printk("\n" );
+           printk(KERN_INFO "DCOR:" );
+           for(i=0; i < 4; i++ ) {
+               tmp = readb(local->dingo_ccr + 0x20 + i*2 );
+               printk(" %02x", tmp );
+           }
+           printk("\n" );
+           printk(KERN_INFO "SCOR:" );
+           for(i=0; i < 10; i++ ) {
+               tmp = readb(local->dingo_ccr + 0x40 + i*2 );
+               printk(" %02x", tmp );
+           }
+           printk("\n" );
+       }
+      #endif
+
+       writeb( 0x01              , local->dingo_ccr + 0x20 );
+       writeb( 0x0c              , local->dingo_ccr + 0x22 );
+       writeb( 0x00              , local->dingo_ccr + 0x24 );
+       writeb( 0x00              , local->dingo_ccr + 0x26 );
+       writeb( 0x00              , local->dingo_ccr + 0x28 );
+    }
+
+    /* The if_port symbol can be set when the module is loaded */
+    local->probe_port=0;
+    if( !if_port ) {
+       local->probe_port=1;
+       dev->if_port = 1;
+    }
+    else if( (if_port >= 1 && if_port <= 2) || (local->mohawk && if_port==4) )
+       dev->if_port = if_port;
+    else
+       printk(KERN_NOTICE "xirc2ps_cs: invalid if_port requested\n");
+
+    /* we can now register the device with the net subsystem */
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    dev->tbusy = 0;
+    if( (err=register_netdev(dev)) ) {
+       printk(KERN_NOTICE "xirc2ps_cs: register_netdev() failed\n");
+       goto config_error;
+    }
+
+    link->state &= ~DEV_CONFIG_PENDING;
+    link->dev = &local->node;
+
+    if( local->dingo )
+       do_reset(dev, 1); /* a kludge to make the cem56 work */
+
+    /* give some infos about the hardware */
+    printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr",
+        dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq );
+    for(i = 0; i < 6; i++)
+       printk("%c%02X", i?':':' ', dev->dev_addr[i]);
+    printk("\n");
+
+    return;
+
+  config_error:
+    link->state &= ~DEV_CONFIG_PENDING;
+    xirc2ps_release((u_long)link);
+    return;
+
+  cis_error:
+    printk(KERN_NOTICE "xirc2ps_cs: unable to parse CIS\n");
+  failure:
+    link->state &= ~DEV_CONFIG_PENDING;
+} /* xirc2ps_config */
+
+/****************
+ * After a card is removed, xirc2ps_release() will unregister the net
+ * device, and release the PCMCIA configuration.  If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+xirc2ps_release( u_long arg)
+{
+    dev_link_t *link = (dev_link_t *) arg;
+    struct net_device *dev = link->priv;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "release(0x%p)\n", link);
+  #endif
+
+    /*
+     * If the device is currently in use, we won't release until it
+     * is actually closed.
+     */
+    if(link->open) {
+      #ifdef PCMCIA_DEBUG
+       if(pc_debug)
+           printk(KDBG_XIRC "release postponed, '%s' "
+                             "still open\n", link->dev->dev_name);
+      #endif
+       link->state |= DEV_STALE_CONFIG;
+       return;
+    }
+
+    if( link->win ) {
+       local_info_t *local = dev->priv;
+       if( local->dingo )
+           iounmap( local->dingo_ccr - 0x0800 );
+       CardServices(ReleaseWindow, link->win );
+    }
+    CardServices(ReleaseConfiguration, link->handle);
+    CardServices(ReleaseIO, link->handle, &link->io);
+    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+
+} /* xirc2ps_release */
+
+/*====================================================================*/
+
+/****************
+ * The card status event handler.  Mostly, this schedules other
+ * stuff to run after an event is received.  A CARD_REMOVAL event
+ * also sets some flags to discourage the net drivers from trying
+ * to talk to the card any more.
+ *
+ * When a CARD_REMOVAL event is received, we immediately set a flag
+ * to block future accesses to this device.  All the functions that
+ * actually access the device should check this flag to make sure
+ * the card is still present.
+ */
+
+static int
+xirc2ps_event(event_t event, int priority,
+             event_callback_args_t * args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+    local_info_t *lp = dev? dev->priv : NULL;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "event(%d)\n", (int)event );
+  #endif
+
+    switch (event) {
+      case CS_EVENT_REGISTRATION_COMPLETE:
+        #ifdef PCMCIA_DEBUG
+         if(pc_debug)
+             printk(KDBG_XIRC "registration complete\n");
+        #endif
+         break;
+      case CS_EVENT_CARD_REMOVAL:
+         link->state &= ~DEV_PRESENT;
+         if(link->state & DEV_CONFIG) {
+             dev->tbusy = 1; dev->start = 0;
+             link->release.expires = jiffies + HZ / 20;
+             add_timer(&link->release);
+         }
+         break;
+      case CS_EVENT_CARD_INSERTION:
+         link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+         xirc2ps_config(link);
+         break;
+      case CS_EVENT_PM_SUSPEND:
+         link->state |= DEV_SUSPEND;
+         /* Fall through... */
+      case CS_EVENT_RESET_PHYSICAL:
+         if(link->state & DEV_CONFIG) {
+             if(link->open) {
+                 dev->tbusy = 1; dev->start = 0;
+                 lp->suspended=1;
+                 do_powerdown(dev);
+             }
+             CardServices(ReleaseConfiguration, link->handle);
+         }
+         break;
+      case CS_EVENT_PM_RESUME:
+         link->state &= ~DEV_SUSPEND;
+         /* Fall through... */
+      case CS_EVENT_CARD_RESET:
+         if(link->state & DEV_CONFIG) {
+            CardServices(RequestConfiguration, link->handle, &link->conf);
+            if( link->open) {
+                do_reset(dev,1);
+                lp->suspended=0;
+                dev->tbusy = 0; dev->start = 1;
+            }
+         }
+         break;
+    }
+    return 0;
+} /* xirc2ps_event */
+
+/*====================================================================*/
+
+/****************
+ * This is the Interrupt service route.
+ */
+static void
+xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = (struct net_device *)dev_id;
+    local_info_t *lp;
+    ioaddr_t ioaddr;
+    u_char saved_page;
+    unsigned bytes_rcvd;
+    unsigned int_status, eth_status, rx_status, tx_status;
+    unsigned rsr, pktlen;
+    ulong start_ticks = jiffies; /* fixme: jiffies rollover every 497 days
+                                 * is this something to worry about?
+                                 * -- on a laptop?
+                                 */
+
+    if( !dev->start )
+       return;
+
+    if( dev->interrupt ) {
+       printk(KERR_XIRC "re-entering isr on irq %d (dev=%p)\n", irq, dev);
+       return;
+    }
+    dev->interrupt = 1;
+    lp = dev->priv;
+    ioaddr = dev->base_addr;
+    if( lp->mohawk ) { /* must disable the interrupt */
+       PutByte(XIRCREG_CR, 0 );
+    }
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug > 6 )
+       printk(KERN_DEBUG "%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr);
+  #endif
+
+    saved_page = GetByte(XIRCREG_PR);
+    /* Read the ISR to see whats the cause for the interrupt.
+     * This also clears the interrupt flags on CE2 cards
+     */
+    int_status = GetByte(XIRCREG_ISR);
+    bytes_rcvd = 0;
+  loop_entry:
+    if( int_status == 0xff ) { /* card may be ejected */
+      #ifdef PCMCIA_DEBUG
+       if( pc_debug > 3 )
+           printk(KERN_DEBUG "%s: interrupt %d for dead card\n", dev->name, irq );
+      #endif
+       goto leave;
+    }
+    eth_status = GetByte(XIRCREG_ESR);
+
+    SelectPage(0x40);
+    rx_status  = GetByte(XIRCREG40_RXST0);
+    PutByte(XIRCREG40_RXST0, (~rx_status & 0xff) );
+    tx_status = GetByte(XIRCREG40_TXST0);
+    tx_status |= GetByte(XIRCREG40_TXST1) << 8;
+    PutByte(XIRCREG40_TXST0, 0 );
+    PutByte(XIRCREG40_TXST1, 0 );
+
+  #ifdef PCMCIA_DEBUG
+    if( pc_debug > 3 )
+       printk(KERN_DEBUG "%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n",
+                   dev->name, int_status, eth_status, rx_status, tx_status );
+  #endif
+
+    /***** receive section ******/
+    SelectPage(0);
+    while( eth_status & FullPktRcvd ) {
+       rsr = GetByte(XIRCREG0_RSR);
+       if( bytes_rcvd > maxrx_bytes && (rsr & PktRxOk) ) {
+           /* too many bytes received during this int, drop the rest of the
+            * packets */
+           lp->stats.rx_dropped++;
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 3 )
+               printk(KNOT_XIRC "%s: RX drop, too much done\n", dev->name);
+         #endif
+           PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */
+       }
+       else if( rsr & PktRxOk ) {
+           struct sk_buff *skb;
+
+           pktlen = GetWord(XIRCREG0_RBC);
+           bytes_rcvd += pktlen;
+
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 5 )
+               printk(KDBG_XIRC "rsr=%#02x packet_length=%u\n", rsr, pktlen );
+         #endif
+
+           skb = dev_alloc_skb(pktlen+3); /* 1 extra so we can use insw */
+           if( !skb ) {
+             #ifdef PCMCIA_DEBUG
+               if( pc_debug )
+                   printk(KNOT_XIRC "low memory, packet dropped (size=%u)\n",
+                                                                    pktlen );
+             #endif
+               lp->stats.rx_dropped++;
+           }
+           else { /* okay get the packet */
+               skb_reserve(skb, 2);
+               if( lp->silicon == 0  ) { /* work around a hardware bug */
+                   unsigned rhsa; /* receive start address */
+
+                   SelectPage(5);
+                   rhsa = GetWord(XIRCREG5_RHSA0);
+                   SelectPage(0);
+                   rhsa += 3; /* skip control infos */
+                   if( rhsa >= 0x8000 )
+                       rhsa = 0;
+                   if( rhsa + pktlen > 0x8000 ) {
+                       unsigned i;
+                       u_char *buf = skb_put(skb, pktlen);
+                       for(i=0; i < pktlen ; i++, rhsa++ ) {
+                           buf[i] = GetByte(XIRCREG_EDP);
+                           if( rhsa == 0x8000 ) {
+                               rhsa = 0;
+                               i--;
+                           }
+                       }
+                   } else {
+                       insw(ioaddr+XIRCREG_EDP,
+                               skb_put(skb, pktlen), (pktlen+1)>>1 );
+                   }
+               }
+             #if 0
+               else if( lp->mohawk ) {
+                   /* To use this 32 bit access we should use
+                    * a manual optimized loop
+                    * Also the words are swapped, we can get more
+                    * performance by using 32 bit access and swapping
+                    * the words in a register. Will need this for cardbus
+                    *
+                    * Note: don't forget to change the ALLOC_SKB to .. +3
+                    */
+                   unsigned i;
+                   u_long *p = skb_put(skb, pktlen);
+                   register u_long a;
+                   ioaddr_t edpreg = ioaddr+XIRCREG_EDP-2;
+                   for(i=0; i < len ; i += 4, p++ ) {
+                       a = inl(edpreg);
+                       __asm__("rorl $16,%0\n\t"
+                               :"=q" (a)
+                               : "0" (a));
+                       *p = a;
+                   }
+               }
+             #endif
+               else {
+                   insw(ioaddr+XIRCREG_EDP, skb_put(skb, pktlen),
+                           (pktlen+1)>>1 );
+               }
+               skb->protocol = eth_type_trans(skb, dev);
+               skb->dev = dev;
+               netif_rx(skb);
+               lp->stats.rx_packets++;
+               lp->stats.rx_bytes += pktlen;
+               if( !(rsr & PhyPkt) )
+                   lp->stats.multicast++;
+           }
+           PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */
+       }
+       else {
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 5 )
+               printk("rsr=%#02x\n", rsr );
+         #endif
+       }
+       if( rsr & PktTooLong ) {
+           lp->stats.rx_frame_errors++;
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 3 )
+               printk(KNOT_XIRC "%s: Packet too long\n", dev->name);
+         #endif
+       }
+       if( rsr & CRCErr ) {
+           lp->stats.rx_crc_errors++;
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 3 )
+               printk(KNOT_XIRC "%s: CRC error\n", dev->name);
+         #endif
+       }
+       if( rsr & AlignErr ) {
+           lp->stats.rx_fifo_errors++; /* okay ? */
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 3 )
+               printk(KNOT_XIRC "%s: Alignment error\n", dev->name);
+         #endif
+       }
+
+       /* get the new ethernet status */
+       eth_status = GetByte(XIRCREG_ESR);
+    }
+    if( rx_status & 0x10 ) { /* Receive overrun */
+       lp->stats.rx_over_errors++;
+       PutByte(XIRCREG_CR, ClearRxOvrun);
+      #ifdef PCMCIA_DEBUG
+       if( pc_debug > 3 )
+           printk(KDBG_XIRC "receive overrun cleared\n" );
+      #endif
+    }
+
+    /***** transmit section ******/
+    if( int_status & PktTxed ) {
+       unsigned n, nn;
+
+       n = lp->last_ptr_value;
+       nn = GetByte(XIRCREG0_PTR);
+       lp->last_ptr_value = nn;
+       if( nn < n ) /* rollover */
+           lp->stats.tx_packets += 256 - n;
+       else if( n == nn ) { /* happens sometimes - don't know why */
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug )
+               printk(KDBG_XIRC "PTR not changed?\n" );
+         #endif
+       }
+       else
+           lp->stats.tx_packets += lp->last_ptr_value - n;
+       dev->tbusy = 0;
+       mark_bh(NET_BH);  /* Inform upper layers. */
+    }
+    if( tx_status & 0x0002 ) { /* Execessive collissions */
+      #ifdef PCMCIA_DEBUG
+       if( pc_debug )
+           printk(KDBG_XIRC "tx restarted due to execssive collissions\n" );
+      #endif
+       PutByte(XIRCREG_CR, RestartTx );  /* restart transmitter process */
+    }
+    if( tx_status & 0x0040 )
+       lp->stats.tx_aborted_errors++;
+
+    /* recalculate our work chunk so that we limit the duration of this
+     * ISR to about 1/10 of a second.
+     * Calculate only if we received a reasonable amount of bytes.
+     */
+    if( bytes_rcvd > 1000 ) {
+       u_long duration = jiffies - start_ticks;
+
+       if( duration >= HZ/10 ) { /* if more than about 1/10 second */
+           maxrx_bytes = (bytes_rcvd * (HZ/10)) / duration;
+           if( maxrx_bytes < 2000 )
+               maxrx_bytes = 2000;
+           else if( maxrx_bytes > 22000 )
+               maxrx_bytes = 22000;
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 1)
+               printk(KDBG_XIRC "set maxrx=%u (rcvd=%u ticks=%lu)\n",
+                                 maxrx_bytes, bytes_rcvd, duration  );
+         #endif
+       }
+       else if( !duration && maxrx_bytes < 22000 ) { /* now much faster*/
+           maxrx_bytes += 2000;
+           if( maxrx_bytes > 22000 )
+               maxrx_bytes = 22000;
+         #ifdef PCMCIA_DEBUG
+           if( pc_debug > 1 )
+               printk(KDBG_XIRC "set maxrx=%u\n", maxrx_bytes );
+         #endif
+       }
+    }
+
+  leave:
+    if( lockup_hack ) {
+       if( int_status != 0xff && (int_status = GetByte(XIRCREG_ISR)) != 0 )
+           goto loop_entry;
+    }
+    SelectPage(saved_page);
+    dev->interrupt = 0;
+    PutByte(XIRCREG_CR, EnableIntr );  /* re-enable interrupts */
+    /* Instead of dropping packets during a receive, we could
+     * force an interrupt with this command:
+     *   PutByte(XIRCREG_CR, EnableIntr|ForceIntr );
+     */
+} /* xirc2ps_interrupt */
+
+/*====================================================================*/
+
+static int
+do_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    local_info_t *lp = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    int okay;
+    unsigned freespace;
+    unsigned pktlen = skb? skb->len : 0;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug>1 )
+       printk(KDBG_XIRC "do_start_xmit(skb=%p, dev=%p) len=%u\n",
+                                                   skb, dev, pktlen );
+  #endif
+
+    /* Transmitter timeout, serious problems */
+    if( dev->tbusy ) {
+       int tickssofar = jiffies - dev->trans_start;
+
+       if( lp->suspended ) {
+           dev_kfree_skb (skb);
+           dev->trans_start = jiffies;
+           lp->stats.tx_dropped++;
+           return 0;
+       }
+       if( tickssofar < TX_TIMEOUT )
+           return 1;
+
+       printk(KERN_NOTICE "%s: transmit timed out\n", dev->name );
+       lp->stats.tx_errors++;
+       /* reset the card */
+       do_reset(dev,1);
+       dev->trans_start = jiffies;
+       dev->tbusy = 0;
+    }
+
+    if( test_and_set_bit(0, (void*)&dev->tbusy ) ) {
+       printk(KWRN_XIRC "transmitter access conflict\n");
+       dev_kfree_skb (skb);
+       return 0;
+    }
+
+    /* adjust the packet length to min. required
+     * and hope that the buffer is large enough
+     * to provide some random data.
+     * fixme: For Mohawk we can change this by sending
+     * a larger packetlen than we actually have; the chip will
+     * pad this in his buffer with random bytes
+     */
+    if( pktlen < ETH_ZLEN )
+       pktlen = ETH_ZLEN;
+
+    SelectPage(0);
+    PutWord(XIRCREG0_TRS, (u_short)pktlen+2 );
+    freespace = GetWord(XIRCREG0_TSO);
+    okay = freespace & 0x8000;
+    freespace &= 0x7fff;
+    /* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */
+    okay = pktlen +2 < freespace;
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug > 2  + ( okay ? 2 : 0) )
+       printk(KERN_DEBUG "%s: avail. tx space=%u%s\n", dev->name, freespace,
+                               okay? " (okay)":" (not enough)" );
+  #endif
+    if( !okay ) { /* not enough space */
+       dev->tbusy = 1;
+       return 1;  /* upper layer may decide to requeue this packet */
+    }
+    /* send the packet */
+    PutWord(XIRCREG_EDP, (u_short)pktlen );
+    outsw(ioaddr+XIRCREG_EDP, skb->data, pktlen>>1 );
+    if( pktlen & 1 )
+       PutByte(XIRCREG_EDP, skb->data[pktlen-1] );
+
+    if( lp->mohawk )
+       PutByte(XIRCREG_CR, TransmitPacket|EnableIntr );
+
+    dev_kfree_skb (skb);
+    dev->trans_start = jiffies;
+    dev->tbusy = 0;
+    lp->stats.tx_bytes += pktlen;
+    return 0;
+}
+
+static struct enet_statistics *
+do_get_stats(struct net_device *dev)
+{
+    local_info_t *lp = dev->priv;
+
+    /* lp->stats.rx_missed_errors = GetByte(?) */
+    return &lp->stats;
+}
+
+/****************
+ * Set all addresses: This first one is the individual address,
+ * the next 9 addresses are taken from the multicast list and
+ * the rest is filled with the individual address.
+ */
+static void
+set_addresses(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    local_info_t *lp = dev->priv;
+    struct dev_mc_list *dmi = dev->mc_list;
+    char *addr;
+    int i,j,k,n;
+
+    SelectPage(k=0x50);
+    for(i=0,j=8,n=0; ; i++, j++) {
+       if( i > 5 ) {
+           if( ++n > 9 )
+               break;
+           i = 0;
+       }
+       if( j > 15 ) {
+           j = 8;
+           k++;
+           SelectPage(k);
+       }
+
+       if( n && n <= dev->mc_count && dmi ) {
+           addr = dmi->dmi_addr;
+           dmi = dmi->next;
+       }
+       else
+           addr = dev->dev_addr;
+
+       if( lp->mohawk )
+           PutByte( j, addr[5-i] );
+       else
+           PutByte( j, addr[i] );
+    }
+    SelectPage(0);
+}
+
+/****************
+ * Set or clear the multicast filter for this adaptor.
+ * We can filter up to 9 addresses, if more are requested we set
+ * multicast promiscuous mode.
+ */
+
+static void
+set_multicast_list(struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+
+    SelectPage(0x42);
+    if( dev->flags & IFF_PROMISC ) { /* snoop */
+       PutByte(XIRCREG42_SWC1, 0x06); /* set MPE and PME */
+    }
+    else if( dev->mc_count > 9 || (dev->flags & IFF_ALLMULTI) ) {
+       PutByte(XIRCREG42_SWC1, 0x06); /* set MPE */
+    }
+    else if( dev->mc_count ) { /* the chip can filter 9 addresses perfectly */
+       PutByte(XIRCREG42_SWC1, 0x00);
+       SelectPage(0x40);
+       PutByte(XIRCREG40_CMD0, Offline );
+       set_addresses(dev);
+       SelectPage(0x40);
+       PutByte(XIRCREG40_CMD0, EnableRecv | Online );
+    }
+    else { /* standard usage */
+       PutByte(XIRCREG42_SWC1, 0x00);
+    }
+    SelectPage(0);
+}
+
+/****************
+ * We never need to do anything when a IIps device is "initialized"
+ * by the net software, because we only register already-found cards.
+ */
+static int
+do_init(struct net_device *dev)
+{
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "do_init(%p)\n", dev );
+  #endif
+    return 0;
+}
+
+static int
+do_config(struct net_device *dev, struct ifmap *map)
+{
+    local_info_t *local = dev->priv;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "do_config(%p)\n", dev );
+  #endif
+
+    if( map->port != 255 && map->port != dev->if_port ) {
+       if( map->port <= 4 ) {
+           if( !map->port ) {
+               local->probe_port = 1;
+               dev->if_port = 1;
+           }
+           else {
+               local->probe_port = 0;
+               dev->if_port = map->port;
+           }
+           printk(KERN_INFO "%s: switching to %s port\n",
+                             dev->name, if_names[dev->if_port]);
+           do_reset(dev,1);  /* not the fine way :-) */
+       }
+       else
+           return -EINVAL;
+    }
+  #ifdef PCMCIA_DEBUG
+    else if( map->port == dev->if_port && local->mohawk ) {
+       /* kludge to print the mii regsiters */
+       mii_dump(dev);
+    }
+  #endif
+    return 0;
+}
+
+/****************
+ * Open the driver
+ */
+static int
+do_open(struct net_device *dev)
+{
+    local_info_t *lp = dev->priv;
+    dev_link_t *link;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "do_open(%p)\n", dev );
+  #endif
+
+    /* Check that the PCMCIA card is still here. */
+    for( link = dev_list; link; link = link->next )
+       if( link->priv == dev )
+           break;
+    /* Physical device present signature. */
+    if( !DEV_OK(link) )
+       return -ENODEV;
+
+    /* okay */
+    link->open++;
+    MOD_INC_USE_COUNT;
+
+    dev->interrupt = 0; dev->tbusy = 0; dev->start = 1;
+    lp->suspended = 0;
+    do_reset(dev,1);
+
+    return 0;
+}
+
+static int
+do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    local_info_t *local = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    u16 *data = (u16 *)&rq->ifr_data;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug > 1)
+       printk(KERN_DEBUG "%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n",
+                          dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+                          data[0], data[1], data[2], data[3] );
+  #endif
+
+    if( !local->mohawk )
+       return -EOPNOTSUPP;
+
+    switch( cmd ) {
+      case SIOCDEVPRIVATE:     /* Get the address of the PHY in use. */
+       data[0] = 0;            /* we have only this address */
+       /* fall trough */
+      case SIOCDEVPRIVATE+1:   /* Read the specified MII register. */
+       data[3] = mii_rd( ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+       break;
+      case SIOCDEVPRIVATE+2:   /* Write the specified MII register */
+       if( !suser() )
+           return -EPERM;
+       mii_wr(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2], 16);
+       break;
+      default:
+       return -EOPNOTSUPP;
+    }
+    return 0;
+}
+
+static void
+hardreset(struct net_device *dev)
+{
+    local_info_t *local = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+
+    SelectPage(4);
+    udelay(1);
+    PutByte(XIRCREG4_GPR1, 0);      /* clear bit 0: power down */
+    busy_loop(HZ/25);               /* wait 40 msec */
+    if( local->mohawk )
+       PutByte(XIRCREG4_GPR1, 1);       /* set bit 0: power up */
+    else
+       PutByte(XIRCREG4_GPR1, 1 | 4);   /* set bit 0: power up, bit 2: AIC */
+    busy_loop(HZ/50);               /* wait 20 msec */
+}
+
+static void
+do_reset(struct net_device *dev, int full)
+{
+    local_info_t *local = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned value;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KERN_DEBUG "%s: do_reset(%p,%d)\n",
+                                         dev? dev->name:"eth?", dev, full );
+  #endif
+
+    hardreset(dev);
+    PutByte(XIRCREG_CR, SoftReset ); /* set */
+    busy_loop(HZ/50);               /* wait 20 msec */
+    PutByte(XIRCREG_CR, 0 );        /* clear */
+    busy_loop(HZ/25);               /* wait 40 msec */
+    if( local->mohawk ) {
+       SelectPage(4);
+       /* set pin GP1 and GP2 to output  (0x0c)
+        * set GP1 to low to power up the ML6692 (0x00)
+        * set GP2 to high to power up the 10Mhz chip  (0x02)
+        */
+       PutByte(XIRCREG4_GPR0, 0x0e);
+    }
+
+    /* give the circuits some time to power up */
+    busy_loop(HZ/2);           /* about 500ms */
+
+    local->last_ptr_value = 0;
+    local->silicon = local->mohawk ? (GetByte(XIRCREG4_BOV) & 0x70) >> 4
+                                  : (GetByte(XIRCREG4_BOV) & 0x30) >> 4;
+
+    if( local->probe_port ) {
+       if( !local->mohawk ) {
+           SelectPage(4);
+           PutByte(XIRCREG4_GPR0, 4);
+           local->probe_port = 0;
+       }
+    }
+    else if( dev->if_port == 2 ) { /* enable 10Base2 */
+       SelectPage(0x42);
+       PutByte(XIRCREG42_SWC1, 0xC0);
+    }
+    else { /* enable 10BaseT */
+       SelectPage(0x42);
+       PutByte(XIRCREG42_SWC1, 0x80);
+    }
+    busy_loop(HZ/25);               /* wait 40 msec to let it complete */
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug) {
+       SelectPage(0);
+       value = GetByte(XIRCREG_ESR);    /* read the ESR */
+       printk(KERN_DEBUG "%s: ESR is: %#02x\n", dev->name, value);
+    }
+  #endif
+
+    /* setup the ECR */
+    SelectPage(1);
+    PutByte(XIRCREG1_IMR0, 0xff ); /* allow all ints */
+    PutByte(XIRCREG1_IMR1, 1   ); /* and Set TxUnderrunDetect */
+    value = GetByte(XIRCREG1_ECR);
+  #if 0
+    if( local->mohawk )
+       value |= DisableLinkPulse;
+    PutByte(XIRCREG1_ECR, value);
+  #endif
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KERN_DEBUG "%s: ECR is: %#02x\n", dev->name, value);
+  #endif
+
+    SelectPage(0x42);
+    PutByte(XIRCREG42_SWC0, 0x20); /* disable source insertion */
+
+    if( local->silicon != 1 ) {
+       /* set the local memory dividing line.
+        * The comments in the sample code say that this is only
+        * settable with the scipper version 2 which is revision 0.
+        * Always for CE3 cards
+        */
+       SelectPage(2);
+       PutWord(XIRCREG2_RBS, 0x2000 );
+    }
+
+    if( full )
+       set_addresses(dev);
+
+    /* Hardware workaround:
+     * The receive byte pointer after reset is off by 1 so we need
+     * to move the offset pointer back to 0.
+     */
+    SelectPage(0);
+    PutWord(XIRCREG0_DO, 0x2000 ); /* change offset command, off=0 */
+
+    /* setup MAC IMRs and clear status registers */
+    SelectPage(0x40);               /* Bit 7 ... bit 0 */
+    PutByte(XIRCREG40_RMASK0, 0xff); /* ROK, RAB, rsv, RO, CRC, AE, PTL, MP */
+    PutByte(XIRCREG40_TMASK0, 0xff); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+    PutByte(XIRCREG40_TMASK1, 0xb0); /* rsv, rsv, PTD, EXT, rsv,rsv,rsv, rsv*/
+    PutByte(XIRCREG40_RXST0,  0x00); /* ROK, RAB, REN, RO, CRC, AE, PTL, MP */
+    PutByte(XIRCREG40_TXST0,  0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+    PutByte(XIRCREG40_TXST1,  0x00); /* TEN, rsv, PTD, EXT, retry_counter:4  */
+
+    if( full && local->mohawk && init_mii(dev) ) {
+       if( dev->if_port == 4 || local->dingo ) { /* and use it */
+           SelectPage(2);
+           value = GetByte(XIRCREG2_MSR);
+           value |= 0x08;     /* Select MII */
+           PutByte(XIRCREG2_MSR, value);
+           busy_loop(HZ/50);   /* wait 20 msec */
+         #ifdef PCMCIA_DEBUG
+           if(pc_debug)
+               printk(KERN_DEBUG "%s: MII selected\n", dev->name);
+         #endif
+       }
+       else {
+           SelectPage(2);
+           PutByte(XIRCREG2_MSR, GetByte(XIRCREG2_MSR) | 0x08);
+           busy_loop(HZ/50);
+         #ifdef PCMCIA_DEBUG
+           if(pc_debug)
+               printk(KERN_DEBUG "%s: MII detected; using 10mbs\n",
+                                                               dev->name);
+         #endif
+           SelectPage(0x42);
+           if( dev->if_port == 2 ) /* enable 10Base2 */
+               PutByte(XIRCREG42_SWC1, 0xC0);
+           else  /* enable 10BaseT */
+               PutByte(XIRCREG42_SWC1, 0x80);
+           busy_loop(HZ/25);   /* wait 40 msec to let it complete */
+       }
+    }
+    else {  /* No MII */
+       SelectPage(0);
+       value = GetByte(XIRCREG_ESR);    /* read the ESR */
+       dev->if_port = (value & MediaSelect) ? 1 : 2;
+    }
+
+    /* configure the LEDs */
+    SelectPage(2);
+    if( dev->if_port == 1 || dev->if_port == 4 ) /* TP: Link and Activity */
+       PutByte(XIRCREG2_LED, 0x3b );
+    else                             /* Coax: Not-Collision and Activity */
+       PutByte(XIRCREG2_LED, 0x3a );
+
+    if (local->dingo)
+       PutByte( 0x0b, 0x04 ); /* 100 Mbit LED */
+
+    /* enable receiver and put the mac online */
+    if( full ) {
+       SelectPage(0x40);
+       PutByte(XIRCREG40_CMD0, EnableRecv | Online );
+    }
+
+    /* setup Ethernet IMR and enable interrupts */
+    SelectPage(1);
+    PutByte(XIRCREG1_IMR0, 0xff );
+    udelay(1);
+    SelectPage(0);
+    PutByte(XIRCREG_CR, EnableIntr );
+    if( local->modem && !local->dingo ) { /* do some magic */
+       if( !(GetByte( 0x10 ) & 0x01 ) )
+           PutByte( 0x10, 0x11 ); /* unmask master-int bit */
+    }
+
+    if( full )
+       printk(KERN_INFO "%s: media %s, silicon revision %d\n", dev->name,
+                                     if_names[dev->if_port], local->silicon);
+    /* We should switch back to page 0 to avoid a bug in revision 0
+     * where regs with offset below 8 can't be read after an access
+     * to the MAC registers */
+    SelectPage(0);
+}
+
+/****************
+ * Initialize the Media-Independent-Interface
+ * Returns: True if we have a good MII
+ */
+static int
+init_mii(struct net_device *dev)
+{
+    local_info_t *local = dev->priv;
+    ioaddr_t ioaddr = dev->base_addr;
+    unsigned control, status, linkpartner;
+    int i;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug>1) {
+       mii_dump(dev);
+    }
+  #endif
+
+    status = mii_rd(ioaddr,  0, 1 );
+    if( (status & 0xff00) != 0x7800 )
+       return 0; /* No MII */
+
+    if( local->probe_port )
+       control = 0x1000; /* auto neg */
+    else if( dev->if_port == 4 )
+       control = 0x2000; /* no auto neg, 100mbs mode */
+    else
+       control = 0x0000; /* no auto neg, 10mbs mode */
+    mii_wr(ioaddr,  0, 0, control, 16 );
+    udelay(100);
+    control = mii_rd(ioaddr, 0, 0 );
+
+    if( control & 0x0400 ) {
+       printk(KERN_NOTICE "%s can't take PHY out of isolation mode\n",
+                                                               dev->name);
+       local->probe_port = 0;
+       return 0;
+    }
+
+    if( local->probe_port ) {
+       /* according to the DP83840A specs the auto negotation process
+        * may take up to 3.5 sec, so we use this also for our ML6692
+        * Fixme: Better to use a timer here!
+        */
+       for(i=0; i < 35; i++ ) {
+           busy_loop(HZ/10);    /* wait 100 msec */
+           status = mii_rd(ioaddr,  0, 1 );
+           if( (status & 0x0020) && (status & 0x0004) )
+               break;
+       }
+
+       if( !(status & 0x0020) ) {
+           printk(KERN_NOTICE "%s: auto negotation failed;"
+                              " using 10mbs\n", dev->name );
+           control = 0x0000;
+           mii_wr(ioaddr,  0, 0, control, 16 );
+           udelay(100);
+           SelectPage(0);
+           dev->if_port = (GetByte(XIRCREG_ESR) & MediaSelect) ? 1 : 2;
+       }
+       else {
+           linkpartner = mii_rd(ioaddr, 0, 5 );
+           printk(KERN_INFO "%s: MII link partner: %04x\n", dev->name,
+                                                       linkpartner );
+           if( linkpartner & 0x0080 ) { /* 100BaseTx capability */
+               dev->if_port = 4;
+           }
+           else
+               dev->if_port = 1;
+       }
+       local->probe_port = 0;
+    }
+
+  #ifdef PCMCIA_DEBUG
+    if( pc_debug )
+       mii_dump(dev);
+  #endif
+
+    return 1;
+}
+
+static void
+do_powerdown(struct net_device *dev)
+{
+
+    ioaddr_t ioaddr = dev->base_addr;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "do_powerdown(%p)\n", dev );
+  #endif
+
+    SelectPage(4);
+    PutByte(XIRCREG4_GPR1, 0);      /* clear bit 0: power down */
+    SelectPage(0);
+}
+
+static int
+do_stop( struct net_device *dev)
+{
+    ioaddr_t ioaddr = dev->base_addr;
+    dev_link_t *link;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "do_stop(%p)\n", dev );
+  #endif
+
+    for(link = dev_list; link; link = link->next)
+       if(link->priv == dev)
+           break;
+    if( !link )
+       return -ENODEV;
+
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "shutting down\n");
+  #endif
+    dev->tbusy = 1;
+    dev->start = 0;
+
+    SelectPage(0);
+    PutByte(XIRCREG_CR, 0 );  /* disable interrupts */
+    SelectPage(0x01);
+    PutByte(XIRCREG1_IMR0, 0x00 ); /* forbid all ints */
+    SelectPage(4);
+    PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */
+    SelectPage(0);
+
+    link->open--; dev->start = 0;
+    if (link->state & DEV_STALE_CONFIG) {
+       link->release.expires = jiffies + HZ/20;
+       link->state |= DEV_RELEASE_PENDING;
+       add_timer(&link->release);
+    }
+
+    MOD_DEC_USE_COUNT;
+
+    return 0;
+}
+
+static int __init
+init_xirc2ps_cs(void)
+{
+    servinfo_t serv;
+
+    printk(KERN_INFO "%s\n", version);
+    if( card_type )
+       printk(KINF_XIRC "option card_type is obsolete\n");
+    if( lockup_hack )
+       printk(KINF_XIRC "lockup hack is enabled\n");
+    CardServices(GetCardServicesInfo, &serv);
+    if( serv.Revision != CS_RELEASE_CODE ) {
+       printk(KNOT_XIRC "Card Services release does not match!\n");
+       return -1;
+    }
+  #ifdef PCMCIA_DEBUG
+    if( pc_debug )
+       printk(KDBG_XIRC "pc_debug=%d\n", pc_debug);
+  #endif
+    register_pccard_driver(&dev_info, &xirc2ps_attach, &xirc2ps_detach);
+    return 0;
+}
+
+static void __exit
+exit_xirc2ps_cs(void)
+{
+  #ifdef PCMCIA_DEBUG
+    if(pc_debug)
+       printk(KDBG_XIRC "unloading\n");
+  #endif
+    unregister_pccard_driver(&dev_info);
+    while( dev_list ) {
+       if( dev_list->state & DEV_CONFIG )
+           xirc2ps_release( (u_long)dev_list );
+       if( dev_list )  /* xirc2ps_release() might already have detached... */
+           xirc2ps_detach( dev_list );
+    }
+}
+
+module_init(init_xirc2ps_cs);
+module_exit(exit_xirc2ps_cs);
+
index 2883046c133235902648da5e642b2d0e99e35fb8..aaedbf74a6b097d11ea2f559ea448c665669aac5 100644 (file)
@@ -2,13 +2,15 @@
 # PCMCIA bus subsystem configuration
 #
 mainmenu_option next_comment
-comment 'PCMCIA/Cardbus support'
+comment 'PCMCIA/CardBus support'
 
-tristate 'PCMCIA/Cardbus support' CONFIG_PCMCIA
+tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
 if [ "$CONFIG_PCMCIA" != "n" ]; then
    if [ "$CONFIG_PCI" != "n" ]; then
       bool '  CardBus support' CONFIG_CARDBUS
    fi
+   bool '  i82365/Yenta compatible bridge support' CONFIG_I82365
+   bool '  Databook TCIC host bridge support' CONFIG_TCIC
 fi
 
 endmenu
index 71750544d22862415cd920f462681d9a3a38eed5..703a7c6c8f022d17fc076e08d87d3708b2ddb78d 100644 (file)
@@ -15,20 +15,34 @@ ALL_SUB_DIRS := $(SUB_DIRS)
 MOD_LIST_NAME := PCMCIA_MODULES
 
 ifeq ($(CONFIG_PCMCIA),y)
-  O_OBJS   := i82365.o tcic.o cistpl.o rsrc_mgr.o bulkmem.o
+  O_OBJS   := cistpl.o rsrc_mgr.o bulkmem.o
   OX_OBJS  := ds.o cs.o
   O_TARGET := pcmcia.o
+  ifeq ($(CONFIG_I82365),y)
+    O_OBJS += i82365.o
+  endif
+  ifeq ($(CONFIG_TCIC),y)
+    O_OBJS += tcic.o
+  endif
   ifeq ($(CONFIG_CARDBUS),y)
     O_OBJS += cardbus.o
+    OX_OBJS += cb_enabler.o
   endif
 else
   ifeq ($(CONFIG_PCMCIA),m)
-     M_OBJS   := i82365.o tcic.o pcmcia_core.o 
+     M_OBJS   := pcmcia_core.o 
      MX_OBJS  := ds.o
      MIX_OBJS  := cs.o
      CORE_OBJS := cistpl.o rsrc_mgr.o bulkmem.o cs.o
+    ifeq ($(CONFIG_I82365),y)
+      M_OBJS += i82365.o
+    endif
+    ifeq ($(CONFIG_TCIC),y)
+      M_OBJS += tcic.o
+    endif
      ifeq ($(CONFIG_CARDBUS),y)
        CORE_OBJS += cardbus.o
+       MX_OBJS += cb_enabler.o
      endif
   endif
 endif
index de8b76da328dfebee0a04dd1293f56328c59cdd9..490182e0093d830f4f16a5793b4ec8d4b9f0bf33 100644 (file)
@@ -2,7 +2,7 @@
   
     Cardbus device configuration
     
-    cardbus.c 1.59 1999/09/15 15:32:19
+    cardbus.c 1.61 1999/10/20 22:36:57
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -325,9 +325,9 @@ int cb_alloc(socket_info_t *s)
        pci_readl(bus, i, PCI_CLASS_REVISION, &c[i].dev.class);
        c[i].dev.class >>= 8;
        c[i].dev.hdr_type = hdr;
-#ifdef CONFIG_PROC_FS  
+#ifdef CONFIG_PROC_FS
        pci_proc_attach_device(&c[i].dev);
-#endif 
+#endif
     }
     
     return CS_SUCCESS;
@@ -344,9 +344,9 @@ void cb_free(socket_info_t *s)
            if (*p == &c[0].dev) break;
        for (q = *p; q; q = q->next) {
            if (q->bus != (*p)->bus) break;
-#ifdef CONFIG_PROC_FS      
+#ifdef CONFIG_PROC_FS
            pci_proc_detach_device(q);
-#endif     
+#endif
        }
        if (*p) *p = q;
        s->cap.cb_bus->devices = NULL;
@@ -496,7 +496,8 @@ int cb_config(socket_info_t *s)
            s->irq.AssignedIRQ = irq;
        }
     }
-    c[0].dev.irq = irq;
+    for (i = 0; i < fn; i++)
+       c[i].dev.irq = irq;
     
     return CS_SUCCESS;
 
index b0e75ceecade5ff980212647e5a808cb0ed87646..00cf824b31ba8e4e4e0a4e670defa7d00c7bfe14 100644 (file)
@@ -2,7 +2,7 @@
 
     Cardbus device enabler
 
-    cb_enabler.c 1.23 1999/09/15 15:32:19
+    cb_enabler.c 1.24 1999/10/20 00:19:09
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -58,7 +58,7 @@ static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"cb_enabler.c 1.23 1999/09/15 15:32:19 (David Hinds)";
+"cb_enabler.c 1.24 1999/10/20 00:19:09 (David Hinds)";
 #else
 #define DEBUG(n, args...) do { } while (0)
 #endif
@@ -372,6 +372,9 @@ void unregister_driver(struct driver_operations *ops)
 
 /*====================================================================*/
 
+EXPORT_SYMBOL(register_driver);
+EXPORT_SYMBOL(unregister_driver);
+
 static int __init init_cb_enabler(void)
 {
     servinfo_t serv;
index 470514c556afb514e839e3b73e10d93978fd98d5..f7fda7d8da1985f95ec738c42c038b87132fb625 100644 (file)
@@ -2,7 +2,7 @@
 
     PCMCIA Card Services -- core services
 
-    cs.c 1.228 1999/09/15 15:32:19
+    cs.c 1.232 1999/10/20 22:17:24
     
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -70,7 +70,7 @@ static int handle_apm_event(apm_event_t event);
 int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 static const char *version =
-"cs.c 1.228 1999/09/15 15:32:19 (David Hinds)";
+"cs.c 1.232 1999/10/20 22:17:24 (David Hinds)";
 #endif
 
 static const char *release = "Linux PCMCIA Card Services " CS_RELEASE;
@@ -353,41 +353,40 @@ void unregister_ss_entry(ss_entry_t ss_entry)
     socket_info_t *s = NULL;
     client_t *client;
 
+#ifdef CONFIG_PROC_FS
+    for (i = 0; i < sockets; i++) {
+       s = socket_table[i];
+       if (s->ss_entry != ss_entry) continue;
+       if (proc_pccard) {
+           char name[3];
+           sprintf(name, "%02d", i);
+#ifdef PCMCIA_DEBUG
+           remove_proc_entry("clients", s->proc);
+#endif
+       }
+    }
+#endif
+
     for (;;) {
        for (i = 0; i < sockets; i++) {
            s = socket_table[i];
            if (s->ss_entry == ss_entry) break;
        }
-       if (i == sockets) {
+       if (i == sockets)
            break;
-       } else {
-#ifdef CONFIG_PROC_FS
-           if (proc_pccard) {
-               char name[3];
-               sprintf(name, "%02d", i);
-#ifdef PCMCIA_DEBUG
-               remove_proc_entry("clients", s->proc);
-#endif
-               remove_proc_entry(name, proc_pccard);
-           }
-#endif
-           while (s->clients) {
-               client = s->clients;
-               s->clients = s->clients->next;
-               kfree(client);
-           }
-           init_socket(s);
-           release_cis_mem(s);
-#ifdef CONFIG_CARDBUS
-           cb_release_cis_mem(s);
-#endif
-           s->ss_entry = NULL;
-           kfree(s);
-           socket_table[i] = NULL;
-           for (j = i; j < sockets-1; j++)
-               socket_table[j] = socket_table[j+1];
-           sockets--;
+       shutdown_socket(i);
+       release_cis_mem(s);
+       while (s->clients) {
+           client = s->clients;
+           s->clients = s->clients->next;
+           kfree(client);
        }
+       s->ss_entry = NULL;
+       kfree(s);
+       socket_table[i] = NULL;
+       for (j = i; j < sockets-1; j++)
+           socket_table[j] = socket_table[j+1];
+       sockets--;
     }
     
 } /* unregister_ss_entry */
@@ -1808,7 +1807,7 @@ static int request_window(client_handle_t *handle, win_req_t *req)
 {
     socket_info_t *s;
     window_t *win;
-    int w;
+    int w, align;
     
     if (CHECK_HANDLE(*handle))
        return CS_BAD_HANDLE;
@@ -1835,9 +1834,10 @@ static int request_window(client_handle_t *handle, win_req_t *req)
     win->sock = s;
     win->base = req->Base;
     win->size = req->Size;
+    align = ((s->cap.features & SS_CAP_MEM_ALIGN) ||
+            (req->Attributes & WIN_STRICT_ALIGN));
     if (find_mem_region(&win->base, win->size, (*handle)->dev_info,
-                       ((s->cap.features & SS_CAP_MEM_ALIGN) ?
-                        req->Size : s->cap.map_size),
+                       (align ? req->Size : s->cap.map_size),
                        (req->Attributes & WIN_MAP_BELOW_1MB) ||
                        !(s->cap.features & SS_CAP_PAGE_REGS)))
        return CS_IN_USE;
index 9e80943e4d9521a4c559dea99f32fa207ce6bbf3..ed7bd3c30e882c40db4b0b169b67592b29f24550 100644 (file)
@@ -3,7 +3,7 @@
     Device driver for Intel 82365 and compatible PC Card controllers,
     and Yenta-compatible PCI-to-CardBus controllers.
 
-    i82365.c 1.254 1999/09/15 15:32:19
+    i82365.c 1.260 1999/10/21 00:56:07
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -76,7 +76,7 @@ static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static const char *version =
-"i82365.c 1.254 1999/09/15 15:32:19 (David Hinds)";
+"i82365.c 1.260 1999/10/21 00:56:07 (David Hinds)";
 #else
 #define DEBUG(n, args...) do { } while (0)
 #endif
@@ -189,7 +189,7 @@ MODULE_PARM(cb_write_post, "i");
 #ifdef CONFIG_ISA
 #ifdef CONFIG_PCI
 /* PCI card status change interrupts? */
-static int pci_csc = 0;
+static int pci_csc = 1;
 /* PCI IO card functional interrupts? */
 static int pci_int = 0;
 MODULE_PARM(pci_csc, "i");
@@ -240,7 +240,7 @@ typedef struct topic_state_t {
 typedef struct socket_info_t {
     u_short            type, flags;
     socket_cap_t       cap;
-    u_short            ioaddr;
+    ioaddr_t           ioaddr;
     u_short            psock;
     u_char             cs_irq, intr;
     void               (*handler)(void *info, u_int events);
@@ -278,18 +278,18 @@ static socket_info_t socket[8] = {
 /* Default ISA interrupt mask */
 #define I365_MASK      0xdeb8  /* irq 15,14,12,11,10,9,7,5,4,3 */
 
-static void pcic_interrupt_wrapper(u_long);
-static void pcic_interrupt(int irq, void *dev,
-                                   struct pt_regs *regs);
-static int pcic_service(u_int sock, u_int cmd, void *arg);
-#ifdef CONFIG_PROC_FS
-static void pcic_proc_remove(u_short sock);
-#endif
-
 #ifdef CONFIG_ISA
 static int grab_irq;
 static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED;
+#define ISA_LOCK(n, f) \
+    if (!(socket[n].flags & IS_CARDBUS)) spin_lock_irqsave(&isa_lock, f)
+#define ISA_UNLOCK(n, f) \
+    if (!(socket[n].flags & IS_CARDBUS)) spin_unlock_irqrestore(&isa_lock, f)
+#else
+#define ISA_LOCK(n, f) do { } while (0)
+#define ISA_UNLOCK(n, f) do { } while (0)
 #endif
+
 static struct timer_list poll_timer;
 
 /*====================================================================*/
@@ -322,7 +322,7 @@ typedef enum pcic_id {
 #ifdef CONFIG_PCI
     IS_PD6729, IS_PD6730, IS_OZ6729, IS_OZ6730,
     IS_I82092AA, IS_OM82C092G,
-    IS_PD6832, IS_OZ6832, IS_OZ6836,
+    IS_PD6832, IS_OZ6832, IS_OZ6836, IS_OZ6812,
     IS_RL5C465, IS_RL5C466, IS_RL5C475, IS_RL5C476, IS_RL5C478,
     IS_SMC34C90,
     IS_TI1130, IS_TI1131, IS_TI1250A, IS_TI1220, IS_TI1221, IS_TI1210,
@@ -388,6 +388,8 @@ static pcic_t pcic[] = {
       PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6832 },
     { "O2Micro OZ6836/OZ6860", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
       PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6836 },
+    { "O2Micro OZ6812", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
+      PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6812 },
     { "Ricoh RL5C465", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
       PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C465 },
     { "Ricoh RL5C466", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
@@ -441,8 +443,6 @@ static pcic_t pcic[] = {
 
 /* Some PCI shortcuts */
 
-#ifdef CONFIG_PCI
-
 #define pci_readb              pcibios_read_config_byte
 #define pci_writeb             pcibios_write_config_byte
 #define pci_readw              pcibios_read_config_word
@@ -457,7 +457,6 @@ static pcic_t pcic[] = {
 
 static void cb_get_power(u_short sock, socket_state_t *state);
 static void cb_set_power(u_short sock, socket_state_t *state);
-#endif
 
 /*====================================================================*/
 
@@ -469,7 +468,7 @@ static u_char i365_get(u_short sock, u_short reg)
     else
 #endif
     {
-       u_short port = socket[sock].ioaddr;
+       ioaddr_t port = socket[sock].ioaddr;
        u_char val;
        reg = I365_REG(socket[sock].psock, reg);
        outb(reg, port); val = inb(port+1);
@@ -485,7 +484,7 @@ static void i365_set(u_short sock, u_short reg, u_char data)
     else
 #endif
     {
-       u_short port = socket[sock].ioaddr;
+       ioaddr_t port = socket[sock].ioaddr;
        u_char val = I365_REG(socket[sock].psock, reg);
        outb(val, port); outb(data, port+1);
     }
@@ -954,6 +953,8 @@ static u_int __init o2micro_set_opts(u_short s, char *buf)
        p->mode_e &= ~O2_MODE_E_MHPG_DMA;
        p->mhpg |= O2_MHPG_CINT_ENA | O2_MHPG_CSC_ENA;
        p->mhpg &= ~O2_MHPG_CHANNEL;
+       if (t->revision == 0x34)
+           p->mode_c = 0x20;
     } else {
        if (p->mode_b & O2_MODE_B_IRQ15_RI) mask &= ~0x8000;
     }
@@ -1125,7 +1126,7 @@ static void __init cb_set_opts(u_short s, char *buf)
     
 ======================================================================*/
 
-static void get_host_state(u_short s)
+static void get_bridge_state(u_short s)
 {
     socket_info_t *t = &socket[s];
     if (t->flags & IS_CIRRUS)
@@ -1148,7 +1149,7 @@ static void get_host_state(u_short s)
 #endif
 }
 
-static void set_host_state(u_short s)
+static void set_bridge_state(u_short s)
 {
     socket_info_t *t = &socket[s];
 #ifdef CONFIG_PCI
@@ -1178,7 +1179,7 @@ static void set_host_state(u_short s)
 #endif
 }
 
-static u_int __init set_host_opts(u_short s, u_short ns)
+static u_int __init set_bridge_opts(u_short s, u_short ns)
 {
     u_short i;
     u_int m = 0xffff;
@@ -1190,7 +1191,7 @@ static u_int __init set_host_opts(u_short s, u_short ns)
            continue;
        }
        buf[0] = '\0';
-       get_host_state(i);
+       get_bridge_state(i);
        if (socket[i].flags & IS_CIRRUS)
            m = cirrus_set_opts(i, buf);
 #ifdef CONFIG_ISA
@@ -1209,7 +1210,7 @@ static u_int __init set_host_opts(u_short s, u_short ns)
        if (socket[i].flags & IS_CARDBUS)
            cb_set_opts(i, buf+strlen(buf));
 #endif
-       set_host_state(i);
+       set_bridge_state(i);
        printk(KERN_INFO "    host opts [%d]:%s\n", i,
               (*buf) ? buf : " none");
     }
@@ -1255,7 +1256,7 @@ static u_int __init test_irq(u_short sock, int irq, int pci)
     if (request_irq(irq, irq_count, (pci?SA_SHIRQ:0), "scan", NULL) != 0)
        return 1;
     irq_hits = 0; irq_sock = sock;
-    current->state = TASK_INTERRUPTIBLE;
+    __set_current_state(TASK_UNINTERRUPTIBLE);
     schedule_timeout(HZ/100);
     if (irq_hits) {
        free_irq(irq, NULL);
@@ -1310,7 +1311,7 @@ static u_int __init isa_scan(u_short sock, u_int mask0)
        (cb_set_irq_mode(sock, 0, 0) == 0))
 #endif
     if (do_scan) {
-       set_host_state(sock);
+       set_bridge_state(sock);
        i365_set(sock, I365_CSCINT, 0);
        for (i = 0; i < 16; i++)
            if ((mask0 & (1 << i)) && (test_irq(sock, i, 0) == 0))
@@ -1351,7 +1352,7 @@ static void __init pci_scan(u_short sock)
     u_int i;
 
     cb_set_irq_mode(sock, 1, 0);
-    set_host_state(sock);
+    set_bridge_state(sock);
     i365_set(sock, I365_CSCINT, 0);
     /* Only probe irq's 9..11, to be conservative */
     for (i = 9; i < 12; i++) {
@@ -1371,7 +1372,7 @@ static void __init pci_scan(u_short sock)
 static int to_cycles(int ns)
 {
     return ns/cycle_time;
-} /* speed_convert */
+}
 
 static int to_ns(int cycles)
 {
@@ -1517,7 +1518,7 @@ static void __init add_pcic(int ns, int type)
        for (i = mask = 0; i < 16; i++)
            mask |= (1<<irq_list[i]);
 #endif
-    mask &= I365_MASK & set_host_opts(base, ns);
+    mask &= I365_MASK & set_bridge_opts(base, ns);
 #ifdef CONFIG_ISA
     /* Scan for ISA interrupts */
     mask = isa_scan(base, mask);
@@ -1672,8 +1673,7 @@ static void __init add_cb_bridge(int type, u_char bus, u_char devfn,
                s->cb_virt = ioremap(s->cb_phys, 0x1000);
                pci_writel(bus, devfn, PCI_BASE_ADDRESS_0, s->cb_phys);
                /* Simple sanity checks */
-               if (((readb(s->cb_virt+0x800+I365_IDENT) & 0xf0)
-                    == 0x80) &&
+               if (!(readb(s->cb_virt+0x800+I365_IDENT) & 0x70) &&
                    !(readb(s->cb_virt+0x800+I365_CSC) &&
                      readb(s->cb_virt+0x800+I365_CSC) &&
                      readb(s->cb_virt+0x800+I365_CSC)))
@@ -1712,7 +1712,7 @@ static void __init add_cb_bridge(int type, u_char bus, u_char devfn,
 
     /* Re-do card type & voltage detection */
     cb_writel(sockets-ns, CB_SOCKET_FORCE, CB_SF_CVSTEST);
-    current->state = TASK_INTERRUPTIBLE;
+    __set_current_state(TASK_UNINTERRUPTIBLE);
     schedule_timeout(HZ/5);
 
     /* Set up PCI bus bridge structures if needed */
@@ -1762,9 +1762,8 @@ static void __init pci_probe(u_int class, void (add_fn)
 
 static void __init isa_probe(void)
 {
-    int i, j, sock, k;
-    int ns, id;
-    u_short port;
+    int i, j, sock, k, ns, id;
+    ioaddr_t port;
 
     if (check_region(i365_base, 2) != 0) {
        if (sockets == 0)
@@ -1812,115 +1811,6 @@ static void __init isa_probe(void)
 
 /*====================================================================*/
 
-static int __init init_i82365(void)
-{
-    servinfo_t serv;
-    CardServices(GetCardServicesInfo, &serv);
-    if (serv.Revision != CS_RELEASE_CODE) {
-       printk(KERN_NOTICE "i82365: Card Services release "
-              "does not match!\n");
-       return -1;
-    }
-    DEBUG(0, "%s\n", version);
-    printk(KERN_INFO "Intel PCIC probe: ");
-    sockets = 0;
-
-#ifdef CONFIG_PCI
-    if (do_pci_probe && pcibios_present()) {
-       pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge);
-       pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge);
-    }
-#endif
-
-#ifdef CONFIG_ISA
-    isa_probe();
-#endif
-
-    if (sockets == 0) {
-       printk("not found.\n");
-       return -ENODEV;
-    }
-
-    /* Set up interrupt handler(s) */
-#ifdef CONFIG_ISA
-    if (grab_irq != 0)
-       request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
-#endif
-#ifdef CONFIG_PCI
-    if (pci_csc) {
-       u_int i, irq, mask = 0;
-       for (i = 0; i < sockets; i++) {
-           irq = socket[i].cap.pci_irq;
-           if (irq && !(mask & (1<<irq)))
-               request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL);
-           mask |= (1<<irq);
-       }
-    }
-#endif
-    
-    if (register_ss_entry(sockets, &pcic_service) != 0)
-       printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
-
-    /* Finally, schedule a polling interrupt */
-    if (poll_interval != 0) {
-       poll_timer.function = pcic_interrupt_wrapper;
-       poll_timer.data = 0;
-       poll_timer.prev = poll_timer.next = NULL;
-       poll_timer.expires = jiffies + poll_interval;
-       add_timer(&poll_timer);
-    }
-    
-    return 0;
-    
-} /* init_i82365 */
-  
-/*====================================================================*/
-
-static void __exit exit_i82365(void)
-{
-    int i;
-#ifdef CONFIG_PROC_FS
-    for (i = 0; i < sockets; i++) pcic_proc_remove(i);
-#endif
-    unregister_ss_entry(&pcic_service);
-    if (poll_interval != 0)
-       del_timer(&poll_timer);
-#ifdef CONFIG_ISA
-    if (grab_irq != 0)
-       free_irq(cs_irq, NULL);
-#endif
-#ifdef CONFIG_PCI
-    if (pci_csc) {
-       u_int irq, mask = 0;
-       for (i = 0; i < sockets; i++) {
-           irq = socket[i].cap.pci_irq;
-           if (irq && !(mask & (1<<irq)))
-               free_irq(irq, NULL);
-           mask |= (1<<irq);
-       }
-    }
-#endif
-    for (i = 0; i < sockets; i++) {
-       i365_set(i, I365_CSCINT, 0);
-#ifdef CONFIG_PCI
-       if (socket[i].cb_virt) {
-           iounmap(socket[i].cb_virt);
-           release_mem_region(socket[i].cb_phys, 0x1000);
-       } else
-#endif
-           release_region(socket[i].ioaddr, 2);
-    }
-} /* exit_i82365 */
-
-/*====================================================================*/
-
-static void pcic_interrupt_wrapper(u_long data)
-{
-    pcic_interrupt(0, NULL, NULL);
-    poll_timer.expires = jiffies + poll_interval;
-    add_timer(&poll_timer);
-}
-
 static void pcic_interrupt(int irq, void *dev,
                                    struct pt_regs *regs)
 {
@@ -1938,17 +1828,18 @@ static void pcic_interrupt(int irq, void *dev,
            if ((socket[i].cs_irq != irq) &&
                (socket[i].cap.pci_irq != irq))
                continue;
-#ifdef CONFIG_ISA
-           if (!(socket[i].flags & IS_CARDBUS))
-               spin_lock_irqsave(&isa_lock, flags);
-#endif
+           ISA_LOCK(i, flags);
            csc = i365_get(i, I365_CSC);
+#ifdef CONFIG_PCI
+           if ((socket[i].flags & IS_CARDBUS) &&
+               (cb_readl(i,CB_SOCKET_EVENT) & (CB_SE_CCD1|CB_SE_CCD2))) {
+               cb_writel(i, CB_SOCKET_EVENT, CB_SE_CCD1|CB_SE_CCD2);
+               csc |= I365_CSC_DETECT;
+           }
+#endif
            if ((csc == 0) || (!socket[i].handler) ||
                (i365_get(i, I365_IDENT) & 0x70)) {
-#ifdef CONFIG_ISA
-               if (!(socket[i].flags & IS_CARDBUS))
-                   spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+               ISA_UNLOCK(i, flags);
                continue;
            }
            events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
@@ -1959,10 +1850,7 @@ static void pcic_interrupt(int irq, void *dev,
                events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
                events |= (csc & I365_CSC_READY) ? SS_READY : 0;
            }
-#ifdef CONFIG_ISA
-           if (!(socket[i].flags & IS_CARDBUS))
-               spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+           ISA_UNLOCK(i, flags);
            DEBUG(2, "i82365: socket %d event 0x%02x\n", i, events);
            if (events)
                socket[i].handler(socket[i].info, events);
@@ -1976,6 +1864,13 @@ static void pcic_interrupt(int irq, void *dev,
     DEBUG(4, "i82365: interrupt done\n");
 } /* pcic_interrupt */
 
+static void pcic_interrupt_wrapper(u_long data)
+{
+    pcic_interrupt(0, NULL, NULL);
+    poll_timer.expires = jiffies + poll_interval;
+    add_timer(&poll_timer);
+}
+
 /*====================================================================*/
 
 static int pcic_register_callback(u_short sock, ss_callback_t *call)
@@ -2149,7 +2044,7 @@ static int i365_set_socket(u_short sock, socket_state_t *state)
                        (t->cap.pci_irq == state->io_irq));
     t->bcr &= ~CB_BCR_CB_RESET;
 #endif
-    set_host_state(sock);
+    set_bridge_state(sock);
     
     /* IO card, RESET flag, IO interrupt */
     reg = t->intr;
@@ -2250,6 +2145,13 @@ static int i365_set_socket(u_short sock, socket_state_t *state)
     }
     i365_set(sock, I365_CSCINT, reg);
     i365_get(sock, I365_CSC);
+#ifdef CONFIG_PCI
+    if (t->flags & IS_CARDBUS) {
+       if (t->cs_irq || (pci_csc && t->cap.pci_irq))
+           cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD);
+       cb_writel(sock, CB_SOCKET_EVENT, -1);
+    }
+#endif
     
     return 0;
 } /* i365_set_socket */
@@ -2428,7 +2330,7 @@ static void cb_get_power(u_short sock, socket_state_t *state)
     case CB_SC_VCC_3V:         state->Vcc = 33; break;
     case CB_SC_VCC_5V:         state->Vcc = 50; break;
     }
-    switch (reg & CB_SC_VCC_MASK) {
+    switch (reg & CB_SC_VPP_MASK) {
     case CB_SC_VPP_3V:         state->Vpp = 33; break;
     case CB_SC_VPP_5V:         state->Vpp = 50; break;
     case CB_SC_VPP_12V:                state->Vpp = 120; break;
@@ -2509,12 +2411,12 @@ static int cb_set_socket(u_short sock, socket_state_t *state)
                        (s->cap.pci_irq == state->io_irq));
     s->bcr &= ~CB_BCR_CB_RESET;
     s->bcr |= (state->flags & SS_RESET) ? CB_BCR_CB_RESET : 0;
-    set_host_state(sock);
+    set_bridge_state(sock);
     
     cb_set_power(sock, state);
     
     /* Handle IO interrupt using ISA routing */
-    reg = i365_get(sock, I365_INTCTL) & ~I365_IRQ_MASK;
+    reg = s->intr;
     if (state->io_irq != s->cap.pci_irq) reg |= state->io_irq;
     i365_set(sock, I365_INTCTL, reg);
     
@@ -2523,6 +2425,9 @@ static int cb_set_socket(u_short sock, socket_state_t *state)
     if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
     i365_set(sock, I365_CSCINT, reg);
     i365_get(sock, I365_CSC);
+    if (s->cs_irq || (pci_csc && s->cap.pci_irq))
+       cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD);
+    cb_writel(sock, CB_SOCKET_EVENT, -1);
     
     return 0;
 } /* cb_set_socket */
@@ -2623,9 +2528,8 @@ static int proc_read_exca(char *buf, char **start, off_t pos,
     
 #ifdef CONFIG_ISA
     u_long flags = 0;
-    if (!(socket[sock].flags & IS_CARDBUS))
-       spin_lock_irqsave(&isa_lock, flags);
 #endif
+    ISA_LOCK(sock, flags);
     top = 0x40;
     if (socket[sock].flags & IS_CARDBUS)
        top = (socket[sock].flags & IS_CIRRUS) ? 0x140 : 0x50;
@@ -2639,10 +2543,7 @@ static int proc_read_exca(char *buf, char **start, off_t pos,
                     i365_get(sock,i+2), i365_get(sock,i+3),
                     ((i % 16) == 12) ? "\n" : " ");
     }
-#ifdef CONFIG_ISA
-    if (!(socket[sock].flags & IS_CARDBUS))
-       spin_unlock_irqrestore(&isa_lock, flags);
-#endif
+    ISA_UNLOCK(sock, flags);
     return (p - buf);
 }
 
@@ -2672,13 +2573,15 @@ static int proc_read_cardbus(char *buf, char **start, off_t pos,
                             int count, int *eof, void *data)
 {
     u_short sock = (socket_info_t *)data - socket;
-    int len;
-    
-    len = sprintf(buf, "%08x %08x %08x %08x %08x %08x\n",
-                 cb_readl(sock,0), cb_readl(sock,4),
-                 cb_readl(sock,8), cb_readl(sock,12),
-                 cb_readl(sock,16), cb_readl(sock,32));
-    return len;
+    char *p = buf;
+    int i, top;
+
+    top = (socket[sock].flags & IS_O2MICRO) ? 0x30 : 0x20;
+    for (i = 0; i < top; i += 0x10)
+       p += sprintf(p, "%08x %08x %08x %08x\n",
+                    cb_readl(sock,i+0x00), cb_readl(sock,i+0x04),
+                    cb_readl(sock,i+0x08), cb_readl(sock,i+0x0c));
+    return (p - buf);
 }
 #endif
 
@@ -2757,7 +2660,11 @@ static subfn_t pcic_service_table[] = {
 static int pcic_service(u_int sock, u_int cmd, void *arg)
 {
     subfn_t fn;
-
+    int ret;
+#ifdef CONFIG_ISA
+    u_long flags = 0;
+#endif
+    
     DEBUG(2, "pcic_ioctl(%d, %d, 0x%p)\n", sock, cmd, arg);
 
     if (cmd >= NFUNC)
@@ -2782,20 +2689,114 @@ static int pcic_service(u_int sock, u_int cmd, void *arg)
     }
 #endif
 
+    ISA_LOCK(sock, flags);
+    ret = (fn == NULL) ? -EINVAL : fn(sock, arg);
+    ISA_UNLOCK(sock, flags);
+    return ret;
+} /* pcic_service */
+
+/*====================================================================*/
+
+static int __init init_i82365(void)
+{
+    servinfo_t serv;
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+       printk(KERN_NOTICE "i82365: Card Services release "
+              "does not match!\n");
+       return -1;
+    }
+    DEBUG(0, "%s\n", version);
+    printk(KERN_INFO "Intel PCIC probe: ");
+    sockets = 0;
+
+#ifdef CONFIG_PCI
+    if (do_pci_probe && pcibios_present()) {
+       pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge);
+       pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge);
+    }
+#endif
+
 #ifdef CONFIG_ISA
-    if (!(socket[sock].flags & IS_CARDBUS)) {
-       int ret;
-       u_long flags;
-       spin_lock_irqsave(&isa_lock, flags);
-       ret = (fn == NULL) ? -EINVAL : fn(sock, arg);
-       spin_unlock_irqrestore(&isa_lock, flags);
-       return ret;
+    isa_probe();
+#endif
+
+    if (sockets == 0) {
+       printk("not found.\n");
+       return -ENODEV;
     }
+
+    /* Set up interrupt handler(s) */
+#ifdef CONFIG_ISA
+    if (grab_irq != 0)
+       request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
 #endif
-    return (fn == NULL) ? -EINVAL : fn(sock, arg);
-} /* pcic_service */
+#ifdef CONFIG_PCI
+    if (pci_csc) {
+       u_int i, irq, mask = 0;
+       for (i = 0; i < sockets; i++) {
+           irq = socket[i].cap.pci_irq;
+           if (irq && !(mask & (1<<irq)))
+               request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL);
+           mask |= (1<<irq);
+       }
+    }
+#endif
+    
+    if (register_ss_entry(sockets, &pcic_service) != 0)
+       printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
 
-/*====================================================================*/
+    /* Finally, schedule a polling interrupt */
+    if (poll_interval != 0) {
+       poll_timer.function = pcic_interrupt_wrapper;
+       poll_timer.data = 0;
+       poll_timer.prev = poll_timer.next = NULL;
+       poll_timer.expires = jiffies + poll_interval;
+       add_timer(&poll_timer);
+    }
+    
+    return 0;
+    
+} /* init_i82365 */
+
+static void __exit exit_i82365(void)
+{
+    int i;
+#ifdef CONFIG_PROC_FS
+    for (i = 0; i < sockets; i++) pcic_proc_remove(i);
+#endif
+    unregister_ss_entry(&pcic_service);
+    if (poll_interval != 0)
+       del_timer(&poll_timer);
+#ifdef CONFIG_ISA
+    if (grab_irq != 0)
+       free_irq(cs_irq, NULL);
+#endif
+#ifdef CONFIG_PCI
+    if (pci_csc) {
+       u_int irq, mask = 0;
+       for (i = 0; i < sockets; i++) {
+           irq = socket[i].cap.pci_irq;
+           if (irq && !(mask & (1<<irq)))
+               free_irq(irq, NULL);
+           mask |= (1<<irq);
+       }
+    }
+#endif
+    for (i = 0; i < sockets; i++) {
+       /* Turn off all interrupt sources! */
+       i365_set(i, I365_CSCINT, 0);
+#ifdef CONFIG_PCI
+       if (socket[i].flags & IS_CARDBUS)
+           cb_writel(i, CB_SOCKET_MASK, 0);
+       if (socket[i].cb_virt) {
+           iounmap(socket[i].cb_virt);
+           release_mem_region(socket[i].cb_phys, 0x1000);
+       } else
+#endif
+           release_region(socket[i].ioaddr, 2);
+    }
+} /* exit_i82365 */
 
 module_init(init_i82365);
 module_exit(exit_i82365);
index 89cb6cf016a235980c49f78d2456187a7b95244c..a3f922239c023d3cc124fc5f02cc2be74edbf780 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * o2micro.h 1.10 1999/09/03 16:43:35
+ * o2micro.h 1.12 1999/10/16 01:43:24
  *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
 #ifndef PCI_DEVICE_ID_O2_6836
 #define PCI_DEVICE_ID_O2_6836          0x6836
 #endif
+#ifndef PCI_DEVICE_ID_O2_6812
+#define PCI_DEVICE_ID_O2_6812          0x6872
+#endif
+
+/* Additional PCI configuration registers */
+
+#define O2_MUX_CONTROL         0x90    /* 32 bit */
+#define  O2_MUX_RING_OUT       0x0000000f
+#define  O2_MUX_SKTB_ACTV      0x000000f0
+#define  O2_MUX_SCTA_ACTV_ENA  0x00000100
+#define  O2_MUX_SCTB_ACTV_ENA  0x00000200
+#define  O2_MUX_SER_IRQ_ROUTE  0x0000e000
+#define  O2_MUX_SER_PCI                0x00010000
+
+#define  O2_MUX_SKTA_TURBO     0x000c0000      /* for 6833, 6860 */
+#define  O2_MUX_SKTB_TURBO     0x00300000
+#define  O2_MUX_AUX_VCC_3V     0x00400000
+#define  O2_MUX_PCI_VCC_5V     0x00800000
+#define  O2_MUX_PME_MUX                0x0f000000
+
+/* Additional ExCA registers */
 
 #define O2_MODE_A              0x38
-#define O2_MODE_A_2            0x26    /* For 6833B, 6860C */
+#define O2_MODE_A_2            0x26    /* for 6833B, 6860C */
 #define  O2_MODE_A_CD_PULSE    0x04
 #define  O2_MODE_A_SUSP_EDGE   0x08
 #define  O2_MODE_A_HOST_SUSP   0x10
-#define  O2_MODE_A_PWRCHIP     0x60
+#define  O2_MODE_A_PWR_MASK    0x60
 #define  O2_MODE_A_QUIET       0x80
 
 #define O2_MODE_B              0x39
-#define O2_MODE_B_2            0x2e    /* For 6833B, 6860C */
+#define O2_MODE_B_2            0x2e    /* for 6833B, 6860C */
 #define  O2_MODE_B_IDENT       0x03
 #define  O2_MODE_B_ID_BSTEP    0x00
 #define  O2_MODE_B_ID_CSTEP    0x01
 #define  O2_MODE_C_DREQ_WP     0x02
 #define  O2_MODE_C_DREQ_BVD2   0x03
 #define  O2_MODE_C_ZVIDEO      0x08
+#define  O2_MODE_C_IREQ_SEL    0x30
+#define  O2_MODE_C_MGMT_SEL    0xc0
 
 #define O2_MODE_D              0x3b
 #define  O2_MODE_D_IRQ_MODE    0x03
+#define  O2_MODE_D_PCI_CLKRUN  0x04
+#define  O2_MODE_D_CB_CLKRUN   0x08
 #define  O2_MODE_D_SKT_ACTV    0x20
 #define  O2_MODE_D_PCI_FIFO    0x40    /* for OZ6729, OZ6730 */
-#define  O2_MODE_D_W97_IRQ     0x40    /* for OZ6832 */
+#define  O2_MODE_D_W97_IRQ     0x40
 #define  O2_MODE_D_ISA_IRQ     0x80
 
 #define O2_MHPG_DMA            0x3c
index 52813cb5a2f89bd7bde3582c5dbc68affd07f1cc..fa6eba768242c3ddf37f32658c87a064d25a2920 100644 (file)
@@ -2,7 +2,7 @@
 
     Resource management routines
 
-    rsrc_mgr.c 1.71 1999/09/15 15:32:19
+    rsrc_mgr.c 1.73 1999/10/19 00:54:04
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -98,124 +98,15 @@ static irq_info_t irq_table[NR_IRQS] = { { 0, 0, 0 }, /* etc */ };
 
 #endif
 
-static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
-
 /*======================================================================
 
     Linux resource management extensions
     
 ======================================================================*/
 
-typedef struct resource_entry_t {
-    u_long                     base, num;
-    char                       *name;
-    struct resource_entry_t    *next;
-} resource_entry_t;
-
-/* Ordered linked lists of allocated IO and memory blocks */
-static resource_entry_t io_list = { 0, 0, NULL, NULL };
-
-static resource_entry_t *find_gap(resource_entry_t *root,
-                                 resource_entry_t *entry)
-{
-    resource_entry_t *p;
-    
-    if (entry->base > entry->base+entry->num-1)
-       return NULL;
-    for (p = root; ; p = p->next) {
-       if ((p != root) && (p->base+p->num-1 >= entry->base)) {
-           p = NULL;
-           break;
-       }
-       if ((p->next == NULL) ||
-           (p->next->base > entry->base+entry->num-1))
-           break;
-    }
-    return p;
-}
-
-static int register_my_resource(resource_entry_t *list,
-                               u_long base, u_long num, char *name)
-{
-    u_long flags;
-    resource_entry_t *p, *entry;
-
-    entry = kmalloc(sizeof(resource_entry_t), GFP_ATOMIC);
-    entry->base = base;
-    entry->num = num;
-    entry->name = name;
-
-    spin_lock_irqsave(&rsrc_lock, flags);
-    p = find_gap(list, entry);
-    if (p == NULL) {
-       spin_unlock_irqrestore(&rsrc_lock, flags);
-       kfree(entry);
-       return -EBUSY;
-    }
-    entry->next = p->next;
-    p->next = entry;
-    spin_unlock_irqrestore(&rsrc_lock, flags);
-    return 0;
-}
-
-static void release_my_resource(resource_entry_t *list,
-                               u_long base, u_long num)
-{
-    u_long flags;
-    resource_entry_t *p, *q;
-
-    spin_lock_irqsave(&rsrc_lock, flags);
-    for (p = list; ; p = q) {
-       q = p->next;
-       if (q == NULL) break;
-       if ((q->base == base) && (q->num == num)) {
-           p->next = q->next;
-           kfree(q);
-           spin_unlock_irqrestore(&rsrc_lock, flags);
-           return;
-       }
-    }
-    spin_unlock_irqrestore(&rsrc_lock, flags);
-    return;
-}
-
-static int check_my_resource(resource_entry_t *list,
-                            u_long base, u_long num)
-{
-    if (register_my_resource(list, base, num, NULL) != 0)
-       return -EBUSY;
-    release_my_resource(list, base, num);
-    return 0;
-}
+static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
 
-int check_io_region(u_long base, u_long num)
-{
-    return check_my_resource(&io_list, base, num);
-}
-void request_io_region(u_long base, u_long num, char *name)
-{
-    register_my_resource(&io_list, base, num, name);
-}
-void release_io_region(u_long base, u_long num)
-{
-    release_my_resource(&io_list, base, num);
-}
-#ifdef CONFIG_PROC_FS
-int proc_read_io(char *buf, char **start, off_t pos,
-                int count, int *eof, void *data)
-{
-    resource_entry_t *r;
-    u_long flags;
-    char *p = buf;
-    
-    spin_lock_irqsave(&rsrc_lock, flags);
-    for (r = io_list.next; r; r = r->next)
-       p += sprintf(p, "%04lx-%04lx : %s\n", r->base,
-                    r->base+r->num-1, r->name);
-    spin_unlock_irqrestore(&rsrc_lock, flags);
-    return (p - buf);
-}
-#endif
+#define check_io_region(b,n) (0)
 
 /*======================================================================
 
@@ -773,7 +664,6 @@ int adjust_resource_info(client_handle_t handle, adjust_t *adj)
 void release_resource_db(void)
 {
     resource_map_t *p, *q;
-    resource_entry_t *u, *v;
     
     for (p = mem_db.next; p != &mem_db; p = q) {
        q = p->next;
@@ -783,8 +673,4 @@ void release_resource_db(void)
        q = p->next;
        kfree(p);
     }
-    for (u = io_list.next; u; u = v) {
-       v = u->next;
-       kfree(u);
-    }
 }
index 3b7f12e0931c625020ec3480c8704a8b472edab6..37faa3b26c6768479f7a3e5f05ac12cf2e5beb12 100644 (file)
 #ifndef _RSRC_MGR_H
 #define _RSRC_MGR_H
 
-#ifdef __BEOS__
-int check_resource(int type, u_long base, u_long num);
-int register_resource(int type, u_long base, u_long num);
-int release_resource(int type, u_long base, u_long num);
-#endif
-
 #endif /* _RSRC_MGR_H */
index 0ce2c49b06febc509107432180ed845a953d8173..14c8cd2ac3f7d6b43482b3821ca22a3f0956ccc7 100644 (file)
@@ -33,7 +33,7 @@
 #define SNDCARD_WAVEFRONT               41
 #define SNDCARD_OPL3SA2                 42
 #define SNDCARD_OPL3SA2_MPU             43
-#define SNDCARD_WAVEARTIST              44     /* Rebel Waveartist */
+#define SNDCARD_WAVEARTIST              44     /* Waveartist */
 #define SNDCARD_OPL3SA2_MSS             45     /* Originally missed */
 #define SNDCARD_AD1816                  88
 
index 85a9efaec91ac43223414ef860f50c85c257dd08..0d2145347ed664c7e83de4354c834a4ae44f2c06 100644 (file)
@@ -1,14 +1,15 @@
 /*
- * drivers/sound/waveartist.c
+ * linux/drivers/sound/waveartist.c
  *
  * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Corel Computer NetWinder.
+ * codec chip used in the Rebel.com NetWinder.
  *
  * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
+ * and Pat Beirne (patb@corel.ca)
  */
 
 /*
- * Copyright (C) by Corel Computer 1998
+ * Copyright (C) by Rebel.com 1998-1999
  *
  * RWA010 specs received under NDA from Rockwell
  *
 
 #define debug_flg (0)
 
-#define DEB(x)
-#define DDB(x)
-#define DEB1(x)
-
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/smp.h>
+#include <linux/spinlock.h>
 
 #include <asm/dec21285.h>
 #include <asm/hardware.h>
+#include <asm/system.h>
 
 #include "soundmodule.h"
 #include "sound_config.h"
 #define _ISA_IRQ(x) (x)
 #endif
 
-#define        VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
-
-#define        MIXER_PRIVATE3_RESET    0x53570000
-#define        MIXER_PRIVATE3_READ     0x53570001
-#define        MIXER_PRIVATE3_WRITE    0x53570002
-
-#define        VNC_INTERNAL_SPKR       0x01    //the sw mute on/off control bit
-#define        VNC_INTERNAL_MIC        0x10    //the hw internal/handset mic bit
-
-/* Use RECSRC = speaker to mark the internal microphone
- *
- * Some cheating involved here: there is no way to relay
- * to the system, which microphone in in use
- * (left = handset, or right = internal)
- *
- * So while I do not flag SPEAKER in the Recording Devices
- * Mask, when on internal
- *
- * mike - I set the speaker bit hi. Some mixers can be
- * confused a bit...
- */
+#define POSSIBLE_RECORDING_DEVICES     (SOUND_MASK_LINE       |\
+                                        SOUND_MASK_MIC        |\
+                                        SOUND_MASK_LINE1)
 
-#define POSSIBLE_RECORDING_DEVICES     (SOUND_MASK_LINE |\
-                                        SOUND_MASK_MIC |\
-                                        SOUND_MASK_LINE1)      //Line1 = analog phone
-
-#define SUPPORTED_MIXER_DEVICES                (SOUND_MASK_SYNTH |\
-                                        SOUND_MASK_PCM |\
-                                        SOUND_MASK_LINE |\
-                                        SOUND_MASK_MIC | \
-                                        SOUND_MASK_LINE1 |\
-                                        SOUND_MASK_RECLEV |\
-                                        SOUND_MASK_VOLUME)
+#define SUPPORTED_MIXER_DEVICES                (SOUND_MASK_SYNTH      |\
+                                        SOUND_MASK_PCM        |\
+                                        SOUND_MASK_LINE       |\
+                                        SOUND_MASK_MIC        |\
+                                        SOUND_MASK_LINE1      |\
+                                        SOUND_MASK_RECLEV     |\
+                                        SOUND_MASK_VOLUME     |\
+                                        SOUND_MASK_IMIX)
 
 static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
        0x5555,         /* Master Volume         */
        0x0000,         /* Bass                  */
        0x0000,         /* Treble                */
-       0x5555,         /* Synth (FM)            */
+       0x2323,         /* Synth (FM)            */
        0x4b4b,         /* PCM                   */
        0x0000,         /* PC Speaker            */
        0x0000,         /* Ext Line              */
@@ -129,15 +107,20 @@ typedef struct {
        int             dev_no;
 
        /* Mixer parameters */
-       unsigned short  *levels;
-       int             handset_state;
-       signed int      slider_vol;        /* hardware slider volume */
+       unsigned short  *levels;           /* cache of volume settings   */
        int             recmask;           /* currently enabled recording device! */
-       int             supported_devices; /* SUPPORTED_MIXER_DEVICES */
+       int             supported_devices; /* SUPPORTED_MIXER_DEVICES    */
        int             rec_devices;       /* POSSIBLE_RECORDING_DEVICES */
-       int             handset_mute_sw :1;/* 1 - handset controlled in sw */
-       int             use_slider      :1;/* use slider setting for o/p vol */
-       int             mute_state      :1;
+
+#ifdef CONFIG_ARCH_NETWINDER
+       signed int      slider_vol;        /* hardware slider volume     */
+       unsigned int    handset_detect  :1;
+       unsigned int    telephone_detect:1;
+       unsigned int    no_autoselect   :1;/* handset/telephone autoselects a path */
+       unsigned int    spkr_mute_state :1;/* set by ioctl or autoselect */
+       unsigned int    line_mute_state :1;/* set by ioctl or autoselect */
+       unsigned int    use_slider      :1;/* use slider setting for o/p vol */
+#endif
 } wavnc_info;
 
 typedef struct wavnc_port_info {
@@ -147,271 +130,18 @@ typedef struct wavnc_port_info {
        int             audio_format;
 } wavnc_port_info;
 
-static int              nr_waveartist_devs;
-static wavnc_info       adev_info[MAX_AUDIO_DEV];
-
-static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
-
-/*
- * Corel Netwinder specifics...
- */
-static struct timer_list vnc_timer;
-extern spinlock_t gpio_lock;
-
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&gpio_lock, flags);
-       cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
-       spin_unlock_irqrestore(&gpio_lock, flags);
-
-       devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
-       static signed int old_slider_volume;
-       unsigned long flags;
-       signed int volume = 255;
-
-       *CSR_TIMER1_LOAD = 0x00ffffff;
-
-       save_flags(flags);
-       cli();
-
-       outb(0xFF, 0x201);
-       *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
-       while (volume && (inb(0x201) & 0x01))
-               volume--;
-
-       *CSR_TIMER1_CNTL = 0;
-
-       restore_flags(flags);
-       
-       volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
+static int             nr_waveartist_devs;
+static wavnc_info      adev_info[MAX_AUDIO_DEV];
+static spinlock_t      waveartist_lock = SPIN_LOCK_UNLOCKED;
 
-#ifndef REVERSE
-       volume = 150 - (volume >> 5);
-#else
-       volume = (volume >> 6) - 25;
+#ifndef machine_is_netwinder
+#define machine_is_netwinder() 0
 #endif
 
-       if (volume < 0)
-               volume = 0;
-
-       if (volume > 100)
-               volume = 100;
-
-       /*
-        * slider quite often reads +-8, so debounce this random noise
-        */
-       if ((volume - old_slider_volume) > 7 ||
-           (old_slider_volume - volume) > 7) {
-               old_slider_volume = volume;
-
-               DEB(printk("Slider volume: %d.\n", old_slider_volume));
-       }
-
-       return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
-       signed int slider_volume;
-       unsigned int temp;
-
-       /*
-        * read the "buttons" state.
-        *  Bit 4 = handset present,
-        *  Bit 5 = offhook
-        */
-       // the state should be "querable" via a private IOCTL call
-       temp = inb(0x201) & 0x30;
-
-       if (!devc->handset_mute_sw &&
-           (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
-               devc->handset_state = temp;
-               devc->handset_mute_sw = 0;
-
-               vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
-       }
-
-       slider_volume = vnc_volume_slider(devc);
-
-       /*
-        * If we're using software controlled volume, and
-        * the slider moves by more than 20%, then we
-        * switch back to slider controlled volume.
-        */
-       if (devc->slider_vol > slider_volume) {
-               if (devc->slider_vol - slider_volume > 20)
-                       devc->use_slider = 1;
-       } else {
-               if (slider_volume - devc->slider_vol > 20)
-                       devc->use_slider = 1;
-       }
-
-       /*
-        * use only left channel
-        */
-       temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
-       if (slider_volume != temp && devc->use_slider) {
-               devc->slider_vol = slider_volume;
-
-               waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
-                       slider_volume | slider_volume << 8);
-
-               return 1;
-       }
-
-       return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
-       int next_timeout;
-
-       if (vnc_slider(adev_info + data))
-               next_timeout = 5;       // mixer reported change
-       else
-               next_timeout = VNC_TIMER_PERIOD;
-
-       mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
-static int
-vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
-{
-       wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
-       int val, temp;
-
-       if (cmd == SOUND_MIXER_PRIVATE1) {
-               /*
-                * Use this call to override the automatic handset
-                * behaviour - ignore handset.
-                *  bit 7 = total control over handset - do not
-                *          to plug/unplug
-                *  bit 4 = internal mic
-                *  bit 0 = mute internal speaker
-                */
-               if (get_user(val, (int *)arg))
-                       return -EFAULT;
-
-               devc->handset_mute_sw = val;
-
-               temp = val & VNC_INTERNAL_SPKR;
-               if (devc->mute_state != temp)
-                       vnc_mute(devc, temp);
-
-               devc->handset_state = val & VNC_INTERNAL_MIC;
-               waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
-               return 0;
-       }
-#if 0
-       if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
-#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
-               int             val;
-
-               val = *(int *) arg;
-
-               printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
-               if (val == VNC_SOUND_PAUSE) {
-                       wa_sendcmd(0x16);    //PAUSE the ADC
-               } else if (val == VNC_SOUND_RESUME) {
-                       wa_sendcmd(0x18);    //RESUME the ADC
-               } else {
-                       return -EINVAL;      //invalid parameters...
-               }
-               return 0;
-       }
-
-       if (cmd == SOUND_MIXER_PRIVATE3) {
-               long unsigned   flags;
-               int             mixer_reg[15];  //reg 14 is actually a command: read,write,reset
-
-               int             val;
-               int             i;
-
-               val = *(int *) arg;
-
-               if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
-                       return (-EFAULT);
-
-               memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
-               if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) {  //reset command??
-                       wavnc_mixer_reset(devc);
-                       return (0);
-               } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) {   //write command??
-//                     printk("WaveArtist Mixer: Private write command.\n");
-
-                       wa_sendcmd(0x32);       //Pair1 - word 1 and 5
-                       wa_sendcmd(mixer_reg[0]);
-                       wa_sendcmd(mixer_reg[4]);
-
-                       wa_sendcmd(0x32);       //Pair2 - word 2 and 6
-                       wa_sendcmd(mixer_reg[1]);
-                       wa_sendcmd(mixer_reg[5]);
-
-                       wa_sendcmd(0x32);       //Pair3 - word 3 and 7
-                       wa_sendcmd(mixer_reg[2]);
-                       wa_sendcmd(mixer_reg[6]);
-
-                       wa_sendcmd(0x32);       //Pair4 - word 4 and 8
-                       wa_sendcmd(mixer_reg[3]);
-                       wa_sendcmd(mixer_reg[7]);
-
-                       wa_sendcmd(0x32);       //Pair5 - word 9 and 10
-                       wa_sendcmd(mixer_reg[8]);
-                       wa_sendcmd(mixer_reg[9]);
-
-                       wa_sendcmd(0x0031);             //set left and right PCM
-                       wa_sendcmd(mixer_reg[0x0A]);
-                       wa_sendcmd(mixer_reg[0x0B]);
-
-                       wa_sendcmd(0x0131);             //set left and right FM
-                       wa_sendcmd(mixer_reg[0x0C]);
-                       wa_sendcmd(mixer_reg[0x0D]);
-
-                       return 0;
-               } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) {    //read command?
-//                     printk("WaveArtist Mixer: Private read command.\n");
-
-                       //first read all current values...
-                       save_flags(flags);
-                       cli();
-
-                       for (i = 0; i < 14; i++) {
-                               wa_sendcmd((i << 8) + 0x30);    // get ready for command nn30H
-
-                               while (!(inb(STATR) & CMD_RF)) {
-                               };      //wait for response ready...
-
-                               mixer_reg[i] = inw(CMDR);
-                       }
-                       restore_flags(flags);
-
-                       if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
-                               return (-EFAULT);
-
-                       memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
-                       return 0;
-               } else
-                       return -EINVAL;
-       }
-#endif
-       return -EINVAL;
-}
+static struct timer_list vnc_timer;
+static void vnc_configure_mixer(wavnc_info *devc);
+static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static void vnc_slider_tick(unsigned long data);
 
 static inline void
 waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
@@ -472,7 +202,7 @@ waveartist_reset(wavnc_info *devc)
        } while (timeout--);
 
        if (timeout == 0) {
-               printk("WaveArtist: reset timeout ");
+               printk(KERN_WARNING "WaveArtist: reset timeout ");
                if (res != (unsigned int)-1)
                        printk("(res=%04X)", res);
                printk("\n");
@@ -481,6 +211,10 @@ waveartist_reset(wavnc_info *devc)
        return 0;
 }
 
+/* Helper function to send and receive words
+ * from WaveArtist.  It handles all the handshaking
+ * and can send or receive multiple words.
+ */
 static int
 waveartist_cmd(wavnc_info *devc,
                int nr_cmd, unsigned int *cmd,
@@ -554,6 +288,32 @@ waveartist_cmd(wavnc_info *devc,
        return timed_out ? 1 : 0;
 }
 
+/*
+ * Send one command word
+ */
+static inline int
+waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
+{
+       return waveartist_cmd(devc, 1, &cmd, 0, NULL);
+}
+
+/*
+ * Send one command, receive one word
+ */
+static inline unsigned int
+waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
+{
+       unsigned int ret;
+
+       waveartist_cmd(devc, 1, &cmd, 1, &ret);
+
+       return ret;
+}
+
+/*
+ * Send a double command, receive one
+ * word (and throw it away)
+ */
 static inline int
 waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
 {
@@ -562,11 +322,12 @@ waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
        vals[0] = cmd;
        vals[1] = arg;
 
-       waveartist_cmd(devc, 2, vals, 1, vals);
-
-       return 0;
+       return waveartist_cmd(devc, 2, vals, 1, vals);
 }
 
+/*
+ * Send a triple command
+ */
 static inline int
 waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
                unsigned int arg1, unsigned int arg2)
@@ -581,72 +342,18 @@ waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
 }
 
 static int
-waveartist_sendcmd(struct address_info *hw, unsigned int cmd)
-{
-       int count;
-
-       if (debug_flg & DEBUG_CMD)
-               printk("waveartist_sendcmd: cmd=0x%04X...", cmd);
-
-       udelay(10);
-
-       if (inb(hw->io_base + STATR) & CMD_RF) {
-               /*
-                * flush the port
-                */
-               count = inw(hw->io_base + CMDR);
-
-               udelay(10);
-
-               if (debug_flg & DEBUG_CMD)
-                       printk(" flushed %04X...", count);
-       }
-
-       /*
-        * preset timeout at 5000 loops
-        */
-       count = 5000;
-
-       while (count --)
-               if (inb(hw->io_base + STATR) & CMD_WE) {
-                       /* wait till CMD_WE is high
-                        * then output the command
-                        */
-                       outw(cmd, hw->io_base + CMDR);
-                       break;
-               }
-
-       /* ready BEFORE timeout?
-        */
-       if (debug_flg & DEBUG_CMD)
-               printk(" %s\n", count ? "Done OK." : "Error!");
-
-       udelay(10);
-
-       return count ? 0 : 1;
-}
-
-static int
-waveartist_getrev(struct address_info *hw, char *rev)
+waveartist_getrev(wavnc_info *devc, char *rev)
 {
-       int temp;
+       unsigned int temp[2];
+       unsigned int cmd = WACMD_GETREV;
 
-       waveartist_sendcmd(hw, 0);
-       udelay(20);
-       temp = inw(hw->io_base + CMDR);
-       udelay(20);
-       inw(hw->io_base + CMDR);        // discard second word == 0
+       waveartist_cmd(devc, 1, &cmd, 2, temp);
 
-       rev[0] = temp >> 8;
-       rev[1] = temp & 255;
+       rev[0] = temp[0] >> 8;
+       rev[1] = temp[0] & 255;
        rev[2] = '\0';
 
-       return temp;
-}
-
-inline void
-waveartist_mute(wavnc_info *devc, int mute)
-{
+       return temp[0];
 }
 
 static void waveartist_halt_output(int dev);
@@ -667,10 +374,9 @@ waveartist_open(int dev, int mode)
        devc  = (wavnc_info *) audio_devs[dev]->devc;
        portc = (wavnc_port_info *) audio_devs[dev]->portc;
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
        if (portc->open_mode || (devc->open_mode & mode)) {
-               restore_flags(flags);
+               spin_unlock_irqrestore(&waveartist_lock, flags);
                return -EBUSY;
        }
 
@@ -683,13 +389,7 @@ waveartist_open(int dev, int mode)
                devc->record_dev = dev;
        if (mode & OPEN_WRITE)
                devc->playback_dev = dev;
-       restore_flags(flags);
-
-       /*
-        * Mute output until the playback really starts. This
-        * decreases clicking (hope so).
-        */
-       waveartist_mute(devc, 1);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 
        return 0;
 }
@@ -701,8 +401,7 @@ waveartist_close(int dev)
        wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
        unsigned long   flags;
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        waveartist_halt(dev);
 
@@ -710,9 +409,7 @@ waveartist_close(int dev)
        devc->open_mode &= ~portc->open_mode;
        portc->open_mode = 0;
 
-       waveartist_mute(devc, 1);
-
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static void
@@ -747,18 +444,17 @@ waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag)
                         */
        }
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        /*
         * set sample count
         */
-       waveartist_cmd2(devc, 0x0024, count);
+       waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
 
        devc->xfer_count = count;
        devc->audio_mode |= PCM_ENABLE_OUTPUT;
 
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static void
@@ -791,19 +487,17 @@ waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag)
                         */
        }
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        /*
         * set sample count
         */
-       waveartist_cmd2(devc, 0x0014, count);
-       waveartist_mute(devc, 0);
+       waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
 
        devc->xfer_count = count;
        devc->audio_mode |= PCM_ENABLE_INPUT;
 
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static int
@@ -869,40 +563,42 @@ waveartist_prepare_for_input(int dev, int bsize, int bcount)
        speed = waveartist_get_speed(portc);
        bits  = waveartist_get_bits(portc);
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
-               printk("waveartist: error setting the record format to %d\n",
-                      portc->audio_format);
+               printk(KERN_WARNING "waveartist: error setting the "
+                      "record format to %d\n", portc->audio_format);
 
        if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
-               printk("waveartist: error setting record to %d channels\n",
-                      portc->channels);
+               printk(KERN_WARNING "waveartist: error setting record "
+                      "to %d channels\n", portc->channels);
 
        /*
         * write cmd SetSampleSpeedTimeConstant
         */
        if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
-               printk("waveartist: error setting the record speed "
-                      "to %dHz.\n", portc->speed);
+               printk(KERN_WARNING "waveartist: error setting the record "
+                      "speed to %dHz.\n", portc->speed);
 
        if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
-               printk("waveartist: error setting the record data path "
-                      "to 0x%X\n", 1);
+               printk(KERN_WARNING "waveartist: error setting the record "
+                      "data path to 0x%X\n", 1);
 
        if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
-               printk("waveartist: error setting the record format to %d\n",
-                      portc->audio_format);
+               printk(KERN_WARNING "waveartist: error setting the record "
+                      "format to %d\n", portc->audio_format);
 
        devc->xfer_count = 0;
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
        waveartist_halt_input(dev);
 
        if (debug_flg & DEBUG_INTR) {
-               printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
-               printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
-               printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
+               printk("WA CTLR reg: 0x%02X.\n",
+                      inb(devc->hw.io_base + CTLR));
+               printk("WA STAT reg: 0x%02X.\n",
+                      inb(devc->hw.io_base + STATR));
+               printk("WA IRQS reg: 0x%02X.\n",
+                      inb(devc->hw.io_base + IRQSTAT));
        }
 
        return 0;
@@ -922,28 +618,27 @@ waveartist_prepare_for_output(int dev, int bsize, int bcount)
        speed = waveartist_get_speed(portc);
        bits  = waveartist_get_bits(portc);
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
            waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
-               printk("waveartist: error setting the playback speed "
-                      "to %dHz.\n", portc->speed);
+               printk(KERN_WARNING "waveartist: error setting the playback "
+                      "speed to %dHz.\n", portc->speed);
 
        if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
-               printk("waveartist: error setting the playback to"
-                      " %d channels\n", portc->channels);
+               printk(KERN_WARNING "waveartist: error setting the playback "
+                      "to %d channels\n", portc->channels);
 
        if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
-               printk("waveartist: error setting the playback data path "
-                      "to 0x%X\n", 0);
+               printk(KERN_WARNING "waveartist: error setting the playback "
+                      "data path to 0x%X\n", 0);
 
        if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
-               printk("waveartist: error setting the playback format to %d\n",
-                      portc->audio_format);
+               printk(KERN_WARNING "waveartist: error setting the playback "
+                      "format to %d\n", portc->audio_format);
 
        devc->xfer_count = 0;
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
        waveartist_halt_output(dev);
 
        if (debug_flg & DEBUG_INTR) {
@@ -961,7 +656,6 @@ waveartist_halt(int dev)
        wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
        wavnc_info      *devc;
 
-
        if (portc->open_mode & OPEN_WRITE)
                waveartist_halt_output(dev);
 
@@ -978,19 +672,13 @@ waveartist_halt_input(int dev)
        wavnc_info      *devc = (wavnc_info *) audio_devs[dev]->devc;
        unsigned long   flags;
 
-       save_flags(flags);
-       cli();
-
-       waveartist_mute(devc, 1);
-
-//RMK  disable_dma(audio_devs[dev]->dmap_in->dma);
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        /*
         * Stop capture
         */
-       waveartist_sendcmd(&devc->hw, 0x17);
+       waveartist_cmd1(devc, WACMD_INPUTSTOP);
 
-//RMK  enable_dma(audio_devs[dev]->dmap_in->dma);
        devc->audio_mode &= ~PCM_ENABLE_INPUT;
 
        /*
@@ -1002,7 +690,7 @@ waveartist_halt_input(int dev)
 
 //     devc->audio_mode &= ~PCM_ENABLE_INPUT;
 
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static void
@@ -1011,16 +699,9 @@ waveartist_halt_output(int dev)
        wavnc_info      *devc = (wavnc_info *) audio_devs[dev]->devc;
        unsigned long   flags;
 
-       save_flags(flags);
-       cli();
-
-       waveartist_mute(devc, 1);
-
-//RMK  disable_dma(audio_devs[dev]->dmap_out->dma);
-
-       waveartist_sendcmd(&devc->hw, 0x27);
+       spin_lock_irqsave(&waveartist_lock, flags);
 
-//RMK  enable_dma(audio_devs[dev]->dmap_out->dma);
+       waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
 
        devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
 
@@ -1033,7 +714,7 @@ waveartist_halt_output(int dev)
 
 //     devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
 
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static void
@@ -1052,8 +733,7 @@ waveartist_trigger(int dev, int state)
                printk("\n");
        }
 
-       save_flags(flags);
-       cli();
+       spin_lock_irqsave(&waveartist_lock, flags);
 
        state &= devc->audio_mode;
 
@@ -1062,18 +742,16 @@ waveartist_trigger(int dev, int state)
                /*
                 * enable ADC Data Transfer to PC
                 */
-               waveartist_sendcmd(&devc->hw, 0x15);
+               waveartist_cmd1(devc, WACMD_INPUTSTART);
 
        if (portc->open_mode & OPEN_WRITE &&
            state & PCM_ENABLE_OUTPUT)
                /*
                 * enable DAC data transfer from PC
                 */
-               waveartist_sendcmd(&devc->hw, 0x25);
+               waveartist_cmd1(devc, WACMD_OUTPUTSTART);
 
-       waveartist_mute(devc, 0);
-
-       restore_flags(flags);
+       spin_unlock_irqrestore(&waveartist_lock, flags);
 }
 
 static int
@@ -1158,7 +836,7 @@ waveartist_intr(int irq, void *dev_id, struct pt_regs *regs)
        if (status & IRQ_REQ)   /* Clear interrupt */
                waveartist_iack(devc);
        else
-               printk("waveartist: unexpected interrupt\n");
+               printk(KERN_WARNING "waveartist: unexpected interrupt\n");
 
 #ifdef CONFIG_AUDIO
        if (irqstatus & 0x01) {
@@ -1175,12 +853,12 @@ waveartist_intr(int irq, void *dev_id, struct pt_regs *regs)
                        temp = 0;
                }
                if (temp)       //default:
-                       printk("WaveArtist: Unknown interrupt\n");
+                       printk(KERN_WARNING "waveartist: Unknown interrupt\n");
        }
 #endif
        if (irqstatus & 0x2)
                // We do not use SB mode natively...
-               printk("WaveArtist: Unexpected SB interrupt...\n");
+               printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
 }
 
 /* -------------------------------------------------------------------------
@@ -1198,6 +876,9 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
 
 #define SCALE(lev,max) ((lev) * (max) / 100)
 
+       if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
+               whichDev = SOUND_MIXER_VOLUME;
+
        switch(whichDev) {
        case SOUND_MIXER_VOLUME:
                mask  = 0x000e;
@@ -1208,6 +889,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
                break;
 
        case SOUND_MIXER_LINE:
+               if ((devc->recmask & SOUND_MASK_LINE) == 0)
+                       return;
                mask  = 0x07c0;
                reg_l = 0x000;
                reg_r = 0x400;
@@ -1216,6 +899,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
                break;
 
        case SOUND_MIXER_MIC:
+               if ((devc->recmask & SOUND_MASK_MIC) == 0)
+                       return;
                mask  = 0x0030;
                reg_l = 0x200;
                reg_r = 0x600;
@@ -1232,6 +917,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
                break;
 
        case SOUND_MIXER_LINE1:
+               if ((devc->recmask & SOUND_MASK_LINE1) == 0)
+                       return;
                mask  = 0x003e;
                reg_l = 0x000;
                reg_r = 0x400;
@@ -1240,12 +927,14 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
                break;
 
        case SOUND_MIXER_PCM:
-               waveartist_cmd3(devc, 0x0031, SCALE(lev_left,  32767),
+               waveartist_cmd3(devc, WACMD_SET_LEVEL,
+                               SCALE(lev_left,  32767),
                                SCALE(lev_right, 32767));
                return;
 
        case SOUND_MIXER_SYNTH:
-               waveartist_cmd3(devc, 0x0131, SCALE(lev_left,  32767),
+               waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
+                               SCALE(lev_left,  32767),
                                SCALE(lev_right, 32767));
                return;
 
@@ -1254,7 +943,7 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
        }
 
        /* read left setting */
-       vals[0] = reg_l + 0x30;
+       vals[0] = reg_l + WACMD_GET_LEVEL;
        waveartist_cmd(devc, 1, vals, 1, vals + 1);
 
        /* read right setting */
@@ -1265,7 +954,7 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev)
        vals[2] = (vals[2] & ~mask) | (lev_right & mask);
 
        /* write left,right back */
-       vals[0] = 0x32;
+       vals[0] = WACMD_SET_MIXER;
        waveartist_cmd(devc, 3, vals, 0, NULL);
 }
 
@@ -1273,20 +962,6 @@ static void
 waveartist_select_input(wavnc_info *devc, unsigned int input)
 {
        unsigned int vals[3];
-#if 1
-       /* New mixer programming - switch recording source
-        * using R/L_ADC_Mux_Select.  We are playing with
-        * left/right mux bit fields in reg 9.
-        *
-        * We can not switch Mux_Select while recording, so
-        * for microphones, enable both left and right and
-        * play with levels only!
-        *
-        * Unfortunately, we need to select the src of mono
-        * recording (left or right) before starting the
-        * recording - so can not dynamically switch between
-        * handset amd internal microphones...
-        */
 
        /*
         * Get reg 9
@@ -1304,47 +979,13 @@ waveartist_select_input(wavnc_info *devc, unsigned int input)
                printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n",
                        vals[1] & 0x07, (vals[1] >> 3) & 0x07);
 
-       vals[1] &= ~0x03F;      //kill current left/right mux input select
+       /*
+        * kill current left/right mux input select
+        */
+       vals[1] &= ~0x03F;
 
        switch (input) {
-               /*
-                * Handset or internal MIC
-                */
        case SOUND_MASK_MIC:
-               /*
-                * handset not plugged in?
-                */
-               if (devc->handset_state & VNC_INTERNAL_MIC) {
-                       /*
-                        * set mono recording from right mic
-                        */
-                       waveartist_sendcmd(&devc->hw, 0x0134);
-#if 0
-                       /*
-                        * right=mic, left=none
-                        */
-                       vals[1] |= 0x0028;
-                       /*
-                        * pretend int mic
-                        */
-                       devc->rec_devices |= SOUND_MASK_SPEAKER;
-#endif
-               } else {
-                       /*
-                        * set mono rec from left mic
-                        */
-                       waveartist_sendcmd(&devc->hw, 0x0034);
-#if 0
-                       /*
-                        * right=none, left=mic
-                        */
-                       vals[1] |= 0x0005;
-                       /*
-                        * show no int mic
-                        */
-                       devc->rec_devices &= ~SOUND_MASK_SPEAKER;
-#endif
-               }
                /*
                 * right=mic, left=mic
                 */
@@ -1352,10 +993,6 @@ waveartist_select_input(wavnc_info *devc, unsigned int input)
                break;
 
        case SOUND_MASK_LINE1:
-               /*
-                * set mono rec from left aux1
-                */
-               waveartist_sendcmd(&devc->hw, 0x0034);
                /*
                 * right=none, left=Aux1;
                 */
@@ -1363,10 +1000,6 @@ waveartist_select_input(wavnc_info *devc, unsigned int input)
                break;
 
        case SOUND_MASK_LINE:
-               /*
-                * set mono rec from left (default)
-                */
-               waveartist_sendcmd(&devc->hw, 0x0034);
                /*
                 * right=Line, left=Line;
                 */
@@ -1378,101 +1011,10 @@ waveartist_select_input(wavnc_info *devc, unsigned int input)
                printk("RECSRC %d: left=0x%04X, right=0x%04X.\n", input,
                        vals[1] & 0x07, (vals[1] >> 3) & 0x07);
 
-#else
-       /* This part is good, if input connected to
-        * a mixer, so can be used for record-only modes...
-        */
-
-       /*
-        * get reg 4
-        */
-       vals[0] = 0x0330;
-       waveartist_cmd(devc, 1, vals, 1, vals + 1);
-
-       /*
-        * get reg 8
-        */
-       vals[0] = 0x0730;
-       waveartist_cmd(devc, 1, vals, 1, vals + 2);
-
-       if (debug_flg & DEBUG_MIXER)
-               printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n",
-                       vals[1], vals[2]);
-
-       /*
-        * kill current left/right mux input select
-        */
-       vals[1] &= ~0x07F8;
-       vals[2] &= ~0x07F8;
-
-       switch (input) {
-               /*
-                * handset or internal mic
-                */
-       case SOUND_MASK_MIC:
-               /*
-                * handset not plugged in?
-                */
-               if (devc->handset_state & VNC_INTERNAL_MIC) {
-                       /*
-                        * set mono recording from right mic
-                        */
-                       waveartist_sendcmd(&devc->hw, 0x0134);
-                       /*
-                        * left = none, right = mic, RX filter gain
-                        */
-                       vals[1] |= 0x0C00;
-                       vals[2] |= 0x0C88;
-                       /*
-                        * pretend int mic
-                        */
-                       devc->rec_devices |= SOUND_MASK_SPEAKER;
-               } else {
-                       /*
-                        * set mono rec from left mic
-                        */
-                       waveartist_sendcmd(&devc->hw, 0x0034);
-                       /*
-                        * left = mic, RX filter gain, right = none;
-                        */
-                       vals[1] |= 0x0C88;
-                       vals[2] |= 0x0C00;
-                       /*
-                        * show no int mic
-                        */
-                       devc->rec_devices &= ~SOUND_MASK_SPEAKER;
-               }
-               break;
-
-       case SOUND_MASK_LINE1:
-               /*
-                * set mono rec from left aux1
-                */
-               waveartist_sendcmd(&devc->hw, 0x0034);
-               /*
-                * left = Aux1, right = none
-                */
-               vals[1] |= 0x0C40;
-               vals[2] |= 0x0C00;
-               break;
-       
-       case SOUND_MASK_LINE:
-               /*
-                * left = Line, right = Line
-                */
-               vals[1] |= 0x0C10;
-               vals[2] |= 0x0C10;
-               break;
-       }
-
-       if (debug_flg & DEBUG_MIXER)
-               printk("RECSRC %d: left(4) 0x%04X, right(8) 0x%04X.\n",
-                       level, vals[1], vals[2]);
-#endif
        /*
         * and finally - write the reg pair back....
         */
-       vals[0] = 0x32;
+       vals[0] = WACMD_SET_MIXER;
 
        waveartist_cmd(devc, 3, vals, 0, NULL);
 }
@@ -1482,8 +1024,7 @@ waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level)
 {
        unsigned int lev_left  = level & 0x007f;
        unsigned int lev_right = (level & 0x7f00) >> 8;
-
-       int left, right, devmask, changed, i;
+       int left, right, devmask;
 
        left = level & 0x7f;
        right = (level & 0x7f00) >> 8;
@@ -1493,85 +1034,38 @@ waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level)
                        whichDev, level);
 
        switch (whichDev) {
-       /* Master volume (0-7)
-        * We have 3 bits on the Left/Right Mixer Gain,
-        * bits 3,2,1 on 3 and 7
-        */
-       case SOUND_MIXER_VOLUME:
+       case SOUND_MIXER_VOLUME:        /* master volume (0-7)       */
+       case SOUND_MIXER_LINE:          /* external line (0-31)      */
+       case SOUND_MIXER_MIC:           /* mono mic (0-3)            */
+       case SOUND_MIXER_RECLEV:        /* recording level (0-7)     */
+       case SOUND_MIXER_LINE1:         /* mono external aux1 (0-31) */
+       case SOUND_MIXER_PCM:           /* Waveartist PCM (0-32767)  */
+       case SOUND_MIXER_SYNTH:         /* internal synth (0-31)     */
+       case SOUND_MIXER_IMIX:          /* recording feedback        */
                devc->levels[whichDev] = lev_left | lev_right << 8;
                waveartist_mixer_update(devc, whichDev);
                break;
 
-
-       /* External line (0-31)
-        * use LOUT/ROUT bits 10...6, reg 1 and 5
-        */
-       case SOUND_MIXER_LINE:
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-       /* Mono microphone (0-3) mute,
-        * 0db,10db,20db
-        */
-       case SOUND_MIXER_MIC:
-#if 1
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-#else
-               /* we do not need to mute volume of
-                * an unused mic - it is simply unused...
-                */
-               if (devc->handset_state & VNC_INTERNAL_MIC)
-                       devc->levels[whichDev] = lev_right << 8;
-               else
-                       levels[whichDev] = lev_left;
-#endif
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-       /* Recording level (0-7)
-        */
-       case SOUND_MIXER_RECLEV:
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-       /* Mono External Aux1 (0-31)
-        * use LINE1 bits 5...1, reg 1 and 5
-        */
-       case SOUND_MIXER_LINE1:
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-       /* WaveArtist PCM (0-32767)
-        */
-       case SOUND_MIXER_PCM:
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-       /* Internal synthesizer (0-31)
-        */
-       case SOUND_MIXER_SYNTH:
-               devc->levels[whichDev] = lev_left | lev_right << 8;
-               waveartist_mixer_update(devc, whichDev);
-               break;
-
-
        /* Select recording input source
         */
        case SOUND_MIXER_RECSRC:
-               devmask = level & POSSIBLE_RECORDING_DEVICES;
+               devmask = level & devc->rec_devices;
 
-               changed = devmask ^ devc->recmask;
-               devc->recmask = devmask;
+#ifdef CONFIG_ARCH_NETWINDER
+               if (machine_is_netwinder())
+                       vnc_configure_mixer(devc);
+               else
+#endif
+               {
+                       waveartist_select_input(devc, level);
 
-               for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
-                       if (changed & (1 << i))
-                               waveartist_mixer_update(devc, i);
+                       /*
+                        * if record monitoring is on, make sure the bit is set
+                        */
+                       if (devc->levels[SOUND_MIXER_IMIX])
+                               waveartist_mixer_update(devc, SOUND_MIXER_IMIX);
+               }
 
-               waveartist_select_input(devc, level);
                /*
                 * do not save in "levels", return current setting
                 */
@@ -1595,53 +1089,54 @@ waveartist_mixer_reset(wavnc_info *devc)
        /*
         * reset mixer cmd
         */
-       waveartist_sendcmd(&devc->hw, 0x33);
+       waveartist_cmd1(devc, WACMD_RST_MIXER);
 
        /*
-        * set input for ADC to come from
-        * a mux (left and right) == reg 9,
-        * initially none
+        * set input for ADC to come from 'quiet'
+        * turn on default modes
         */
-       waveartist_cmd3(devc, 0x0032, 0x9800, 0xa836);
+       waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
 
        /*
         * set mixer input select to none, RX filter gains 0 db
         */
-       waveartist_cmd3(devc, 0x0032, 0x4c00, 0x8c00);
+       waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
 
        /*
         * set bit 0 reg 2 to 1 - unmute MonoOut
         */
-       waveartist_cmd3(devc, 0x0032, 0x2801, 0x6800);
-
-       for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
-               waveartist_mixer_update(devc, i);
+       waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
 
        /* set default input device = internal mic
         * current recording device = none
-        * no handset
         */
        devc->recmask = 0;
-       devc->handset_state = VNC_INTERNAL_MIC;
-//     waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
 
-       /*
-        * start from enabling the hw setting
-        */
-       devc->handset_mute_sw = 0;
+       for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+               waveartist_mixer_update(devc, i);
+
        devc->supported_devices = SUPPORTED_MIXER_DEVICES;
        devc->rec_devices = POSSIBLE_RECORDING_DEVICES;
+
+       if (machine_is_netwinder()) {
+               devc->supported_devices |= SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT;
+               devc->rec_devices |= SOUND_MASK_PHONEIN;
+       }
 }
 
 static int
 waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
        wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+       int ret;
 
-       if (cmd == SOUND_MIXER_PRIVATE1 ||
-           cmd == SOUND_MIXER_PRIVATE2 ||
-           cmd == SOUND_MIXER_PRIVATE3)
-               return vnc_private_ioctl(dev, cmd, arg);
+#ifdef CONFIG_ARCH_NETWINDER
+       if (machine_is_netwinder()) {
+               ret = vnc_private_ioctl(dev, cmd, arg);
+               if (ret != -ENOIOCTLCMD)
+                       return ret;
+       }
+#endif
 
        if (((cmd >> 8) & 0xff) == 'M') {
                if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
@@ -1650,30 +1145,14 @@ waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
                        if (get_user(val, (int *)arg))
                                return -EFAULT;
 
-                       /*
-                        * special case for master volume: if we
-                        * received this call - switch from hw
-                        * volume control to a software volume
-                        * control, till the hw volume is modified
-                        * to signal that user wants to be back in
-                        * hardware...
-                        */
-                       if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
-                               devc->use_slider = 0;
-
                        return waveartist_mixer_set(devc, cmd & 0xff, val);
                } else {
-                       int ret;
-
                        /*
                         * Return parameters
                         */
                        switch (cmd & 0xff) {
                        case SOUND_MIXER_RECSRC:
                                ret = devc->recmask;
-
-                               if (devc->handset_state & VNC_INTERNAL_MIC)
-                                       ret |= SOUND_MASK_SPEAKER;
                                break;
 
                        case SOUND_MIXER_DEVMASK:
@@ -1700,7 +1179,7 @@ waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
                                        return -EINVAL;
                        }
 
-                       return put_user(ret, (int *)arg) ? -EINVAL : 0;
+                       return put_user(ret, (int *)arg) ? -EFAULT : 0;
                }
        }
 
@@ -1725,7 +1204,7 @@ waveartist_init(wavnc_info *devc)
 
        sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
 
-       if (waveartist_getrev(&devc->hw, rev)) {
+       if (waveartist_getrev(devc, rev)) {
                strcat(dev_name, " rev. ");
                strcat(dev_name, rev);
        }
@@ -1758,20 +1237,20 @@ waveartist_init(wavnc_info *devc)
        waveartist_iack(devc);
 
        if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
-               printk("%s: IRQ %d in use\n",
+               printk(KERN_ERR "%s: IRQ %d in use\n",
                        devc->hw.name, devc->hw.irq);
                goto uninstall;
        }
 
        if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
-               printk("%s: Can't allocate DMA%d\n",
+               printk(KERN_ERR "%s: Can't allocate DMA%d\n",
                        devc->hw.name, devc->hw.dma);
                goto uninstall_irq;
        }
 
        if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
                if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
-                       printk("%s: can't allocate DMA%d\n",
+                       printk(KERN_ERR "%s: can't allocate DMA%d\n",
                                devc->hw.name, devc->hw.dma2);
                        goto uninstall_dma;
                }
@@ -1809,22 +1288,24 @@ probe_waveartist(struct address_info *hw_config)
        wavnc_info *devc = &adev_info[nr_waveartist_devs];
 
        if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
-               printk("waveartist: too many audio devices\n");
+               printk(KERN_WARNING "waveartist: too many audio devices\n");
                return 0;
        }
 
        if (check_region(hw_config->io_base, 15))  {
-               printk("WaveArtist: I/O port conflict\n");
+               printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
                return 0;
        }
 
        if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
-               printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
+               printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
+                      hw_config->irq);
                return 0;
        }
 
        if (hw_config->dma != _ISA_DMA(3)) {
-               printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
+               printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
+                      hw_config->dma);
                return 0;
        }
 
@@ -1864,15 +1345,18 @@ attach_waveartist(struct address_info *hw)
        if (devc->dev_no < 0)
                release_region(hw->io_base, 15);
        else {
-               init_timer(&vnc_timer);
-               vnc_timer.function = vnc_slider_tick;
-               vnc_timer.expires  = jiffies;
-               vnc_timer.data     = nr_waveartist_devs;
-               add_timer(&vnc_timer);
-
+#ifdef CONFIG_ARCH_NETWINDER
+               if (machine_is_netwinder()) {
+                       init_timer(&vnc_timer);
+                       vnc_timer.function = vnc_slider_tick;
+                       vnc_timer.expires  = jiffies;
+                       vnc_timer.data     = nr_waveartist_devs;
+                       add_timer(&vnc_timer);
+
+                       vnc_configure_mixer(devc);
+               }
+#endif
                nr_waveartist_devs += 1;
-
-               vnc_mute(devc, 0);
        }
 }
 
@@ -1891,6 +1375,11 @@ unload_waveartist(struct address_info *hw)
        if (devc != NULL) {
                int mixer;
 
+#ifdef CONFIG_ARCH_NETWINDER
+               if (machine_is_netwinder())
+                       del_timer(&vnc_timer);
+#endif
+
                release_region(devc->hw.io_base, 15);
 
                waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
@@ -1904,8 +1393,6 @@ unload_waveartist(struct address_info *hw)
                    devc->hw.dma2 != NO_DMA)
                        sound_free_dma(devc->hw.dma2);
 
-               del_timer(&vnc_timer);
-
                mixer = audio_devs[devc->dev_no]->mixer_dev;
 
                if (mixer >= 0)
@@ -1919,7 +1406,356 @@ unload_waveartist(struct address_info *hw)
                for (; i < nr_waveartist_devs; i++)
                        adev_info[i] = adev_info[i + 1];
        } else
-               printk("waveartist: can't find device to unload\n");
+               printk(KERN_WARNING "waveartist: can't find device "
+                      "to unload\n");
+}
+
+/*
+ * Rebel.com Netwinder specifics...
+ */
+
+#define        VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
+
+#define        MIXER_PRIVATE3_RESET    0x53570000
+#define        MIXER_PRIVATE3_READ     0x53570001
+#define        MIXER_PRIVATE3_WRITE    0x53570002
+
+#define        VNC_MUTE_INTERNAL_SPKR  0x01    //the sw mute on/off control bit
+#define        VNC_MUTE_LINE_OUT       0x10
+#define VNC_PHONE_DETECT       0x20
+#define VNC_HANDSET_DETECT     0x40
+#define VNC_DISABLE_AUTOSWITCH 0x80
+
+extern spinlock_t gpio_lock;
+
+static inline void
+vnc_update_spkr_mute(wavnc_info *devc)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpio_lock, flags);
+       cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
+       spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static void
+vnc_mute_lout(wavnc_info *devc, int mute)
+{
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+       static signed int old_slider_volume;
+       unsigned long flags;
+       signed int volume = 255;
+
+       *CSR_TIMER1_LOAD = 0x00ffffff;
+
+       save_flags(flags);
+       cli();
+
+       outb(0xFF, 0x201);
+       *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+       while (volume && (inb(0x201) & 0x01))
+               volume--;
+
+       *CSR_TIMER1_CNTL = 0;
+
+       restore_flags(flags);
+       
+       volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+       volume = 150 - (volume >> 5);
+#else
+       volume = (volume >> 6) - 25;
+#endif
+
+       if (volume < 0)
+               volume = 0;
+
+       if (volume > 100)
+               volume = 100;
+
+       /*
+        * slider quite often reads +-8, so debounce this random noise
+        */
+       if (abs(volume - old_slider_volume) > 7) {
+               old_slider_volume = volume;
+
+               if (debug_flg & DEBUG_MIXER)
+                       printk(KERN_DEBUG "Slider volume: %d.\n", volume);
+       }
+
+       return old_slider_volume;
+}
+
+static void
+vnc_configure_mixer(wavnc_info *devc)
+{
+       u_int vals[3];
+
+       if (!devc->no_autoselect) {
+               if (devc->handset_detect) {
+                       devc->recmask = SOUND_MASK_LINE1;
+                       devc->spkr_mute_state = devc->line_mute_state = 1;
+               } else if (devc->telephone_detect) {
+                       devc->recmask = SOUND_MASK_PHONEIN;
+                       devc->spkr_mute_state = devc->line_mute_state = 1;
+               } else {
+                       /* unless someone has asked for LINE-IN,
+                        * we default to MIC
+                        */
+                       if ((devc->recmask & SOUND_MASK_LINE) == 0)
+                               devc->recmask = SOUND_MASK_MIC;
+                       devc->spkr_mute_state = devc->line_mute_state = 0;
+               }
+               vnc_update_spkr_mute(devc);
+               vnc_mute_lout(devc, devc->spkr_mute_state);
+       }
+
+       /* Ok.  At this point, we have done the autoswitch logic, or we
+        * have had a command from an ioctl.  We have a valid devc->recmask.
+        * Now we have to connect up the hardware to reflect the recmask.
+        */
+       vals[1] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x800);
+       vals[2] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x900);
+
+       vals[1] &= ~0x3f;
+
+       switch(devc->recmask) {
+       case SOUND_MASK_MIC:            /* builtin mic */
+               waveartist_cmd1(devc, WACMD_SET_MONO | 0x100);  /* right */
+               vals[1] |= 0x28;
+               break;
+
+       case SOUND_MASK_LINE1:          /* out handset */
+               waveartist_cmd1(devc, WACMD_SET_MONO);          /* left */
+               vals[1] |= 0x05;
+               break;
+
+       case SOUND_MASK_PHONEIN:        /* our telephone mic */
+               waveartist_cmd1(devc, WACMD_SET_MONO);          /* left */
+               vals[1] |= 0x04;
+               break;
+
+       case SOUND_MASK_LINE:           /* stereo line in */
+               vals[1] |= 12;
+               break;
+
+       default:
+               return;
+       }
+
+       vals[0] = WACMD_SET_MIXER;
+       waveartist_cmd(devc, 3, vals, 0, NULL);
+
+       waveartist_mixer_update(devc, SOUND_MIXER_IMIX);
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+       signed int slider_volume;
+       unsigned int temp, old_hs, old_td;
+
+       /*
+        * read the "buttons" state.
+        *  Bit 4 = handset present,
+        *  Bit 5 = offhook
+        */
+       temp = inb(0x201) & 0x30;
+
+       old_hs = devc->handset_detect;
+       old_td = devc->telephone_detect;
+
+       devc->handset_detect = !(temp & 0x10);
+       devc->telephone_detect = !!(temp & 0x20);
+
+       if (!devc->no_autoselect &&
+           (old_hs != devc->handset_detect ||
+            old_td != devc->telephone_detect))
+               vnc_configure_mixer(devc);
+
+       slider_volume = vnc_volume_slider(devc);
+
+       /*
+        * If we're using software controlled volume, and
+        * the slider moves by more than 20%, then we
+        * switch back to slider controlled volume.
+        */
+       if (abs(devc->slider_vol - slider_volume) > 20)
+               devc->use_slider = 1;
+
+       /*
+        * use only left channel
+        */
+       temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+       if (slider_volume != temp && devc->use_slider) {
+               devc->slider_vol = slider_volume;
+
+               waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+                       slider_volume | slider_volume << 8);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+       int next_timeout;
+
+       if (vnc_slider(adev_info + data))
+               next_timeout = 5;       // mixer reported change
+       else
+               next_timeout = VNC_TIMER_PERIOD;
+
+       mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+       wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+       int val;
+
+       switch (cmd) {
+       case SOUND_MIXER_PRIVATE1:
+       {
+               u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
+               int val;
+
+               get_user_ret(val, (int *)arg, -EFAULT);
+
+               /* check if parameter is logical */
+               if (val & ~(VNC_MUTE_INTERNAL_SPKR |
+                           VNC_MUTE_LINE_OUT |
+                           VNC_DISABLE_AUTOSWITCH))
+                       return -EINVAL;
+
+               prev_auto_state = devc->no_autoselect;
+               prev_spkr_mute  = devc->spkr_mute_state;
+               prev_line_mute  = devc->line_mute_state;
+
+               devc->no_autoselect   = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
+               devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
+               devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
+
+               if (prev_spkr_mute != devc->spkr_mute_state)
+                       vnc_update_spkr_mute(devc);
+
+               if (prev_line_mute != devc->line_mute_state)
+                       vnc_mute_lout(devc, devc->line_mute_state);
+
+               if (prev_auto_state != devc->no_autoselect)
+                       vnc_configure_mixer(devc);
+               waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+               return 0;
+       }
+
+       case SOUND_MIXER_PRIVATE2:
+               get_user_ret(val, (int *)arg, -EFAULT);
+
+               switch (val) {
+#define VNC_SOUND_PAUSE         0x53    //to pause the DSP
+#define VNC_SOUND_RESUME        0x57    //to unpause the DSP
+               case VNC_SOUND_PAUSE:
+                       waveartist_cmd1(devc, 0x16);
+                       break;
+
+               case VNC_SOUND_RESUME:
+                       waveartist_cmd1(devc, 0x18);
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+               return 0;
+
+       /* private ioctl to allow bulk access to waveartist */
+       case SOUND_MIXER_PRIVATE3:
+       {
+               unsigned long   flags;
+               int             mixer_reg[15], i, val;
+
+               get_user_ret(val, (int *)arg, -EFAULT);
+               copy_from_user_ret(mixer_reg, (void *)val, sizeof(mixer_reg), -EFAULT);
+
+               switch (mixer_reg[14]) {
+               case MIXER_PRIVATE3_RESET:
+                       waveartist_mixer_reset(devc);
+                       break;
+
+               case MIXER_PRIVATE3_WRITE:
+                       waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
+                       waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
+                       waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
+                       waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
+                       waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
+
+                       waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
+                       waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
+                       break;
+
+               case MIXER_PRIVATE3_READ:
+                       spin_lock_irqsave(&waveartist_lock, flags);
+
+                       for (i = 0x30; i < 14 << 8; i += 1 << 8)
+                               waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
+
+                       spin_unlock_irqrestore(&waveartist_lock, flags);
+
+                       copy_to_user_ret((void *)val, mixer_reg, sizeof(mixer_reg), -EFAULT);
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       /* read back the state from PRIVATE1 */
+       case SOUND_MIXER_PRIVATE4:
+               val = (devc->spkr_mute_state  ? VNC_MUTE_INTERNAL_SPKR : 0) |
+                     (devc->line_mute_state  ? VNC_MUTE_LINE_OUT      : 0) |
+                     (devc->handset_detect   ? VNC_HANDSET_DETECT     : 0) |
+                     (devc->telephone_detect ? VNC_PHONE_DETECT       : 0) |
+                     (devc->no_autoselect    ? VNC_DISABLE_AUTOSWITCH : 0);
+
+               return put_user(val, (int *)arg) ? -EFAULT : 0;
+       }
+
+       if (((cmd >> 8) & 0xff) == 'M') {
+               if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+                       /*
+                        * special case for master volume: if we
+                        * received this call - switch from hw
+                        * volume control to a software volume
+                        * control, till the hw volume is modified
+                        * to signal that user wants to be back in
+                        * hardware...
+                        */
+                       if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
+                               devc->use_slider = 0;
+               } else if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) {
+                       val = devc->supported_devices &
+                               ~(SOUND_MASK_IMIX |
+                                 SOUND_MASK_MIC |
+                                 SOUND_MASK_LINE1 |
+                                 SOUND_MASK_PHONEIN |
+                                 SOUND_MASK_PHONEOUT);
+                       return put_user(val, (int *)arg) ? -EFAULT : 0;
+               }
+       }
+
+       return -ENOIOCTLCMD;
 }
 
 #ifdef MODULE
index 45b62d1b55f19964df56c23908ae2caabb382e42..2d2163fa875a59c17cf01045281a38fdf3ccc263 100644 (file)
@@ -1,5 +1,8 @@
-
-// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder
+/*
+ * linux/drivers/sound/waveartist.h
+ *
+ * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
+ */
 
 //registers
 #define CMDR   0
@@ -33,7 +36,8 @@
 
 //commands
 
-#define        WACMD_SYSTEMID  0
+#define        WACMD_SYSTEMID          0x00
+#define WACMD_GETREV           0x00
 #define        WACMD_INPUTFORMAT       0x10    //0-8S, 1-16S, 2-8U
 #define        WACMD_INPUTCHANNELS     0x11    //1-Mono, 2-Stereo
 #define        WACMD_INPUTSPEED        0x12    //sampling rate
 #define        WACMD_OUTPUTRESUME      0x28    //resume ADC
 #define        WACMD_OUTPUTPIO         0x29    //PIO ADC
 
+#define        WACMD_GET_LEVEL         0x30
+#define        WACMD_SET_LEVEL         0x31
+#define        WACMD_SET_MIXER         0x32
+#define        WACMD_RST_MIXER         0x33
+#define        WACMD_SET_MONO          0x34
 
-
-
-int             wa_sendcmd(unsigned int cmd);
-int             wa_writecmd(unsigned int cmd, unsigned int arg);
+int wa_sendcmd(unsigned int cmd);
+int wa_writecmd(unsigned int cmd, unsigned int arg);
index c08feee15ba02dcaf4a1b528b614277a7d329e38..20be3faab8e24c4f02d8a768b1dc467411c2245b 100644 (file)
@@ -127,7 +127,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned
 {
        unsigned int status;
        struct uhci_td *tmp;
-       int count = 1000, bytesreceived = 0;
+       int count = 1000, actlength, explength;
 
        if (rval)
                *rval = 0;
@@ -145,26 +145,28 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned
        do {
                status = uhci_status_bits(tmp->status);
 
-               if (status) {
-                       if (debug) {
-                               /* Must reset the toggle on first error */
-                               if (uhci_debug)
-                                       printk(KERN_DEBUG "Set toggle from %x rval %ld\n",
-                                               (unsigned int)tmp, rval ? *rval : 0);
-
-                               usb_settoggle(dev->usb, uhci_endpoint(tmp->info),
-                                       uhci_packetout(tmp->info) ^ 1,
-                                       uhci_toggle(tmp->info));
-                               break;
-                       }
-               }
+               if (status)
+                       break;
 
                /* The length field is only valid if the TD was completed */
-               if (!status)
                if (!(tmp->status & TD_CTRL_ACTIVE) && uhci_packetin(tmp->info)) {
-                       bytesreceived += uhci_actual_length(tmp->status);
+                       explength = uhci_expected_length(tmp->info);
+                       actlength = uhci_actual_length(tmp->status);
                        if (rval)
-                               *rval += uhci_actual_length(tmp->status);
+                               *rval += actlength;
+                       if (explength != actlength) {
+                               /* Reset the data toggle on error. */
+                               if (debug || uhci_debug)
+                                       printk(KERN_DEBUG "Set toggle from %p rval %ld%c for status=%x to %d, exp=%d, act=%d\n",
+                                               tmp, rval ? *rval : 0,
+                                               rval ? '*' : '/', tmp->status,
+                                               uhci_toggle(tmp->info) ^ 1,
+                                               explength, actlength);
+                               usb_settoggle(dev->usb, uhci_endpoint(tmp->info),
+                                       uhci_packetout(tmp->info),
+                                       uhci_toggle(tmp->info) ^ 1);
+                               break;  // Short packet
+                       }
                }
 
                if ((tmp->link & UHCI_PTR_TERM) ||
@@ -194,12 +196,10 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned
                    tmp->pipetype == PIPE_CONTROL)
                        return USB_ST_NOERROR;
 
-#if 0
                /* We got to an error, but the controller hasn't finished */
-               /*  with it, yet */
+               /*  with it yet. */
                if (tmp->status & TD_CTRL_ACTIVE)
                        return USB_ST_NOCHANGE;
-#endif
 
                /* If this wasn't the last TD and SPD is set, ACTIVE */
                /*  is not and NAK isn't then we received a short */
@@ -1147,6 +1147,9 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre
        __u32 nextlink;
        unsigned long bytesrequested = len;
        unsigned long bytesread = 0;
+#ifdef DUMP_RAW
+       unsigned char *orig_data = (unsigned char *) data;
+#endif
 
        first = td = uhci_td_alloc(dev);
        if (!td)
@@ -1269,6 +1272,23 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre
                       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
        }
 
+#ifdef DUMP_RAW
+       if (!ret && usb_pipein(pipe)) { /* good Input control msg */
+               int i;
+
+               printk (KERN_CRIT "ctrl msg [%02x %02x %04x %04x %04x] on pipe %x returned:\n",
+                       cmd->requesttype, cmd->request,
+                       cmd->value, cmd->index, cmd->length, pipe);
+               for (i = 0; i < bytesrequested; ) {
+                       printk(" %02x", orig_data[i]);
+                       if (++i % 16 == 0)
+                               printk("\n");
+               }
+               if (i % 16 != 0)
+                       printk("\n");
+       }
+#endif
+
        return ret;
 }
 
@@ -1750,9 +1770,9 @@ int uhci_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned lo
 
                list_add(&td->irq_list, &uhci->interrupt_list);
 
-               usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1);
+               usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info));
                td->info &= ~(1 << TD_TOKEN_TOGGLE);    /* clear data toggle */
-               td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info),
+               td->info |= usb_gettoggle(usb_dev, uhci_endpoint(td->info),
                        uhci_packetout(td->info)) << TD_TOKEN_TOGGLE; /* toggle between data0 and data1 */
                td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
                /* The HC only removes it when it completed */
@@ -1764,7 +1784,7 @@ int uhci_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned lo
 
                /* marked for removal */
                td->flags &= ~UHCI_TD_REMOVE;
-               usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), uhci_packetout(td->info));
+               usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info));
                uhci_remove_qh(td->qh->skel, td->qh);
                uhci_qh_free(td->qh);
                if (td->pipetype == PIPE_INTERRUPT)
@@ -1792,7 +1812,7 @@ static void uhci_interrupt_notify(struct uhci *uhci)
                /*  TD's completed successfully */
                status = uhci_td_result(td->dev, td, &rval, 0);
 
-               if ((status == USB_ST_NOERROR) && (td->status & TD_CTRL_ACTIVE))
+               if (status == USB_ST_NOCHANGE)
                        continue;
 
                /* remove from IRQ list */
@@ -1925,7 +1945,8 @@ static void start_hc(struct uhci *uhci)
        }
 
        /* Turn on all interrupts */
-       outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+       outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+               io_addr + USBINTR);
 
        /* Start at frame 0 */
        outw(0, io_addr + USBFRNUM);
index 96bd338867f757a7900aac46e1f401ba0f3ccb3f..e460fb858cb4bd7b23c93f636ac2568b00906a60 100644 (file)
@@ -125,7 +125,7 @@ struct uhci_framelist {
 #define TD_TOKEN_TOGGLE                19
 
 #define uhci_maxlen(token)     ((token) >> 21)
-#define uhci_expected_length(info) ((info >> 21) + 1)  /* 1-based */ 
+#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ 
 #define uhci_toggle(token)     (((token) >> TD_TOKEN_TOGGLE) & 1)
 #define uhci_endpoint(token)   (((token) >> 15) & 0xf)
 #define uhci_devaddr(token)    (((token) >> 8) & 0x7f)
index 5581b2e0da009788b558880d776a19c323eb0aff..f31c5b60ba3fe374395d11875d9bb0554e4efef7 100644 (file)
@@ -722,7 +722,7 @@ void usb_show_device(struct usb_device *);
 void usb_show_string(struct usb_device *dev, char *id, int index);
 
 #ifdef USB_DEBUG
-#define PRINTD(format, args...) printk("usb: " format "\n" , ## args);
+#define PRINTD(format, args...) printk(KERN_DEBUG "usb: " format "\n" , ## args);
 #else /* NOT DEBUGGING */
 #define PRINTD(fmt, arg...) do {} while (0)
 #endif /* USB_DEBUG */
index 24504b17a84d3f0124b6eb53a9308faee813853d..9bcb6920edfd8040c77e2cd8a9cc03b0574027a4 100644 (file)
@@ -68,6 +68,8 @@ extern int offb_init(void);
 extern int offb_setup(char*);
 extern int atyfb_init(void);
 extern int atyfb_setup(char*);
+extern int aty128fb_init(void);
+extern int aty128fb_setup(char*);
 extern int igafb_init(void);
 extern int igafb_setup(char*);
 extern int imsttfb_init(void);
@@ -150,6 +152,9 @@ static struct {
 #ifdef CONFIG_FB_ATY
        { "atyfb", atyfb_init, atyfb_setup },
 #endif
+#ifdef CONFIG_FB_ATY128
+       { "aty128fb", aty128fb_init, aty128fb_setup },
+#endif
 #ifdef CONFIG_FB_IGA
         { "igafb", igafb_init, igafb_setup },
 #endif
index 1961ec33a49a0d0a10b438f8b496a0f0dd1a24ea..dea4f07120b3867f338b77b24d8884c17fcf468f 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -246,7 +246,7 @@ int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
                                        memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len);
                        }
                        err = copy_from_user(kaddr + offset, str, bytes_to_copy);
-                       flush_page_to_ram(kaddr);
+                       flush_page_to_ram(page);
                        kunmap((unsigned long)kaddr, KM_WRITE);
 
                        if (err)
index 41f2c05bda90784a59643c58c66160d54ebc21cb..b827c490ceb94bc0840cf8a5901383134dc1ac13 100644 (file)
@@ -117,8 +117,8 @@ csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len,
        adcs    %0, %0, %4
        adcs    %0, %0, %5
        adc     %0, %0, #0"
-       : "=r"(sum)
-       : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto*256)
+       : "=&r"(sum)
+       : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto << 8)
        : "cc");
        return sum;
 }      
index 6f85554cadf692a4b69bf37d0402c97722579d1b..40e5ea046d49ec1e12883cea33ef034aab95ef99 100644 (file)
@@ -12,8 +12,7 @@ struct pt_regs;
 struct pci_bus;        
 
 struct machdep_calls {
-       void            (*setup_arch)(unsigned long * memory_start_p,
-                               unsigned long * memory_end_p);
+       void            (*setup_arch)(void);
        /* Optional, may be NULL. */
        int             (*setup_residual)(char *buffer);
        /* Optional, may be NULL. */
index b67ca5cc0f9e98b33266ec3189ddd20e3db689be..55168a8b2b3ed91f44038dd4f20766cf93d8fd42 100644 (file)
@@ -76,7 +76,7 @@ typedef unsigned long pgprot_t;
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)       (((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
-extern void clear_page(unsigned long page);
+extern void clear_page(void *page);
 #define copy_page(to,from)     memcpy((void *)(to), (void *)(from), PAGE_SIZE)
 
 /* map phys->virtual and virtual->phys for RAM pages */
index e3f3c15909b0392a23b3938f84825f10edbd5b6b..47a2cc87db79b4bf1af7d99c165c72ec02debd0e 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _ASM_PCI_BRIDGE_H
 #define _ASM_PCI_BRIDGE_H
 
-unsigned long pmac_find_bridges(unsigned long, unsigned long);
+void pmac_find_bridges(void);
 
 /*
  * pci_io_base returns the memory address at which you can access
index 3a822590b18a3bc37ad8096f7904c8cc63a9a375..ad2eba41fd9f11f674dd369d1e9d36104aae703c 100644 (file)
@@ -5,6 +5,7 @@
 
 #ifndef __ASSEMBLY__
 #include <linux/mm.h>
+#include <linux/threads.h>
 #include <asm/processor.h>             /* For TASK_SIZE */
 #include <asm/mmu.h>
 #include <asm/page.h>
@@ -50,7 +51,8 @@ extern inline void flush_hash_page(unsigned context, unsigned long va)
 #define flush_cache_page(vma, p)       do { } while (0)
 
 extern void flush_icache_range(unsigned long, unsigned long);
-extern void flush_page_to_ram(unsigned long);
+extern void __flush_page_to_ram(unsigned long page_va);
+#define flush_page_to_ram(page)        __flush_page_to_ram(page_address(page))
 
 extern unsigned long va_to_phys(unsigned long address);
 extern pte_t *va_to_pte(struct task_struct *tsk, unsigned long address);
@@ -109,6 +111,16 @@ extern unsigned long ioremap_bot, ioremap_base;
 #define PTRS_PER_PGD   1024
 #define USER_PTRS_PER_PGD      (TASK_SIZE / PGDIR_SIZE)
 
+#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
+
+#define pte_ERROR(e) \
+       printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+       printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+       printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+
 /*
  * Just any arbitrary offset to the start of the vmalloc VM area: the
  * current 64MB value just means that there will be a 64MB "hole" after the
@@ -126,7 +138,7 @@ extern unsigned long ioremap_bot, ioremap_base;
  * system.  This really does become a problem for machines with good amounts
  * of RAM.  -- Cort
  */
-#define VMALLOC_OFFSET (0x4000000) /* 64M */
+#define VMALLOC_OFFSET (0x1000000) /* 16M */
 #define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)))
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
 #define VMALLOC_END    ioremap_bot
@@ -216,6 +228,14 @@ extern unsigned long ioremap_bot, ioremap_base;
 #define __S110 PAGE_SHARED
 #define __S111 PAGE_SHARED
 
+#ifndef __ASSEMBLY__
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned long empty_zero_page[1024];
+#define ZERO_PAGE(vaddr) (mem_map + MAP_NR(empty_zero_page))
+
 /*
  * BAD_PAGETABLE is used when we need a bogus page-table, while
  * BAD_PAGE is used for a bogus page.
@@ -223,15 +243,12 @@ extern unsigned long ioremap_bot, ioremap_base;
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
  */
-#ifndef __ASSEMBLY__
 extern pte_t __bad_page(void);
 extern pte_t * __bad_pagetable(void);
 
-extern unsigned long empty_zero_page[1024];
-#endif __ASSEMBLY__
 #define BAD_PAGETABLE  __bad_pagetable()
 #define BAD_PAGE       __bad_page()
-#define ZERO_PAGE(vaddr)       ((unsigned long) empty_zero_page)
+#endif /* __ASSEMBLY__ */
 
 /* number of bits that fit into a memory pointer */
 #define BITS_PER_PTR   (8*sizeof(unsigned long))
@@ -243,17 +260,24 @@ extern unsigned long empty_zero_page[1024];
 /* 64-bit machines, beware!  SRB. */
 #define SIZEOF_PTR_LOG2        2
 
-#ifndef __ASSEMBLY__
-extern inline int pte_none(pte_t pte)          { return !pte_val(pte); }
-extern inline int pte_present(pte_t pte)       { return pte_val(pte) & _PAGE_PRESENT; }
-extern inline void pte_clear(pte_t *ptep)      { pte_val(*ptep) = 0; }
+#define pte_none(pte)          (!pte_val(pte))
+#define pte_present(pte)       (pte_val(pte) & _PAGE_PRESENT)
+#define pte_clear(ptep)                do { pte_val(*(ptep)) = 0; } while (0)
+#define pte_pagenr(x)          ((unsigned long)((pte_val(x) >> PAGE_SHIFT)))
 
-extern inline int pmd_none(pmd_t pmd)          { return !pmd_val(pmd); }
-extern inline int pmd_bad(pmd_t pmd)           { return (pmd_val(pmd) & ~PAGE_MASK) != 0; }
-extern inline int pmd_present(pmd_t pmd)       { return (pmd_val(pmd) & PAGE_MASK) != 0; }
-extern inline void pmd_clear(pmd_t * pmdp)     { pmd_val(*pmdp) = 0; }
+#define pmd_none(pmd)          (!pmd_val(pmd))
+#define        pmd_bad(pmd)            ((pmd_val(pmd) & ~PAGE_MASK) != 0)
+#define        pmd_present(pmd)        ((pmd_val(pmd) & PAGE_MASK) != 0)
+#define        pmd_clear(pmdp)         do { pmd_val(*(pmdp)) = 0; } while (0)
 
+/*
+ * Permanent address of a page.
+ */
+#define page_address(page) (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#define pages_to_mb(x)         ((x) >> (20-PAGE_SHIFT))
+#define pte_page(x)            (mem_map+pte_pagenr(x))
 
+#ifndef __ASSEMBLY__
 /*
  * The "pgd_xxx()" functions here are trivial for a folded two-level
  * setup: the pgd is never bad, and a pmd always exists (as it's folded
@@ -262,7 +286,10 @@ extern inline void pmd_clear(pmd_t * pmdp) { pmd_val(*pmdp) = 0; }
 extern inline int pgd_none(pgd_t pgd)          { return 0; }
 extern inline int pgd_bad(pgd_t pgd)           { return 0; }
 extern inline int pgd_present(pgd_t pgd)       { return 1; }
-extern inline void pgd_clear(pgd_t * pgdp)     { }
+#define pgd_clear(xp)  do { pgd_val(*(xp)) = 0; } while (0)
+
+#define pgd_page(pgd) \
+       ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))
 
 /*
  * The following only work if pte_present() is true.
@@ -313,43 +340,37 @@ extern inline pte_t pte_mkyoung(pte_t pte) {
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
  */
-#if 1
 #define set_pte(pteptr, pteval)        ((*(pteptr)) = (pteval))
-#else
-extern inline void set_pte(pte_t *pteptr, pte_t pteval)
-{
-       unsigned long val = pte_val(pteval);
-       extern void xmon(void *);
-
-       if ((val & _PAGE_PRESENT) && ((val < 0x111000 || (val & 0x800)
-           || ((val & _PAGE_HWWRITE) && (~val & (_PAGE_RW|_PAGE_DIRTY)))) {
-               printk("bad pte val %lx ptr=%p\n", val, pteptr);
-               xmon(0);
-       }
-       *pteptr = pteval;
-}
-#endif
 
 /*
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
  */
 
-static inline pte_t mk_pte_phys(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = (page) | pgprot_val(pgprot); return pte; }
+extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
+{
+       pte_t pte;
+       pte_val(pte) = physpage | pgprot_val(pgprot);
+       return pte;
+}
 
-extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = __pa(page) | pgprot_val(pgprot); return pte; }
+extern inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
+{
+       pte_t pte;
+       pte_val(pte) = ((page - mem_map) << PAGE_SHIFT) | pgprot_val(pgprot);
+       return pte;
+}
 
 extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
-{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
-
-extern inline unsigned long pte_page(pte_t pte)
-{ return (unsigned long) __va(pte_val(pte) & PAGE_MASK); }
+{
+       pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot);
+       return pte;
+}
 
-extern inline unsigned long pmd_page(pmd_t pmd)
-{ return pmd_val(pmd); }
+#define page_pte_prot(page,prot) mk_pte(page, prot)
+#define page_pte(page) page_pte_prot(page, __pgprot(0))
 
+#define pmd_page(pmd)  (pmd_val(pmd))
 
 /* to find an entry in a kernel page-table-directory */
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
@@ -418,7 +439,7 @@ extern unsigned long get_zero_page_fast(void);
 
 extern __inline__ pgd_t *get_pgd_slow(void)
 {
-       pgd_t *ret/* = (pgd_t *)__get_free_page(GFP_KERNEL)*/, *init;
+       pgd_t *ret, *init;
 
        if ( (ret = (pgd_t *)get_zero_page_fast()) == NULL )
        {
@@ -427,7 +448,6 @@ extern __inline__ pgd_t *get_pgd_slow(void)
        }
        if (ret) {
                init = pgd_offset(&init_mm, 0);
-               /*memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));*/
                memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
                        (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
        }
@@ -612,9 +632,9 @@ extern void flush_hash_segments(unsigned low_vsid, unsigned high_vsid);
 extern void flush_hash_page(unsigned context, unsigned long va);
 
 
-#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
-#define SWP_OFFSET(entry) ((entry) >> 8)
-#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+#define SWP_TYPE(entry) (((pte_val(entry)) >> 1) & 0x7f)
+#define SWP_OFFSET(entry) ((pte_val(entry)) >> 8)
+#define SWP_ENTRY(type,offset) __pte(((type) << 1) | ((offset) << 8))
 
 #define module_map      vmalloc
 #define module_unmap    vfree
index d6521ab67b40697ba7254a1236b653c15dfc8d74..1f5ec5f7a201aef8d29b55025ae05a4a41f5582b 100644 (file)
@@ -28,8 +28,8 @@
 #define ACPI_MINOR_DEV 167
 
 /* RSDP location */
-#define ACPI_BIOS_ROM_BASE ((__u8*) 0xe0000)
-#define ACPI_BIOS_ROM_END  ((__u8*) 0x100000)
+#define ACPI_BIOS_ROM_BASE (0x0e0000)
+#define ACPI_BIOS_ROM_END  (0x100000)
 
 /* Table signatures */
 #define ACPI_RSDP1_SIG 0x20445352 /* 'RSD ' */
index e0e9e2993b8b8021ac56037c159f46822b26f40a..3879d1e6170f12d3c885b08411187b7568a91525 100644 (file)
@@ -59,7 +59,7 @@ extern inline void memclear_highpage_flush(struct page *page, unsigned int offse
                BUG();
        kaddr = kmap(page, KM_WRITE);
        memset((void *)(kaddr + offset), 0, size);
-       flush_page_to_ram(kaddr);
+       flush_page_to_ram(page);
        kunmap(kaddr, KM_WRITE);
 }
 
index 2325ad3b2083a958ac51e723278134f71a00f2ad..f8e6b970566ac320d00c0912d97b1c3aa6c22716 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * cs.h 1.67 1999/09/02 18:34:59
+ * cs.h 1.68 1999/10/20 18:59:32
  *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
@@ -292,6 +292,7 @@ typedef struct win_req_t {
 #define WIN_SHARED             0x0040
 #define WIN_FIRST_SHARED       0x0080
 #define WIN_USE_WAIT           0x0100
+#define WIN_STRICT_ALIGN       0x0200
 #define WIN_MAP_BELOW_1MB      0x0400
 #define WIN_PREFETCH           0x0800
 #define WIN_CACHEABLE          0x1000
@@ -439,30 +440,6 @@ extern int CardServices(int func, void *a1, void *a2, void *a3);
 extern int CardServices(int func, ...);
 #endif
 
-#ifdef __BEOS__
-#define SS_MODULE_NAME(s)      ("busses/pcmcia/" s "/v1")
-#define MTD_MODULE_NAME(s)     ("busses/pcmcia/" s "/v1")
-#define CS_CLIENT_MODULE_NAME  "bus_managers/pcmcia_cs/client/v1"
-typedef struct cs_client_module_info {
-    bus_manager_info   binfo;
-    int (*_CardServices)(int, ...);
-    int (*_MTDHelperEntry)(int, ...);
-    void (*_add_timer)(struct timer_list *);
-    void (*_del_timer)(struct timer_list *);
-} cs_client_module_info;
-#define CS_SOCKET_MODULE_NAME "bus_managers/pcmcia_cs/socket/v1"
-typedef struct cs_socket_module_info {
-    bus_manager_info   binfo;
-    int (*_register_ss_entry)(int, ss_entry_t);
-    void (*_unregister_ss_entry)(ss_entry_t);
-    void (*_add_timer)(struct timer_list *);
-    void (*_del_timer)(struct timer_list *);
-    int (*register_resource)(int, u_long, u_long);
-    int (*release_resource)(int, u_long, u_long);
-    int (*check_resource)(int, u_long, u_long);
-} cs_socket_module_info;
-#endif
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_CS_H */
index 3df641d41ca1bcae6b2a7113a50a28e4eaaee8ab..5e2a688a0c016ddd671ba34ddb781adcbb499044 100644 (file)
@@ -30,9 +30,7 @@
 #ifndef _LINUX_CS_TYPES_H
 #define _LINUX_CS_TYPES_H
 
-#ifdef __linux__
 #include <linux/types.h>
-#endif
 
 typedef u_short        socket_t;
 typedef u_short        ioaddr_t;
index 395374cc081d97c352f2fc52abb4813b20e7bdbd..88bcaa9dd7b4866245f0977afd91cee024f6f4fa 100644 (file)
@@ -68,15 +68,6 @@ typedef struct driver_operations {
 int register_driver(struct driver_operations *ops);
 void unregister_driver(struct driver_operations *ops);
 
-#ifdef __BEOS__
-#define CB_ENABLER_MODULE_NAME "bus_managers/cb_enabler/v1"
-typedef struct cb_enabler_module_info {
-    bus_manager_info   binfo;
-    int (*register_driver)(struct driver_operations *ops);
-    void (*unregister_driver)(struct driver_operations *ops);
-} cb_enabler_module_info;
-#endif /* __BEOS__ */
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_DRIVER_OPS_H */
index 06aa7f1cb62793da496bfac24ffddfafa4b63d2f..2663780b5222f4bedf86c9509ecfe61c1bba7a62 100644 (file)
@@ -143,21 +143,6 @@ int unregister_pccard_driver(dev_info_t *dev_info);
 #define register_pcmcia_driver register_pccard_driver
 #define unregister_pcmcia_driver unregister_pccard_driver
 
-#ifdef __BEOS__
-#define DS_MODULE_NAME "bus_managers/pcmcia_ds/v1"
-typedef struct ds_module_info {
-    bus_manager_info binfo;
-    int (*_register_pccard_driver)(dev_info_t *,
-                                  dev_link_t *(*)(void),
-                                  void (*)(dev_link_t *));
-    int (*_unregister_pccard_driver)(dev_info_t *);
-    struct driver_info_t **root_driver;
-    int *sockets;
-    struct socket_info_t **socket_table;
-    sem_id *list_sem;
-} ds_module_info;
-#endif /* __BEOS__ */
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_DS_H */
index 0df81b31e277974bf66932af71aedebd28bc2e16..bb0b27e40af28e5bf0d218e38853e5bd55d08a11 100644 (file)
@@ -1,4 +1,4 @@
-/* version.h 1.72 1999/08/05 06:09:53 (David Hinds) */
+/* version.h 1.74 1999/09/29 20:41:44 (David Hinds) */
 
-#define CS_RELEASE "3.1.0"
-#define CS_RELEASE_CODE 0x3100
+#define CS_RELEASE "3.1.2"
+#define CS_RELEASE_CODE 0x3102
index 6d14da625447dc991480399a8a5651b028e7389f..4945a0223e6141c85c0a1de12de35ea8582df6ef 100644 (file)
@@ -52,12 +52,12 @@ repeat:
        if (write) {
                maddr = kmap(page, KM_WRITE);
                memcpy((char *)maddr + (addr & ~PAGE_MASK), buf, len);
-               flush_page_to_ram(maddr);
+               flush_page_to_ram(page);
                kunmap(maddr, KM_WRITE);
        } else {
                maddr = kmap(page, KM_READ);
                memcpy(buf, (char *)maddr + (addr & ~PAGE_MASK), len);
-               flush_page_to_ram(maddr);
+               flush_page_to_ram(page);
                kunmap(maddr, KM_READ);
        }
        return len;
index 88979392828656b6147836f90b4f563c95961a31..fb9f4551e0e519322a5527dd6370360fc18a1b81 100644 (file)
@@ -742,7 +742,7 @@ struct page * put_dirty_page(struct task_struct * tsk, struct page *page,
                __free_page(page);
                return 0;
        }
-       flush_page_to_ram(pte_page(page));
+       flush_page_to_ram(page);
        set_pte(pte, pte_mkwrite(page_pte_prot(page, PAGE_COPY)));
 /* no need for flush_tlb */
        return page;
index 0a78127f2c9817da3340dde99402704d644b1a38..c3f6b271c6cb77cbd0446c0519b68cc987f299e7 100644 (file)
@@ -50,7 +50,10 @@ static struct inode_operations swapper_inode_operations = {
        NULL                            /* revalidate */
 };
 
-struct inode swapper_inode = { i_op: &swapper_inode_operations };
+struct inode swapper_inode = {
+       i_op: &swapper_inode_operations,
+       i_pages: {&swapper_inode.i_pages,&swapper_inode.i_pages}
+};
 
 #ifdef SWAP_CACHE_INFO
 unsigned long swap_cache_add_total = 0;
index ebe589d856d0c3aedb49ae5cc1a8957c9f4e852a..b0c25309e9a97f073c34412a4e57ec6810f50d28 100644 (file)
@@ -37,7 +37,9 @@ static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned lo
                if (pte_none(page))
                        continue;
                if (pte_present(page)) {
-                       __free_page(mem_map+pte_pagenr(page));
+                       unsigned long map_nr = pte_pagenr(page);
+                       if (map_nr < max_mapnr)
+                               __free_page(mem_map + map_nr);
                        continue;
                }
                printk("Whee.. Swapped out page in kernel page table\n");