From df72ed19d6fff6120b7970ed3ba07d3c1bd4e935 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:27:57 -0500 Subject: [PATCH] Import 2.3.23pre6 --- CREDITS | 10 +- Documentation/Changes | 6 +- Documentation/Configure.help | 136 +- MAINTAINERS | 12 +- arch/arm/boot/compressed/misc.c | 2 +- arch/arm/mm/init.c | 35 +- arch/i386/defconfig | 16 +- arch/i386/kernel/setup.c | 6 +- arch/ppc/kernel/apus_setup.c | 3 +- arch/ppc/kernel/chrp_setup.c | 99 +- arch/ppc/kernel/gemini_setup.c | 2 +- arch/ppc/kernel/idle.c | 7 +- arch/ppc/kernel/irq.c | 14 +- arch/ppc/kernel/m8xx_setup.c | 10 +- arch/ppc/kernel/mbx_setup.c | 9 +- arch/ppc/kernel/misc.S | 4 +- arch/ppc/kernel/pci.c | 22 + arch/ppc/kernel/pmac_pci.c | 22 +- arch/ppc/kernel/pmac_pic.c | 9 +- arch/ppc/kernel/pmac_setup.c | 4 +- arch/ppc/kernel/ppc_ksyms.c | 1 - arch/ppc/kernel/prep_setup.c | 2 +- arch/ppc/kernel/setup.c | 16 +- arch/ppc/mm/init.c | 298 +- drivers/block/icside.c | 1 - drivers/block/rd.c | 3 +- drivers/block/sl82c105.c | 2 +- drivers/char/mem.c | 3 + drivers/char/vt.c | 7 +- drivers/macintosh/macserial.c | 571 +++- drivers/macintosh/macserial.h | 49 +- drivers/misc/acpi.c | 12 +- drivers/net/Makefile | 12 +- drivers/net/irda/w83977af_ir.c | 8 +- drivers/net/pcmcia/3c574_cs.c | 1494 +++++++++ drivers/net/pcmcia/3c589_cs.c | 40 +- drivers/net/pcmcia/Config.in | 31 +- drivers/net/pcmcia/Makefile | 88 +- drivers/net/pcmcia/fmvj18x_cs.c | 1205 ++++++++ drivers/net/pcmcia/i82593.h | 224 ++ drivers/net/pcmcia/netwave_cs.c | 1709 +++++++++++ drivers/net/pcmcia/nmclan_cs.c | 1778 +++++++++++ drivers/net/pcmcia/ositech.h | 358 +++ drivers/net/pcmcia/pcnet_cs.c | 21 +- drivers/net/pcmcia/smc91c92_cs.c | 2013 +++++++++++++ drivers/net/pcmcia/wavelan.h | 383 +++ drivers/net/pcmcia/wavelan_cs.c | 4856 ++++++++++++++++++++++++++++++ drivers/net/pcmcia/wavelan_cs.h | 773 +++++ drivers/net/pcmcia/xirc2ps_cs.c | 2364 +++++++++++++++ drivers/pcmcia/Config.in | 6 +- drivers/pcmcia/Makefile | 18 +- drivers/pcmcia/cardbus.c | 13 +- drivers/pcmcia/cb_enabler.c | 7 +- drivers/pcmcia/cs.c | 66 +- drivers/pcmcia/i82365.c | 367 +-- drivers/pcmcia/o2micro.h | 35 +- drivers/pcmcia/rsrc_mgr.c | 120 +- drivers/pcmcia/rsrc_mgr.h | 6 - drivers/sound/dev_table.h | 2 +- drivers/sound/waveartist.c | 1314 ++++---- drivers/sound/waveartist.h | 21 +- drivers/usb/uhci.c | 71 +- drivers/usb/uhci.h | 2 +- drivers/usb/usb.h | 2 +- drivers/video/fbmem.c | 5 + fs/exec.c | 2 +- include/asm-arm/checksum.h | 4 +- include/asm-ppc/machdep.h | 3 +- include/asm-ppc/page.h | 2 +- include/asm-ppc/pci-bridge.h | 2 +- include/asm-ppc/pgtable.h | 110 +- include/linux/acpi.h | 4 +- include/linux/highmem.h | 2 +- include/pcmcia/cs.h | 27 +- include/pcmcia/cs_types.h | 2 - include/pcmcia/driver_ops.h | 9 - include/pcmcia/ds.h | 15 - include/pcmcia/version.h | 6 +- kernel/ptrace.c | 4 +- mm/memory.c | 2 +- mm/swap_state.c | 5 +- mm/vmalloc.c | 4 +- 82 files changed, 19297 insertions(+), 1711 deletions(-) create mode 100644 drivers/net/pcmcia/3c574_cs.c create mode 100644 drivers/net/pcmcia/fmvj18x_cs.c create mode 100644 drivers/net/pcmcia/i82593.h create mode 100644 drivers/net/pcmcia/netwave_cs.c create mode 100644 drivers/net/pcmcia/nmclan_cs.c create mode 100644 drivers/net/pcmcia/ositech.h create mode 100644 drivers/net/pcmcia/smc91c92_cs.c create mode 100644 drivers/net/pcmcia/wavelan.h create mode 100644 drivers/net/pcmcia/wavelan_cs.c create mode 100644 drivers/net/pcmcia/wavelan_cs.h create mode 100644 drivers/net/pcmcia/xirc2ps_cs.c diff --git a/CREDITS b/CREDITS index 70a53842e246..56427bd90212 100644 --- 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 diff --git a/Documentation/Changes b/Documentation/Changes index 7048d0f14c60..a1c994b2a13e 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -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 ========= diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 619bc25aaaec..0e4511e5b21e 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index 628715592dad..e92c24e876c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 0a18737b247b..66b4f2d0e870 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -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 diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 0c2ffec4eecc..115cec885d82 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -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(); diff --git a/arch/i386/defconfig b/arch/i386/defconfig index d6821a99333d..8b63706674b3 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -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 # diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index e6a61e821577..31c77bb1d3b0 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -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) { diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index 80377490a326..353482a18170 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -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[]; diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 934f377b50c9..58d2003c495f 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c index 6e599cff2134..23060ba2673e 100644 --- a/arch/ppc/kernel/gemini_setup.c +++ b/arch/ppc/kernel/gemini_setup.c @@ -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[]; diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 09066a84870a..42ebfd9f4f63 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -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: diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 9c157d41b9fd..9ab3589f6cb8 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -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 ); } diff --git a/arch/ppc/kernel/m8xx_setup.c b/arch/ppc/kernel/m8xx_setup.c index f99f5b3357d8..4d510612bd1b 100644 --- a/arch/ppc/kernel/m8xx_setup.c +++ b/arch/ppc/kernel/m8xx_setup.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -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 diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index 5ba7d878e914..f6487783bcf8 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -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"); diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 4d041cc98b18..89994881beb0 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -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 diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index de0a0a36d592..0d07c289d906 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -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 & 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 */ +} diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 932e7dbb6d64..62161f68a54b 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index 683aac568282..f7224f5dd38d 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -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 */ diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 41ff21aed173..0a23c4473d03 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -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 */ diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index cabce054c900..399b99052ba9 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -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); diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index a04347d4b97b..fe7043206541 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -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; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 7177c800677b..518446bf96e4 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -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); } diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index a708c59426bf..7ccbe656f552 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -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 #include #include +#include #ifdef CONFIG_BLK_DEV_INITRD #include /* 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 */ diff --git a/drivers/block/icside.c b/drivers/block/icside.c index a9282de3fa5b..5d007fce3c52 100644 --- a/drivers/block/icside.c +++ b/drivers/block/icside.c @@ -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) { diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 7aeb3a30f846..7c2b23dab115 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -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; diff --git a/drivers/block/sl82c105.c b/drivers/block/sl82c105.c index d4e22d33b945..fa47adbb70fe 100644 --- a/drivers/block/sl82c105.c +++ b/drivers/block/sl82c105.c @@ -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 */ diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 42ea12a2c736..7d5e5f11ea97 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -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; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 3595fff91f8e..3480c1bf041e 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -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). diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 53590a42a4bd..6b5c57f99316 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -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 . + * + * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $ */ #include @@ -26,6 +30,7 @@ #ifdef CONFIG_SERIAL_CONSOLE #include #endif +#include #include #include @@ -41,6 +46,7 @@ #ifdef CONFIG_KGDB #include #endif +#include #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); diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h index e2f137f73916..3ba2e60624fa 100644 --- a/drivers/macintosh/macserial.h +++ b/drivers/macintosh/macserial.h @@ -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 */ diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c index e6878dde6826..03c39fb613a0 100644 --- a/drivers/misc/acpi.c +++ b/drivers/misc/acpi.c @@ -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); diff --git a/drivers/net/Makefile b/drivers/net/Makefile index fc3541048880..c2971c8daabf 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -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 diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 0885e74a3037..32e965275e59 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -10,16 +10,16 @@ * Modified by: Dag Brattli * * Copyright (c) 1998-1999 Dag Brattli - * 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 index 000000000000..40c53a2cf8d7 --- /dev/null +++ b/drivers/net/pcmcia/3c574_cs.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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< 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<= 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: + */ diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 6e511f4b7e3a..dea14d53a2cb 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -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) diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in index cc4efbf886dc..cca2ff57aa0d 100644 --- a/drivers/net/pcmcia/Config.in +++ b/drivers/net/pcmcia/Config.in @@ -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 diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile index 3b59b1b93ed9..779abb20ce47 100644 --- a/drivers/net/pcmcia/Makefile +++ b/drivers/net/pcmcia/Makefile @@ -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 index 000000000000..82e3ce57ac7f --- /dev/null +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + 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 index 000000000000..6d66baf6de05 --- /dev/null +++ b/drivers/net/pcmcia/i82593.h @@ -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 + * + * 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 index 000000000000..0e9a1045f84e --- /dev/null +++ b/drivers/net/pcmcia/netwave_cs.c @@ -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 + * Dag Brattli + * David Hinds + * Created at: A long time ago! + * Modified at: Mon Nov 10 11:54:37 1997 + * Modified by: Dag Brattli + * + * Copyright (c) 1997 University of Tromsø, Norway + * + * Revision History: + * + * 08-Nov-97 15:14:47 John Markus Bjørndalen + * - 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 + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_NET_RADIO +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..45aab6d625d7 --- /dev/null +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -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 + 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 + +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. + Donald Becker + David Hinds + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------------- +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, ®); + 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, ®); + /* 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, ®); + /* 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 index 000000000000..749bbaf21944 --- /dev/null +++ b/drivers/net/pcmcia/ositech.h @@ -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 + + }; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index f5d4b3c49c7e..26fa47ee96bc 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -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 index 000000000000..3abdf7d9d009 --- /dev/null +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 000000000000..0e1ee227de51 --- /dev/null +++ b/drivers/net/pcmcia/wavelan.h @@ -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 index 000000000000..5e9c9e6e21bd --- /dev/null +++ b/drivers/net/pcmcia/wavelan_cs.c @@ -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 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;isigqual[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;isigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + average_slow=average_fast; + for(i=WAVEPOINT_FAST_HISTORY;isigqual[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; /* */ + + 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; /* :-) */ +} + +/* 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, ®); + 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, ®); + 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, ®); + 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 index 000000000000..84dc2114d0cd --- /dev/null +++ b/drivers/net/pcmcia/wavelan_cs.h @@ -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 + * 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 , + * writting a Wavelan ISA driver for the MACH microkernel. Girish + * Welling had also worked on it. + * Keith Moore modify this for the Pcmcia hardware. + * + * Robert Morris port these two drivers to BSDI + * and add specific Pcmcia support (there is currently no equivalent + * of the PCMCIA package under BSD...). + * + * Jim Binkley port both BSDI drivers to FreeBSD. + * + * Bruce Janson port the BSDI ISA driver to Linux. + * + * Anthony D. Joseph started modify Bruce driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li finished is work. + * Joe Finney patched the driver to start + * correctly 2.00 cards (2.4 GHz with frequency selection). + * David Hinds 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 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 , + * Loeke Brederveld , + * Allan Creighton (allanc@cs.su.oz.au), + * Brent Elphick , + * Joe Finney , + * Matthew Geier (matthew@cs.su.oz.au), + * Remo di Giovanni (remo@cs.su.oz.au), + * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + * David Hinds , + * Jan Hoogendoorn (c/o marteijn@lucent.com), + * Bruce Janson , + * Anthony D. Joseph , + * Anders Klemets (klemets@paul.rutgers.edu), + * Yunzhou Li , + * 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 + * Peter Storey, + * Jean Tourrilhes , + * Girish Welling (welling@paul.rutgers.edu) + * Clark Woodworth + * Yongguang Zhang ... + */ + +/* ------------------------- 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 & David Hinds : + * ----------------------------------------------------------------- + * - Roaming "hack" from Joe Finney + * - PSA CRC code from Bob Gray + * - Better initialisation of the i82593 controller + * from Joseph K. O'Sullivan + * + * 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 + * - 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 + * - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_RADIO +#include /* Wireless extensions */ +#endif + +/* Pcmcia headers that we need */ +#include +#include +#include +#include +#include +#include + +/* 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 =' + */ + +/* 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 index 000000000000..d09230655989 --- /dev/null +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + +/* 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, + ® )) ) { + 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, + ® )) ) { + 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); + diff --git a/drivers/pcmcia/Config.in b/drivers/pcmcia/Config.in index 2883046c1332..aaedbf74a6b0 100644 --- a/drivers/pcmcia/Config.in +++ b/drivers/pcmcia/Config.in @@ -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 diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 71750544d228..703a7c6c8f02 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -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 diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index de8b76da328d..490182e0093d 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -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; diff --git a/drivers/pcmcia/cb_enabler.c b/drivers/pcmcia/cb_enabler.c index b0e75ceecade..00cf824b31ba 100644 --- a/drivers/pcmcia/cb_enabler.c +++ b/drivers/pcmcia/cb_enabler.c @@ -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; diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 470514c556af..f7fda7d8da19 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -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; diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 9e80943e4d95..ed7bd3c30e88 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -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<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<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<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); - } } diff --git a/drivers/pcmcia/rsrc_mgr.h b/drivers/pcmcia/rsrc_mgr.h index 3b7f12e0931c..37faa3b26c67 100644 --- a/drivers/pcmcia/rsrc_mgr.h +++ b/drivers/pcmcia/rsrc_mgr.h @@ -30,10 +30,4 @@ #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 */ diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 0ce2c49b06fe..14c8cd2ac3f7 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -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 diff --git a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c index 85a9efaec91a..0d2145347ed6 100644 --- a/drivers/sound/waveartist.c +++ b/drivers/sound/waveartist.c @@ -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 * @@ -29,19 +30,17 @@ #define debug_flg (0) -#define DEB(x) -#define DDB(x) -#define DEB1(x) - #include #include #include #include #include #include +#include #include #include +#include #include "soundmodule.h" #include "sound_config.h" @@ -54,45 +53,24 @@ #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 diff --git a/drivers/sound/waveartist.h b/drivers/sound/waveartist.h index 45b62d1b55f1..2d2163fa875a 100644 --- a/drivers/sound/waveartist.h +++ b/drivers/sound/waveartist.h @@ -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 @@ -56,8 +60,11 @@ #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); diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index c08feee15ba0..20be3faab8e2 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -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); diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 96bd338867f7..e460fb858cb4 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -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) diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index 5581b2e0da00..f31c5b60ba3f 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -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 */ diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 24504b17a84d..9bcb6920edfd 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -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 diff --git a/fs/exec.c b/fs/exec.c index 1961ec33a49a..dea4f07120b3 100644 --- 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) diff --git a/include/asm-arm/checksum.h b/include/asm-arm/checksum.h index 41f2c05bda90..b827c490ceb9 100644 --- a/include/asm-arm/checksum.h +++ b/include/asm-arm/checksum.h @@ -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; } diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index 6f85554cadf6..40e5ea046d49 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -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. */ diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index b67ca5cc0f9e..55168a8b2b3e 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -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 */ diff --git a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h index e3f3c15909b0..47a2cc87db79 100644 --- a/include/asm-ppc/pci-bridge.h +++ b/include/asm-ppc/pci-bridge.h @@ -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 diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 3a822590b18a..ad2eba41fd9f 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -5,6 +5,7 @@ #ifndef __ASSEMBLY__ #include +#include #include /* For TASK_SIZE */ #include #include @@ -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 diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d6521ab67b40..1f5ec5f7a201 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -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 ' */ diff --git a/include/linux/highmem.h b/include/linux/highmem.h index e0e9e2993b8b..3879d1e6170f 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -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); } diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 2325ad3b2083..f8e6b970566a 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -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 */ diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h index 3df641d41ca1..5e2a688a0c01 100644 --- a/include/pcmcia/cs_types.h +++ b/include/pcmcia/cs_types.h @@ -30,9 +30,7 @@ #ifndef _LINUX_CS_TYPES_H #define _LINUX_CS_TYPES_H -#ifdef __linux__ #include -#endif typedef u_short socket_t; typedef u_short ioaddr_t; diff --git a/include/pcmcia/driver_ops.h b/include/pcmcia/driver_ops.h index 395374cc081d..88bcaa9dd7b4 100644 --- a/include/pcmcia/driver_ops.h +++ b/include/pcmcia/driver_ops.h @@ -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 */ diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 06aa7f1cb627..2663780b5222 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -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 */ diff --git a/include/pcmcia/version.h b/include/pcmcia/version.h index 0df81b31e277..bb0b27e40af2 100644 --- a/include/pcmcia/version.h +++ b/include/pcmcia/version.h @@ -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 diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6d14da625447..4945a0223e61 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -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; diff --git a/mm/memory.c b/mm/memory.c index 889793928286..fb9f4551e0e5 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -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; diff --git a/mm/swap_state.c b/mm/swap_state.c index 0a78127f2c98..c3f6b271c6cb 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -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; diff --git a/mm/vmalloc.c b/mm/vmalloc.c index ebe589d856d0..b0c25309e9a9 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -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"); -- 2.39.5