## Automatically generated incremental diff ## From: linux-2.5.3-pre2 ## To: linux-2.5.3-pre3 ## Robot: $Id: make-incremental-diff,v 1.9 2001/12/10 00:06:56 hpa Exp $ diff -urN linux-2.5.3-pre2/CREDITS linux/CREDITS --- linux-2.5.3-pre2/CREDITS Mon Jan 21 17:22:31 2002 +++ linux/CREDITS Mon Jan 21 17:23:22 2002 @@ -1281,6 +1281,13 @@ D: bug toaster (A1 sauce makes all the difference) D: Random linux hacker +N: Tim Hockin +E: thockin@hockin.org +W: http://www.hockin.org/~thockin +D: Natsemi ethernet +D: Cobalt Networks (x86) support +D: This-and-That + N: Dirk Hohndel E: hohndel@suse.de D: The XFree86[tm] Project @@ -1451,11 +1458,10 @@ N: Dave Jones E: davej@suse.de -W: http://www.suse.de/~davej -D: Moved PCI bridge tuning to userspace (Powertweak). -D: Various x86 (& clones) setup code hacking. -D: AFFS fixes for 2.3.x -D: Various Janitorial hacks. (kernel-janitor.sourceforge.net) +W: http://www.codemonkey.org.uk +D: x86 errata/setup maintenance. +D: Backport/Forwardport merge monkey. +D: Various Janitor work. S: c/o SuSE Linux UK Ltd S: The Kinetic Centre S: Theobald Street @@ -1660,6 +1666,13 @@ S: 80-283 Gdansk S: Poland +N: Jakob Kemi +E: jakob.kemi@telia.com +D: V4L W9966 Webcam driver +S: Forsbyvägen 33 +S: 74143 Knivsta +S: Sweden + N: Gero Kuhlmann E: gero@gkminix.han.de D: mounting root via NFS @@ -1832,6 +1845,11 @@ S: 00200 Helsinki S: Finland +N: Daniel J. Maas +E: dmaas@dcine.com +W: http://www.maasdigital.com +D: dv1394 + N: Hamish Macdonald E: hamishm@lucent.com D: Linux/68k port @@ -2526,7 +2544,7 @@ S: Australia N: Gerard Roudier -E: groudier@iplus.fr +E: groudier@free.fr D: Contributed to asynchronous read-ahead improvement S: 21 Rue Carnot S: 95170 Deuil La Barre diff -urN linux-2.5.3-pre2/Documentation/Changes linux/Documentation/Changes --- linux-2.5.3-pre2/Documentation/Changes Fri Nov 30 08:53:20 2001 +++ linux/Documentation/Changes Mon Jan 21 17:23:22 2002 @@ -50,10 +50,10 @@ o Gnu C 2.95.3 # gcc --version o Gnu make 3.77 # make --version -o binutils 2.9.5.0.24 # ld -v +o binutils 2.9.5.0.25 # ld -v o util-linux 2.10o # fdformat --version o modutils 2.4.2 # insmod -V -o e2fsprogs 1.19 # tune2fs +o e2fsprogs 1.25 # tune2fs o reiserfsprogs 3.x.0j # reiserfsck 2>&1|grep reiserfsprogs o pcmcia-cs 3.1.21 # cardmgr -V o PPP 2.4.0 # pppd --version @@ -304,8 +304,7 @@ E2fsprogs --------- -o -o +o Reiserfsprogs ------------- diff -urN linux-2.5.3-pre2/Documentation/Configure.help linux/Documentation/Configure.help --- linux-2.5.3-pre2/Documentation/Configure.help Mon Jan 21 17:22:32 2002 +++ linux/Documentation/Configure.help Mon Jan 21 17:23:22 2002 @@ -8144,6 +8144,19 @@ this option only if you have an IEEE 1394 video device connected to an OHCI-1394 card. +OHCI-DV I/O support +CONFIG_IEEE1394_DV1394 + This driver allows you to transmit and receive DV (digital video) + streams on an OHCI-1394 card using a simple frame-oriented + interface. + + The user-space API for dv1394 is documented in dv1394.h. + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called dv1394.o. + SBP-2 support (Harddisks etc.) CONFIG_IEEE1394_SBP2 This option enables you to use SBP-2 devices connected to your IEEE diff -urN linux-2.5.3-pre2/Documentation/DocBook/Makefile linux/Documentation/DocBook/Makefile --- linux-2.5.3-pre2/Documentation/DocBook/Makefile Tue Jan 8 13:15:52 2002 +++ linux/Documentation/DocBook/Makefile Mon Jan 21 17:23:22 2002 @@ -93,6 +93,8 @@ $(TOPDIR)/drivers/net/8390.c \ $(TOPDIR)/drivers/char/serial.c \ $(TOPDIR)/drivers/pci/pci.c \ + $(TOPDIR)/drivers/hotplug/pci_hotplug_core.c \ + $(TOPDIR)/drivers/hotplug/pci_hotplug_util.c \ $(TOPDIR)/drivers/block/ll_rw_blk.c \ $(TOPDIR)/drivers/sound/sound_core.c \ $(TOPDIR)/drivers/sound/sound_firmware.c \ diff -urN linux-2.5.3-pre2/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl --- linux-2.5.3-pre2/Documentation/DocBook/kernel-api.tmpl Tue Jan 8 13:15:52 2002 +++ linux/Documentation/DocBook/kernel-api.tmpl Mon Jan 21 17:23:22 2002 @@ -162,6 +162,10 @@ PCI Support Library !Edrivers/pci/pci.c + PCI Hotplug Support Library +!Edrivers/hotplug/pci_hotplug_core.c +!Edrivers/hotplug/pci_hotplug_util.c + MCA Architecture MCA Device Functions !Earch/i386/kernel/mca.c diff -urN linux-2.5.3-pre2/Documentation/IRQ-affinity.txt linux/Documentation/IRQ-affinity.txt --- linux-2.5.3-pre2/Documentation/IRQ-affinity.txt Thu Feb 24 22:41:16 2000 +++ linux/Documentation/IRQ-affinity.txt Mon Jan 21 17:23:22 2002 @@ -8,7 +8,7 @@ affinity then the value will not change from the default 0xffffffff. Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting -the IRQ to CPU4-8 (this is an 8-CPU SMP box): +the IRQ to CPU4-7 (this is an 8-CPU SMP box): [root@moon 44]# cat smp_affinity ffffffff diff -urN linux-2.5.3-pre2/Documentation/filesystems/devfs/ChangeLog linux/Documentation/filesystems/devfs/ChangeLog --- linux-2.5.3-pre2/Documentation/filesystems/devfs/ChangeLog Mon Jan 14 09:40:29 2002 +++ linux/Documentation/filesystems/devfs/ChangeLog Mon Jan 21 17:23:22 2002 @@ -1869,3 +1869,11 @@ - Added support for multiple Compaq cpqarray controllers - Fixed (rare, old) race in +=============================================================================== +Changes for patch v207 + +- Fixed deadlock bug in + +- Tag VFS deletable in if handle ignored + +- Updated README from master HTML file diff -urN linux-2.5.3-pre2/Documentation/filesystems/devfs/README linux/Documentation/filesystems/devfs/README --- linux-2.5.3-pre2/Documentation/filesystems/devfs/README Thu Dec 27 08:29:14 2001 +++ linux/Documentation/filesystems/devfs/README Mon Jan 21 17:23:22 2002 @@ -3,7 +3,16 @@ Linux Devfs (Device File System) FAQ Richard Gooch -21-DEC-2001 +20-JAN-2002 + + +Document languages: + + + + + + ----------------------------------------------------------------------------- @@ -69,6 +78,7 @@ What I don't like about devfs How to report bugs Strange kernel messages +Compilation problems with devfsd Other resources @@ -837,8 +847,8 @@ will now work as before. Hopefully for most people devfs will have enough support so that they -can mount devfs directly over /dev without loosing most functionality -(i.e. loosing access to various devices). As of 22-JAN-1998 (devfs +can mount devfs directly over /dev without losing most functionality +(i.e. losing access to various devices). As of 22-JAN-1998 (devfs patch version 10) I am now running this way. All the devices I have are available in devfs, so I don't lose anything. @@ -1477,6 +1487,7 @@ What I don't like about devfs How to report bugs Strange kernel messages +Compilation problems with devfsd @@ -1677,7 +1688,7 @@ minor limitation -simplying increasing the device number size is insufficient. Apart +simply increasing the device number size is insufficient. Apart from causing a lot of pain, it doesn't solve the management issues of a /dev with thousands or more device nodes @@ -1828,6 +1839,29 @@ + +Compilation problems with devfsd + +Usually, you can compile devfsd just by typing in +make in the source directory, followed by a make +install (as root). Sometimes, you may have problems, particularly +on broken configurations. + + + +error messages relating to DEVFSD_NOTIFY_DELETE + +This happened because you have an ancient set of kernel headers +installed in /usr/include/linux or /usr/src/linux. +Install kernel 2.4.10 or later. You may need to pass the +KERNEL_DIR variable to make (if you did not install +the new kernel sources as /usr/src/linux), or you may copy +the devfs_fs.h file in the kernel source tree into +/usr/include/linux. + + + + ----------------------------------------------------------------------------- @@ -1882,9 +1916,25 @@ +The document master (in English) by rgooch@atnf.csiro.au is +available at + +http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html + + + A Korean translation by viatoris@nownuri.net is available at -http://home.nownuri.net/~viatoris/devfs/devfs.html +http://home.nownuri.net/~viatoris/devfs/devfs.html + +A newer version is under construcation at +http://viatoris.new21.org/devfs/devfs.html + + +----------------------------------------------------------------------------- +Most flags courtesy of ITA's +Flags of All Countries +used with permission. diff -urN linux-2.5.3-pre2/Documentation/kernel-parameters.txt linux/Documentation/kernel-parameters.txt --- linux-2.5.3-pre2/Documentation/kernel-parameters.txt Wed Jun 20 11:21:33 2001 +++ linux/Documentation/kernel-parameters.txt Mon Jan 21 17:23:22 2002 @@ -125,6 +125,13 @@ BusLogic= [HW,SCSI] + cachesize= [BUGS=ix86] Override level 2 CPU cache size detection. + Sometimes CPU hardware bugs make them report the cache + size incorrectly. The kernel will attempt work arounds + to fix known problems, but for some CPUs it is not + possible to determine what the correct size should be. + This option provides an override for these situations. + cdu31a= [HW,CD] chandev= [HW,NET] diff -urN linux-2.5.3-pre2/Documentation/pci.txt linux/Documentation/pci.txt --- linux-2.5.3-pre2/Documentation/pci.txt Sun Nov 4 09:31:57 2001 +++ linux/Documentation/pci.txt Mon Jan 21 17:23:22 2002 @@ -104,6 +104,10 @@ If you are sure the driver is not a hotplug driver then use only __init/exit __initdata/exitdata. + Pointers to functions marked as __devexit must be created using + __devexit_p(function_name). That will generate the function + name or NULL if the __devexit function will be discarded. + 2. How to find PCI devices manually (the old style) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN linux-2.5.3-pre2/MAINTAINERS linux/MAINTAINERS --- linux-2.5.3-pre2/MAINTAINERS Mon Jan 21 17:22:32 2002 +++ linux/MAINTAINERS Mon Jan 21 17:23:22 2002 @@ -412,6 +412,12 @@ L: linux-decnet-user@lists.sourceforge.net S: Maintained +DELL LAPTOP SMM DRIVER +P: Massimo Dal Zotto +M: dz@debian.org +W: http://www.debian.org/~dz/i8k/ +S: Maintained + DEVICE NUMBER REGISTRY P: H. Peter Anvin M: hpa@zytor.com @@ -926,6 +932,12 @@ W: http://www.sistina.com/lvm S: Maintained +LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers +P: Gerard Roudier +M: groudier@free.fr +L: linux-scsi@vger.kernel.org +S: Maintained + M68K P: Jes Sorensen M: jes@trained-monkey.org @@ -1010,6 +1022,11 @@ M: andrewtv@usa.net S: Maintained +NATSEMI ETHERNET DRIVER (DP8381x) +P: Tim Hockin +M: thockin@hockin.org +S: Maintained + NCP FILESYSTEM P: Petr Vandrovec M: vandrove@vc.cvut.cz @@ -1133,8 +1150,8 @@ S: Maintained OPL3-SA2, SA3, and SAx DRIVER -P: Scott Murray -M: scott@spiteful.org +P: Zwane Mwaikambo +M: zwane@commfireservices.com L: linux-sound@vger.kernel.org S: Maintained diff -urN linux-2.5.3-pre2/Makefile linux/Makefile --- linux-2.5.3-pre2/Makefile Mon Jan 21 17:22:32 2002 +++ linux/Makefile Mon Jan 21 17:23:22 2002 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 3 -EXTRAVERSION =-pre2 +EXTRAVERSION =-pre3 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -urN linux-2.5.3-pre2/arch/cris/Makefile linux/arch/cris/Makefile --- linux-2.5.3-pre2/arch/cris/Makefile Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/Makefile Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.22 2001/10/01 14:42:38 bjornw Exp $ +# $Id: Makefile,v 1.3 2002/01/21 15:21:23 bjornw Exp $ # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -29,18 +29,17 @@ -e s/@CONFIG_ETRAX_DRAM_SIZE_M@/$(CONFIG_ETRAX_DRAM_SIZE)/ \ < $(LD_SCRIPT) > $(LD_SCRIPT).tmp; \ else true; \ - fi && $(CROSS_COMPILE)gcc -mlinux -nostdlib + fi && $(CROSS_COMPILE)ld -mcrislinux -LINKFLAGS = -mlinux -T $(LD_SCRIPT).tmp +LINKFLAGS = -T $(LD_SCRIPT).tmp # objcopy is used to make binary images from the resulting linked file OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -# normally, gcc on a linux box adds __linux__ but we do it "manually" -# -mlinux enables -march=v10, -fno-underscores among others +# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others -CFLAGS := $(CFLAGS) -mlinux -fno-strict-aliasing -pipe -D__linux__ +CFLAGS := $(CFLAGS) -mlinux -pipe ifdef CONFIG_ETRAX_KGDB CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g @@ -57,7 +56,7 @@ # each others config options SUBDIRS += arch/cris/boot/rescue endif -CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o +CORE_FILES := arch/cris/kernel/kernel.o arch/cris/mm/mm.o $(CORE_FILES) DRIVERS += arch/cris/drivers/drivers.o LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a) LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LIBGCC) diff -urN linux-2.5.3-pre2/arch/cris/README.mm linux/arch/cris/README.mm --- linux-2.5.3-pre2/arch/cris/README.mm Thu Feb 8 16:32:44 2001 +++ linux/arch/cris/README.mm Mon Jan 21 17:23:22 2002 @@ -3,6 +3,9 @@ HISTORY: $Log: README.mm,v $ +Revision 1.1.1.1 2001/12/17 13:59:27 bjornw +Import of Linux 2.5.1 + Revision 1.1 2000/07/10 16:25:21 bjornw Initial revision diff -urN linux-2.5.3-pre2/arch/cris/boot/compressed/README linux/arch/cris/boot/compressed/README --- linux-2.5.3-pre2/arch/cris/boot/compressed/README Thu Feb 8 16:32:44 2001 +++ linux/arch/cris/boot/compressed/README Mon Jan 21 17:23:22 2002 @@ -1,6 +1,6 @@ Creation of the self-extracting compressed kernel image (vmlinuz) ----------------------------------------------------------------- -$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $ +$Id: README,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ This can be slightly confusing because it's a process with many steps. diff -urN linux-2.5.3-pre2/arch/cris/boot/compressed/misc.c linux/arch/cris/boot/compressed/misc.c --- linux-2.5.3-pre2/arch/cris/boot/compressed/misc.c Tue May 1 16:04:56 2001 +++ linux/arch/cris/boot/compressed/misc.c Mon Jan 21 17:23:22 2002 @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $ + * $Id: misc.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/head.S linux/arch/cris/boot/rescue/head.S --- linux-2.5.3-pre2/arch/cris/boot/rescue/head.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/boot/rescue/head.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.8 2001/10/03 17:15:15 bjornw Exp $ +/* $Id: head.S,v 1.2 2001/12/18 13:35:12 bjornw Exp $ * * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition @@ -114,7 +114,7 @@ #define NOP_DI 0xf025050f #define RAM_INIT_MAGIC 0x56902387 - + .text ;; This is the entry point of the rescue code @@ -144,7 +144,13 @@ jumptarget: .dword 0xffffffff ; can be overwritten later to insert new code -no_newjump: +no_newjump: +#ifdef CONFIG_ETRAX_ETHERNET + ;; Start MII clock to make sure it is running when tranceiver is reset + move.d 0x3, $r0 ; enable = on, phy = mii_clk + move.d $r0, [R_NETWORK_GEN_CONFIG] +#endif + ;; We need to setup the bus registers before we start using the DRAM #include "../../lib/dram_init.S" diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/kimagerescue.S linux/arch/cris/boot/rescue/kimagerescue.S --- linux-2.5.3-pre2/arch/cris/boot/rescue/kimagerescue.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/boot/rescue/kimagerescue.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 bjornw Exp $ +/* $Id: kimagerescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * Rescue code to be prepended on a kimage and copied to the * rescue serial port. diff -urN linux-2.5.3-pre2/arch/cris/boot/rescue/testrescue.S linux/arch/cris/boot/rescue/testrescue.S --- linux-2.5.3-pre2/arch/cris/boot/rescue/testrescue.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/boot/rescue/testrescue.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 bjornw Exp $ +/* $Id: testrescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * Simple testcode to download by the rescue block. * Just lits some LEDs to show it was downloaded correctly. diff -urN linux-2.5.3-pre2/arch/cris/config.in linux/arch/cris/config.in --- linux-2.5.3-pre2/arch/cris/config.in Thu Jan 10 16:23:44 2002 +++ linux/arch/cris/config.in Mon Jan 21 17:23:22 2002 @@ -35,6 +35,9 @@ bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG +if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then + bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY +fi endmenu @@ -250,6 +253,6 @@ if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi -endmenu source lib/Config.in +endmenu diff -urN linux-2.5.3-pre2/arch/cris/drivers/Config.in linux/arch/cris/drivers/Config.in --- linux-2.5.3-pre2/arch/cris/drivers/Config.in Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/drivers/Config.in Mon Jan 21 17:23:22 2002 @@ -175,11 +175,11 @@ hex ' PB user changeable bits mask' CONFIG_ETRAX_PB_CHANGEABLE_BITS FF fi -bool 'ARTPEC-1 support' CONFIG_JULIETTE - -if [ "$CONFIG_JULIETTE" = "y" ]; then - source arch/cris/drivers/juliette/Config.in -fi +#bool 'ARTPEC-1 support' CONFIG_JULIETTE +# +#if [ "$CONFIG_JULIETTE" = "y" ]; then +# source arch/cris/drivers/juliette/Config.in +#fi bool 'USB host' CONFIG_ETRAX_USB_HOST if [ "$CONFIG_ETRAX_USB_HOST" = "y" ]; then diff -urN linux-2.5.3-pre2/arch/cris/drivers/axisflashmap.c linux/arch/cris/drivers/axisflashmap.c --- linux-2.5.3-pre2/arch/cris/drivers/axisflashmap.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/drivers/axisflashmap.c Mon Jan 21 17:23:22 2002 @@ -11,6 +11,17 @@ * partition split defined below. * * $Log: axisflashmap.c,v $ + * Revision 1.2 2001/12/18 13:35:15 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.17 2001/11/12 19:42:38 pkj + * Fixed compiler warnings. + * + * Revision 1.16 2001/11/08 11:18:58 jonashg + * Always read from uncached address to avoid problems with flushing + * cachelines after write and MTD-erase. No performance loss have been + * seen yet. + * * Revision 1.15 2001/10/19 12:41:04 jonashg * Name of probe has changed in MTD. * @@ -121,7 +132,7 @@ static void flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len); + memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len); } static void flash_write8(struct map_info *map, __u8 d, unsigned long adr) @@ -237,7 +248,7 @@ int use_default_ptable = 1; /* Until proven otherwise */ const char *pmsg = " /dev/flash%d at 0x%x, size 0x%x\n"; - printk(KERN_NOTICE "Axis flash mapping: %x at %x\n", + printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n", WINDOW_SIZE, FLASH_CACHED_ADDR); #ifdef CONFIG_MTD_CFI diff -urN linux-2.5.3-pre2/arch/cris/drivers/bluetooth/Makefile linux/arch/cris/drivers/bluetooth/Makefile --- linux-2.5.3-pre2/arch/cris/drivers/bluetooth/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/cris/drivers/bluetooth/Makefile Mon Jan 21 17:23:22 2002 @@ -0,0 +1,25 @@ +include $(APPS)/Rules.elinux + +all: + +install: src/bluetooth.c include/btcommon.h + ln -sfn ../../arch/cris/drivers/bluetooth/include ../../../../include/linux/bluetooth + if ! grep arch/cris/drivers/bluetooth/src/Config.in ../Config.in; then \ + echo '' >> ../Config.in; \ + echo 'if [ "$$CONFIG_ETRAX_SERIAL" = "y" ]; then' >> ../Config.in; \ + echo ' source arch/cris/drivers/bluetooth/src/Config.in' >> ../Config.in; \ + echo 'fi' >> ../Config.in; \ + fi + if ! grep bluetooth/src/bt.o ../Makefile; then \ + perl -pi -e "s:include:obj-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src/bt.o\nsubdir-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src\n\ninclude:" ../Makefile; \ + fi + +clean: + +src/bluetooth.c: + @echo "You must install the OpenBT src directory before install can be done here!" + @exit 1 + +include/btcommon.h: + @echo "You must install the OpenBT include directory before install can be done here!" + @exit 1 diff -urN linux-2.5.3-pre2/arch/cris/drivers/ds1302.c linux/arch/cris/drivers/ds1302.c --- linux-2.5.3-pre2/arch/cris/drivers/ds1302.c Wed Jul 4 11:50:38 2001 +++ linux/arch/cris/drivers/ds1302.c Mon Jan 21 17:23:22 2002 @@ -7,6 +7,9 @@ *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status *! *! $Log: ds1302.c,v $ +*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw +*! Import of Linux 2.5.1 +*! *! Revision 1.11 2001/06/14 12:35:52 jonashg *! The ATA hack is back. It is unfortunately the only way to set g27 to output. *! @@ -82,7 +85,7 @@ *! *! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN *! -*! $Id: ds1302.c,v 1.11 2001/06/14 12:35:52 jonashg Exp $ +*! $Id: ds1302.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ *! *!***************************************************************************/ diff -urN linux-2.5.3-pre2/arch/cris/drivers/eeprom.c linux/arch/cris/drivers/eeprom.c --- linux-2.5.3-pre2/arch/cris/drivers/eeprom.c Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/drivers/eeprom.c Mon Jan 21 17:23:22 2002 @@ -20,6 +20,9 @@ *! in the spin-lock. *! *! $Log: eeprom.c,v $ +*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw +*! Import of Linux 2.5.1 +*! *! Revision 1.8 2001/06/15 13:24:29 jonashg *! * Added verification of pointers from userspace in read and write. *! * Made busy counter volatile. diff -urN linux-2.5.3-pre2/arch/cris/drivers/ethernet.c linux/arch/cris/drivers/ethernet.c --- linux-2.5.3-pre2/arch/cris/drivers/ethernet.c Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/drivers/ethernet.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: ethernet.c,v 1.18 2001/10/03 14:40:43 jonashg Exp $ +/* $Id: ethernet.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $ * * e100net.c: A network driver for the ETRAX 100LX network controller. * @@ -7,6 +7,24 @@ * The outline of this driver comes from skeleton.c. * * $Log: ethernet.c,v $ + * Revision 1.2 2001/12/18 13:35:15 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.21 2001/11/23 11:54:49 starvik + * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list + * Removed compiler warnings + * + * Revision 1.20 2001/11/12 19:26:00 pkj + * * Corrected e100_negotiate() to not assign half to current_duplex when + * it was supposed to compare them... + * * Cleaned up failure handling in e100_open(). + * * Fixed compiler warnings. + * + * Revision 1.19 2001/11/09 07:43:09 starvik + * Added full duplex support + * Added ioctl to set speed and duplex + * Clear LED timer only runs when LED is lit + * * Revision 1.18 2001/10/03 14:40:43 jonashg * Update rx_bytes counter. * @@ -104,6 +122,7 @@ #include #include #include +#include //#define ETHDEBUG #define D(x) @@ -120,7 +139,7 @@ static struct sockaddr default_mac = { 0, - { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 } + { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 } }; /* Information that need to be kept for each board. */ @@ -136,6 +155,14 @@ }; +/* Duplex settings */ +enum duplex +{ + half, + full, + autoneg +}; + /* Dma descriptors etc. */ #define RX_BUF_SIZE 32768 @@ -148,9 +175,18 @@ /* ** MDIO constants. */ -#define MDIO_BASE_STATUS_REG 0x1 -#define MDIO_BASE_CONTROL_REG 0x0 -#define MDIO_LINK_UP_MASK 0x4 +#define MDIO_BASE_STATUS_REG 0x1 +#define MDIO_BASE_CONTROL_REG 0x0 +#define MDIO_BC_NEGOTIATE 0x0200 +#define MDIO_BC_FULL_DUPLEX_MASK 0x0100 +#define MDIO_BC_AUTO_NEG_MASK 0x1000 +#define MDIO_BC_SPEED_SELECT_MASK 0x2000 +#define MDIO_ADVERTISMENT_REG 0x4 +#define MDIO_ADVERT_100_FD 0x100 +#define MDIO_ADVERT_100_HD 0x080 +#define MDIO_ADVERT_10_FD 0x040 +#define MDIO_ADVERT_10_HD 0x020 +#define MDIO_LINK_UP_MASK 0x4 #define MDIO_START 0x1 #define MDIO_READ 0x2 #define MDIO_WRITE 0x1 @@ -158,6 +194,7 @@ /* Broadcom specific */ #define MDIO_AUX_CTRL_STATUS_REG 0x18 +#define MDIO_FULL_DUPLEX_IND 0x1 #define MDIO_SPEED 0x2 #define MDIO_PHYS_ADDR 0x0 @@ -165,18 +202,25 @@ #define NET_FLASH_TIME (HZ/50) /* 20 ms */ #define NET_FLASH_PAUSE (HZ/100) /* 10 ms */ #define NET_LINK_UP_CHECK_INTERVAL (2*HZ) /* 2 s */ +#define NET_DUPLEX_CHECK_INTERVAL (2*HZ) /* 2 s */ #define NO_NETWORK_ACTIVITY 0 #define NETWORK_ACTIVITY 1 #define RX_DESC_BUF_SIZE 256 #define NBR_OF_RX_DESC (RX_BUF_SIZE / \ - RX_DESC_BUF_SIZE) + RX_DESC_BUF_SIZE) #define GET_BIT(bit,val) (((val) >> (bit)) & 0x01) +/* Define some macros to access ETRAX 100 registers */ +#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ + IO_FIELD(##reg##, field, val) +#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ + IO_STATE(##reg##, field, val) + static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to - to be processed */ + to be processed */ static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */ static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */ @@ -187,13 +231,21 @@ static struct sk_buff *tx_skb; +static unsigned int network_rec_config_shadow = 0; + /* Network speed indication. */ static struct timer_list speed_timer; static struct timer_list clear_led_timer; -static int current_speed; +static int current_speed; /* Speed read from tranceiver */ +static int current_speed_selection; /* Speed selected by user */ static int led_next_time; static int led_active; +/* Duplex */ +static struct timer_list duplex_timer; +static int full_duplex; +static enum duplex current_duplex; + /* Index to functions, as function prototypes. */ static int etrax_ethernet_init(struct net_device *dev); @@ -206,6 +258,8 @@ static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void e100_rx(struct net_device *dev); static int e100_close(struct net_device *dev); +static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static void e100_tx_timeout(struct net_device *dev); static struct net_device_stats *e100_get_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static void e100_hardware_send_packet(char *buf, int length); @@ -213,6 +267,11 @@ static void update_tx_stats(struct net_device_stats *); static void e100_check_speed(unsigned long dummy); +static void e100_set_speed(unsigned long speed); +static void e100_check_duplex(unsigned long dummy); +static void e100_set_duplex(enum duplex); +static void e100_negotiate(void); + static unsigned short e100_get_mdio_reg(unsigned char reg_num); static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd); static void e100_send_mdio_bit(unsigned char bit); @@ -278,6 +337,8 @@ dev->get_stats = e100_get_stats; dev->set_multicast_list = set_multicast_list; dev->set_mac_address = e100_set_mac_address; + dev->do_ioctl = e100_ioctl; + dev->tx_timeout = e100_tx_timeout; /* set the default MAC address */ @@ -287,7 +348,7 @@ /* Initialise receive descriptors */ - for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) { + for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { RxDescList[i].ctrl = 0; RxDescList[i].sw_len = RX_DESC_BUF_SIZE; RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); @@ -313,12 +374,18 @@ /* Initialize speed indicator stuff. */ current_speed = 10; + current_speed_selection = 0; /* Auto */ speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL; speed_timer.function = e100_check_speed; add_timer(&speed_timer); + clear_led_timer.function = e100_clear_network_leds; - clear_led_timer.expires = jiffies + HZ/10; - add_timer(&clear_led_timer); + + full_duplex = 0; + current_duplex = autoneg; + duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL; + duplex_timer.function = e100_check_duplex; + add_timer(&duplex_timer); return 0; } @@ -335,7 +402,7 @@ /* remember it */ - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); /* Write it to the hardware. * Note the way the address is wrapped: @@ -409,21 +476,21 @@ if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0, cardname, (void *)dev)) { - goto grace_exit; + goto grace_exit0; } /* allocate the irq corresponding to the transmitting DMA */ if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0, cardname, (void *)dev)) { - goto grace_exit; + goto grace_exit1; } /* allocate the irq corresponding to the network errors etc */ if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0, cardname, (void *)dev)) { - goto grace_exit; + goto grace_exit2; } /* @@ -431,18 +498,12 @@ * and clean up on failure. */ - if(request_dma(NETWORK_TX_DMA_NBR, cardname)) { - goto grace_exit; + if (request_dma(NETWORK_TX_DMA_NBR, cardname)) { + goto grace_exit3; } - if(request_dma(NETWORK_RX_DMA_NBR, cardname)) { - grace_exit: - /* this will cause some 'trying to free free irq' but what the heck... */ - free_dma(NETWORK_TX_DMA_NBR); - free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev); - free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev); - free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev); - return -EAGAIN; + if (request_dma(NETWORK_RX_DMA_NBR, cardname)) { + goto grace_exit4; } /* give the HW an idea of what MAC address we want */ @@ -459,9 +520,10 @@ *R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */ #else - *R_NETWORK_REC_CONFIG = - IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | - IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable); + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive); + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable); + SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; #endif *R_NETWORK_GEN_CONFIG = @@ -507,6 +569,17 @@ netif_start_queue(dev); return 0; + +grace_exit4: + free_dma(NETWORK_TX_DMA_NBR); +grace_exit3: + free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev); +grace_exit2: + free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev); +grace_exit1: + free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev); +grace_exit0: + return -EAGAIN; } @@ -532,10 +605,119 @@ add_timer(&speed_timer); } +static void +e100_negotiate(void) +{ + unsigned short cmd; + unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); + int bitCounter; + + /* Discard old speed and duplex settings */ + data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | + MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD); + + switch (current_speed_selection) { + case 10 : + if (current_duplex == full) + data |= MDIO_ADVERT_10_FD; + else if (current_duplex == half) + data |= MDIO_ADVERT_10_HD; + else + data |= MDIO_ADVERT_10_HD | MDIO_ADVERT_10_FD; + break; + + case 100 : + if (current_duplex == full) + data |= MDIO_ADVERT_100_FD; + else if (current_duplex == half) + data |= MDIO_ADVERT_100_HD; + else + data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD; + break; + + case 0 : /* Auto */ + if (current_duplex == full) + data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD; + else if (current_duplex == half) + data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD; + else + data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; + break; + + default : /* assume autoneg speed and duplex */ + data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | + MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; + } + + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | + (MDIO_ADVERTISMENT_REG<< 2); + + e100_send_mdio_cmd(cmd, 1); + + /* Data... */ + for (bitCounter=15; bitCounter>=0 ; bitCounter--) { + e100_send_mdio_bit(GET_BIT(bitCounter, data)); + } + + /* Renegotiate with link partner */ + data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); + data |= MDIO_BC_NEGOTIATE; + + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | + (MDIO_BASE_CONTROL_REG<< 2); + + e100_send_mdio_cmd(cmd, 1); + + /* Data... */ + for (bitCounter=15; bitCounter>=0 ; bitCounter--) { + e100_send_mdio_bit(GET_BIT(bitCounter, data)); + } +} + +static void +e100_set_speed(unsigned long speed) +{ + current_speed_selection = speed; + e100_negotiate(); +} + +static void +e100_check_duplex(unsigned long dummy) +{ + unsigned long data; + + data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); + + if (data & MDIO_FULL_DUPLEX_IND) { + if (!full_duplex) { /* Duplex changed to full? */ + full_duplex = 1; + SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; + } + } else { /* half */ + if (full_duplex) { /* Duplex changed to half? */ + full_duplex = 0; + SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; + } + } + + /* Reinitialize the timer. */ + duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL; + add_timer(&duplex_timer); +} + +static void +e100_set_duplex(enum duplex new_duplex) +{ + current_duplex = new_duplex; + e100_negotiate(); +} + + static unsigned short e100_get_mdio_reg(unsigned char reg_num) { - unsigned long flags; unsigned short cmd; /* Data to be sent on MDIO port */ unsigned short data; /* Data read from MDIO */ int bitCounter; @@ -549,7 +731,7 @@ data = 0; /* Data... */ - for(bitCounter=15; bitCounter>=0 ; bitCounter--) { + for (bitCounter=15; bitCounter>=0 ; bitCounter--) { data |= (e100_receive_mdio_bit() << bitCounter); } @@ -563,14 +745,14 @@ unsigned char data = 0x2; /* Preamble */ - for(bitCounter = 31; bitCounter>= 0; bitCounter--) + for (bitCounter = 31; bitCounter>= 0; bitCounter--) e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE)); - for(bitCounter = 15; bitCounter >= 2; bitCounter--) + for (bitCounter = 15; bitCounter >= 2; bitCounter--) e100_send_mdio_bit(GET_BIT(bitCounter, cmd)); /* Turnaround */ - for(bitCounter = 1; bitCounter >= 0 ; bitCounter--) + for (bitCounter = 1; bitCounter >= 0 ; bitCounter--) if (write_cmd) e100_send_mdio_bit(GET_BIT(bitCounter, data)); else @@ -606,7 +788,6 @@ static void e100_reset_tranceiver(void) { - unsigned long flags; unsigned short cmd; unsigned short data; int bitCounter; @@ -619,7 +800,7 @@ data |= 0x8000; - for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) { + for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) { e100_send_mdio_bit(GET_BIT(bitCounter, data)); } } @@ -706,15 +887,14 @@ struct net_device *dev = (struct net_device *)dev_id; unsigned long irqbits = *R_IRQ_MASK2_RD; - if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) { - + if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) { /* acknowledge the eop interrupt */ *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do); /* check if one or more complete packets were indeed received */ - while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) { + while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) { /* Take out the buffer and give it to the OS, then * allocate a new buffer to put a packet in. */ @@ -747,8 +927,7 @@ struct net_local *np = (struct net_local *)dev->priv; /* check for a dma0_eop interrupt */ - if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { - + if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { /* This protects us from concurrent execution of * our dev->hard_start_xmit function above. */ @@ -759,7 +938,7 @@ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); - if(*R_DMA_CH0_FIRST == 0 && tx_skb) { + if (*R_DMA_CH0_FIRST == 0 && tx_skb) { np->stats.tx_bytes += tx_skb->len; np->stats.tx_packets++; /* dma is ready with the transmission of the data in tx_skb, so now @@ -784,19 +963,19 @@ unsigned long irqbits = *R_IRQ_MASK0_RD; /* check for underrun irq */ - if(irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { + if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr); np->stats.tx_errors++; D(printk("ethernet receiver underrun!\n")); } /* check for overrun irq */ - if(irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { + if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { update_rx_stats(&np->stats); /* this will ack the irq */ D(printk("ethernet receiver overrun!\n")); } /* check for excessive collision irq */ - if(irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { + if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr); np->stats.tx_errors++; D(printk("ethernet excessive collisions!\n")); @@ -809,11 +988,13 @@ e100_rx(struct net_device *dev) { struct sk_buff *skb; - int length=0; - int i; + int length = 0; struct net_local *np = (struct net_local *)dev->priv; struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc; unsigned char *skb_data_ptr; +#ifdef ETHDEBUG + int i; +#endif if (!led_active && jiffies > led_next_time) { /* light the network leds depending on the current speed. */ @@ -822,6 +1003,7 @@ /* Set the earliest time we may clear the LED */ led_next_time = jiffies + NET_FLASH_TIME; led_active = 1; + mod_timer(&clear_led_timer, jiffies + HZ/10); } /* If the packet is broken down in many small packages then merge @@ -842,7 +1024,7 @@ printk("Got a packet of length %d:\n", length); /* dump the first bytes in the packet */ skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf); - for(i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) { printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3], skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]); @@ -869,7 +1051,7 @@ /* this loop can be made using max two memcpy's if optimized */ - while(mySaveRxDesc != myNextRxDesc) { + while (mySaveRxDesc != myNextRxDesc) { memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), mySaveRxDesc->sw_len); skb_data_ptr += mySaveRxDesc->sw_len; @@ -946,6 +1128,37 @@ return 0; } +static int +e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + /* Maybe default should return -EINVAL instead? */ + switch (cmd) { + case SET_ETH_SPEED_10: /* 10 Mbps */ + e100_set_speed(10); + break; + case SET_ETH_SPEED_100: /* 100 Mbps */ + e100_set_speed(100); + break; + case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */ + e100_set_speed(0); + break; + case SET_ETH_DUPLEX_HALF: /* Hhalf duplex. */ + e100_set_duplex(half); + break; + case SET_ETH_DUPLEX_FULL: /* Full duplex. */ + e100_set_duplex(full); + break; + case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/ + e100_set_duplex(autoneg); + break; + default: /* Auto neg */ + e100_set_speed(0); + e100_set_duplex(autoneg); + break; + } + return 0; +} + static void update_rx_stats(struct net_device_stats *es) { @@ -996,26 +1209,31 @@ int num_addr = dev->mc_count; unsigned long int lo_bits; unsigned long int hi_bits; - if (num_addr == -1) + if (dev->flags & IFF_PROMISC) { /* promiscuous mode */ lo_bits = 0xfffffffful; hi_bits = 0xfffffffful; - /* Enable individual receive */ - *R_NETWORK_REC_CONFIG = - IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | - IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable) | - IO_STATE(R_NETWORK_REC_CONFIG, individual, receive); + /* Enable individual receive */ + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; + } else if (dev->flags & IFF_ALLMULTI) { + /* enable all multicasts */ + lo_bits = 0xfffffffful; + hi_bits = 0xfffffffful; + + /* Disable individual receive */ + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } else if (num_addr == 0) { /* Normal, clear the mc list */ lo_bits = 0x00000000ul; hi_bits = 0x00000000ul; - /* Disable individual receive */ - *R_NETWORK_REC_CONFIG = - IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | - IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable); + /* Disable individual receive */ + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } else { /* MC mode, receive normal and MC packets */ char hash_ix; @@ -1057,10 +1275,9 @@ } dmi = dmi->next; } - /* Disable individual receive */ - *R_NETWORK_REC_CONFIG = - IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | - IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable); + /* Disable individual receive */ + SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } *R_NETWORK_GA_0 = lo_bits; *R_NETWORK_GA_1 = hi_bits; @@ -1078,6 +1295,7 @@ /* Set the earliest time we may clear the LED */ led_next_time = jiffies + NET_FLASH_TIME; led_active = 1; + mod_timer(&clear_led_timer, jiffies + HZ/10); } /* configure the tx dma descriptor */ @@ -1095,16 +1313,13 @@ static void e100_clear_network_leds(unsigned long dummy) { - if (led_active && jiffies > led_next_time) { + if (led_active && jiffies > led_next_time) { e100_set_network_leds(NO_NETWORK_ACTIVITY); /* Set the earliest time we may set the LED */ led_next_time = jiffies + NET_FLASH_PAUSE; led_active = 0; } - - clear_led_timer.expires = jiffies + HZ/10; - add_timer(&clear_led_timer); } static void @@ -1143,7 +1358,7 @@ d->init = etrax_ethernet_init; - if(register_netdev(d) == 0) + if (register_netdev(d) == 0) return 0; else return -ENODEV; diff -urN linux-2.5.3-pre2/arch/cris/drivers/examples/kiobuftest.c linux/arch/cris/drivers/examples/kiobuftest.c --- linux-2.5.3-pre2/arch/cris/drivers/examples/kiobuftest.c Fri Apr 6 10:42:55 2001 +++ linux/arch/cris/drivers/examples/kiobuftest.c Mon Jan 21 17:23:22 2002 @@ -10,6 +10,9 @@ * Author: Bjorn Wesen * * $Log: kiobuftest.c,v $ + * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw + * Import of Linux 2.5.1 + * * Revision 1.2 2001/02/27 13:52:50 bjornw * malloc.h -> slab.h * diff -urN linux-2.5.3-pre2/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c --- linux-2.5.3-pre2/arch/cris/drivers/gpio.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/drivers/gpio.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $ +/* $Id: gpio.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $ * * Etrax general port I/O device * @@ -9,6 +9,13 @@ * Johan Adolfsson (read/set directions, write) * * $Log: gpio.c,v $ + * Revision 1.2 2001/12/18 13:35:15 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.12 2001/11/12 19:42:15 pkj + * * Corrected return values from gpio_leds_ioctl(). + * * Fixed compiler warnings. + * * Revision 1.11 2001/10/30 14:39:12 johana * Added D() around gpio_write printk. * @@ -74,7 +81,9 @@ static char gpio_name[] = "etrax gpio"; +#if 0 static wait_queue_head_t *gpio_wq; +#endif static int gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); @@ -143,7 +152,7 @@ { /* TODO poll on alarms! */ #if 0 - if(!ANYTHING_WANTED) { + if (!ANYTHING_WANTED) { D(printk("gpio_select sleeping task\n")); select_wait(&gpio_wq, table); return 0; @@ -160,16 +169,14 @@ unsigned char data, clk_mask, data_mask, write_msb; unsigned long flags; ssize_t retval = count; - if (verify_area(VERIFY_READ, buf, count)) - { + if (verify_area(VERIFY_READ, buf, count)) { return -EFAULT; } clk_mask = priv->clk_mask; data_mask = priv->data_mask; /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) - { + if (clk_mask == 0 || data_mask == 0) { return -EPERM; } write_msb = priv->write_msb; @@ -178,7 +185,7 @@ int i; data = *buf++; if (priv->write_msb) { - for (i = 7; i>=0;i--) { + for (i = 7; i >= 0;i--) { save_flags(flags); cli(); *priv->port = *priv->shadow &= ~clk_mask; if (data & 1<port = *priv->shadow &= ~clk_mask; if (data & 1<i_rdev); - if(p >= NUM_PORTS && p != LEDS) + if (p >= NUM_PORTS && p != LEDS) return -EINVAL; priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), GFP_KERNEL); - if(!priv) + if (!priv) return -ENOMEM; priv->minor = p; @@ -254,10 +261,10 @@ /* unlink from alarmlist and free the private structure */ - if(p == todel) { + if (p == todel) { alarmlist = todel->next; } else { - while(p->next != todel) + while (p->next != todel) p = p->next; p->next = todel->next; } @@ -280,7 +287,7 @@ { unsigned long flags; struct gpio_private *priv = (struct gpio_private *)file->private_data; - if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { return -EINVAL; } @@ -353,7 +360,7 @@ if (!((priv->clk_mask & priv->changeable_bits) && (priv->data_mask & priv->changeable_bits) && (priv->clk_mask & *priv->dir_shadow) && - (priv->data_mask & *priv->dir_shadow)) ) + (priv->data_mask & *priv->dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; @@ -361,7 +368,7 @@ } break; default: - if(priv->minor == LEDS) + if (priv->minor == LEDS) return gpio_leds_ioctl(cmd, arg); else return -EINVAL; @@ -375,6 +382,7 @@ { unsigned char green; unsigned char red; + switch (_IOC_NR(cmd)) { case IO_LEDACTIVE_SET: green = ((unsigned char) arg) & 1; @@ -382,14 +390,20 @@ LED_ACTIVE_SET_G(green); LED_ACTIVE_SET_R(red); break; - case IO_LED_SETBIT: - LED_BIT_SET(arg); - break; - case IO_LED_CLRBIT: - LED_BIT_CLR(arg); + + case IO_LED_SETBIT: + LED_BIT_SET(arg); + break; + + case IO_LED_CLRBIT: + LED_BIT_CLR(arg); + break; + default: return -EINVAL; } + + return 0; } struct file_operations gpio_fops = { @@ -406,30 +420,32 @@ static __init int gpio_init(void) { - int res,i; + extern void init_ioremap(void); + int res; +#if defined (CONFIG_ETRAX_CSP0_LEDS) + int i; +#endif /* do the formalities */ res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "gpio: couldn't get a major number.\n"); return res; } /* Clear all leds */ -#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - - init_ioremap(); - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); +#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) + init_ioremap(); + LED_NETWORK_SET(0); + LED_ACTIVE_SET(0); + LED_DISK_READ(0); + LED_DISK_WRITE(0); #if defined (CONFIG_ETRAX_CSP0_LEDS) - for( i = 0; i < 32; i ++) - { - LED_BIT_SET(i); - } + for (i = 0; i < 32; i++) { + LED_BIT_SET(i); + } #endif #endif diff -urN linux-2.5.3-pre2/arch/cris/drivers/i2c.c linux/arch/cris/drivers/i2c.c --- linux-2.5.3-pre2/arch/cris/drivers/i2c.c Sat May 19 17:43:05 2001 +++ linux/arch/cris/drivers/i2c.c Mon Jan 21 17:23:22 2002 @@ -12,6 +12,9 @@ *! don't use PB_I2C if DS1302 uses same bits, *! use PB. *! $Log: i2c.c,v $ +*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw +*! Import of Linux 2.5.1 +*! *! Revision 1.7 2001/04/04 13:11:36 markusl *! Updated according to review remarks *! @@ -43,7 +46,7 @@ *! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.7 2001/04/04 13:11:36 markusl Exp $ */ +/* $Id: i2c.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ #include diff -urN linux-2.5.3-pre2/arch/cris/drivers/i2c.h linux/arch/cris/drivers/i2c.h --- linux-2.5.3-pre2/arch/cris/drivers/i2c.h Sat May 19 17:43:05 2001 +++ linux/arch/cris/drivers/i2c.h Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */ +/* $Id: i2c.h,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */ /* High level I2C actions */ int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); diff -urN linux-2.5.3-pre2/arch/cris/drivers/ide.c linux/arch/cris/drivers/ide.c --- linux-2.5.3-pre2/arch/cris/drivers/ide.c Wed Jul 4 11:50:38 2001 +++ linux/arch/cris/drivers/ide.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: ide.c,v 1.19 2001/05/09 12:53:16 johana Exp $ +/* $Id: ide.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * Etrax specific IDE functions, like init and PIO-mode setting etc. * Almost the entire ide.c is used for the rest of the Etrax ATA driver. @@ -8,6 +8,9 @@ * Mikael Starvik (pio setup stuff) * * $Log: ide.c,v $ + * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw + * Import of Linux 2.5.1 + * * Revision 1.19 2001/05/09 12:53:16 johana * Added #include * diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/bintocarr.pl linux/arch/cris/drivers/lpslave/bintocarr.pl --- linux-2.5.3-pre2/arch/cris/drivers/lpslave/bintocarr.pl Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/drivers/lpslave/bintocarr.pl Mon Jan 21 17:23:22 2002 @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $ +# $Id: bintocarr.pl,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ # Copy of mkjulbin.pl made by Olof # convert a binary stdin to a C-file containing a char array of the input # first argument is the symbol name diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslave.S linux/arch/cris/drivers/lpslave/e100lpslave.S --- linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslave.S Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/drivers/lpslave/e100lpslave.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ - ;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $ + ;; $Id: e100lpslave.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ ;; ;; Etrax100 slave network<->parport forwarder ;; diff -urN linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslavenet.c linux/arch/cris/drivers/lpslave/e100lpslavenet.c --- linux-2.5.3-pre2/arch/cris/drivers/lpslave/e100lpslavenet.c Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/drivers/lpslave/e100lpslavenet.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: e100lpslavenet.c,v 1.4 2001/06/21 16:55:26 olof Exp $ +/* $Id: e100lpslavenet.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * e100lpslavenet.c: A network driver for the ETRAX 100LX slave controller. * @@ -7,6 +7,9 @@ * The outline of this driver comes from skeleton.c. * * $Log: e100lpslavenet.c,v $ + * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw + * Import of Linux 2.5.1 + * * Revision 1.4 2001/06/21 16:55:26 olof * Minimized par port setup time to gain bandwidth * diff -urN linux-2.5.3-pre2/arch/cris/drivers/parport.c linux/arch/cris/drivers/parport.c --- linux-2.5.3-pre2/arch/cris/drivers/parport.c Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/drivers/parport.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: parport.c,v 1.8 2001/09/26 11:51:52 bjornw Exp $ +/* $Id: parport.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * Elinux parallel port driver * NOTE! diff -urN linux-2.5.3-pre2/arch/cris/drivers/serial.c linux/arch/cris/drivers/serial.c --- linux-2.5.3-pre2/arch/cris/drivers/serial.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/drivers/serial.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: serial.c,v 1.23 2001/10/30 17:53:26 pkj Exp $ +/* $Id: serial.c,v 1.3 2001/12/19 10:32:35 johana Exp $ * * Serial port driver for the ETRAX 100LX chip * @@ -7,6 +7,34 @@ * Many, many authors. Based once upon a time on serial.c for 16x50. * * $Log: serial.c,v $ + * Revision 1.3 2001/12/19 10:32:35 johana + * Cleaned up write_rs485() - now it works correctly without padding extra + * char. + * Added sane default initialisation of rs485. + * Added #ifdef around dummy variables. + * + * Revision 1.27 2001/11/29 17:00:41 pkj + * 2kB seems to be too small a buffer when using 921600 bps, + * so increase it to 4kB (this was already done for the elinux + * version of the serial driver). + * + * Revision 1.26 2001/11/19 14:20:41 pkj + * Minor changes to comments and unused code. + * + * Revision 1.25 2001/11/12 20:03:43 pkj + * Fixed compiler warnings. + * + * Revision 1.24 2001/11/12 15:10:05 pkj + * Total redesign of the receiving part of the serial driver. + * Uses eight chained descriptors to write to a 4kB buffer. + * This data is then serialised into a 2kB buffer. From there it + * is copied into the TTY's flip buffers when they become available. + * A lot of copying, and the sizes of the buffers might need to be + * tweaked, but all in all it should work better than the previous + * version, without the need to modify the TTY code in any way. + * Also note that erroneous bytes are now correctly marked in the + * flag buffers (instead of always marking the first byte). + * * Revision 1.23 2001/10/30 17:53:26 pkj * * Set info->uses_dma to 0 when a port is closed. * * Mark the timer1 interrupt as a fast one (SA_INTERRUPT). @@ -123,12 +151,12 @@ * Changed %ul to %lu in printf's * * Revision 1.47 2000/10/18 15:06:53 pkj - * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and - * CONFIG_SERIAL_PROC_ENTRY together. + * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and + * CONFIG_ETRAX_SERIAL_PROC_ENTRY together. * Some clean-up of the /proc/serial file. * * Revision 1.46 2000/10/16 12:59:40 johana - * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info. + * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info. * * Revision 1.45 2000/10/13 17:10:59 pkj * Do not flush DMAs while flipping TTY buffers. @@ -171,7 +199,7 @@ * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS. * * Revision 1.36 2000/09/20 13:12:52 johana - * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS: + * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS: * Number of timer ticks between flush of receive fifo (1 tick = 10ms). * Try 0-3 for low latency applications. Approx 5 for high load * applications (e.g. PPP). Maybe this should be more adaptive some day... @@ -255,7 +283,7 @@ * */ -static char *serial_version = "$Revision: 1.23 $"; +static char *serial_version = "$Revision: 1.3 $"; #include #include @@ -272,6 +300,7 @@ #include #include #include +#include #if (LINUX_VERSION_CODE >= 131343) #include #endif @@ -302,6 +331,8 @@ #include "serial_compat.h" #endif +#define _INLINE_ inline + static DECLARE_TASK_QUEUE(tq_serial); struct tty_driver serial_driver, callout_driver; @@ -313,11 +344,6 @@ #define SERIAL_TYPE_CALLOUT 2 #endif -#define DEBUG_LOG(line, string, value) - -/* Add an x here to log a lot of timer stuff */ -#define TIMERD(x) - /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 @@ -337,6 +363,14 @@ #define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10) +#define SERIAL_RECV_SIZE 4096 +#define SERIAL_DESCR_BUF_SIZE 512 + +/* Add an x here to log a lot of timer stuff */ +#define TIMERD(x) + +#define DEBUG_LOG(line, string, value) + #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS /* Default number of timer ticks before flushing rx fifo * When using "little data, low latency applications: use 0 @@ -345,8 +379,6 @@ #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 #endif -#define _INLINE_ inline - static void change_speed(struct e100_serial *info); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static int rs_write(struct tty_struct * tty, int from_user, @@ -403,9 +435,9 @@ static struct e100_serial rs_table[] = { { DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */ R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD, - R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, + R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR, R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD, - R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, + R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR, STD_FLAGS, DEF_RX, DEF_TX, 2, #ifdef CONFIG_ETRAX_SERIAL_PORT0 1 @@ -416,9 +448,9 @@ #ifndef CONFIG_SVINTO_SIM { DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */ R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD, - R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, + R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR, R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD, - R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, + R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR, STD_FLAGS, DEF_RX, DEF_TX, 3 , #ifdef CONFIG_ETRAX_SERIAL_PORT1 1 @@ -429,9 +461,9 @@ { DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */ R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD, - R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, + R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR, R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD, - R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, + R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR, STD_FLAGS, DEF_RX, DEF_TX, 0, #ifdef CONFIG_ETRAX_SERIAL_PORT2 1 @@ -442,9 +474,9 @@ { DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */ R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD, - R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, + R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR, R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD, - R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, + R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR, STD_FLAGS, DEF_RX, DEF_TX, 1, #ifdef CONFIG_ETRAX_SERIAL_PORT3 1 @@ -462,9 +494,9 @@ static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; -#ifdef CONFIG_SERIAL_PROC_ENTRY +#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY #define PROCSTAT(x) x -struct ser_statistics_type{ +struct ser_statistics_type { int overrun_cnt; int early_errors_cnt; int ser_ints_ok_cnt; @@ -484,7 +516,7 @@ #define PROCSTAT(x) -#endif /* CONFIG_SERIAL_PROC_ENTRY */ +#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */ /* RS-485 */ #if defined(CONFIG_ETRAX_RS485) @@ -497,14 +529,20 @@ /* For now we assume that all bits are on the same port for each serial port */ /* Dummy shadow variables */ +#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB) static unsigned char dummy_ser0 = 0x00; -static unsigned char dummy_ser1 = 0x00; -static unsigned char dummy_ser2 = 0x00; -static unsigned char dummy_ser3 = 0x00; - static unsigned char dummy_dir_ser0 = 0x00; +#endif +#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB) +static unsigned char dummy_ser1 = 0x00; static unsigned char dummy_dir_ser1 = 0x00; +#endif +#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA) +static unsigned char dummy_ser2 = 0x00; static unsigned char dummy_dir_ser2 = 0x00; +#endif + +static unsigned char dummy_ser3 = 0x00; static unsigned char dummy_dir_ser3 = 0x00; /* Info needed for each ports extra control/status signals. @@ -523,52 +561,56 @@ static const struct control_pins e100_modem_pins[NR_PORTS] = { -/* Ser 0 */ - { + /* Ser 0 */ + { #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB) - R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, - CONFIG_ETRAX_SER0_DTR_ON_PB_BIT, - CONFIG_ETRAX_SER0_RI_ON_PB_BIT, - CONFIG_ETRAX_SER0_DSR_ON_PB_BIT, - CONFIG_ETRAX_SER0_CD_ON_PB_BIT + R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, + CONFIG_ETRAX_SER0_DTR_ON_PB_BIT, + CONFIG_ETRAX_SER0_RI_ON_PB_BIT, + CONFIG_ETRAX_SER0_DSR_ON_PB_BIT, + CONFIG_ETRAX_SER0_CD_ON_PB_BIT #else - &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3 -#endif - }, -/* Ser 1 */ - { + &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3 +#endif + }, + + /* Ser 1 */ + { #if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB) - R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, - CONFIG_ETRAX_SER1_DTR_ON_PB_BIT, - CONFIG_ETRAX_SER1_RI_ON_PB_BIT, - CONFIG_ETRAX_SER1_DSR_ON_PB_BIT, - CONFIG_ETRAX_SER1_CD_ON_PB_BIT + R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, + CONFIG_ETRAX_SER1_DTR_ON_PB_BIT, + CONFIG_ETRAX_SER1_RI_ON_PB_BIT, + CONFIG_ETRAX_SER1_DSR_ON_PB_BIT, + CONFIG_ETRAX_SER1_CD_ON_PB_BIT #else - &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3 -#endif - }, -/* Ser 2 */ - { + &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3 +#endif + }, + + /* Ser 2 */ + { #if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA) - R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow, - CONFIG_ETRAX_SER2_DTR_ON_PA_BIT, - CONFIG_ETRAX_SER2_RI_ON_PA_BIT, - CONFIG_ETRAX_SER2_DSR_ON_PA_BIT, - CONFIG_ETRAX_SER2_CD_ON_PA_BIT + R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow, + CONFIG_ETRAX_SER2_DTR_ON_PA_BIT, + CONFIG_ETRAX_SER2_RI_ON_PA_BIT, + CONFIG_ETRAX_SER2_DSR_ON_PA_BIT, + CONFIG_ETRAX_SER2_CD_ON_PA_BIT #else - &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3 -#endif - }, -/* Ser 3 */ - { - &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3 - } + &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3 +#endif + }, + + /* Ser 3 */ + { + &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3 + } }; #if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA) unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT; #endif + #define E100_RTS_MASK 0x20 #define E100_CTS_MASK 0x40 @@ -840,8 +882,8 @@ { #ifndef CONFIG_SVINTO_SIM /* disable the receiver */ - info->port[REG_REC_CTRL] = info->rx_ctrl &= - ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable); + info->port[REG_REC_CTRL] = + (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); #endif } @@ -850,8 +892,8 @@ { #ifndef CONFIG_SVINTO_SIM /* enable the receiver */ - info->port[REG_REC_CTRL] = info->rx_ctrl |= - IO_MASK(R_SERIAL0_REC_CTRL, rec_enable); + info->port[REG_REC_CTRL] = + (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); #endif } @@ -943,13 +985,8 @@ static int e100_write_rs485(struct tty_struct *tty,struct rs485_write *r) { - int stop_delay; - int total, i; - int max_j, delay_ms, bits; - tcflag_t cflags; - int size = (*r).outc_size; + int total; struct e100_serial * info = (struct e100_serial *)tty->driver_data; - struct wait_queue wait = { current, NULL }; /* If we are in RS-485 mode, we need to toggle RTS and disable * the receiver before initiating a DMA transfer @@ -975,44 +1012,20 @@ * enable the receiver */ - /* wait on transmit shift register */ - /* All is sent, check if we should wait more before toggling rts */ - - /* calc. number of bits / data byte */ - cflags = info->tty->termios->c_cflag; - - /* databits + startbit and 1 stopbit */ - if ((cflags & CSIZE) == CS7) - bits = 9; - else - bits = 10; - - if (cflags & CSTOPB) /* 2 stopbits ? */ - bits++; - - if (cflags & PARENB) /* parity bit ? */ - bits++; - - /* calc timeout */ - delay_ms = ((bits * size * 1000) / info->baud) + 1; - max_j = jiffies + (delay_ms * HZ)/1000 + 10; - - while (jiffies < max_j) { - if (info->port[REG_STATUS] & - IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) { - for (i = 0; i < 100; i++) - ; - if (info->port[REG_STATUS] & - IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) { - /* ~25 for loops per usec */ - stop_delay = 1000000 / info->baud; - if (cflags & CSTOPB) - stop_delay *= 2; - udelay(stop_delay); - break; - } - } + /* Sleep until all sent */ + tty_wait_until_sent(tty, 0); +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER + /* Now sleep a little more so that shift register is empty */ + schedule_usleep(info->char_time_usec * 2); +#else + { + unsigned int val; + /* wait on transmit shift register */ + do{ + get_lsr_info(info, &val); + }while (!(val & TIOCSER_TEMT)); } +#endif e100_rts(info, info->rs485.rts_after_sent); @@ -1118,7 +1131,7 @@ } return; #endif - /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */ + /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ *info->oclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); @@ -1168,14 +1181,14 @@ #if defined(CONFIG_ETRAX_RS485) /* Check if we should toggle RTS now */ - if (info->rs485.enabled) - { + if (info->rs485.enabled) { /* Make sure fifo is empty */ - int in_fifo = 0 ; - do{ + int in_fifo = 0; + + do { in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail, - *info->ostatusadr); - } while (in_fifo > 0) ; + *info->ostatusadr); + } while (in_fifo > 0); /* Any way to really check transmitter empty? (TEMT) */ /* Control RTS to set to RX mode */ e100_rts(info, info->rs485.rts_after_sent); @@ -1201,7 +1214,6 @@ *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */ /* DMA is now running (hopefully) */ - } static void @@ -1220,13 +1232,122 @@ transmit_chars(info); } +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +static int serial_fast_timer_started = 0; +static int serial_fast_timer_expired = 0; +static void flush_timeout_function(unsigned long data); +#define START_FLUSH_FAST_TIMER(info, string) {\ + unsigned long timer_flags; \ + save_flags(timer_flags); \ + cli(); \ + if (fast_timers[info->line].function == NULL) { \ + serial_fast_timer_started++; \ + TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \ + TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \ + start_one_shot_timer(&fast_timers[info->line], \ + flush_timeout_function, \ + (unsigned long)info, \ + info->char_time_usec*4, \ + string); \ + } \ + else { \ + TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \ + } \ + restore_flags(timer_flags); \ +} + +#else +#define START_FLUSH_FAST_TIMER(info, string) +#endif + +static int +add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag) +{ + if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE)) + return 0; + + info->recv.buf[info->recv.head] = data; + info->flag_buf[info->recv.head] = flag; + info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1); + + info->icount.rx++; + + return 1; +} + +static _INLINE_ unsigned int +copy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf) +{ + unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); + unsigned int length = 0; + + while (length < recvl && count) { + if (length + count > recvl) + count = recvl - length; + + memcpy(info->recv.buf + info->recv.head, buf + length, count); + memset(info->flag_buf + info->recv.head, '\0', count); + info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1); + length += count; + + count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); + } + + if (length != recvl) { + printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length); + PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length); + } + + return length; +} + +static _INLINE_ unsigned int +copy_all_descr_data(struct e100_serial *info) +{ + struct etrax_dma_descr *descr; + unsigned int recvl; + unsigned int ret = 0; + + while (1) + { + descr = &info->rec_descr[info->cur_rec_descr]; + + if (descr == phys_to_virt(*info->idescradr)) + break; + + if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS) + info->cur_rec_descr = 0; + + /* find out how many bytes were read */ + + /* if the eop bit was not set, all data has been received */ + if (!(descr->status & d_eop)) { + recvl = descr->sw_len; + } else { + /* otherwise we find the amount of data received here */ + recvl = descr->hw_len; + } + + /* Reset the status information */ + descr->status = 0; + + DEBUG_LOG(info->line, "recvl %lu\n", recvl); + + /* update stats */ + info->icount.rx += recvl; + + ret += copy_descr_data(info, recvl, phys_to_virt(descr->buf)); + } + + return ret; +} + static _INLINE_ void receive_chars(struct e100_serial *info) { struct tty_struct *tty; unsigned char rstat; - unsigned int recvl; - struct etrax_dma_descr *descr; + unsigned int old_head; #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of @@ -1235,168 +1356,96 @@ return; #endif - tty = info->tty; - - /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */ - - // ? + /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ *info->iclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - if (!tty) /* something wrong... */ + tty = info->tty; + if (!tty) /* Something wrong... */ return; - descr = &info->rec_descr; +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_enable_serial_data_irq(info); +#endif - /* find out how many bytes were read */ + if (info->errorcode == ERRCODE_INSERT_BREAK) + add_char_and_flag(info, '\0', TTY_BREAK); - /* if the eop bit was not set, all data has been received */ - if (!(descr->status & d_eop)) { - recvl = descr->sw_len; - } else { - /* otherwise we find the amount of data received here */ - recvl = descr->hw_len; - } + old_head = info->recv.head; + + if (copy_all_descr_data(info) && info->errorcode == ERRCODE_SET_BREAK) + info->flag_buf[old_head] = TTY_BREAK; - /* read the status register so we can detect errors, - * but we can't really do anything about those errors - * anyway, since we have the DMA in "force eop at error" mode - * the fault characters are not in the buffer anyway. - */ + info->errorcode = 0; + /* Read the status register to detect errors */ rstat = info->port[REG_STATUS]; - if ((rstat & SER_ERROR_MASK) != 0) { - unsigned char data; - /* if we got an error, we must reset it by reading the + if (rstat & SER_ERROR_MASK) { + /* If we got an error, we must reset it by reading the * data_in field */ - data = info->port[REG_DATA]; + unsigned char data = info->port[REG_DATA]; + PROCSTAT(ser_stat[info->line].errors_cnt++); - DEBUG_LOG(info->line, " #dERR: s d 0x%04X\n", + DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n", ((rstat & SER_ERROR_MASK) << 8) | data); - /* Only handle the saved error code, that indicates that we got - * the last character of a break that looks like it's ok, but - * is not - */ - if (info->errorcode == 0) { - *tty->flip.flag_buf_ptr = TTY_NORMAL; - } else { - unsigned char data; - data = info->port[REG_DATA]; - if (info->errorcode & ERRCODE_INSERT) { - unsigned char *currbuf; - /* Get the current buffer */ - if (tty->flip.buf_num) { - currbuf = tty->flip.char_buf + TTY_FLIPBUF_SIZE; - } else { - currbuf = tty->flip.char_buf; - } - /* We should insert a character in the buffer! */ - if (recvl == 0) { - recvl = 1; - DEBUG_LOG(info->line, "insert to %lu\n", recvl); - } else { - /* Move stuff around.. */ - DEBUG_LOG(info->line, "#insert to %lu!\n", recvl); - if (recvl < TTY_FLIPBUF_SIZE) { - int i; - /* Move the data 1 step right */ - i = recvl; - while (i) { - currbuf[i] = currbuf[i-1]; - i--; - } - recvl++; - } else { - /* We can't move it all! Skip break! */ - /* TODO: Handle full buffer? */ - DEBUG_LOG(info->line, "#BRK skipped! %lu!\n", recvl); - info->errorcode = 0; - } - } - } - - PROCSTAT(ser_stat[info->line].errors_cnt++); - DEBUG_LOG(info->line, " #bERR: s d 0x%04X\n", - ((rstat & SER_ERROR_MASK) << 8) | data); - *tty->flip.flag_buf_ptr = (info->errorcode & 0xFF); - info->errorcode = 0; -#if 0 - printk("SERERR: 0x%02X data: 0x%02X\n", rstat & SER_ERROR_MASK, data); -#endif - /* we only ever write errors into the first byte in - * the flip flag buffer, so we dont have to clear it - * all every time - */ - } + if (rstat & SER_PAR_ERR_MASK) + add_char_and_flag(info, data, TTY_PARITY); + else if (rstat & SER_OVERRUN_MASK) + add_char_and_flag(info, data, TTY_OVERRUN); + else if (rstat & SER_FRAMING_ERR_MASK) + add_char_and_flag(info, data, TTY_FRAME); } - DEBUG_LOG(info->line, "recvl %lu\n", recvl); - - if (recvl) { - unsigned char *buf; - struct async_icount *icount = &info->icount; - - /* update stats */ - icount->rx += recvl; - - /* use the flip buffer next in turn to restart DMA into */ - - if (tty->flip.buf_num) { - buf = tty->flip.char_buf; - } else { - buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE; - } + if (!E100_RTS_GET(info) && + CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) < TTY_THROTTLE_LIMIT) + info->tty->driver.throttle(info->tty); + + START_FLUSH_FAST_TIMER(info, "receive_chars"); - if (buf == phys_to_virt(descr->buf)) { - printk("ttyS%d flip-buffer overrun!\n", info->line); - icount->overrun++; - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - /* restart old buffer */ - } else { - descr->buf = virt_to_phys(buf); + /* Restart the receiving DMA */ + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); +} - /* schedule or push a flip of the buffer */ +static _INLINE_ int +start_recv_dma(struct e100_serial *info) +{ + struct etrax_dma_descr *descr = info->rec_descr; + unsigned char *buf = info->recv.buf + 2*SERIAL_RECV_SIZE; + int i; - info->tty->flip.count = recvl; + /* Set up the receiving descriptors */ + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { + descr[i].ctrl = d_int; + descr[i].buf = virt_to_phys(buf); + descr[i].sw_len = SERIAL_DESCR_BUF_SIZE; + descr[i].hw_len = 0; + descr[i].status = 0; + descr[i].next = virt_to_phys(&descr[i+1]); -#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ - /* this includes a check for low-latency */ - tty_flip_buffer_push(tty); -#else - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); -#endif - } + buf += SERIAL_DESCR_BUF_SIZE; } - /* restart the receiving dma */ + /* Link the last descriptor to the first */ + descr[i-1].next = virt_to_phys(&descr[0]); - descr->sw_len = TTY_FLIPBUF_SIZE; - descr->ctrl = d_int | d_eol | d_eop; - descr->hw_len = 0; - descr->status = 0; + /* Start with the first descriptor in the list */ + info->cur_rec_descr = 0; - *info->ifirstadr = virt_to_phys(descr); + /* Start the DMA */ + *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]); *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); -#ifdef SERIAL_HANDLE_EARLY_ERRORS - e100_enable_serial_data_irq(info); -#endif - /* input dma should be running now */ - - /* unthrottle if we have throttled */ - if (E100_RTS_GET(info)) - tty->driver.unthrottle(info->tty); + /* Input DMA should be running now */ + return 1; } static void start_receive(struct e100_serial *info) { - struct etrax_dma_descr *descr; - #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of * the serial interface works, and this piece will just be removed. @@ -1410,21 +1459,10 @@ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - descr = &info->rec_descr; - - /* start the receiving dma into the flip buffer */ - - descr->ctrl = d_int | d_eol | d_eop; - descr->sw_len = TTY_FLIPBUF_SIZE; - descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr); - descr->hw_len = 0; - descr->status = 0; - info->tty->flip.count = 0; - *info->ifirstadr = virt_to_phys(descr); - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); - + start_recv_dma(info); + #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST start_flush_timer(); #endif @@ -1474,9 +1512,16 @@ info = rs_table + i; if (!info->uses_dma) continue; - /* check for dma_descr (dont need to check for dma_eop in output dma for serial */ + /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ if (ireg & info->irq) { /* we can send a new dma bunch. make it so. */ + DEBUG_LOG(info->line, "tr_interrupt %i\n", i); + /* Read jiffies_usec first, + * we want this time to be as late as possible + */ + PROCSTAT(ser_stat[info->line].tx_dma_ints++); + info->last_tx_active_usec = GET_JIFFIES_USEC(); + info->last_tx_active = jiffies; transmit_chars(info); } @@ -1524,77 +1569,26 @@ } } -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER -static int serial_fast_timer_started = 0; -static int serial_fast_timer_expired = 0; -static void flush_timeout_function(unsigned long data); -#define START_FLUSH_FAST_TIMER(info, string) {\ - unsigned long timer_flags; \ - save_flags(timer_flags); \ - cli(); \ - TIMERD(DEBUG_LOG(info->line, "start_timer? %i ", info->line)); \ - if (fast_timers[info->line].function == NULL) { \ - serial_fast_timer_started++; \ - TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \ - TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \ - start_one_shot_timer(&fast_timers[info->line], \ - flush_timeout_function, \ - (unsigned long)info, \ - info->char_time_usec*4, \ - string); \ - } \ - else { \ - /* DEBUG_LOG(info->line, " ## timer %i running ##\n", info->line); */ \ - } \ - restore_flags(timer_flags); \ -} - -#else -#define START_FLUSH_FAST_TIMER(info, string) -#endif - -void _INLINE_ check_flush_timeout(struct e100_serial *info) +static _INLINE_ int +force_eop_if_needed(struct e100_serial *info) { - unsigned char rstat; - unsigned int magic; - - if (0 /*info->tty->processing_flip*/) { - if (!E100_RTS_GET(info)) { - int left = (*info->ihwswadr >> 16) - (*info->istatusadr & 0x3F); - - if (left < TTY_THROTTLE_LIMIT) - info->tty->driver.throttle(info->tty); - } - - PROCSTAT(ser_stat[info->line].processing_flip++); - START_FLUSH_FAST_TIMER(info, "flip"); - return; - } - /* We check data_avail bit to determine if data has * arrived since last time */ - magic = info->fifo_magic; -#ifdef SERIAL_DEBUG_DATA - if (info->fifo_magic || info->fifo_didmagic) { - DEBUG_LOG(info->line, "timeout_int: did fifo_magic %03X\n", - (info->fifo_didmagic << 8) | info->fifo_magic); - } -#endif - rstat = info->port[REG_STATUS]; + unsigned char rstat = info->port[REG_STATUS]; + /* error or datavail? */ if (rstat & SER_ERROR_MASK) { - /* Some error has occured */ - /* If there has been valid data, - * an EOP interrupt will be made automatically. - * If no data, the normal ser_interrupt should be enabled - * and handle it. + /* Some error has occurred. If there has been valid data, an + * EOP interrupt will be made automatically. If no data, the + * normal ser_interrupt should be enabled and handle it. * So do nothing! */ DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n", rstat | (info->line << 8)); - return; + return 0; } + if (rstat & SER_DATA_AVAIL_MASK) { /* Ok data, no error, count it */ TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n", @@ -1602,32 +1596,87 @@ /* Read data to clear status flags */ (void)info->port[REG_DATA]; - magic++; + info->forced_eop = 0; + START_FLUSH_FAST_TIMER(info, "magic"); + return 0; } - if (magic != info->fifo_magic) { - info->fifo_magic = magic; - info->fifo_didmagic = 0; - START_FLUSH_FAST_TIMER(info, "magic"); - } else { - /* hit the timeout, force an EOP for the input - * dma channel if we haven't already - */ - if (!info->fifo_didmagic && magic) { - info->fifo_didmagic = 1; - info->fifo_magic = 0; - PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); - DEBUG_LOG(info->line, "timeout EOP %i\n", info->line); - TIMERD(DEBUG_LOG(info->line, "timeout magic %i\n", magic)); - FORCE_EOP(info); - } + /* hit the timeout, force an EOP for the input + * dma channel if we haven't already + */ + if (!info->forced_eop) { + info->forced_eop = 1; + PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); + DEBUG_LOG(info->line, "timeout EOP %i\n", info->line); + FORCE_EOP(info); } -} /* check_flush_timeout */ + + return 1; +} + +static _INLINE_ void +flush_to_flip_buffer(struct e100_serial *info) +{ + struct tty_struct *tty = info->tty; + unsigned int count = CIRC_CNT_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); + unsigned int length; + unsigned long flags; + + if (!count) + return; + + save_flags(flags); + cli(); + + length = tty->flip.count; + + do { + if (length + count > TTY_FLIPBUF_SIZE) + count = TTY_FLIPBUF_SIZE - length; + + memcpy(tty->flip.char_buf_ptr + length, info->recv.buf + info->recv.tail, count); + memcpy(tty->flip.flag_buf_ptr + length, info->flag_buf + info->recv.tail, count); + info->recv.tail = ((info->recv.tail + count) & (SERIAL_RECV_SIZE-1)); + length += count; + + count = CIRC_CNT_TO_END(info->recv.head, + info->recv.tail, + SERIAL_RECV_SIZE); + } while (length < TTY_FLIPBUF_SIZE && count); + + tty->flip.count = length; + + restore_flags(flags); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66) + /* this includes a check for low-latency */ + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif + + /* unthrottle if we have throttled */ + if (E100_RTS_GET(info) && + CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) > TTY_THROTTLE_LIMIT) + tty->driver.unthrottle(info->tty); +} + +static _INLINE_ void +check_flush_timeout(struct e100_serial *info) +{ + force_eop_if_needed(info); + + flush_to_flip_buffer(info); + + if (CIRC_CNT(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE)) + START_FLUSH_FAST_TIMER(info, "flip"); +} #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER static void flush_timeout_function(unsigned long data) { struct e100_serial *info = (struct e100_serial *)data; + fast_timers[info->line].function = NULL; serial_fast_timer_expired++; TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line)); @@ -1681,7 +1730,6 @@ { struct e100_serial *info; int i; - unsigned int magic; #ifdef CONFIG_SVINTO_SIM return; @@ -1689,35 +1737,8 @@ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->enabled || !(info->flags & ASYNC_INITIALIZED)) - continue; - - /* istatusadr (bit 6-0) hold number of bytes in fifo - * ihwswadr (bit 31-16) holds number of bytes in dma buffer - * ihwswadr (bit 15-0) specifies size of dma buffer - */ - - magic = (*info->istatusadr & 0x3f); - magic += ((*info->ihwswadr & 0xffff) - (*info->ihwswadr >> 16)); - - /* if magic is equal to fifo_magic (magic in previous - * timeout_interrupt) then no new data has arrived since last - * interrupt and we'll force eop to flush fifo+dma buffers - */ - - if (magic != info->fifo_magic) { - info->fifo_magic = magic; - info->fifo_didmagic = 0; - } else { - /* hit the timeout, force an EOP for the input - * dma channel if we haven't already - */ - if (!info->fifo_didmagic && magic) { - info->fifo_didmagic = 1; - info->fifo_magic = 0; - FORCE_EOP(info); - } - } + if (info->uses_dma) + check_flush_timeout(info); } /* restart flush timer */ @@ -1770,7 +1791,7 @@ Multiple frame errors with data == 0x00 (B), but the part of the break trigs is interpreted as a start bit (and possibly -som 0 bits followed by a number of 1 bits and a stop bit). +some 0 bits followed by a number of 1 bits and a stop bit). Depending on parity settings etc. this last character can be either a fake "valid" char (F) or have a parity error (E). @@ -1805,19 +1826,20 @@ /* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ if (rstat & SER_ERROR_MASK) { unsigned char data; + info->last_rx_active_usec = GET_JIFFIES_USEC(); info->last_rx_active = jiffies; - /* if we got an error, we must reset it by - * reading the data_in field + /* If we got an error, we must reset it by reading the + * data_in field */ data = info->port[REG_DATA]; - if ((data == 0x00) && (rstat & SER_FRAMING_ERR_MASK)) { - /* Most likely a break, but we get - * interrupts over and over again. + if (!data && (rstat & SER_FRAMING_ERR_MASK)) { + /* Most likely a break, but we get interrupts over and + * over again. */ - if (info->break_detected_cnt == 0) { + if (!info->break_detected_cnt) { DEBUG_LOG(info->line, "#BRK start\n", 0); } if (rstat & SER_RXD_MASK) { @@ -1833,46 +1855,46 @@ } info->break_detected_cnt++; } else { - /* Error doesn't look like a break, - * but could be end of a break + /* The error does not look like a break, but could be + * the end of one */ if (info->break_detected_cnt) { DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); info->errorcode = ERRCODE_INSERT_BREAK; + } else { + if (info->errorcode == ERRCODE_INSERT_BREAK) + add_char_and_flag(info, '\0', TTY_BREAK); + + if (rstat & SER_PAR_ERR_MASK) + add_char_and_flag(info, data, TTY_PARITY); + else if (rstat & SER_OVERRUN_MASK) + add_char_and_flag(info, data, TTY_OVERRUN); + else if (rstat & SER_FRAMING_ERR_MASK) + add_char_and_flag(info, data, TTY_FRAME); + info->errorcode = 0; } info->break_detected_cnt = 0; DEBUG_LOG(info->line, "#iERR s d %04X\n", ((rstat & SER_ERROR_MASK) << 8) | data); } PROCSTAT(ser_stat[info->line].early_errors_cnt++); - -#if 0 - /* Reset DMA before starting */ - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); -#endif - } else { /* it was a valid byte, now let the dma do the rest */ - unsigned char data; + } else { /* It was a valid byte, now let the DMA do the rest */ unsigned long curr_time_u = GET_JIFFIES_USEC(); unsigned long curr_time = jiffies; if (info->break_detected_cnt) { - /* Detect if this character is a new - * valid char or the last char in a - * break sequence: - * If LSBits are 0 and MSBits are high - * AND the time is close to the - * previous interrupt we should discard - * it. + /* Detect if this character is a new valid char or the + * last char in a break sequence: If LSBits are 0 and + * MSBits are high AND the time is close to the + * previous interrupt we should discard it. */ long elapsed_usec = - (curr_time - info->last_rx_active) * (1000000/HZ) + - curr_time_u - info->last_rx_active_usec; - if (elapsed_usec<2*info->char_time_usec) { + (curr_time - info->last_rx_active) * (1000000/HZ) + + curr_time_u - info->last_rx_active_usec; + if (elapsed_usec < 2*info->char_time_usec) { DEBUG_LOG(info->line, "FBRK %i\n", info->line); - /* Report as BREAK (error) and - * let receive_chars handle it + /* Report as BREAK (error) and let + * receive_chars() handle it */ info->errorcode = ERRCODE_SET_BREAK; } else { @@ -1880,22 +1902,19 @@ } DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt); } - /* Reset data_avail by - * reading the data_in field - */ - data = info->port[REG_DATA]; - info->break_detected_cnt = 0; - info->fifo_magic++; /* Count received chars */ + #ifdef SERIAL_DEBUG_INTR printk("** OK, disabling ser_interupts\n"); #endif - PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); - DEBUG_LOG(info->line, " ser_int OK %03X\n", - (info->line << 8) | data); e100_disable_serial_data_irq(info); + + info->break_detected_cnt = 0; + + PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); + DEBUG_LOG(info->line, "ser_int OK %d\n", info->line); } - /* restart the DMA never hurts */ + /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); } /* handle_ser_interrupt */ @@ -1973,7 +1992,7 @@ { struct e100_serial *info = (struct e100_serial *) private_; struct tty_struct *tty; - + tty = info->tty; if (!tty) return; @@ -1985,43 +2004,46 @@ startup(struct e100_serial * info) { unsigned long flags; - unsigned long page; + unsigned long xmit_page; + unsigned char *recv_page; + + xmit_page = get_zeroed_page(GFP_KERNEL); + if (!xmit_page) + return -ENOMEM; - page = get_zeroed_page(GFP_KERNEL); - if (!page) + recv_page = kmalloc(2 * SERIAL_RECV_SIZE + SERIAL_RECV_DESCRIPTORS * SERIAL_DESCR_BUF_SIZE, GFP_KERNEL); + if (!recv_page) { + free_page(xmit_page); return -ENOMEM; + } save_flags(flags); cli(); /* if it was already initialized, skip this */ - + if (info->flags & ASYNC_INITIALIZED) { - free_page(page); restore_flags(flags); + free_page(xmit_page); + kfree(recv_page); return 0; } - + if (info->xmit.buf) - free_page(page); + free_page(xmit_page); else - info->xmit.buf = (unsigned char *) page; - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit.buf); -#endif - - if (info->tty) { - - /* clear the tty flip flag buffer since we will not - * be using it (we only use the first byte..) - */ + info->xmit.buf = (unsigned char *) xmit_page; - memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2); + if (info->recv.buf) + kfree(recv_page); + else { + info->recv.buf = (unsigned char *) recv_page; + info->flag_buf = info->recv.buf + SERIAL_RECV_SIZE; } - save_flags(flags); - cli(); - +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf); +#endif + #ifdef CONFIG_SVINTO_SIM /* Bits and pieces collected from below. Better to have them in one ifdef:ed clause than to mix in a lot of ifdefs, @@ -2029,7 +2051,8 @@ if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; - + info->recv.head = info->recv.tail = 0; + /* No real action in the simulator, but may set info important to ioctl. */ change_speed(info); @@ -2039,33 +2062,35 @@ * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ - + /* * Reset the DMA channels and make sure their interrupts are cleared */ - + info->uses_dma = 1; *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - /* wait until reset cycle is complete */ + /* Wait until reset cycle is complete */ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) == IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - + + /* Make sure the irqs are cleared */ *info->iclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); *info->oclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit.head = info->xmit.tail = 0; + info->xmit.head = info->xmit.tail = 0; + info->recv.head = info->recv.tail = 0; /* * and set the speed and other flags of the serial port @@ -2085,7 +2110,7 @@ e100_enable_txdma_irq(info); e100_enable_rxdma_irq(info); - info->tr_running = 0; /* to be sure we dont lock up the transmitter */ + info->tr_running = 0; /* to be sure we don't lock up the transmitter */ /* setup the dma input descriptor and start dma */ @@ -2120,7 +2145,7 @@ unsigned long flags; #ifndef CONFIG_SVINTO_SIM - /* shut down the transmitter and receiver */ + /* shut down the transmitter and receiver */ e100_disable_rx(info); info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40); @@ -2150,9 +2175,14 @@ cli(); /* Disable interrupts */ if (info->xmit.buf) { - unsigned long pg = (unsigned long) info->xmit.buf; - info->xmit.buf = 0; - free_page(pg); + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + } + + if (info->recv.buf) { + kfree(info->recv.buf); + info->recv.buf = NULL; + info->flag_buf = NULL; } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { @@ -2258,11 +2288,11 @@ struct e100_serial *info = (struct e100_serial *)tty->driver_data; unsigned long flags; - if (info->tr_running - || info->xmit.head == info->xmit.tail - || tty->stopped - || tty->hw_stopped - || !info->xmit.buf) + if (info->tr_running || + info->xmit.head == info->xmit.tail || + tty->stopped || + tty->hw_stopped || + !info->xmit.buf) return; #ifdef SERIAL_DEBUG_FLOW @@ -2299,7 +2329,7 @@ #ifdef CONFIG_SVINTO_SIM /* Really simple. The output is here and now. */ SIMCOUT(buf, count); - return; + return count; #endif save_flags(flags); @@ -2370,8 +2400,8 @@ * the IRQ's are not running anyway for this port. */ - if (info->xmit.head != info->xmit.tail - && !tty->stopped && + if (info->xmit.head != info->xmit.tail && + !tty->stopped && !tty->hw_stopped && !info->tr_running) { start_transmit(info); @@ -2528,20 +2558,20 @@ tmp.flags = info->flags; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; - if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; } static int -set_serial_info(struct e100_serial * info, - struct serial_struct * new_info) +set_serial_info(struct e100_serial *info, + struct serial_struct *new_info) { struct serial_struct new_serial; struct e100_serial old_info; - int retval = 0; + int retval = 0; - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; old_info = *info; @@ -2636,6 +2666,7 @@ char *get_control_state_str(int MLines, char *s) { int i = 0; + s[0]='\0'; while (control_state_str[i].str != NULL) { if (MLines & control_state_str[i].state) { @@ -2805,9 +2836,13 @@ rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - int error; struct e100_serial * info = (struct e100_serial *)tty->driver_data; +#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + int error; +#endif +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ int retval; +#endif if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && @@ -3108,7 +3143,7 @@ struct e100_serial *info) { DECLARE_WAITQUEUE(wait, current); - unsigned long flags; + unsigned long flags; int retval; int do_clocal = 0, extra_count = 0; @@ -3292,7 +3327,7 @@ } /* - * If the port is the middle of closing, bail out now + * If the port is in the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { @@ -3346,12 +3381,11 @@ static inline int line_info(char *buf, struct e100_serial *info) { - char stat_buf[30], control, status; + char stat_buf[30]; int ret; - unsigned long flags; ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d", - info->line, info->port, info->irq); + info->line, (unsigned long)info->port, info->irq); if (!info->port || (info->type == PORT_UNKNOWN)) { ret += sprintf(buf+ret, "\n"); @@ -3376,19 +3410,24 @@ ret += sprintf(buf+ret, " baud:%d", info->baud); ret += sprintf(buf+ret, " tx:%lu rx:%lu", - info->icount.tx, info->icount.rx); + (unsigned long)info->icount.tx, + (unsigned long)info->icount.rx); if (info->icount.frame) - ret += sprintf(buf+ret, " fe:%lu", info->icount.frame); + ret += sprintf(buf+ret, " fe:%lu", + (unsigned long)info->icount.frame); if (info->icount.parity) - ret += sprintf(buf+ret, " pe:%lu", info->icount.parity); + ret += sprintf(buf+ret, " pe:%lu", + (unsigned long)info->icount.parity); if (info->icount.brk) - ret += sprintf(buf+ret, " brk:%lu", info->icount.brk); + ret += sprintf(buf+ret, " brk:%lu", + (unsigned long)info->icount.brk); if (info->icount.overrun) - ret += sprintf(buf+ret, " oe:%lu", info->icount.overrun); + ret += sprintf(buf+ret, " oe:%lu", + (unsigned long)info->icount.overrun); /* * Last thing is the RS-232 status lines @@ -3526,8 +3565,7 @@ info->tty = 0; info->type = PORT_ETRAX; info->tr_running = 0; - info->fifo_magic = 0; - info->fifo_didmagic = 0; + info->forced_eop = 0; info->flags = 0; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; @@ -3541,8 +3579,21 @@ info->normal_termios = serial_driver.init_termios; init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); - info->xmit.buf = 0; + info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; + info->recv.buf = NULL; + info->recv.tail = info->recv.head = 0; + info->flag_buf = NULL; + info->last_tx_active_usec = 0; + info->last_tx_active = 0; + +#if defined(CONFIG_ETRAX_RS485) + /* Set sane defaults */ + info->rs485.rts_on_send = 0; + info->rs485.rts_after_sent = 1; + info->rs485.delay_rts_before_send = 0; + info->rs485.enabled = 0; +#endif if (info->enabled) { printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n", diff -urN linux-2.5.3-pre2/arch/cris/drivers/serial.h linux/arch/cris/drivers/serial.h --- linux-2.5.3-pre2/arch/cris/drivers/serial.h Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/drivers/serial.h Mon Jan 21 17:23:22 2002 @@ -23,81 +23,90 @@ * For definitions of the flags field, see tty.h */ +#define SERIAL_RECV_DESCRIPTORS 8 + struct e100_serial { - int baud; - volatile u8 *port; /* R_SERIALx_CTRL */ - u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ - - volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */ - volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */ - volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD, output */ - const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */ - volatile u32 *ohwswadr; /* adr to R_DMA_CHx_HWSW, output */ - - volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */ - volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */ - volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD, input */ - const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */ - volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW, input */ - - int flags; /* defined in tty.h */ - - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ - u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ - u8 iseteop; /* bit number for R_SET_EOP for the input dma */ - int enabled; /* Set to 1 if the port is enabled in HW config */ - + int baud; + volatile u8 *port; /* R_SERIALx_CTRL */ + u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ + + /* Output registers */ + volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ + volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */ + volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */ + const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */ + volatile u32 *ohwswadr; /* adr to R_DMA_CHx_HWSW */ + volatile u32 *odescradr; /* adr to R_DMA_CHx_DESCR */ + + /* Input registers */ + volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ + volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */ + volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ + const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS */ + volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW */ + volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ + + int flags; /* defined in tty.h */ + + u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ + u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ + u8 iseteop; /* bit number for R_SET_EOP for the input dma */ + + int enabled; /* Set to 1 if the port is enabled in HW config */ -/* end of fields defined in rs_table[] in .c-file */ - int uses_dma; /* Set to 1 if DMA should be used */ - unsigned char fifo_didmagic; /* a fifo eop has been forced */ + /* end of fields defined in rs_table[] in .c-file */ - struct etrax_dma_descr tr_descr, rec_descr; + int uses_dma; /* Set to 1 if DMA should be used */ + unsigned char forced_eop; /* a fifo eop has been forced */ - int fifo_magic; /* fifo amount - bytes left in dma buffer */ + struct etrax_dma_descr tr_descr; + struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS]; + int cur_rec_descr; - volatile int tr_running; /* 1 if output is running */ + volatile int tr_running; /* 1 if output is running */ - struct tty_struct *tty; + struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int x_char; /* xon/xoff character */ int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; + unsigned short closing_wait; + unsigned short closing_wait2; unsigned long event; unsigned long last_active; int line; - int type; /* PORT_ETRAX */ + int type; /* PORT_ETRAX */ int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ - struct circ_buf xmit; + struct circ_buf xmit; + struct circ_buf recv; + unsigned char *flag_buf; struct tq_struct tqueue; - struct async_icount icount; /* error-statistics etc.*/ - struct termios normal_termios; - struct termios callout_termios; + struct async_icount icount; /* error-statistics etc.*/ + struct termios normal_termios; + struct termios callout_termios; #ifdef DECLARE_WAITQUEUE - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; -#else - struct wait_queue *open_wait; - struct wait_queue *close_wait; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; +#else + struct wait_queue *open_wait; + struct wait_queue *close_wait; #endif - unsigned long char_time_usec; /* The time for 1 char, in usecs */ - unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */ - unsigned long last_tx_active; /* Last tx time in jiffies */ - unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */ - unsigned long last_rx_active; /* Last rx time in jiffies */ + unsigned long char_time_usec; /* The time for 1 char, in usecs */ + unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */ + unsigned long last_tx_active; /* Last tx time in jiffies */ + unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */ + unsigned long last_rx_active; /* Last rx time in jiffies */ - int break_detected_cnt; - int errorcode; + int break_detected_cnt; + int errorcode; #ifdef CONFIG_RS485 - struct rs485_control rs485; /* RS-485 support */ + struct rs485_control rs485; /* RS-485 support */ #endif }; @@ -116,4 +125,4 @@ #endif /* __KERNEL__ */ -#endif /* !(_ETRAX_SERIAL_H) */ +#endif /* !_ETRAX_SERIAL_H */ diff -urN linux-2.5.3-pre2/arch/cris/drivers/sync_serial.c linux/arch/cris/drivers/sync_serial.c --- linux-2.5.3-pre2/arch/cris/drivers/sync_serial.c Tue May 1 16:04:56 2001 +++ linux/arch/cris/drivers/sync_serial.c Mon Jan 21 17:23:22 2002 @@ -711,7 +711,7 @@ { port->out_descr.hw_len = 0; port->out_descr.next = 0; - port->out_descr.ctrl = d_int | d_eol | d_eop; + port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait; port->out_descr.sw_len = count; port->out_descr.buf = virt_to_phys(port->out_buffer); port->out_descr.status = 0; diff -urN linux-2.5.3-pre2/arch/cris/drivers/usb-host.c linux/arch/cris/drivers/usb-host.c --- linux-2.5.3-pre2/arch/cris/drivers/usb-host.c Sun Jan 6 18:49:21 2002 +++ linux/arch/cris/drivers/usb-host.c Mon Jan 21 17:23:22 2002 @@ -192,15 +192,15 @@ static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4))); -static urb_t *URB_List[NBR_OF_EP_DESC]; +static struct urb *URB_List[NBR_OF_EP_DESC]; static kmem_cache_t *usb_desc_cache; static struct usb_bus *etrax_usb_bus; static void dump_urb (struct urb *urb); static void init_rx_buffers(void); -static int etrax_rh_unlink_urb (urb_t *urb); -static void etrax_rh_send_irq(urb_t *urb); -static void etrax_rh_init_int_timer(urb_t *urb); +static int etrax_rh_unlink_urb (struct urb *urb); +static void etrax_rh_send_irq(struct urb *urb); +static void etrax_rh_init_int_timer(struct urb *urb); static void etrax_rh_int_timer_do(unsigned long ptr); static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, @@ -210,13 +210,13 @@ static void etrax_usb_free_epid(char epid); static void cleanup_sb(USB_SB_Desc_t *sb); -static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen); -static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen); +static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen); +static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen); -static int etrax_usb_submit_ctrl_urb(urb_t *urb); +static int etrax_usb_submit_ctrl_urb(struct urb *urb); -static int etrax_usb_submit_urb(urb_t *urb); -static int etrax_usb_unlink_urb(urb_t *urb); +static int etrax_usb_submit_urb(struct urb *urb); +static int etrax_usb_unlink_urb(struct urb *urb); static int etrax_usb_get_frame_number(struct usb_device *usb_dev); static int etrax_usb_allocate_dev(struct usb_device *usb_dev); static int etrax_usb_deallocate_dev(struct usb_device *usb_dev); @@ -225,7 +225,7 @@ static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs); static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs); -static int etrax_rh_submit_urb (urb_t *urb); +static int etrax_rh_submit_urb (struct urb *urb); static int etrax_usb_hc_init(void); static void etrax_usb_hc_cleanup(void); @@ -421,7 +421,7 @@ } -static int etrax_usb_unlink_intr_urb(urb_t *urb) +static int etrax_usb_unlink_intr_urb(struct urb *urb) { struct usb_device *usb_dev = urb->dev; etrax_hc_t *hc = usb_dev->bus->hcpriv; @@ -512,7 +512,7 @@ } while (tmp_ep != first_ep); } -static int etrax_usb_submit_intr_urb(urb_t *urb) +static int etrax_usb_submit_intr_urb(struct urb *urb) { USB_EP_Desc_t *tmp_ep; USB_EP_Desc_t *first_ep; @@ -643,7 +643,7 @@ static int handle_intr_transfer_attn(char epid, int status) { - urb_t *old_urb; + struct urb *old_urb; DBFENTER; @@ -696,7 +696,7 @@ DBFEXIT; } -static int etrax_rh_unlink_urb (urb_t *urb) +static int etrax_rh_unlink_urb (struct urb *urb) { etrax_hc_t *hc; @@ -713,7 +713,7 @@ return 0; } -static void etrax_rh_send_irq(urb_t *urb) +static void etrax_rh_send_irq(struct urb *urb) { __u16 data = 0; etrax_hc_t *hc = urb->dev->bus->hcpriv; @@ -746,7 +746,7 @@ /* DBFEXIT; */ } -static void etrax_rh_init_int_timer(urb_t *urb) +static void etrax_rh_init_int_timer(struct urb *urb) { etrax_hc_t *hc; @@ -765,12 +765,12 @@ static void etrax_rh_int_timer_do(unsigned long ptr) { - urb_t *urb; + struct urb *urb; etrax_hc_t *hc; /* DBFENTER; */ - urb = (urb_t*)ptr; + urb = (struct urb *)ptr; hc = urb->dev->bus->hcpriv; if (hc->rh.send) { @@ -904,7 +904,7 @@ return -1; } -static int etrax_usb_submit_bulk_urb(urb_t *urb) +static int etrax_usb_submit_bulk_urb(struct urb *urb) { char epid; char devnum; @@ -912,7 +912,7 @@ char maxlen; char slow; - urb_t *tmp_urb; + struct urb *tmp_urb; etrax_urb_priv_t *urb_priv; unsigned long flags; @@ -962,7 +962,7 @@ return 0; } -static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen) +static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen) { USB_SB_Desc_t *sb_desc_1; @@ -1080,7 +1080,7 @@ static int handle_bulk_transfer_attn(char epid, int status) { - urb_t *old_urb; + struct urb *old_urb; etrax_urb_priv_t *hc_priv; unsigned long flags; @@ -1159,7 +1159,7 @@ /* ---------------------------------------------------------------------------- */ -static int etrax_usb_submit_ctrl_urb(urb_t *urb) +static int etrax_usb_submit_ctrl_urb(struct urb *urb) { char epid; char devnum; @@ -1167,7 +1167,7 @@ char maxlen; char slow; - urb_t *tmp_urb; + struct urb *tmp_urb; etrax_urb_priv_t *urb_priv; unsigned long flags; @@ -1217,7 +1217,7 @@ return 0; } -static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen) +static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen) { USB_SB_Desc_t *sb_desc_1; USB_SB_Desc_t *sb_desc_2; @@ -1358,7 +1358,7 @@ DBFEXIT; } -static int etrax_usb_submit_urb(urb_t *urb) +static int etrax_usb_submit_urb(struct urb *urb) { etrax_hc_t *hc; int rval = -EINVAL; @@ -1403,7 +1403,7 @@ return rval; } -static int etrax_usb_unlink_urb(urb_t *urb) +static int etrax_usb_unlink_urb(struct urb *urb) { etrax_hc_t *hc = urb->dev->bus->hcpriv; int epid; @@ -1448,7 +1448,7 @@ cli(); for (epid = 0; epid < 32; epid++) { - urb_t *u = URB_List[epid]; + struct urb *u = URB_List[epid]; pos = 0; for (; u; u = u->next) { @@ -1474,7 +1474,7 @@ URB_List[epid] = u->next; } else { - urb_t *up; + struct urb *up; for (up = URB_List[epid]; up->next != u; up = up->next); up->next = u->next; } @@ -1522,7 +1522,7 @@ etrax_hc_t *hc = (etrax_hc_t *)vhc; int epid; char eol; - urb_t *urb; + struct urb *urb; USB_EP_Desc_t *tmp_ep; USB_SB_Desc_t *tmp_sb; @@ -1551,7 +1551,7 @@ static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs) { int epid = 0; - urb_t *urb; + struct urb *urb; etrax_urb_priv_t *urb_priv; *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); @@ -1649,7 +1649,7 @@ static int handle_control_transfer_attn(char epid, int status) { - urb_t *old_urb; + struct urb *old_urb; etrax_urb_priv_t *hc_priv; DBFENTER; @@ -1710,7 +1710,7 @@ static void etrax_usb_hc_intr_bottom_half(void *data) { struct usb_reg_context *reg = (struct usb_reg_context *)data; - urb_t *old_urb; + struct urb *old_urb; int error_code; int epid; @@ -1976,7 +1976,7 @@ DBFEXIT; } -static int etrax_rh_submit_urb(urb_t *urb) +static int etrax_rh_submit_urb(struct urb *urb) { struct usb_device *usb_dev = urb->dev; etrax_hc_t *hc = usb_dev->bus->hcpriv; diff -urN linux-2.5.3-pre2/arch/cris/kernel/Makefile linux/arch/cris/kernel/Makefile --- linux-2.5.3-pre2/arch/cris/kernel/Makefile Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/Makefile Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.9 2001/10/22 13:10:21 pkj Exp $ +# $Id: Makefile,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ # # Makefile for the linux kernel. # diff -urN linux-2.5.3-pre2/arch/cris/kernel/debugport.c linux/arch/cris/kernel/debugport.c --- linux-2.5.3-pre2/arch/cris/kernel/debugport.c Tue May 1 16:04:56 2001 +++ linux/arch/cris/kernel/debugport.c Mon Jan 21 17:23:22 2002 @@ -12,6 +12,9 @@ * init_etrax_debug() * * $Log: debugport.c,v $ + * Revision 1.2 2002/01/21 15:21:50 bjornw + * Update for kdev_t changes + * * Revision 1.6 2001/04/17 13:58:39 orjanf * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. * @@ -214,7 +217,7 @@ static kdev_t console_device(struct console *c) { - return MKDEV(TTY_MAJOR, 64 + c->index); + return mk_kdev(TTY_MAJOR, 64 + c->index); } static int __init @@ -224,17 +227,16 @@ } static struct console sercons = { - "ttyS", - console_write, - NULL, - console_device, - NULL, - NULL, - console_setup, - CON_PRINTBUFFER, - DEBUG_PORT_IDX, - 0, - NULL + name: "ttyS", + write: console_write, + read: NULL, + device: console_device, + unblank: NULL, + setup: console_setup, + flags: CON_PRINTBUFFER, + index: DEBUG_PORT_IDX, + cflag: 0, + next: NULL }; /* diff -urN linux-2.5.3-pre2/arch/cris/kernel/entry.S linux/arch/cris/kernel/entry.S --- linux-2.5.3-pre2/arch/cris/kernel/entry.S Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/entry.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.35 2001/10/30 17:10:15 bjornw Exp $ +/* $Id: entry.S,v 1.3 2002/01/21 15:22:20 bjornw Exp $ * * linux/arch/cris/entry.S * @@ -7,6 +7,18 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: entry.S,v $ + * Revision 1.3 2002/01/21 15:22:20 bjornw + * NICE_DOGGY fix from 2.4 arch/cris + * + * Revision 1.37 2001/12/07 17:03:55 bjornw + * Call a c-hook called watchdog_bite_hook instead of show_registers directly + * + * Revision 1.36 2001/11/22 13:36:36 bjornw + * * In ret_from_intr, check regs->dccr for usermode reentrance instead of + * DCCR explicitely (because the latter might not reflect current reality) + * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before + * since $r9 is call-clobbered and is potentially needed afterwards + * * Revision 1.35 2001/10/30 17:10:15 bjornw * Add some syscalls * @@ -217,8 +229,11 @@ ret_from_intr: ;; check for resched only if we're going back to user-mode - - move $ccr, $r0 + ;; this test matches the user_regs(regs) macro + ;; we cannot simply test $dccr, because that does not necessarily + ;; reflect what mode we'll return into. + + move.d [$sp + LDCCR], $r0; regs->dccr btstq 8, $r0 ; U-flag bpl _Rexit ; go back directly nop @@ -468,8 +483,6 @@ moveq 1, $r10 push $r10 ; frametype == 1, BUSFAULT frame type - moveq 0, $r9 ; busfault is equivalent to an irq - move.d $sp, $r10 ; pt_regs argument to handle_mmu_bus_fault jsr handle_mmu_bus_fault ; in arch/cris/mm/fault.c @@ -479,6 +492,8 @@ ;; process due to a SEGV, scheduled due to a page blocking or ;; whatever. + moveq 0, $r9 ; busfault is equivalent to an irq + ba ret_from_intr nop @@ -559,6 +574,17 @@ ;; We'll see this in ksymoops dumps. Watchdog_bite: +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + ;; We just restart the watchdog here to be sure we dont get + ;; hit while printing the watchdogmsg below + ;; This restart is compatible with the rest of the C-code, so + ;; the C-code can keep restarting the watchdog after this point. + ;; The non-NICE_DOGGY code below though, disables the possibility + ;; to restart since it changes the watchdog key, to avoid any + ;; buggy loops etc. keeping the watchdog alive after this. + jsr reset_watchdog +#else + ;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have ;; time for an oops-dump over a 115k2 serial wire. Another 100ms should do. @@ -576,6 +602,8 @@ | IO_STATE (R_WATCHDOG, enable, start), $r10 move.d $r10, [$r11] +#endif + ;; Note that we don't do "setf m" here (or after two necessary NOPs), ;; since *not* doing that saves us from re-entrancy checks. We don't want ;; to get here again due to possible subsequent NMIs; we want the watchdog @@ -585,7 +613,7 @@ jsr printk move.d $sp, $r10 - jsr show_registers + jsr watchdog_bite_hook ;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps ;; rather than "spurious_interrupt". diff -urN linux-2.5.3-pre2/arch/cris/kernel/head.S linux/arch/cris/kernel/head.S --- linux-2.5.3-pre2/arch/cris/kernel/head.S Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/head.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.41 2001/10/29 14:55:58 pkj Exp $ +/* $Id: head.S,v 1.2 2001/12/18 13:35:19 bjornw Exp $ * * Head of the kernel - alter with care * @@ -7,6 +7,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: head.S,v $ + * Revision 1.2 2001/12/18 13:35:19 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.43 2001/11/08 15:09:43 starvik + * Only start MII clock if Ethernet is configured + * + * Revision 1.42 2001/11/08 14:37:34 starvik + * Start MII clock early to make sure that it is running at tranceiver reset + * * Revision 1.41 2001/10/29 14:55:58 pkj * Corrected pa$r0 to par0. * @@ -156,7 +165,10 @@ #define CRAMFS_MAGIC 0x28cd3d45 #define RAM_INIT_MAGIC 0x56902387 - + +#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\ + IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) + ;; exported symbols .globl etrax_irv @@ -301,6 +313,12 @@ ;; after init. .section ".text.init" _inflash: +#ifdef CONFIG_ETRAX_ETHERNET + ;; Start MII clock to make sure it is running when tranceiver is reset + move.d START_ETHERNET_CLOCK, $r0 + move.d $r0, [R_NETWORK_GEN_CONFIG] +#endif + ;; We need to initialze DRAM registers before we start using the DRAM cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? diff -urN linux-2.5.3-pre2/arch/cris/kernel/irq.c linux/arch/cris/kernel/irq.c --- linux-2.5.3-pre2/arch/cris/kernel/irq.c Wed Nov 28 13:22:25 2001 +++ linux/arch/cris/kernel/irq.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.17 2001/07/25 16:08:01 bjornw Exp $ +/* $Id: irq.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ * * linux/arch/cris/kernel/irq.c * @@ -138,7 +138,7 @@ /* IRQ0 and 1 are special traps */ void hwbreakpoint(void); void IRQ1_interrupt(void); -BUILD_IRQ(2, 0x04) /* the timer interrupt */ +BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */ BUILD_IRQ(3, 0x08) BUILD_IRQ(4, 0x10) BUILD_IRQ(5, 0x20) diff -urN linux-2.5.3-pre2/arch/cris/kernel/kgdb.c linux/arch/cris/kernel/kgdb.c --- linux-2.5.3-pre2/arch/cris/kernel/kgdb.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/kgdb.c Mon Jan 21 17:23:22 2002 @@ -18,6 +18,9 @@ *! Jul 21 1999 Bjorn Wesen eLinux port *! *! $Log: kgdb.c,v $ +*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw +*! Import of Linux 2.5.1 +*! *! Revision 1.6 2001/10/09 13:10:03 matsfg *! Added $ on registers and removed some underscores *! @@ -55,7 +58,7 @@ *! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.6 2001/10/09 13:10:03 matsfg Exp $ +*! $Id: kgdb.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ *! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! diff -urN linux-2.5.3-pre2/arch/cris/kernel/ksyms.c linux/arch/cris/kernel/ksyms.c --- linux-2.5.3-pre2/arch/cris/kernel/ksyms.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/ksyms.c Mon Jan 21 17:23:22 2002 @@ -23,34 +23,45 @@ extern void dump_thread(struct pt_regs *, struct user *); extern unsigned long get_cmos_time(void); +extern void __Udiv(void); extern void __ashrdi3(void); extern void iounmap(void *addr); -/* platform dependent support */ - +/* Platform dependent support */ EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(loops_per_usec); +/* String functions */ +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(simple_strtol); EXPORT_SYMBOL(strstr); - +EXPORT_SYMBOL(strcpy); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strcmp); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strncmp); + +/* Math functions */ +EXPORT_SYMBOL(__Udiv); EXPORT_SYMBOL(__ashrdi3); +/* Memory functions */ EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); -/* export shadow registers for the CPU I/O pins */ +/* Semaphore functions */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +/* Export shadow registers for the CPU I/O pins */ EXPORT_SYMBOL(genconfig_shadow); EXPORT_SYMBOL(port_pa_data_shadow); EXPORT_SYMBOL(port_pa_dir_shadow); @@ -59,8 +70,7 @@ EXPORT_SYMBOL(port_pb_config_shadow); EXPORT_SYMBOL(port_g_data_shadow); -/* other stuff */ - +/* Userspace access functions */ EXPORT_SYMBOL(strncpy_from_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__generic_copy_from_user); @@ -71,8 +81,8 @@ #undef memcpy #undef memset -extern void * memset(void *,int,__kernel_size_t); -extern void * memcpy(void *,const void *,__kernel_size_t); +extern void * memset(void *, int, __kernel_size_t); +extern void * memcpy(void *, const void *, __kernel_size_t); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); diff -urN linux-2.5.3-pre2/arch/cris/kernel/process.c linux/arch/cris/kernel/process.c --- linux-2.5.3-pre2/arch/cris/kernel/process.c Thu Dec 27 08:21:28 2001 +++ linux/arch/cris/kernel/process.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.20 2001/10/03 08:21:39 jonashg Exp $ +/* $Id: process.c,v 1.3 2002/01/21 15:22:49 bjornw Exp $ * * linux/arch/cris/kernel/process.c * @@ -8,6 +8,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: process.c,v $ + * Revision 1.3 2002/01/21 15:22:49 bjornw + * current->counter is gone + * + * Revision 1.22 2001/11/13 09:40:43 orjanf + * Added dump_fpu (needed for core dumps). + * + * Revision 1.21 2001/11/12 18:26:21 pkj + * Fixed compiler warnings. + * * Revision 1.20 2001/10/03 08:21:39 jonashg * cause_of_death does not exist if CONFIG_SVINTO_SIM is defined. * @@ -57,6 +66,7 @@ #include #include #include +#include #include #include @@ -77,7 +87,6 @@ * setup. */ -static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; @@ -135,14 +144,15 @@ * code to know about it than the watchdog handler in entry.S and * this code, implementing hard reset through the watchdog. */ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) extern int cause_of_death; +#endif printk("*** HARD RESET ***\n"); cli(); #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) cause_of_death = 0xbedead; - #else /* Since we dont plan to keep on reseting the watchdog, the key can be arbitrary hence three */ @@ -243,9 +253,10 @@ */ void dump_thread(struct pt_regs * regs, struct user * dump) { - int i; #if 0 -/* changed the size calculations - should hopefully work better. lbt */ + int i; + + /* changed the size calculations - should hopefully work better. lbt */ dump->magic = CMAGIC; dump->start_code = 0; dump->start_stack = regs->esp & ~(PAGE_SIZE - 1); @@ -263,6 +274,12 @@ dump->u_fpvalid = dump_fpu (regs, &dump->i387); #endif +} + +/* Fill in the fpu structure for a core dump. */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) +{ + return 0; } /* diff -urN linux-2.5.3-pre2/arch/cris/kernel/ptrace.c linux/arch/cris/kernel/ptrace.c --- linux-2.5.3-pre2/arch/cris/kernel/ptrace.c Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/kernel/ptrace.c Mon Jan 21 17:23:22 2002 @@ -8,6 +8,12 @@ * Authors: Bjorn Wesen * * $Log: ptrace.c,v $ + * Revision 1.2 2001/12/18 13:35:20 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.8 2001/11/12 18:26:21 pkj + * Fixed compiler warnings. + * * Revision 1.7 2001/09/26 11:53:49 bjornw * PTRACE_DETACH works more simple in 2.4.10 * @@ -74,8 +80,6 @@ static inline int put_reg(struct task_struct *task, unsigned int regno, unsigned long data) { - unsigned long *addr; - if (regno == PT_USP) task->thread.usp = data; else if (regno < PT_MAX) @@ -207,9 +211,7 @@ break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - long tmp; - + case PTRACE_CONT: /* restart after signal. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; @@ -222,16 +224,13 @@ wake_up_process(child); ret = 0; break; - } /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ - case PTRACE_KILL: { - long tmp; - + case PTRACE_KILL: ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; @@ -239,11 +238,8 @@ /* TODO: make sure any pending breakpoint is killed */ wake_up_process(child); break; - } - - case PTRACE_SINGLESTEP: { /* set the trap flag. */ - long tmp; + case PTRACE_SINGLESTEP: /* set the trap flag. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; @@ -256,7 +252,6 @@ wake_up_process(child); ret = 0; break; - } case PTRACE_DETACH: ret = ptrace_detach(child, data); diff -urN linux-2.5.3-pre2/arch/cris/kernel/setup.c linux/arch/cris/kernel/setup.c --- linux-2.5.3-pre2/arch/cris/kernel/setup.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/setup.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.22 2001/10/23 17:42:58 pkj Exp $ +/* $Id: setup.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ * * linux/arch/cris/kernel/setup.c * @@ -26,10 +26,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -72,10 +74,10 @@ void __init setup_arch(char **cmdline_p) { - unsigned long bootmap_size; + extern void init_etrax_debug(void); + unsigned long bootmap_size; unsigned long start_pfn, max_pfn; unsigned long memory_start; - extern void console_print_etrax(const char *b); /* register an initial console printing routine for printk's */ @@ -87,12 +89,12 @@ if(romfs_in_flash || !romfs_length) { /* if we have the romfs in flash, or if there is no rom filesystem, - * our free area starts directly after the BSS + * our free area starts directly after the BSS */ memory_start = (unsigned long) &_end; } else { /* otherwise the free area starts after the ROM filesystem */ - printk("ROM fs in RAM, size %d bytes\n", romfs_length); + printk("ROM fs in RAM, size %lu bytes\n", romfs_length); memory_start = romfs_start + romfs_length; } @@ -193,7 +195,7 @@ #define HAS_ATA 0x0020 #define HAS_USB 0x0040 #define HAS_IRQ_BUG 0x0080 -#define HAS_MMU_BUG 0x0100 +#define HAS_MMU_BUG 0x0100 static struct cpu_info { char *model; @@ -213,50 +215,27 @@ { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG }, { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG }, - { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, + { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, { "Unknown", 0, 0 } /* This entry MUST be the last */ }; -/* - * get_cpuinfo - Get information on one CPU for use by the procfs. - * - * Prints info on the next CPU into buffer. Beware, doesn't check for - * buffer overflow. Current implementation of procfs assumes that the - * resulting data is <= 1K. - * - * BUFFER is PAGE_SIZE - 1K bytes long. - * - * Args: - * buffer -- you guessed it, the data buffer - * cpu_np -- Input: next cpu to get (start at 0). Output: Updated. - * - * Returns number of bytes written to buffer. - */ -int get_cpuinfo(char *buffer, unsigned *cpu_np) +static int show_cpuinfo(struct seq_file *m, void *v) { - int revision; - struct cpu_info *info; - unsigned n; + unsigned long revision; + struct cpu_info *info; /* read the version register in the CPU and print some stuff */ revision = rdvr(); - if (revision < 0 || revision >= sizeof cpu_info/sizeof *cpu_info) { + if (revision >= sizeof cpu_info/sizeof *cpu_info) info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1]; - } else + else info = &cpu_info[revision]; - /* No SMP at the moment, so just toggle 0/1 */ - n = *cpu_np; - *cpu_np = 1; - if (n != 0) { - return (0); - } - - return sprintf(buffer, + return seq_printf(m, "cpu\t\t: CRIS\n" - "cpu revision\t: %d\n" + "cpu revision\t: %lu\n" "cpu model\t: %s\n" "cache size\t: %d kB\n" "fpu\t\t: %s\n" @@ -283,4 +262,28 @@ (loops_per_jiffy * HZ + 500) / 500000, ((loops_per_jiffy * HZ + 500) / 5000) % 100); } + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + /* We only got one CPU... */ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; + #endif /* CONFIG_PROC_FS */ diff -urN linux-2.5.3-pre2/arch/cris/kernel/shadows.c linux/arch/cris/kernel/shadows.c --- linux-2.5.3-pre2/arch/cris/kernel/shadows.c Tue May 1 16:04:56 2001 +++ linux/arch/cris/kernel/shadows.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $ +/* $Id: shadows.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * Various shadow registers. Defines for these are in include/asm-etrax100/io.h */ diff -urN linux-2.5.3-pre2/arch/cris/kernel/sys_cris.c linux/arch/cris/kernel/sys_cris.c --- linux-2.5.3-pre2/arch/cris/kernel/sys_cris.c Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/kernel/sys_cris.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $ +/* $Id: sys_cris.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * linux/arch/cris/kernel/sys_cris.c * diff -urN linux-2.5.3-pre2/arch/cris/kernel/time.c linux/arch/cris/kernel/time.c --- linux-2.5.3-pre2/arch/cris/kernel/time.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/time.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.9 2001/10/25 10:26:37 johana Exp $ +/* $Id: time.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ * * linux/arch/cris/kernel/time.c * @@ -18,6 +18,7 @@ * Linux/CRIS specific code: * * Authors: Bjorn Wesen + * Johan Adolfsson * */ @@ -61,6 +62,7 @@ static unsigned long do_slow_gettimeoffset(void) { unsigned long count; + unsigned long usec_count = 0; static unsigned long count_p = LATCH; /* for the first call after boot */ static unsigned long jiffies_p = 0; @@ -93,16 +95,20 @@ */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { + /* Timer wrapped */ + count = count_p; + usec_count = 1000000/CLOCK_TICK_RATE/2; } } else jiffies_p = jiffies_t; - count_p = count; - + /* Convert timer value to usec using table lookup */ + usec_count += cris_timer0_value_us[count]; +#if 0 count = ((LATCH-1) - count) * TICK_SIZE; count = (count + LATCH/2) / LATCH; - - return count; +#endif + return usec_count; } static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; @@ -160,9 +166,8 @@ { int retval = 0; int real_seconds, real_minutes, cmos_minutes; - unsigned char save_control, save_freq_select; - printk("set_rtc_mmss(%d)\n", nowtime); + printk("set_rtc_mmss(%lu)\n", nowtime); if(!have_rtc) return 0; @@ -225,7 +230,9 @@ /* right now, starting the watchdog is the same as resetting it */ #define start_watchdog reset_watchdog +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) static int watchdog_key = 0; /* arbitrary number */ +#endif /* number of pages to consider "out of memory". it is normal that the memory * is used though, so put this really low. @@ -306,12 +313,12 @@ if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) + xtime.tv_usec < 500000 + (tick >> 1)) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else last_rtc_update = xtime.tv_sec - 600; - + } } #if 0 @@ -322,6 +329,7 @@ { unsigned long flags; unsigned int newjiff; + save_flags(flags); cli(); newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); @@ -337,7 +345,6 @@ get_cmos_time(void) { unsigned int year, mon, day, hour, min, sec; - int i; sec = CMOS_READ(RTC_SECONDS); min = CMOS_READ(RTC_MINUTES); diff -urN linux-2.5.3-pre2/arch/cris/kernel/traps.c linux/arch/cris/kernel/traps.c --- linux-2.5.3-pre2/arch/cris/kernel/traps.c Fri Nov 9 13:58:02 2001 +++ linux/arch/cris/kernel/traps.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.15 2001/07/18 14:02:37 bjornw Exp $ +/* $Id: traps.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ * * linux/arch/cris/traps.c * @@ -30,6 +30,55 @@ int kstack_depth_to_print = 24; +void show_trace(unsigned long * stack) +{ + unsigned long addr, module_start, module_end; + extern char _stext, _etext; + int i; + + printk("\nCall Trace: "); + + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + + while (((long) stack & (THREAD_SIZE-1)) != 0) { + if (__get_user (addr, stack)) { + /* This message matches "failing address" marked + s390 in ksymoops, so lines containing it will + not be filtered out by ksymoops. */ + printk ("Failing address 0x%lx\n", (unsigned long)stack); + break; + } + stack++; + + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } +} + +void show_trace_task(struct task_struct *tsk) +{ + /* TODO, this is not really useful since its called from + * SysRq-T and we don't have a keyboard.. :) + */ +} + + /* * These constants are for searching for possible module text * segments. MODULE_RANGE is a guess of how much space is likely @@ -48,9 +97,8 @@ void show_stack(unsigned long *sp) { - unsigned long *stack, addr, module_start, module_end; + unsigned long *stack, addr; int i; - extern char _stext, _etext; /* * debugging aid: "show_stack(NULL);" prints a @@ -62,7 +110,7 @@ stack = sp; - printk("\nStack from %08lx:\n ", stack); + printk("\nStack from %08lx:\n ", (unsigned long)stack); for(i = 0; i < kstack_depth_to_print; i++) { if (((long) stack & (THREAD_SIZE-1)) == 0) break; @@ -72,45 +120,13 @@ /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - printk ("Failing address 0x%lx\n", stack); + printk ("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; printk("%08lx ", addr); } - - printk("\nCall Trace: "); - stack = sp; - i = 1; - module_start = VMALLOC_START; - module_end = VMALLOC_END; - while (((long) stack & (THREAD_SIZE-1)) != 0) { - if (__get_user (addr, stack)) { - /* This message matches "failing address" marked - s390 in ksymoops, so lines containing it will - not be filtered out by ksymoops. */ - printk ("Failing address 0x%lx\n", stack); - break; - } - stack++; - - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (((addr >= (unsigned long) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - printk("\n "); - printk("[<%08lx>] ", addr); - i++; - } - } + show_trace(sp); } #if 0 @@ -148,7 +164,7 @@ regs->r8, regs->r9, regs->r10, regs->r11); printk("r12: %08lx r13: %08lx oR10: %08lx\n", regs->r12, regs->r13, regs->orig_r10); - printk("R_MMU_CAUSE: %08lx\n", *R_MMU_CAUSE); + printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, (unsigned long)current); @@ -195,25 +211,56 @@ } } +/* Called from entry.S when the watchdog has bitten + * We print out something resembling an oops dump, and if + * we have the nice doggy development flag set, we halt here + * instead of rebooting. + */ + +void +watchdog_bite_hook(struct pt_regs *regs) +{ +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + cli(); + stop_watchdog(); + show_registers(regs); + while(1) /* nothing */; +#else + show_registers(regs); +#endif +} + +/* This is normally the 'Oops' routine */ + void die_if_kernel(const char * str, struct pt_regs * regs, long err) { + extern void reset_watchdog(void); + extern void stop_watchdog(void); + if(user_mode(regs)) return; +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* This printout might take too long and trigger the + * watchdog normally. If we're in the nice doggy + * development mode, stop the watchdog during printout. + */ stop_watchdog(); +#endif printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY reset_watchdog(); - +#endif do_exit(SIGSEGV); } void __init trap_init(void) { - /* Nothing needs to be done */ + /* Nothing needs to be done */ } diff -urN linux-2.5.3-pre2/arch/cris/lib/checksum.S linux/arch/cris/lib/checksum.S --- linux-2.5.3-pre2/arch/cris/lib/checksum.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/lib/checksum.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $ +/* $Id: checksum.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * A fast checksum routine using movem * Copyright (c) 1998-2001 Axis Communications AB * diff -urN linux-2.5.3-pre2/arch/cris/lib/checksumcopy.S linux/arch/cris/lib/checksumcopy.S --- linux-2.5.3-pre2/arch/cris/lib/checksumcopy.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/lib/checksumcopy.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $ +/* $Id: checksumcopy.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * A fast checksum+copy routine using movem * Copyright (c) 1998, 2001 Axis Communications AB * diff -urN linux-2.5.3-pre2/arch/cris/lib/dmacopy.c linux/arch/cris/lib/dmacopy.c --- linux-2.5.3-pre2/arch/cris/lib/dmacopy.c Thu Feb 8 16:32:44 2001 +++ linux/arch/cris/lib/dmacopy.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ +/* $Id: dmacopy.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax */ diff -urN linux-2.5.3-pre2/arch/cris/lib/dram_init.S linux/arch/cris/lib/dram_init.S --- linux-2.5.3-pre2/arch/cris/lib/dram_init.S Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/lib/dram_init.S Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: dram_init.S,v 1.10 2001/10/04 12:00:21 martinnn Exp $ +/* $Id: dram_init.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files @@ -11,6 +11,9 @@ * Authors: Mikael Starvik (starvik@axis.com) * * $Log: dram_init.S,v $ + * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw + * Import of Linux 2.5.1 + * * Revision 1.10 2001/10/04 12:00:21 martinnn * Added missing underscores. * diff -urN linux-2.5.3-pre2/arch/cris/lib/hw_settings.S linux/arch/cris/lib/hw_settings.S --- linux-2.5.3-pre2/arch/cris/lib/hw_settings.S Wed Jul 4 11:50:39 2001 +++ linux/arch/cris/lib/hw_settings.S Mon Jan 21 17:23:22 2002 @@ -1,5 +1,5 @@ /* - * $Id: hw_settings.S,v 1.3 2001/04/21 17:02:46 bjornw Exp $ + * $Id: hw_settings.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. diff -urN linux-2.5.3-pre2/arch/cris/lib/old_checksum.c linux/arch/cris/lib/old_checksum.c --- linux-2.5.3-pre2/arch/cris/lib/old_checksum.c Thu Feb 8 16:32:44 2001 +++ linux/arch/cris/lib/old_checksum.c Mon Jan 21 17:23:22 2002 @@ -1,4 +1,4 @@ -/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ +/* $Id: old_checksum.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ * * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket diff -urN linux-2.5.3-pre2/arch/cris/mm/extable.c linux/arch/cris/mm/extable.c --- linux-2.5.3-pre2/arch/cris/mm/extable.c Mon Oct 8 11:43:54 2001 +++ linux/arch/cris/mm/extable.c Mon Jan 21 17:23:22 2002 @@ -2,6 +2,9 @@ * linux/arch/cris/mm/extable.c * * $Log: extable.c,v $ + * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw + * Import of Linux 2.5.1 + * * Revision 1.3 2001/09/27 13:52:40 bjornw * Harmonize underscore-ness with other parts * diff -urN linux-2.5.3-pre2/arch/cris/mm/fault.c linux/arch/cris/mm/fault.c --- linux-2.5.3-pre2/arch/cris/mm/fault.c Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/mm/fault.c Mon Jan 21 17:23:22 2002 @@ -6,6 +6,20 @@ * Authors: Bjorn Wesen * * $Log: fault.c,v $ + * Revision 1.2 2001/12/18 13:35:22 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.20 2001/11/22 13:34:06 bjornw + * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted + * unaligned write, because the second half of the write will be corrupted + * otherwise. Affected unaligned writes spanning not-yet mapped pages. + * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss + * was due to a read or a write (before we didn't know this until the next + * restart of the interrupted instruction, thus wasting one fault-irq) + * + * Revision 1.19 2001/11/12 19:02:10 pkj + * Fixed compiler warnings. + * * Revision 1.18 2001/07/18 22:14:32 bjornw * Enable interrupts in the bulk of do_page_fault * @@ -78,7 +92,14 @@ int error_code); /* debug of low-level TLB reload */ +#undef DEBUG + +#ifdef DEBUG +#define D(x) x +#else #define D(x) +#endif + /* debug of higher-level faults */ #define DPG(x) @@ -94,9 +115,12 @@ handle_mmu_bus_fault(struct pt_regs *regs) { int cause, select; +#ifdef DEBUG int index; int page_id; - int miss, we, acc, inv; + int acc, inv; +#endif + int miss, we, writeac; pmd_t *pmd; pte_t pte; int errcode; @@ -106,75 +130,83 @@ select = *R_TLB_SELECT; address = cause & PAGE_MASK; /* get faulting address */ - - D(page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause)); - D(acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause)); - D(inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause)); - D(index = IO_EXTRACT(R_TLB_SELECT, index, select)); + +#ifdef DEBUG + page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); + acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); + inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); + index = IO_EXTRACT(R_TLB_SELECT, index, select); +#endif miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); + writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); + + /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned + * write causes a MMU-fault, it will not be restarted correctly. + * This could happen if a write crosses a page-boundary and the + * second page is not yet COW'ed or even loaded. The workaround + * is to clear the unaligned bit in the CPU status record, so + * that the CPU will rerun both the first and second halves of + * the instruction. This will not have any sideeffects unless + * the first half goes to any device or memory that can't be + * written twice, and which is mapped through the MMU. + * + * We only need to do this for writes. + */ + + if(writeac) + regs->csrinstr &= ~(1 << 5); - /* Note: the reason we don't set errcode's r/w flag here - * using the 'we' flag, is because the latter is only given - * if there is a write-protection exception, not given as a - * general r/w access mode flag. It is currently not possible - * to get this from the MMU (TODO: check if this is the case - * for LXv2). - * - * The page-fault code won't care, but there will be two page- - * faults instead of one for the case of a write to a non-tabled - * page (miss, then write-protection). + /* Set errcode's R/W flag according to the mode which caused the + * fault */ - errcode = 0; + errcode = writeac << 1; - D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, " - "idx %d pid %d\n", + D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n", regs->irp, address, miss, inv, we, acc, index, page_id)); /* for a miss, we need to reload the TLB entry */ - if(miss) { - + if (miss) { /* see if the pte exists at all * refer through current_pgd, dont use mm->pgd */ - + pmd = (pmd_t *)(current_pgd + pgd_index(address)); - if(pmd_none(*pmd)) + if (pmd_none(*pmd)) goto dofault; - if(pmd_bad(*pmd)) { - printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd); + if (pmd_bad(*pmd)) { + printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd); pmd_clear(pmd); return; } pte = *pte_offset(pmd, address); - if(!pte_present(pte)) + if (!pte_present(pte)) goto dofault; - - D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte))); - D( - { - if(pte_val(pte) & _PAGE_SILENT_WRITE) - printk("Silent-W "); - if(pte_val(pte) & _PAGE_KERNEL) - printk("Kernel "); - if(pte_val(pte) & _PAGE_SILENT_READ) - printk("Silent-R "); - if(pte_val(pte) & _PAGE_GLOBAL) - printk("Global "); - if(pte_val(pte) & _PAGE_PRESENT) - printk("Present "); - if(pte_val(pte) & _PAGE_ACCESSED) - printk("Accessed "); - if(pte_val(pte) & _PAGE_MODIFIED) - printk("Modified "); - if(pte_val(pte) & _PAGE_READ) - printk("Readable "); - if(pte_val(pte) & _PAGE_WRITE) - printk("Writeable "); - printk("\n"); - }); + +#ifdef DEBUG + printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte)); + if (pte_val(pte) & _PAGE_SILENT_WRITE) + printk("Silent-W "); + if (pte_val(pte) & _PAGE_KERNEL) + printk("Kernel "); + if (pte_val(pte) & _PAGE_SILENT_READ) + printk("Silent-R "); + if (pte_val(pte) & _PAGE_GLOBAL) + printk("Global "); + if (pte_val(pte) & _PAGE_PRESENT) + printk("Present "); + if (pte_val(pte) & _PAGE_ACCESSED) + printk("Accessed "); + if (pte_val(pte) & _PAGE_MODIFIED) + printk("Modified "); + if (pte_val(pte) & _PAGE_READ) + printk("Readable "); + if (pte_val(pte) & _PAGE_WRITE) + printk("Writeable "); + printk("\n"); +#endif /* load up the chosen TLB entry * this assumes the pte format is the same as the TLB_LO layout. @@ -189,10 +221,10 @@ } errcode = 1 | (we << 1); - + dofault: /* leave it to the MM system fault handler below */ - D(printk("do_page_fault %p errcode %d\n", address, errcode)); + D(printk("do_page_fault %lx errcode %d\n", address, errcode)); do_page_fault(address, regs, errcode); } @@ -221,20 +253,19 @@ struct mm_struct *mm; struct vm_area_struct * vma; int writeaccess; - int fault; unsigned long fixup; siginfo_t info; tsk = current; - /* - * We fault-in kernel-space virtual memory on-demand. The - * 'reference' page table is init_mm.pgd. - * - * NOTE! We MUST NOT take any locks for this case. We may - * be in an interrupt or a critical region, and should - * only copy the information from the master page table, - * nothing more. + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. * * NOTE2: This is done so that, when updating the vmalloc * mappings we don't have to walk all processes pgdirs and @@ -243,13 +274,13 @@ * bit set so sometimes the TLB can use a lingering entry. * * This verifies that the fault happens in kernel space - * and that the fault was not a protection error (error_code & 1). - */ + * and that the fault was not a protection error (error_code & 1). + */ - if (address >= VMALLOC_START && + if (address >= VMALLOC_START && !(error_code & 1) && !user_mode(regs)) - goto vmalloc_fault; + goto vmalloc_fault; /* we can and should enable interrupts at this point */ sti(); @@ -312,28 +343,27 @@ */ switch (handle_mm_fault(mm, vma, address, writeaccess)) { - case 1: - tsk->min_flt++; - break; - case 2: - tsk->maj_flt++; - break; - case 0: - goto do_sigbus; - default: - goto out_of_memory; + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; } up_read(&mm->mmap_sem); return; - + /* * Something tried to access memory that isn't in our memory map.. * Fix it, but check if it's kernel or user first.. */ bad_area: - up_read(&mm->mmap_sem); bad_area_nosemaphore: @@ -361,10 +391,10 @@ * code) */ - if ((fixup = search_exception_table(regs->irp)) != 0) { + if ((fixup = search_exception_table(regs->irp)) != 0) { /* Adjust the instruction pointer in the stackframe */ - regs->irp = fixup; + regs->irp = fixup; /* We do not want to return by restoring the CPU-state * anymore, so switch frame-types (see ptrace.h) @@ -372,9 +402,9 @@ regs->frametype = CRIS_FRAME_NORMAL; - D(printk("doing fixup to 0x%x\n", fixup)); - return; - } + D(printk("doing fixup to 0x%lx\n", fixup)); + return; + } /* * Oops. The kernel tried to access some bad page. We'll have to @@ -397,9 +427,9 @@ */ out_of_memory: - up_read(&mm->mmap_sem); + up_read(&mm->mmap_sem); printk("VM: killing process %s\n", tsk->comm); - if(user_mode(regs)) + if (user_mode(regs)) do_exit(SIGKILL); goto no_context; @@ -407,40 +437,40 @@ up_read(&mm->mmap_sem); /* - * Send a sigbus, regardless of whether we were in kernel - * or user mode. - */ + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ info.si_code = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; info.si_addr = (void *)address; force_sig_info(SIGBUS, &info, tsk); - - /* Kernel mode? Handle exceptions or die */ - if (!user_mode(regs)) - goto no_context; - return; + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + return; vmalloc_fault: - { - /* - * Synchronize this task's top level page-table - * with the 'reference' page table. + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. * * Use current_pgd instead of tsk->active_mm->pgd * since the latter might be unavailable if this * code is executed in a misfortunately run irq * (like inside schedule() between switch_mm and * switch_to...). - */ + */ - int offset = pgd_index(address); - pgd_t *pgd, *pgd_k; - pmd_t *pmd, *pmd_k; + int offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; pte_t *pte_k; - pgd = current_pgd + offset; - pgd_k = init_mm.pgd + offset; + pgd = (pgd_t *)current_pgd + offset; + pgd_k = init_mm.pgd + offset; /* Since we're two-level, we don't need to do both * set_pgd and set_pmd (they do the same thing). If @@ -454,13 +484,13 @@ * it exists. */ - pmd = pmd_offset(pgd, address); - pmd_k = pmd_offset(pgd_k, address); + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); - if (!pmd_present(*pmd_k)) - goto bad_area_nosemaphore; + if (!pmd_present(*pmd_k)) + goto bad_area_nosemaphore; - set_pmd(pmd, *pmd_k); + set_pmd(pmd, *pmd_k); /* Make sure the actual PTE exists as well to * catch kernel vmalloc-area accesses to non-mapped @@ -468,10 +498,10 @@ * silently loop forever. */ - pte_k = pte_offset(pmd_k, address); - if (!pte_present(*pte_k)) - goto no_context; + pte_k = pte_offset(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; - return; - } + return; + } } diff -urN linux-2.5.3-pre2/arch/cris/mm/init.c linux/arch/cris/mm/init.c --- linux-2.5.3-pre2/arch/cris/mm/init.c Thu Jul 26 15:10:06 2001 +++ linux/arch/cris/mm/init.c Mon Jan 21 17:23:22 2002 @@ -7,6 +7,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: init.c,v $ + * Revision 1.2 2001/12/18 13:35:22 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.31 2001/11/13 16:22:00 bjornw + * Skip calculating totalram and sharedram in si_meminfo + * + * Revision 1.30 2001/11/12 19:02:10 pkj + * Fixed compiler warnings. + * * Revision 1.29 2001/07/25 16:09:50 bjornw * val->sharedram will stay 0 * @@ -459,29 +468,18 @@ free_page(addr); totalram_pages++; } - printk ("Freeing unused kernel memory: %dk freed\n", + printk ("Freeing unused kernel memory: %luk freed\n", (&__init_end - &__init_begin) >> 10); } void si_meminfo(struct sysinfo *val) { - int i; - - i = max_mapnr; - val->totalram = 0; - val->sharedram = 0; - val->freeram = nr_free_pages(); - val->bufferram = atomic_read(&buffermem_pages); - while (i-- > 0) { - if (PageReserved(mem_map+i)) - continue; - val->totalram++; - if (!atomic_read(&mem_map[i].count)) - continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; - } - val->mem_unit = PAGE_SIZE; - val->totalhigh = 0; - val->freehigh = 0; + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; } diff -urN linux-2.5.3-pre2/arch/i386/defconfig linux/arch/i386/defconfig --- linux-2.5.3-pre2/arch/i386/defconfig Mon Jan 21 17:22:32 2002 +++ linux/arch/i386/defconfig Mon Jan 21 17:23:22 2002 @@ -491,8 +491,6 @@ # CONFIG_PCMCIA_XIRTULIP is not set CONFIG_NET_PCMCIA_RADIO=y CONFIG_PCMCIA_RAYCS=y -# CONFIG_PCMCIA_NETWAVE is not set -# CONFIG_PCMCIA_WAVELAN is not set # CONFIG_AIRONET4500_CS is not set # @@ -738,6 +736,7 @@ # USB Host Controller Drivers # # CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_OHCI_HCD is not set CONFIG_USB_UHCI_ALT=y # CONFIG_USB_OHCI is not set diff -urN linux-2.5.3-pre2/arch/i386/kernel/i8259.c linux/arch/i386/kernel/i8259.c --- linux-2.5.3-pre2/arch/i386/kernel/i8259.c Mon Sep 17 23:03:09 2001 +++ linux/arch/i386/kernel/i8259.c Mon Jan 21 17:23:22 2002 @@ -79,6 +79,7 @@ * through the ICC by us (IPIs) */ #ifdef CONFIG_SMP +BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR) BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR) BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) @@ -472,6 +473,9 @@ * IPI, driven by wakeup. */ set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); + + /* IPI for task migration */ + set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt); /* IPI for invalidation */ set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); diff -urN linux-2.5.3-pre2/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- linux-2.5.3-pre2/arch/i386/kernel/smp.c Mon Jan 7 12:55:17 2002 +++ linux/arch/i386/kernel/smp.c Mon Jan 21 17:23:22 2002 @@ -485,6 +485,35 @@ do_flush_tlb_all_local(); } +static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED; +static task_t *new_task; + +/* + * This function sends a 'task migration' IPI to another CPU. + * Must be called from syscall contexts, with interrupts *enabled*. + */ +void smp_migrate_task(int cpu, task_t *p) +{ + /* + * The target CPU will unlock the migration spinlock: + */ + spin_lock(&migration_lock); + new_task = p; + send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR); +} + +/* + * Task migration callback. + */ +asmlinkage void smp_task_migration_interrupt(void) +{ + task_t *p; + + ack_APIC_irq(); + p = new_task; + spin_unlock(&migration_lock); + sched_task_migrated(p); +} /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing diff -urN linux-2.5.3-pre2/arch/i386/kernel/smpboot.c linux/arch/i386/kernel/smpboot.c --- linux-2.5.3-pre2/arch/i386/kernel/smpboot.c Mon Jan 7 12:55:17 2002 +++ linux/arch/i386/kernel/smpboot.c Mon Jan 21 17:23:22 2002 @@ -462,6 +462,7 @@ * things done here to the most necessary things. */ cpu_init(); + init_idle(); smp_callin(); while (!atomic_read(&smp_commenced)) rep_nop(); @@ -470,8 +471,8 @@ * the local TLBs too. */ local_flush_tlb(); + idle_startup_done(); - init_idle(); return cpu_idle(); } diff -urN linux-2.5.3-pre2/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c --- linux-2.5.3-pre2/drivers/block/ps2esdi.c Mon Dec 31 17:56:19 2001 +++ linux/drivers/block/ps2esdi.c Mon Jan 21 17:23:22 2002 @@ -422,7 +422,7 @@ blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 128); for (i = 0; i < ps2esdi_drives; i++) { - register_disk(&ps2esdi_gendisk,MKDEV(MAJOR_NR,i<<6),1<<6, + register_disk(&ps2esdi_gendisk,mk_kdev(MAJOR_NR,i<<6),1<<6, &ps2esdi_fops, ps2esdi_info[i].head * ps2esdi_info[i].sect * ps2esdi_info[i].cyl); @@ -466,7 +466,7 @@ #if 0 printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld, buffer: %p\n", DEVICE_NAME, - CURRENT_DEV, MINOR(CURRENT->rq_dev), + CURRENT_DEV, minor(CURRENT->rq_dev), CURRENT->cmd, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->buffer); #endif @@ -481,12 +481,12 @@ } /* check for above 16Mb dmas */ else if ((CURRENT_DEV < ps2esdi_drives) && (CURRENT->sector + CURRENT->current_nr_sectors <= - ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects) && + ps2esdi[minor(CURRENT->rq_dev)].nr_sects) && CURRENT->flags & REQ_CMD) { #if 0 printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld\n", DEVICE_NAME, - CURRENT_DEV, MINOR(CURRENT->rq_dev), + CURRENT_DEV, minor(CURRENT->rq_dev), CURRENT->cmd, CURRENT->sector, CURRENT->current_nr_sectors); #endif @@ -510,7 +510,7 @@ /* is request is valid */ else { printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives, - CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects); + CURRENT->sector, ps2esdi[minor(CURRENT->rq_dev)].nr_sects); end_request(FAIL); } @@ -849,7 +849,7 @@ switch (int_ret_code & 0x0f) { case INT_TRANSFER_REQ: ps2esdi_prep_dma(CURRENT->buffer, CURRENT->current_nr_sectors, - (CURRENT->cmd == READ) + (rq_data_dir(CURRENT) == READ) ? MCA_DMA_MODE_16 | MCA_DMA_MODE_WRITE | MCA_DMA_MODE_XFER : MCA_DMA_MODE_16 | MCA_DMA_MODE_READ); outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR, ESDI_CONTROL); diff -urN linux-2.5.3-pre2/drivers/ieee1394/Config.in linux/drivers/ieee1394/Config.in --- linux-2.5.3-pre2/drivers/ieee1394/Config.in Thu Jul 19 17:48:15 2001 +++ linux/drivers/ieee1394/Config.in Mon Jan 21 17:23:22 2002 @@ -19,6 +19,7 @@ comment "Protocol Drivers" dep_tristate ' OHCI-1394 Video support' CONFIG_IEEE1394_VIDEO1394 $CONFIG_IEEE1394_OHCI1394 dep_tristate ' SBP-2 support (Harddisks etc.)' CONFIG_IEEE1394_SBP2 $CONFIG_SCSI $CONFIG_IEEE1394 + dep_tristate ' OHCI-DV I/O support' CONFIG_IEEE1394_DV1394 $CONFIG_IEEE1394_OHCI1394 dep_tristate ' Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394 bool 'Excessive debugging output' CONFIG_IEEE1394_VERBOSEDEBUG diff -urN linux-2.5.3-pre2/drivers/ieee1394/Makefile linux/drivers/ieee1394/Makefile --- linux-2.5.3-pre2/drivers/ieee1394/Makefile Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/Makefile Mon Jan 21 17:23:22 2002 @@ -16,6 +16,7 @@ obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o +obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o include $(TOPDIR)/Rules.make diff -urN linux-2.5.3-pre2/drivers/ieee1394/csr.c linux/drivers/ieee1394/csr.c --- linux-2.5.3-pre2/drivers/ieee1394/csr.c Thu Jul 19 17:48:15 2001 +++ linux/drivers/ieee1394/csr.c Mon Jan 21 17:23:22 2002 @@ -70,7 +70,7 @@ { host->csr.lock = SPIN_LOCK_UNLOCKED; - host->csr.rom_size = host->template->get_rom(host, &host->csr.rom); + host->csr.rom_size = host->ops->get_rom(host, &host->csr.rom); host->csr.state = 0; host->csr.node_ids = 0; @@ -152,7 +152,7 @@ case CSR_CYCLE_TIME: oldcycle = host->csr.cycle_time; host->csr.cycle_time = - host->template->devctl(host, GET_CYCLE_COUNTER, 0); + host->ops->devctl(host, GET_CYCLE_COUNTER, 0); if (oldcycle > host->csr.cycle_time) { /* cycle time wrapped around */ @@ -163,7 +163,7 @@ case CSR_BUS_TIME: oldcycle = host->csr.cycle_time; host->csr.cycle_time = - host->template->devctl(host, GET_CYCLE_COUNTER, 0); + host->ops->devctl(host, GET_CYCLE_COUNTER, 0); if (oldcycle > host->csr.cycle_time) { /* cycle time wrapped around */ @@ -181,32 +181,32 @@ return RCODE_ADDRESS_ERROR; case CSR_BUS_MANAGER_ID: - if (host->template->hw_csr_reg) - ret = host->template->hw_csr_reg(host, 0, 0, 0); + if (host->ops->hw_csr_reg) + ret = host->ops->hw_csr_reg(host, 0, 0, 0); else ret = host->csr.bus_manager_id; *(buf++) = cpu_to_be32(ret); out; case CSR_BANDWIDTH_AVAILABLE: - if (host->template->hw_csr_reg) - ret = host->template->hw_csr_reg(host, 1, 0, 0); + if (host->ops->hw_csr_reg) + ret = host->ops->hw_csr_reg(host, 1, 0, 0); else ret = host->csr.bandwidth_available; *(buf++) = cpu_to_be32(ret); out; case CSR_CHANNELS_AVAILABLE_HI: - if (host->template->hw_csr_reg) - ret = host->template->hw_csr_reg(host, 2, 0, 0); + if (host->ops->hw_csr_reg) + ret = host->ops->hw_csr_reg(host, 2, 0, 0); else ret = host->csr.channels_available_hi; *(buf++) = cpu_to_be32(ret); out; case CSR_CHANNELS_AVAILABLE_LO: - if (host->template->hw_csr_reg) - ret = host->template->hw_csr_reg(host, 3, 0, 0); + if (host->ops->hw_csr_reg) + ret = host->ops->hw_csr_reg(host, 3, 0, 0); else ret = host->csr.channels_available_lo; @@ -244,7 +244,7 @@ host->csr.node_ids &= NODE_MASK << 16; host->csr.node_ids |= be32_to_cpu(*(data++)) & (BUS_MASK << 16); host->node_id = host->csr.node_ids >> 16; - host->template->devctl(host, SET_BUS_ID, host->node_id >> 6); + host->ops->devctl(host, SET_BUS_ID, host->node_id >> 6); out; case CSR_RESET_START: @@ -269,7 +269,7 @@ case CSR_CYCLE_TIME: /* should only be set by cycle start packet, automatically */ host->csr.cycle_time = be32_to_cpu(*data); - host->template->devctl(host, SET_CYCLE_COUNTER, + host->ops->devctl(host, SET_CYCLE_COUNTER, be32_to_cpu(*(data++))); out; case CSR_BUS_TIME: @@ -318,10 +318,10 @@ data = be32_to_cpu(data); arg = be32_to_cpu(arg); - if (host->template->hw_csr_reg) { + if (host->ops->hw_csr_reg) { quadlet_t old; - old = host->template-> + old = host->ops-> hw_csr_reg(host, (csraddr - CSR_BUS_MANAGER_ID) >> 2, data, arg); diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394-private.h linux/drivers/ieee1394/dv1394-private.h --- linux-2.5.3-pre2/drivers/ieee1394/dv1394-private.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/ieee1394/dv1394-private.h Mon Jan 21 17:23:22 2002 @@ -0,0 +1,606 @@ +/* + * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive, proc_fs by Dan Dennedy + * + * based on: + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + */ + +#ifndef _DV_1394_PRIVATE_H +#define _DV_1394_PRIVATE_H + +#include "ieee1394.h" +#include +#include + +/* data structures private to the dv1394 driver */ +/* none of this is exposed to user-space */ + + +/* + the 8-byte CIP (Common Isochronous Packet) header that precedes + each packet of DV data. + + See the IEC 61883 standard. +*/ + +struct CIP_header { unsigned char b[8]; }; + +static inline void fill_cip_header(struct CIP_header *cip, + unsigned char source_node_id, + unsigned long counter, + enum pal_or_ntsc format, + unsigned long timestamp) +{ + cip->b[0] = source_node_id; + cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */ + cip->b[2] = 0x00; + cip->b[3] = counter; + + cip->b[4] = 0x80; /* const */ + + switch(format) { + case DV1394_PAL: + cip->b[5] = 0x80; + break; + case DV1394_NTSC: + cip->b[5] = 0x00; + break; + } + + cip->b[6] = timestamp >> 8; + cip->b[7] = timestamp & 0xFF; +} + + + +/* + DMA commands used to program the OHCI's DMA engine + + See the Texas Instruments OHCI 1394 chipset documentation. +*/ + +struct output_more_immediate { u32 q[8]; }; +struct output_more { u32 q[4]; }; +struct output_last { u32 q[4]; }; +struct input_more { u32 q[4]; }; +struct input_last { u32 q[4]; }; + +/* outputs */ + +static inline void fill_output_more_immediate(struct output_more_immediate *omi, + unsigned char tag, + unsigned char channel, + unsigned char sync_tag, + unsigned int payload_size) +{ + omi->q[0] = 0x02000000 | 8 ; /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */ + omi->q[1] = 0; + omi->q[2] = 0; + omi->q[3] = 0; + + /* IT packet header */ + omi->q[4] = (0x0 << 16) /* DMA_SPEED_100 */ + | (tag << 14) + | (channel << 8) + | (TCODE_ISO_DATA << 4) + | (sync_tag); + + omi->q[5] = payload_size << 16; + omi->q[5] |= (0x7F << 8) | 0xA0; /* reserved field; mimic behavior of my Sony DSR-40 */ + + omi->q[6] = 0; + omi->q[7] = 0; +} + +static inline void fill_output_more(struct output_more *om, + unsigned int data_size, + unsigned long data_phys_addr) +{ + om->q[0] = 0; /* OUTPUT_MORE */ + om->q[0] |= data_size; + + om->q[1] = data_phys_addr; + om->q[2] = 0; + om->q[3] = 0; +} + +static inline void fill_output_last(struct output_last *ol, + int want_timestamp, + int want_interrupt, + unsigned int data_size, + unsigned long data_phys_addr) +{ + ol->q[0] = 0; + ol->q[0] |= 1 << 28; /* OUTPUT_LAST */ + + if(want_timestamp) /* controller will update timestamp at DMA time */ + ol->q[0] |= 1 << 27; + + if(want_interrupt) + ol->q[0] |= 3 << 20; + + ol->q[0] |= 3 << 18; /* must take branch */ + ol->q[0] |= data_size; + + ol->q[1] = data_phys_addr; + ol->q[2] = 0; + ol->q[3] = 0; +} + +/* inputs */ + +static inline void fill_input_more(struct input_more *im, + int want_interrupt, + unsigned int data_size, + unsigned long data_phys_addr) +{ + im->q[0] = 2 << 28; /* INPUT_MORE */ + im->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */ + if (want_interrupt) + im->q[0] |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */ + im->q[0] |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */ + /* disable wait on sync field, not used in DV :-( */ + im->q[0] |= data_size; + + im->q[1] = data_phys_addr; + im->q[2] = 0; /* branchAddress and Z not use in packet-per-buffer mode */ + im->q[3] = 0; /* xferStatus & resCount, resCount must be initialize to data_size */ +} + +static inline void fill_input_last(struct input_last *il, + unsigned int data_size, + unsigned long data_phys_addr) +{ + il->q[0] = 3 << 28; /* INPUT_LAST */ + il->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */ + il->q[0] |= 3 << 20; /* enable interrupts */ + il->q[0] |= 0xC << 16; /* enable branch to address */ + /* disable wait on sync field, not used in DV :-( */ + il->q[0] |= data_size; + + il->q[1] = data_phys_addr; + il->q[2] = 1; /* branchAddress (filled in later) and Z = 1 descriptor in next block */ + il->q[3] = data_size; /* xferStatus & resCount, resCount must be initialize to data_size */ +} + + + +/* + A "DMA descriptor block" consists of several contiguous DMA commands. + struct DMA_descriptor_block encapsulates all of the commands necessary + to send one packet of DV data. + + There are three different types of these blocks: + + 1) command to send an empty packet (CIP header only, no DV data): + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_LAST <-- points to the CIP header + + 2) command to send a full packet when the DV data payload does NOT + cross a page boundary: + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_MORE <-- points to the CIP header + OUTPUT_LAST <-- points to entire DV data payload + + 3) command to send a full packet when the DV payload DOES cross + a page boundary: + + OUTPUT_MORE-Immediate <-- contains the iso header in-line + OUTPUT_MORE <-- points to the CIP header + OUTPUT_MORE <-- points to first part of DV data payload + OUTPUT_LAST <-- points to second part of DV data payload + + This struct describes all three block types using unions. + + !!! It is vital that an even number of these descriptor blocks fit on one + page of memory, since a block cannot cross a page boundary !!! + + */ + +struct DMA_descriptor_block { + + union { + struct { + /* iso header, common to all output block types */ + struct output_more_immediate omi; + + union { + /* empty packet */ + struct { + struct output_last ol; /* CIP header */ + } empty; + + /* full packet */ + struct { + struct output_more om; /* CIP header */ + + union { + /* payload does not cross page boundary */ + struct { + struct output_last ol; /* data payload */ + } nocross; + + /* payload crosses page boundary */ + struct { + struct output_more om; /* data payload */ + struct output_last ol; /* data payload */ + } cross; + } u; + + } full; + } u; + } out; + + struct { + struct input_last il; + } in; + + } u; + + /* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 + by padding out to 128 bytes */ + u32 __pad__[12]; +}; + + +/* struct frame contains all data associated with one frame in the + ringbuffer these are allocated when the DMA context is initialized + do_dv1394_init(). They are re-used after the card finishes + transmitting the frame. */ + +struct video_card; /* forward declaration */ + +struct frame { + + /* points to the struct video_card that owns this frame */ + struct video_card *video; + + /* index of this frame in video_card->frames[] */ + unsigned int frame_num; + + /* FRAME_CLEAR - DMA program not set up, waiting for data + FRAME_READY - DMA program written, ready to transmit + + Changes to these should be locked against the interrupt + */ + enum { + FRAME_CLEAR = 0, + FRAME_READY + } state; + + /* whether this frame has been DMA'ed already; used only from + the IRQ handler to determine whether the frame can be reset */ + int done; + + + /* kernel virtual pointer to the start of this frame's data in + the user ringbuffer. Use only for CPU access; to get the DMA + bus address you must go through the video->user_dma mapping */ + unsigned long data; + + /* Max # of packets per frame */ + #define MAX_PACKETS 320 + + + /* a PAGE_SIZE memory pool for allocating CIP headers + !header_pool must be aligned to PAGE_SIZE! */ + struct CIP_header *header_pool; + dma_addr_t header_pool_dma; + + + /* a physically contiguous memory pool for allocating DMA + descriptor blocks; usually around 64KB in size + !descriptor_pool must be aligned to PAGE_SIZE! */ + struct DMA_descriptor_block *descriptor_pool; + dma_addr_t descriptor_pool_dma; + unsigned long descriptor_pool_size; + + + /* # of packets allocated for this frame */ + unsigned int n_packets; + + + /* below are several pointers (kernel virtual addresses, not + DMA bus addresses) to parts of the DMA program. These are + set each time the DMA program is written in + frame_prepare(). They are used later on, e.g. from the + interrupt handler, to check the status of the frame */ + + /* points to status/timestamp field of first DMA packet */ + /* (we'll check it later to monitor timestamp accuracy) */ + u32 *frame_begin_timestamp; + + /* the timestamp we assigned to the first packet in the frame */ + u32 assigned_timestamp; + + /* pointer to the first packet's CIP header (where the timestamp goes) */ + struct CIP_header *cip_syt1; + + /* pointer to the second packet's CIP header + (only set if the first packet was empty) */ + struct CIP_header *cip_syt2; + + /* in order to figure out what caused an interrupt, + store pointers to the status fields of the two packets + that can cause interrupts. We'll check these from the + interrupt handler. + */ + u32 *mid_frame_timestamp; + u32 *frame_end_timestamp; + + /* branch address field of final packet. This is effectively + the "tail" in the chain of DMA descriptor blocks. + We will fill it with the address of the first DMA descriptor + block in the subsequent frame, once it is ready. + */ + u32 *frame_end_branch; + + /* the number of descriptors in the first descriptor block + of the frame. Needed to start DMA */ + int first_n_descriptors; +}; + + +struct packet { + u16 timestamp; + u16 invalid; + u16 iso_header; + u16 data_length; + u32 cip_h1; + u32 cip_h2; + unsigned char data[480]; + unsigned char padding[16]; /* force struct size =512 for page alignment */ +}; + + +/* allocate/free a frame */ +static struct frame* frame_new(unsigned int frame_num, struct video_card *video); +static void frame_delete(struct frame *f); + +/* reset f so that it can be used again */ +static void frame_reset(struct frame *f); + + +/* structure for bookkeeping of a large non-physically-contiguous DMA buffer */ + +struct dma_region { + unsigned int n_pages; + unsigned int n_dma_pages; + struct scatterlist *sglist; +}; + +/* return the DMA bus address of the byte with the given offset + relative to the beginning of the dma_region */ + +static inline dma_addr_t dma_offset_to_bus(struct dma_region *dma, unsigned long offset) +{ + int i; + struct scatterlist *sg; + + for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) { + if(offset < sg_dma_len(sg)) { + return sg_dma_address(sg) + offset; + } + offset -= sg_dma_len(sg); + } + + printk(KERN_ERR "dv1394: dma_offset_to_bus failed for offset %lu!\n", offset); + return 0; +} + + +/* struct video_card contains all data associated with one instance + of the dv1394 driver +*/ + +struct video_card { + + /* ohci card to which this instance corresponds */ + struct ti_ohci *ohci; + + /* OHCI card id; the link between the VFS inode and a specific video_card + (essentially the device minor number) */ + int id; + + /* entry in dv1394_cards */ + struct list_head list; + + /* handle to /dev/ieee1394/dv/N, NULL if devfs not in use */ + devfs_handle_t devfs_handle; + + /* OHCI card IT DMA context number, -1 if not in use */ + int ohci_it_ctx; + + /* register offsets for current IT DMA context, 0 if not in use */ + u32 ohci_IsoXmitContextControlSet; + u32 ohci_IsoXmitContextControlClear; + u32 ohci_IsoXmitCommandPtr; + + /* OHCI card IR DMA context number, -1 if not in use */ + int ohci_ir_ctx; + + /* register offsets for current IR DMA context, 0 if not in use */ + u32 ohci_IsoRcvContextControlSet; + u32 ohci_IsoRcvContextControlClear; + u32 ohci_IsoRcvCommandPtr; + u32 ohci_IsoRcvContextMatch; + + + /* CONCURRENCY CONTROL */ + + /* there are THREE levels of locking associated with video_card. */ + + /* + 1) the 'open' flag - this prevents more than one process from + opening the device. (the driver currently assumes only one opener). + This is a regular int, but use test_and_set_bit() (on bit zero) + for atomicity. + */ + int open; + + /* + 2) the spinlock - this provides mutual exclusion between the interrupt + handler and process-context operations. Generally you must take the + spinlock under the following conditions: + 1) DMA (and hence the interrupt handler) may be running + AND + 2) you need to operate on the video_card, especially active_frame + + It is OK to play with video_card without taking the spinlock if + you are certain that DMA is not running. Even if DMA is running, + it is OK to *read* active_frame with the lock, then drop it + immediately. This is safe because the interrupt handler will never + advance active_frame onto a frame that is not READY (and the spinlock + must be held while marking a frame READY). + */ + spinlock_t spinlock; + + /* + 3) the sleeping semaphore 'sem' - this is used from process context only, + to serialize various operations on the video_card. Even though only one + open() is allowed, we still need to prevent multiple threads of execution + from entering calls like read, write, ioctl, etc. + + I honestly can't think of a good reason to use dv1394 from several threads + at once, but we need to serialize anyway to prevent oopses =). + + NOTE: if you need both spinlock and sem, take sem first to avoid deadlock! + */ + struct semaphore sem; + + /* people waiting for buffer space, please form a line here... */ + wait_queue_head_t waitq; + + /* support asynchronous I/O signals (SIGIO) */ + struct fasync_struct *fasync; + + /* the large, non-contiguous (rvmalloc()) ringbuffer for DV + data, exposed to user-space via mmap() */ + unsigned char *user_buf; + unsigned long user_buf_size; + struct dma_region user_dma; + + /* next byte in the ringbuffer that a write() call will fill */ + size_t write_off; + + struct frame *frames[DV1394_MAX_FRAMES]; + + /* n_frames also serves as an indicator that this struct video_card is + intialized and ready to run DMA buffers */ + + int n_frames; + + /* this is the frame that is currently "owned" by the OHCI DMA controller + (set to -1 iff DMA is not running) + + ! must lock against the interrupt handler when accessing it ! + + RULES: + + Only the interrupt handler may change active_frame if DMA + is running; if not, process may change it + + If the next frame is READY, the interrupt handler will advance + active_frame when the current frame is finished. + + If the next frame is CLEAR, the interrupt handler will re-transmit + the current frame, and the dropped_frames counter will be incremented. + + The interrupt handler will NEVER advance active_frame to a + frame that is not READY. + + */ + int active_frame; + int first_run; + + /* the same locking rules apply to these three fields also: */ + + /* altered ONLY from process context. Must check first_clear_frame->state; + if it's READY, that means the ringbuffer is full with READY frames; + if it's CLEAR, that means one or more ringbuffer frames are CLEAR */ + unsigned int first_clear_frame; + + /* altered both by process and interrupt */ + unsigned int n_clear_frames; + + /* only altered by the interrupt */ + unsigned int dropped_frames; + + + + /* the CIP accumulator and continuity counter are properties + of the DMA stream as a whole (not a single frame), so they + are stored here in the video_card */ + + unsigned long cip_accum; + unsigned long cip_n, cip_d; + unsigned int syt_offset; + unsigned int continuity_counter; + + enum pal_or_ntsc pal_or_ntsc; + + /* redundant, but simplifies the code somewhat */ + unsigned int frame_size; /* in bytes */ + + /* the isochronous channel to use, -1 if video card is inactive */ + int channel; + + + /* physically contiguous packet ringbuffer for receive */ +#define MAX_PACKET_BUFFER 30 + struct packet *packet_buffer; + dma_addr_t packet_buffer_dma; + unsigned long packet_buffer_size; + + unsigned int current_packet; + int first_frame; /* received first start frame marker? */ +}; + +/* + if the video_card is not initialized, then the ONLY fields that are valid are: + ohci + open + n_frames +*/ + +static inline int video_card_initialized(struct video_card *v) +{ + return v->n_frames > 0; +} + +static int do_dv1394_init(struct video_card *video, struct dv1394_init *init); +static int do_dv1394_init_default(struct video_card *video); +static int do_dv1394_shutdown(struct video_card *video, int free_user_buf); + + +/* NTSC empty packet rate accurate to within 0.01%, + calibrated against a Sony DSR-40 DVCAM deck */ + +#define CIP_N_NTSC 68000000 +#define CIP_D_NTSC 1000000000 + +#define CIP_N_PAL 1 +#define CIP_D_PAL 16 + +#endif /* _DV_1394_PRIVATE_H */ + diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394.c linux/drivers/ieee1394/dv1394.c --- linux-2.5.3-pre2/drivers/ieee1394/dv1394.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/ieee1394/dv1394.c Mon Jan 21 17:23:22 2002 @@ -0,0 +1,2722 @@ +/* + * dv1394.c - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive, proc_fs by Dan Dennedy + * + * based on: + * video1394.c - video driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + */ + +/* + OVERVIEW + + I designed dv1394 as a "pipe" that you can use to shoot DV onto a + FireWire bus. In transmission mode, dv1394 does the following: + + 1. accepts contiguous frames of DV data from user-space, via write() + or mmap() (see dv1394.h for the complete API) + 2. wraps IEC 61883 packets around the DV data, inserting + empty synchronization packets as necessary + 3. assigns accurate SYT timestamps to the outgoing packets + 4. shoots them out using the OHCI card's IT DMA engine + + Thanks to Dan Dennedy, we now have a receive mode that does the following: + + 1. accepts raw IEC 61883 packets from the OHCI card + 2. re-assembles the DV data payloads into contiguous frames, + discarding empty packets + 3. sends the DV data to user-space via read() or mmap() +*/ + +/* + TODO: + + - expose xmit and recv as separate devices + + - tunable frame-drop behavior: either loop last frame, or halt transmission + + - use a scatter/gather buffer for DMA programs (f->descriptor_pool) + so that we don't rely on allocating 64KB of contiguous kernel memory + via pci_alloc_consistent() + + DONE: + - safely obtain and release ISO Tx channels in cooperation with OHCI driver + - map received DIF blocks to their proper location in DV frame (ensure + recovery if dropped packet) + - handle bus resets gracefully (OHCI card seems to take care of this itself(!)) + - do not allow resizing the user_buf once allocated; eliminate nuke_buffer_mappings + - eliminated #ifdef DV1394_DEBUG_LEVEL by inventing macros debug_printk and irq_printk + - added wmb() and mb() to places where PCI read/write ordering needs to be enforced + - set video->id correctly + - store video_cards in an array indexed by OHCI card ID, rather than a list + - implement DMA context allocation to cooperate with other users of the OHCI + - fix all XXX showstoppers + - disable IR/IT DMA interrupts on shutdown + - flush pci writes to the card by issuing a read + - devfs and character device dispatching (* needs testing with Linux 2.2.x) + - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!) + - keep all video_cards in a list (for open() via chardev), set file->private_data = video + - dv1394_poll should indicate POLLIN when receiving buffers are available + - add proc fs interface to set cip_n, cip_d, syt_offset, and video signal + +*/ + +#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 +#include +#include +#include + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "highlevel.h" +#include "dv1394.h" +#include "dv1394-private.h" + +#include "ohci1394.h" + +#ifndef virt_to_page +#define virt_to_page(x) MAP_NR(x) +#endif + +#ifndef vmalloc_32 +#define vmalloc_32(x) vmalloc(x) +#endif + + +/* DEBUG LEVELS: + 0 - no debugging messages + 1 - some debugging messages, but none during DMA frame transmission + 2 - lots of messages, including during DMA frame transmission + (will cause undeflows if your machine is too slow!) +*/ + +#define DV1394_DEBUG_LEVEL 0 + +/* for debugging use ONLY: allow more than one open() of the device */ +/* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */ + +#if DV1394_DEBUG_LEVEL >= 2 +#define irq_printk( args... ) printk( args ) +#else +#define irq_printk( args... ) +#endif + +#if DV1394_DEBUG_LEVEL >= 1 +#define debug_printk( args... ) printk( args) +#else +#define debug_printk( args... ) +#endif + +/* issue a dummy PCI read to force the preceding write + to be posted to the PCI bus immediately */ + +static inline void flush_pci_write(struct ti_ohci *ohci) +{ + mb(); + reg_read(ohci, OHCI1394_IsochronousCycleTimer); +} + +static void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent, void *data); + + +/* GLOBAL DATA */ + +/* list of all video_cards */ +static LIST_HEAD(dv1394_cards); +static spinlock_t dv1394_cards_lock = SPIN_LOCK_UNLOCKED; + +static struct hpsb_highlevel *hl_handle; /* = NULL; */ + +static devfs_handle_t dv1394_devfs_handle; + +/* translate from a struct file* to the corresponding struct video_card* */ + +static inline struct video_card* file_to_video_card(struct file *file) +{ + return (struct video_card*) file->private_data; +} + + +/* Taken from bttv.c */ +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define page_address(x) (x) +#endif + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline struct page *uvirt_to_page(pgd_t *pgd, unsigned long adr) +{ + pmd_t *pmd; + pte_t *ptep, pte; + struct page *ret = NULL; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) + ret = pte_page(pte); + } + } + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved, and for + * handling page faults on the rvmalloc()ed buffer + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = (unsigned long) page_address(uvirt_to_page(pgd_offset_k(va), va)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc_32(size); + if (mem) { + memset(mem, 0, size); /* Clear the ram out, + no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(virt_to_page(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) { + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(virt_to_page(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +/***********************************/ +/* END Memory management functions */ +/***********************************/ + + +/*** FRAME METHODS *********************************************************/ + +static void frame_reset(struct frame *f) +{ + f->state = FRAME_CLEAR; + f->done = 0; + f->n_packets = 0; + f->frame_begin_timestamp = NULL; + f->assigned_timestamp = 0; + f->cip_syt1 = NULL; + f->cip_syt2 = NULL; + f->mid_frame_timestamp = NULL; + f->frame_end_timestamp = NULL; + f->frame_end_branch = NULL; +} + +static struct frame* frame_new(unsigned int frame_num, struct video_card *video) +{ + struct frame *f = kmalloc(sizeof(*f), GFP_KERNEL); + if(!f) + return NULL; + + f->video = video; + f->frame_num = frame_num; + + f->header_pool = pci_alloc_consistent(f->video->ohci->dev, PAGE_SIZE, &f->header_pool_dma); + if(!f->header_pool) { + printk(KERN_ERR "dv1394: failed to allocate CIP header pool\n"); + kfree(f); + return NULL; + } + + debug_printk("dv1394: frame_new: allocated CIP header pool at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", + (unsigned long) f->header_pool, (unsigned long) f->header_pool_dma, PAGE_SIZE); + + f->descriptor_pool_size = MAX_PACKETS * sizeof(struct DMA_descriptor_block); + /* make it an even # of pages */ + f->descriptor_pool_size += PAGE_SIZE - (f->descriptor_pool_size%PAGE_SIZE); + + f->descriptor_pool = pci_alloc_consistent(f->video->ohci->dev, + f->descriptor_pool_size, + &f->descriptor_pool_dma); + if(!f->descriptor_pool) { + pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); + kfree(f); + return NULL; + } + + debug_printk("dv1394: frame_new: allocated DMA program memory at virt 0x%08lx (contig) dma 0x%08lx size %ld\n", + (unsigned long) f->descriptor_pool, (unsigned long) f->descriptor_pool_dma, f->descriptor_pool_size); + + f->data = 0; + frame_reset(f); + + return f; +} + +static void frame_delete(struct frame *f) +{ + pci_free_consistent(f->video->ohci->dev, PAGE_SIZE, f->header_pool, f->header_pool_dma); + pci_free_consistent(f->video->ohci->dev, f->descriptor_pool_size, f->descriptor_pool, f->descriptor_pool_dma); + kfree(f); +} + + + + +/* + frame_prepare() - build the DMA program for transmitting + + Frame_prepare() must be called OUTSIDE the video->spinlock. + However, frame_prepare() must still be serialized, so + it should be called WITH the video->sem taken. + */ + +static void frame_prepare(struct video_card *video, unsigned int this_frame) +{ + struct frame *f = video->frames[this_frame]; + int last_frame; + + struct DMA_descriptor_block *block; + dma_addr_t block_dma; + struct CIP_header *cip; + dma_addr_t cip_dma; + + unsigned int n_descriptors, full_packets, packets_per_frame, payload_size; + + /* these flags denote packets that need special attention */ + int empty_packet, first_packet, last_packet, mid_packet; + + u32 *branch_address, *last_branch_address = NULL; + unsigned long data_p; + int first_packet_empty = 0; + u32 cycleTimer, ct_sec, ct_cyc, ct_off; + unsigned long irq_flags; + + irq_printk("frame_prepare( %d ) ---------------------\n", this_frame); + + full_packets = 0; + + + + if(video->pal_or_ntsc == DV1394_PAL) + packets_per_frame = DV1394_PAL_PACKETS_PER_FRAME; + else + packets_per_frame = DV1394_NTSC_PACKETS_PER_FRAME; + + while( full_packets < packets_per_frame ) { + empty_packet = first_packet = last_packet = mid_packet = 0; + + data_p = f->data + full_packets * 480; + + /************************************************/ + /* allocate a descriptor block and a CIP header */ + /************************************************/ + + /* note: these should NOT cross a page boundary (DMA restriction) */ + + if(f->n_packets >= MAX_PACKETS) { + printk(KERN_ERR "dv1394: FATAL ERROR: max packet count exceeded\n"); + return; + } + + /* the block surely won't cross a page boundary, + since an even number of descriptor_blocks fit on a page */ + block = &(f->descriptor_pool[f->n_packets]); + + /* DMA address of the block = offset of block relative + to the kernel base address of the descriptor pool + + DMA base address of the descriptor pool */ + block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; + + + /* the whole CIP pool fits on one page, so no worries about boundaries */ + if( ((unsigned long) &(f->header_pool[f->n_packets]) - (unsigned long) f->header_pool) + > PAGE_SIZE) { + printk(KERN_ERR "dv1394: FATAL ERROR: no room to allocate CIP header\n"); + return; + } + + cip = &(f->header_pool[f->n_packets]); + + /* DMA address of the CIP header = offset of cip + relative to kernel base address of the header pool + + DMA base address of the header pool */ + cip_dma = (unsigned long) cip % PAGE_SIZE + f->header_pool_dma; + + /* is this an empty packet? */ + + if(video->cip_accum > video->cip_d) { + empty_packet = 1; + payload_size = 8; + video->cip_accum -= video->cip_d; + } else { + payload_size = 488; + video->cip_accum += video->cip_n; + } + + /* there are three important packets each frame: + + the first packet in the frame - we ask the card to record the timestamp when + this packet is actually sent, so we can monitor + how accurate our timestamps are. Also, the first + packet serves as a semaphore to let us know that + it's OK to free the *previous* frame's DMA buffer + + the last packet in the frame - this packet is used to detect buffer underflows. + if this is the last ready frame, the last DMA block + will have a branch back to the beginning of the frame + (so that the card will re-send the frame on underflow). + if this branch gets taken, we know that at least one + frame has been dropped. When the next frame is ready, + the branch is pointed to its first packet, and the + semaphore is disabled. + + a "mid" packet slightly before the end of the frame - this packet should trigger + an interrupt so we can go and assign a timestamp to the first packet + in the next frame. We don't use the very last packet in the frame + for this purpose, because that would leave very little time to set + the timestamp before DMA starts on the next frame. + */ + + if(f->n_packets == 0) { + first_packet = 1; + } else if ( full_packets == (packets_per_frame-1) ) { + last_packet = 1; + } else if (f->n_packets == packets_per_frame) { + mid_packet = 1; + } + + + /********************/ + /* setup CIP header */ + /********************/ + + /* the timestamp will be written later from the + mid-frame interrupt handler. For now we just + store the address of the CIP header(s) that + need a timestamp. */ + + /* first packet in the frame needs a timestamp */ + if(first_packet) { + f->cip_syt1 = cip; + if(empty_packet) + first_packet_empty = 1; + + } else if(first_packet_empty && (f->n_packets == 1) ) { + /* if the first packet was empty, the second + packet's CIP header also needs a timestamp */ + f->cip_syt2 = cip; + } + + fill_cip_header(cip, + /* the node ID number of the OHCI card */ + reg_read(video->ohci, OHCI1394_NodeID) & 0x3F, + video->continuity_counter, + video->pal_or_ntsc, + 0xFFFF /* the timestamp is filled in later */); + + /* advance counter, only for full packets */ + if( ! empty_packet ) + video->continuity_counter++; + + /******************************/ + /* setup DMA descriptor block */ + /******************************/ + + /* first descriptor - OUTPUT_MORE_IMMEDIATE, for the controller's IT header */ + fill_output_more_immediate( &(block->u.out.omi), + /* tag - what is this??? */ 1, + video->channel, + /* sync tag - what is this??? */ 0, + payload_size); + + if(empty_packet) { + /* second descriptor - OUTPUT_LAST for CIP header */ + fill_output_last( &(block->u.out.u.empty.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupts on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + sizeof(struct CIP_header), /* data size */ + cip_dma); + + if(first_packet) + f->frame_begin_timestamp = &(block->u.out.u.empty.ol.q[3]); + else if(mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.empty.ol.q[3]); + else if(last_packet) { + f->frame_end_timestamp = &(block->u.out.u.empty.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.empty.ol.q[2]); + } + + branch_address = &(block->u.out.u.empty.ol.q[2]); + n_descriptors = 3; + if(first_packet) + f->first_n_descriptors = n_descriptors; + + } else { /* full packet */ + + /* second descriptor - OUTPUT_MORE for CIP header */ + fill_output_more( &(block->u.out.u.full.om), + sizeof(struct CIP_header), /* data size */ + cip_dma); + + + /* third (and possibly fourth) descriptor - for DV data */ + /* the 480-byte payload can cross a page boundary; if so, + we need to split it into two DMA descriptors */ + + /* does the 480-byte data payload cross a page boundary? */ + if( (PAGE_SIZE- ((unsigned long)data_p % PAGE_SIZE) ) < 480 ) { + + /* page boundary crossed */ + + fill_output_more( &(block->u.out.u.full.u.cross.om), + /* data size - how much of data_p fits on the first page */ + PAGE_SIZE - (data_p % PAGE_SIZE), + + /* DMA address of data_p */ + dma_offset_to_bus(&f->video->user_dma, + data_p - (unsigned long) f->video->user_buf)); + + fill_output_last( &(block->u.out.u.full.u.cross.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupt on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* data size - remaining portion of data_p */ + 480 - (PAGE_SIZE - (data_p % PAGE_SIZE)), + + /* DMA address of data_p + PAGE_SIZE - (data_p % PAGE_SIZE) */ + dma_offset_to_bus(&f->video->user_dma, + data_p + PAGE_SIZE - (data_p % PAGE_SIZE) - (unsigned long) f->video->user_buf)); + + if(first_packet) + f->frame_begin_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + else if(mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + else if(last_packet) { + f->frame_end_timestamp = &(block->u.out.u.full.u.cross.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.full.u.cross.ol.q[2]); + } + + branch_address = &(block->u.out.u.full.u.cross.ol.q[2]); + + n_descriptors = 5; + if(first_packet) + f->first_n_descriptors = n_descriptors; + + full_packets++; + + } else { + /* fits on one page */ + + fill_output_last( &(block->u.out.u.full.u.nocross.ol), + + /* want completion status on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + /* want interrupt on all interesting packets */ + (first_packet || mid_packet || last_packet) ? 1 : 0, + + 480, /* data size (480 bytes of DV data) */ + + + /* DMA address of data_p */ + dma_offset_to_bus(&f->video->user_dma, + data_p - (unsigned long) f->video->user_buf)); + + if(first_packet) + f->frame_begin_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + else if(mid_packet) + f->mid_frame_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + else if(last_packet) { + f->frame_end_timestamp = &(block->u.out.u.full.u.nocross.ol.q[3]); + f->frame_end_branch = &(block->u.out.u.full.u.nocross.ol.q[2]); + } + + branch_address = &(block->u.out.u.full.u.nocross.ol.q[2]); + + n_descriptors = 4; + if(first_packet) + f->first_n_descriptors = n_descriptors; + + full_packets++; + } + } + + /* link this descriptor block into the DMA program by filling in + the branch address of the previous block */ + + /* note: we are not linked into the active DMA chain yet */ + + if(last_branch_address) { + *(last_branch_address) = block_dma | n_descriptors; + } + + last_branch_address = branch_address; + + + f->n_packets++; + + } + + /* when we first assemble a new frame, set the final branch + to loop back up to the top */ + *(f->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors; + + + /* make the latest version of the frame buffer visible to the PCI card */ + /* could optimize this by only syncing the pages associated with this frame */ + pci_dma_sync_sg(video->ohci->dev, + &video->user_dma.sglist[0], + video->user_dma.n_dma_pages, + PCI_DMA_TODEVICE); + + /* lock against DMA interrupt */ + spin_lock_irqsave(&video->spinlock, irq_flags); + + f->state = FRAME_READY; + + video->n_clear_frames--; + + last_frame = video->first_clear_frame - 1; + if(last_frame == -1) + last_frame = video->n_frames-1; + + video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; + + irq_printk(" frame %d prepared, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n last=%d\n", + this_frame, video->active_frame, video->n_clear_frames, video->first_clear_frame, last_frame); + + irq_printk(" begin_ts %08lx mid_ts %08lx end_ts %08lx end_br %08lx\n", + (unsigned long) f->frame_begin_timestamp, + (unsigned long) f->mid_frame_timestamp, + (unsigned long) f->frame_end_timestamp, + (unsigned long) f->frame_end_branch); + + if(video->active_frame != -1) { + + /* if DMA is already active, we are almost done */ + /* just link us onto the active DMA chain */ + if(video->frames[last_frame]->frame_end_branch) { + + /* point the previous frame's tail to this frame's head */ + *(video->frames[last_frame]->frame_end_branch) = f->descriptor_pool_dma | f->first_n_descriptors; + + /* this write MUST precede the next one, or we could silently drop frames */ + wmb(); + + /* disable the want_status semaphore on the last packet */ + *(video->frames[last_frame]->frame_end_branch - 2) &= 0xF7CFFFFF; + + /* flush these writes to memory ASAP */ + flush_pci_write(video->ohci); + + /* NOTE: + ideally the writes should be "atomic": if + the OHCI card reads the want_status flag in + between them, we'll falsely report a + dropped frame. Hopefully this window is too + small to really matter, and the consequence + is rather harmless. */ + + + irq_printk(" new frame %d linked onto DMA chain\n", this_frame); + + } else { + printk(KERN_ERR "dv1394: last frame not ready???\n"); + } + + } else { + + u32 transmit_sec, transmit_cyc; + u32 ts_cyc, ts_off; + + /* DMA is stopped, so this is the very first frame */ + video->active_frame = this_frame; + + /* set CommandPtr to address and size of first descriptor block */ + reg_write(video->ohci, video->ohci_IsoXmitCommandPtr, + video->frames[video->active_frame]->descriptor_pool_dma | + f->first_n_descriptors); + + /* assign a timestamp based on the current cycle time... + We'll tell the card to begin DMA 100 cycles from now, + and assign a timestamp 103 cycles from now */ + + cycleTimer = reg_read(video->ohci, OHCI1394_IsochronousCycleTimer); + + ct_sec = cycleTimer >> 25; + ct_cyc = (cycleTimer >> 12) & 0x1FFF; + ct_off = cycleTimer & 0xFFF; + + transmit_sec = ct_sec; + transmit_cyc = ct_cyc + 100; + + transmit_sec += transmit_cyc/8000; + transmit_cyc %= 8000; + + ts_off = ct_off; + ts_cyc = transmit_cyc + 3; + ts_cyc %= 8000; + + f->assigned_timestamp = (ts_cyc&0xF) << 12; + + /* now actually write the timestamp into the appropriate CIP headers */ + if(f->cip_syt1) { + f->cip_syt1->b[6] = f->assigned_timestamp >> 8; + f->cip_syt1->b[7] = f->assigned_timestamp & 0xFF; + } + if(f->cip_syt2) { + f->cip_syt2->b[6] = f->assigned_timestamp >> 8; + f->cip_syt2->b[7] = f->assigned_timestamp & 0xFF; + } + + /* --- start DMA --- */ + + /* clear all bits in ContextControl register */ + + reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, 0xFFFFFFFF); + wmb(); + + /* the OHCI card has the ability to start ISO transmission on a + particular cycle (start-on-cycle). This way we can ensure that + the first DV frame will have an accurate timestamp. + + However, start-on-cycle only appears to work if the OHCI card + is cycle master! Since the consequences of messing up the first + timestamp are minimal*, just disable start-on-cycle for now. + + * my DV deck drops the first few frames before it "locks in;" + so the first frame having an incorrect timestamp is inconsequential. + */ + +#if 0 + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, + (1 << 31) /* enable start-on-cycle */ + | ( (transmit_sec & 0x3) << 29) + | (transmit_cyc << 16)); + wmb(); +#endif + + + + /* set the 'run' bit */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000); + flush_pci_write(video->ohci); + + /* --- DMA should be running now --- */ + + debug_printk(" Cycle = %4u ContextControl = %08x CmdPtr = %08x\n", + (reg_read(video->ohci, OHCI1394_IsochronousCycleTimer) >> 12) & 0x1FFF, + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)); + + debug_printk(" DMA start - current cycle %4u, transmit cycle %4u (%2u), assigning ts cycle %2u\n", + ct_cyc, transmit_cyc, transmit_cyc & 0xF, ts_cyc & 0xF); + +#if DV1394_DEBUG_LEVEL >= 2 + { + /* check if DMA is really running */ + int i = 0; + while(i < 20) { + mb(); + mdelay(1); + if(reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) { + printk("DMA ACTIVE after %d msec\n", i); + break; + } + i++; + } + + printk("set = %08x, cmdPtr = %08x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) + ); + + if( ! (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { + printk("DMA did NOT go active after 20ms, event = %x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & 0x1F); + } else + printk("DMA is RUNNING!\n"); + } +#endif + + } + + + spin_unlock_irqrestore(&video->spinlock, irq_flags); +} + + + +/*** RECEIVE FUNCTIONS *****************************************************/ + +/* + frame method put_packet + + map and copy the packet data to its location in the frame + based upon DIF section and sequence +*/ + +static void inline +frame_put_packet (struct frame *f, struct packet *p) +{ + int section_type = p->data[0] >> 5; /* section type is in bits 5 - 7 */ + int dif_sequence = p->data[1] >> 4; /* dif sequence number is in bits 4 - 7 */ + int dif_block = p->data[2]; + + switch (section_type) { + case 0: /* 1 Header block */ + memcpy( (void *) f->data + dif_sequence * 150 * 80, p->data, 480); + break; + + case 1: /* 2 Subcode blocks */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (1 + dif_block) * 80, p->data, 480); + break; + + case 2: /* 3 VAUX blocks */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (3 + dif_block) * 80, p->data, 480); + break; + + case 3: /* 9 Audio blocks interleaved with video */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (6 + dif_block * 16) * 80, p->data, 480); + break; + + case 4: /* 135 Video blocks interleaved with audio */ + memcpy( (void *) f->data + dif_sequence * 150 * 80 + (7 + (dif_block / 15) + dif_block) * 80, p->data, 480); + break; + + default: /* we can not handle any other data */ + break; + } +} + + +static void start_dma_receive(struct video_card *video, struct frame *frame) +{ + /* reset iso recv control register */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, 0xFFFFFFFF); + wmb(); + + /* clear bufferFill, set isochHeader and speed (0=100) */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x40000000); + + /* match on all tags, listen on channel */ + reg_write(video->ohci, video->ohci_IsoRcvContextMatch, 0xf0000000 | video->channel); + + /* address and first descriptor block + Z=1 */ + reg_write(video->ohci, video->ohci_IsoRcvCommandPtr, + frame->descriptor_pool_dma | 1); /* Z=1 */ + wmb(); + + /* run */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000); + flush_pci_write(video->ohci); + + debug_printk("dv1394: DMA started\n"); + +#if DV1394_DEBUG_LEVEL >= 2 + { + int i; + + for(i = 0; i < 1000; ++i) { + mdelay(1); + if(reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) { + printk("DMA ACTIVE after %d msec\n", i); + break; + } + } + if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { + printk("DEAD, event = %x\n", + reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); + } else + printk("RUNNING!\n"); + } +#endif +} + + +/* + receive_packets() - build the DMA program for receiving +*/ + +static void receive_packets(struct video_card *video, struct frame *f) +{ + struct DMA_descriptor_block *block = NULL; + dma_addr_t block_dma = 0; + struct packet *data = NULL; + dma_addr_t data_dma = 0; + u32 *last_branch_address = NULL; + unsigned long irq_flags; + + spin_lock_irqsave(&video->spinlock, irq_flags); + + video->n_clear_frames = 0; + video->first_clear_frame = -1; + + for (video->current_packet = 0; video->current_packet < MAX_PACKET_BUFFER; ++video->current_packet) { + /* locate a descriptor block and packet from the buffer */ + block = &(f->descriptor_pool[video->current_packet]); + block_dma = ((unsigned long) block - (unsigned long) f->descriptor_pool) + f->descriptor_pool_dma; + + data = &(video->packet_buffer[video->current_packet]); + data_dma = ((unsigned long) data - (unsigned long) video->packet_buffer) + video->packet_buffer_dma; + + /* setup DMA descriptor block */ + fill_input_last( &(block->u.in.il), 512, data_dma); + + /* link descriptors */ + last_branch_address = f->frame_end_branch; + + if (last_branch_address) + *(last_branch_address) = block_dma | 1; /* set Z=1 */ + + f->frame_end_branch = &(block->u.in.il.q[2]); + } + + /* loop tail to head */ + if (f->frame_end_branch) + *(f->frame_end_branch) = f->descriptor_pool_dma | 1; /* set Z=1 */ + + spin_unlock_irqrestore(&video->spinlock, irq_flags); + + if (video->first_run) { + /* start DMA once all of the frames are READY */ + video->first_run = 0; + video->current_packet = 0; + video->active_frame = f->frame_num; + start_dma_receive(video, f); + } + else if( reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 11) ) { + debug_printk("DEAD, event = %x\n", + reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & 0x1F); + + /* wake */ + reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, (1 << 12)); + } +} + + + +/*** MANAGEMENT FUNCTIONS **************************************************/ + +static int do_dv1394_init(struct video_card *video, struct dv1394_init *init) +{ + unsigned long flags, new_buf_size; + int i; + u64 chan_mask; + int retval = -EINVAL; + + if(init->api_version != DV1394_API_VERSION) + goto err; + + /* first sanitize all the parameters */ + if( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) ) + goto err; + + if( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) ) + goto err; + + if( (init->syt_offset == 0) || (init->syt_offset > 50) ) + /* default SYT offset is 3 cycles */ + init->syt_offset = 3; + + if( (init->channel > 63) || (init->channel < 0) ) + init->channel = 63; + + chan_mask = (u64)1 << init->channel; + + /* calculate what size DMA buffer is needed */ + if(init->format == DV1394_NTSC) + new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames; + else + new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames; + + /* round up to PAGE_SIZE */ + if(new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE); + + /* don't allow the user to allocate the DMA buffer more than once */ + if( (video->user_buf) && + (video->user_buf_size != new_buf_size) ) { + goto err; + } + + /* shutdown the card if it's currently active */ + /* (the card should not be reset if the parameters are screwy) */ + if( video_card_initialized(video) ) + do_dv1394_shutdown(video, 0); + + + /* try to claim the ISO channel */ + spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); + if(video->ohci->ISO_channel_usage & chan_mask) { + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + retval = -EBUSY; + goto err; + } + video->ohci->ISO_channel_usage |= chan_mask; + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + + video->channel = init->channel; + + + /* find and claim DMA contexts on the OHCI card */ + + if(video->ohci_it_ctx == -1) { + + for(i = 0; i < video->ohci->nb_iso_xmit_ctx; i++) { + + if(! test_and_set_bit(i, &video->ohci->it_ctx_usage)) { + video->ohci_it_ctx = i; + debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx); + break; + } + } + + if(i == video->ohci->nb_iso_xmit_ctx) { + printk(KERN_ERR "dv1394: could not find an available IT DMA context\n"); + retval = -EBUSY; + goto err_ctx; + } + } + + + if(video->ohci_ir_ctx == -1) { + for(i = 0; i < video->ohci->nb_iso_rcv_ctx; i++) { + + if(! test_and_set_bit(i, &video->ohci->ir_ctx_usage)) { + video->ohci_ir_ctx = i; + debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx); + break; + } + } + + if(i == video->ohci->nb_iso_rcv_ctx) { + printk(KERN_ERR "dv1394: could not find an available IR DMA context\n"); + retval = -EBUSY; + goto err_ctx; + } + } + + + /* allocate struct frames */ + for(i = 0; i < init->n_frames; i++) { + video->frames[i] = frame_new(i, video); + + if(!video->frames[i]) { + printk(KERN_ERR "dv1394: Cannot allocate frame structs\n"); + retval = -ENOMEM; + goto err_frames; + } + } + + /* initialize misc. fields of video */ + video->n_frames = init->n_frames; + video->pal_or_ntsc = init->format; + + + video->cip_accum = 0; + video->continuity_counter = 0; + + video->active_frame = -1; + video->first_clear_frame = 0; + video->n_clear_frames = video->n_frames; + video->dropped_frames = 0; + + video->write_off = 0; + + video->first_run = 1; + video->current_packet = -1; + video->first_frame = 0; + + + if(video->pal_or_ntsc == DV1394_NTSC) { + video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC; + video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC; + video->frame_size = DV1394_NTSC_FRAME_SIZE; + } else { + video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL; + video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL; + video->frame_size = DV1394_PAL_FRAME_SIZE; + } + + video->syt_offset = init->syt_offset; + + if(video->user_buf == NULL) { + unsigned int i; + + /* allocate the ringbuffer */ + video->user_buf = rvmalloc(new_buf_size); + if(!video->user_buf) { + printk(KERN_ERR "dv1394: Cannot allocate frame buffers\n"); + goto err_frames; + } + video->user_buf_size = new_buf_size; + + /* allocate the sglist to hold the DMA addresses */ + video->user_dma.n_pages = video->user_buf_size / PAGE_SIZE; + video->user_dma.sglist = kmalloc(video->user_dma.n_pages * sizeof(struct scatterlist), GFP_KERNEL); + if(!video->user_dma.sglist) { + printk(KERN_ERR "dv1394: Cannot allocate sglist for user buffer\n"); + goto err_user_buf; + } + + /* initialize all fields of all sglist entries to zero + (new requirement due to PCI changes in 2.4.13) */ + + memset(video->user_dma.sglist, 0, video->user_dma.n_pages * sizeof(struct scatterlist)); + + + /* fill the sglist with the kernel addresses of pages in the non-contiguous buffer */ + for(i = 0; i < video->user_dma.n_pages; i++) { + unsigned long va = VMALLOC_VMADDR( (unsigned long) video->user_buf + i * PAGE_SIZE ); + + video->user_dma.sglist[i].page = uvirt_to_page(pgd_offset_k(va), va); + video->user_dma.sglist[i].length = PAGE_SIZE; + } + + /* map the buffer in the IOMMU */ + /* the user_data buffer only allows DMA *to* the card for transmission; + incoming DV data comes through the packet_buffer first, and then is copied to user_data */ + video->user_dma.n_dma_pages = pci_map_sg(video->ohci->dev, + &video->user_dma.sglist[0], + video->user_dma.n_pages, + PCI_DMA_TODEVICE); + if(video->user_dma.n_dma_pages == 0) { + printk(KERN_ERR "dv1394: Error mapping user buffer to the IOMMU\n"); + goto err_user_buf; + } + + debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", + video->n_frames, video->user_dma.n_pages, + video->user_dma.n_dma_pages, video->user_buf_size); + } + + /* set up the frame->data pointers */ + for(i = 0; i < video->n_frames; i++) + video->frames[i]->data = (unsigned long) video->user_buf + i * video->frame_size; + + /* allocate packet buffers */ + video->packet_buffer_size = sizeof(struct packet) * MAX_PACKET_BUFFER; + if (video->packet_buffer_size % PAGE_SIZE) + video->packet_buffer_size += PAGE_SIZE - (video->packet_buffer_size % PAGE_SIZE); + + + video->packet_buffer = kmalloc(video->packet_buffer_size, GFP_KERNEL); + + if(!video->packet_buffer) { + printk(KERN_ERR "dv1394: Cannot allocate packet buffers"); + retval = -ENOMEM; + goto err_user_buf; + } + + /* map the packet buffer into the IOMMU */ + video->packet_buffer_dma = pci_map_single(video->ohci->dev, + video->packet_buffer, + video->packet_buffer_size, + PCI_DMA_FROMDEVICE); + if(!video->packet_buffer_dma) { + printk(KERN_ERR "dv1394: Cannot map packet buffer to IOMMU"); + kfree(video->packet_buffer); + video->packet_buffer = NULL; + retval = -ENOMEM; + goto err_user_buf; + } + + debug_printk("dv1394: Allocated %d packet buffers for receive, total %lu bytes\n", + MAX_PACKET_BUFFER, video->packet_buffer_size); + + + /* set up register offsets for IT context */ + /* IT DMA context registers are spaced 16 bytes apart */ + video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx; + video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx; + video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx; + + /* enable interrupts for IT context */ + reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx)); + debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx); + + /* set up register offsets for IR context */ + /* IR DMA context registers are spaced 32 bytes apart */ + video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx; + video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx; + video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx; + video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx; + + /* enable interrupts for IR context */ + reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) ); + debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx); + + return 0; + + err_user_buf: + if(video->user_buf) { + if(video->user_dma.sglist) { + if(video->user_dma.n_dma_pages > 0) { + /* unmap it from the IOMMU */ + pci_unmap_sg(video->ohci->dev, + video->user_dma.sglist, + video->user_dma.n_pages, + PCI_DMA_TODEVICE); + video->user_dma.n_dma_pages = 0; + } + kfree(video->user_dma.sglist); + video->user_dma.sglist = NULL; + video->user_dma.n_pages = 0; + } + rvfree(video->user_buf, video->user_buf_size); + video->user_buf = NULL; + video->user_buf_size = 0; + } + + err_frames: + for(i = 0; i < DV1394_MAX_FRAMES; i++) { + if(video->frames[i]) + frame_delete(video->frames[i]); + } + video->n_frames = 0; + + err_ctx: + if(video->ohci_it_ctx != -1) { + clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage); + video->ohci_it_ctx = -1; + } + if(video->ohci_ir_ctx != -1) { + clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage); + video->ohci_ir_ctx = -1; + } + + spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); + video->ohci->ISO_channel_usage &= ~chan_mask; + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + err: + return retval; +} + +/* if the user doesn't bother to call ioctl(INIT) before starting + mmap() or read()/write(), just give him some default values */ + +static int do_dv1394_init_default(struct video_card *video) +{ + struct dv1394_init init; + + init.api_version = DV1394_API_VERSION; + init.channel = 63; + init.n_frames = 2; + /* the following are now set via proc_fs */ + init.format = video->pal_or_ntsc; + init.cip_n = video->cip_n; + init.cip_d = video->cip_d; + init.syt_offset = video->syt_offset; + + return do_dv1394_init(video, &init); +} + +/* do NOT call from interrupt context */ +static void stop_dma(struct video_card *video) +{ + unsigned long flags; + int i; + + /* no interrupts */ + spin_lock_irqsave(&video->spinlock, flags); + + /* stop DMA if in progress */ + if( (video->active_frame != -1) || + (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || + (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { + + /* clear the .run bits */ + reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15)); + reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15)); + flush_pci_write(video->ohci); + + video->active_frame = -1; + video->first_run = 1; + + + /* wait until DMA really stops */ + i = 0; + while(i < 1000) { + + /* wait 0.1 millisecond */ + udelay(100); + + if( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) || + (reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) { + /* still active */ + mb(); + } else { + debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10); + break; + } + + i++; + } + + if(i == 1000) { + printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10); + } + } + + spin_unlock_irqrestore(&video->spinlock, flags); +} + + + +static int do_dv1394_shutdown(struct video_card *video, int free_user_buf) +{ + int i; + + debug_printk("dv1394: shutdown...\n"); + + /* stop DMA if in progress */ + stop_dma(video); + + /* release the ISO channel */ + if(video->channel != -1) { + u64 chan_mask; + unsigned long flags; + + chan_mask = (u64)1 << video->channel; + + spin_lock_irqsave(&video->ohci->IR_channel_lock, flags); + video->ohci->ISO_channel_usage &= ~(chan_mask); + spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags); + + video->channel = -1; + } + + /* release the DMA contexts */ + if(video->ohci_it_ctx != -1) { + video->ohci_IsoXmitContextControlSet = 0; + video->ohci_IsoXmitContextControlClear = 0; + video->ohci_IsoXmitCommandPtr = 0; + + /* disable interrupts for IT context */ + reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx)); + + clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage); + debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx); + video->ohci_it_ctx = -1; + } + + if(video->ohci_ir_ctx != -1) { + video->ohci_IsoRcvContextControlSet = 0; + video->ohci_IsoRcvContextControlClear = 0; + video->ohci_IsoRcvCommandPtr = 0; + video->ohci_IsoRcvContextMatch = 0; + + /* disable interrupts for IR context */ + reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx)); + + clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage); + debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx); + video->ohci_ir_ctx = -1; + } + + /* free the frame structs */ + for(i = 0; i < DV1394_MAX_FRAMES; i++) { + if(video->frames[i]) + frame_delete(video->frames[i]); + video->frames[i] = NULL; + } + + video->n_frames = 0; + + /* we can't free the DMA buffer unless it is guaranteed that + no more user-space mappings exist */ + + if(free_user_buf && video->user_buf) { + if(video->user_dma.sglist) { + if(video->user_dma.n_dma_pages > 0) { + /* unmap it from the IOMMU */ + pci_unmap_sg(video->ohci->dev, + video->user_dma.sglist, + video->user_dma.n_pages, + PCI_DMA_TODEVICE); + video->user_dma.n_dma_pages = 0; + } + kfree(video->user_dma.sglist); + video->user_dma.sglist = NULL; + video->user_dma.n_pages = 0; + } + rvfree(video->user_buf, video->user_buf_size); + video->user_buf = NULL; + video->user_buf_size = 0; + } + + if (video->packet_buffer) { + pci_unmap_single(video->ohci->dev, + video->packet_buffer_dma, + video->packet_buffer_size, + PCI_DMA_FROMDEVICE); + kfree(video->packet_buffer); + video->packet_buffer = NULL; + video->packet_buffer_size = 0; + } + + debug_printk("dv1394: shutdown complete\n"); + + return 0; +} + + + +/* + ********************************** + *** MMAP() THEORY OF OPERATION *** + ********************************** + + The ringbuffer cannot be re-allocated or freed while + a user program maintains a mapping of it. (note that a mapping + can persist even after the device fd is closed!) + + So, only let the user process allocate the DMA buffer once. + To resize or deallocate it, you must close the device file + and open it again. + + Previously Dan M. hacked out a scheme that allowed the DMA + buffer to change by forcefully unmapping it from the user's + address space. It was prone to error because it's very hard to + track all the places the buffer could have been mapped (we + would have had to walk the vma list of every process in the + system to be sure we found all the mappings!). Instead, we + force the user to choose one buffer size and stick with + it. This small sacrifice is worth the huge reduction in + error-prone code in dv1394. + + Note: dv1394_mmap does no page table manipulation. The page + table entries are created by the dv1394_nopage() handler as + page faults are taken by the user. +*/ + +static struct page * dv1394_nopage(struct vm_area_struct * area, unsigned long address, int write_access) +{ + unsigned long offset; + unsigned long page, kernel_virt_addr; + struct page *ret = NOPAGE_SIGBUS; + + struct video_card *video = (struct video_card*) area->vm_private_data; + + /* guard against process-context operations and the interrupt */ + /* (by definition page faults are taken in interrupt context) */ + spin_lock(&video->spinlock); + + if(!video->user_buf) + goto out; + + if( (address < (unsigned long) area->vm_start) || + (address > (unsigned long) area->vm_start + video->user_buf_size) ) + goto out; + + offset = address - area->vm_start; + kernel_virt_addr = (unsigned long) video->user_buf + offset; + + page = kvirt_to_pa(kernel_virt_addr); + + ret = virt_to_page(__va(page)); + get_page(ret); + + out: + spin_unlock(&video->spinlock); + return ret; +} + +static struct vm_operations_struct dv1394_vm_ops = { + nopage: dv1394_nopage +}; + +/* + dv1394_mmap does no page table manipulation. The page table entries + are created by the dv1394_nopage() handler as page faults are taken + by the user. +*/ + +int dv1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_card *video = file_to_video_card(file); + unsigned long size; + int res = -EINVAL; + + /* serialize mmap */ + down(&video->sem); + + if( ! video_card_initialized(video) ) { + res = do_dv1394_init_default(video); + if(res) + goto err; + } + + /* region must be page-aligned */ + if(vma->vm_pgoff != 0) + goto err; + + /* check the size the user is trying to map */ + size = vma->vm_end - vma->vm_start; + if(size > video->user_buf_size) + goto err; + + /* + we don't actually mess with the page tables here. + (nopage() takes care of that from the page fault handler) + Just set up the vma->vm_ops. + */ + + vma->vm_ops = &dv1394_vm_ops; + vma->vm_private_data = video; + vma->vm_file = file; + + /* don't try to swap this out =) */ + vma->vm_flags |= VM_RESERVED; + + up(&video->sem); + return 0; + err: + up(&video->sem); + return res; +} + + +/*** DEVICE FILE INTERFACE *************************************************/ + +/* no need to serialize, multiple threads OK */ +static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_card *video = file_to_video_card(file); + unsigned int mask = 0; + unsigned long flags; + + poll_wait(file, &video->waitq, wait); + + spin_lock_irqsave(&video->spinlock, flags); + if( video->n_frames == 0 ) { + + } else if( video->active_frame == -1 ) { + /* nothing going on */ + mask |= POLLOUT; + } else { + /* any clear/ready buffers? */ + if(video->n_clear_frames >0) + mask |= POLLOUT | POLLIN; + } + spin_unlock_irqrestore(&video->spinlock, flags); + + return mask; +} + +static int dv1394_fasync(int fd, struct file *file, int on) +{ + /* I just copied this code verbatim from Alan Cox's mouse driver example + (linux/Documentation/DocBook/) */ + + struct video_card *video = file_to_video_card(file); + + int retval = fasync_helper(fd, file, on, &video->fasync); + + if (retval < 0) + return retval; + return 0; +} + +static ssize_t dv1394_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct video_card *video = file_to_video_card(file); + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + size_t cnt; + unsigned long flags; + int target_frame; + + /* serialize this to prevent multi-threaded mayhem */ + if(file->f_flags & O_NONBLOCK) { + if(down_trylock(&video->sem)) + return -EAGAIN; + } else { + if(down_interruptible(&video->sem)) + return -ERESTARTSYS; + } + + if( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if(ret) { + up(&video->sem); + return ret; + } + } + + ret = 0; + add_wait_queue(&video->waitq, &wait); + + while(count > 0) { + + /* must set TASK_INTERRUPTIBLE *before* checking for free + buffers; otherwise we could miss a wakeup if the interrupt + fires between the check and the schedule() */ + + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + target_frame = video->first_clear_frame; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if(video->frames[target_frame]->state == FRAME_CLEAR) { + + /* how much room is left in the target frame buffer */ + cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); + + } else { + /* buffer is already used */ + cnt = 0; + } + + if(cnt > count) + cnt = count; + + if (cnt <= 0) { + /* no room left, gotta wait */ + if(file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + schedule(); + + continue; /* start over from 'while(count > 0)...' */ + } + + if(copy_from_user(video->user_buf + video->write_off, buffer, cnt)) { + if(!ret) + ret = -EFAULT; + break; + } + + video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) + frame_prepare(video, target_frame); + } + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + up(&video->sem); + return ret; +} + + +static ssize_t dv1394_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct video_card *video = file_to_video_card(file); + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + size_t cnt; + unsigned long flags; + int target_frame; + + /* serialize this to prevent multi-threaded mayhem */ + if(file->f_flags & O_NONBLOCK) { + if(down_trylock(&video->sem)) + return -EAGAIN; + } else { + if(down_interruptible(&video->sem)) + return -ERESTARTSYS; + } + + if( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if(ret) { + up(&video->sem); + return ret; + } + receive_packets(video, video->frames[video->first_clear_frame]); + } + + ret = 0; + add_wait_queue(&video->waitq, &wait); + + while(count > 0) { + + /* must set TASK_INTERRUPTIBLE *before* checking for free + buffers; otherwise we could miss a wakeup if the interrupt + fires between the check and the schedule() */ + + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + target_frame = video->first_clear_frame; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if(target_frame >= 0 && + video->n_clear_frames > 0 && + video->frames[target_frame]->state == FRAME_CLEAR) { + + /* how much room is left in the target frame buffer */ + cnt = video->frame_size - (video->write_off - target_frame * video->frame_size); + + } else { + /* buffer is already used */ + cnt = 0; + } + + if(cnt > count) + cnt = count; + + if (cnt <= 0) { + /* no room left, gotta wait */ + if(file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + + schedule(); + + continue; /* start over from 'while(count > 0)...' */ + } + + if(copy_to_user(buffer, video->user_buf + video->write_off, cnt)) { + if(!ret) + ret = -EFAULT; + break; + } + + video->write_off = (video->write_off + cnt) % (video->n_frames * video->frame_size); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if(video->write_off == video->frame_size * ((target_frame + 1) % video->n_frames)) { + spin_lock_irqsave(&video->spinlock, flags); + video->n_clear_frames--; + video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames; + spin_unlock_irqrestore(&video->spinlock, flags); + } + } + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + up(&video->sem); + return ret; +} + + +/*** DEVICE IOCTL INTERFACE ************************************************/ + +/* I *think* the VFS serializes ioctl() for us, so we don't have to worry + about situations like having two threads in here at once... */ + +static int dv1394_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_card *video = file_to_video_card(file); + unsigned long flags; + int ret = -EINVAL; + + DECLARE_WAITQUEUE(wait, current); + + /* serialize this to prevent multi-threaded mayhem */ + if(file->f_flags & O_NONBLOCK) { + if(down_trylock(&video->sem)) + return -EAGAIN; + } else { + if(down_interruptible(&video->sem)) + return -ERESTARTSYS; + } + + switch(cmd) + { + case DV1394_SUBMIT_FRAMES: { + unsigned int n_submit; + + if( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if(ret) + goto out; + } + + n_submit = (unsigned int) arg; + + if(n_submit > video->n_frames) { + ret = -EINVAL; + goto out; + } + + while(n_submit > 0) { + + add_wait_queue(&video->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + /* wait until video->first_clear_frame is really CLEAR */ + while(video->frames[video->first_clear_frame]->state != FRAME_CLEAR) { + + spin_unlock_irqrestore(&video->spinlock, flags); + + if(signal_pending(current)) { + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = -EINTR; + goto out; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + } + spin_unlock_irqrestore(&video->spinlock, flags); + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + + frame_prepare(video, video->first_clear_frame); + + n_submit--; + } + + ret = 0; + break; + } + + case DV1394_WAIT_FRAMES: { + unsigned int n_wait; + + if( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + n_wait = (unsigned int) arg; + + /* since we re-run the last frame on underflow, we will + never actually have n_frames clear frames; at most only + n_frames - 1 */ + + if(n_wait > (video->n_frames-1) ) { + ret = -EINVAL; + goto out; + } + + add_wait_queue(&video->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + + while(video->n_clear_frames < n_wait) { + + spin_unlock_irqrestore(&video->spinlock, flags); + + if(signal_pending(current)) { + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = -EINTR; + goto out; + } + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&video->spinlock, flags); + } + + spin_unlock_irqrestore(&video->spinlock, flags); + + remove_wait_queue(&video->waitq, &wait); + set_current_state(TASK_RUNNING); + ret = 0; + break; + } + + case DV1394_RECEIVE_FRAMES: { + unsigned int n_recv; + + if( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + n_recv = (unsigned int) arg; + + /* at least one frame must be active */ + if(n_recv > (video->n_frames-1) ) { + ret = -EINVAL; + goto out; + } + + spin_lock_irqsave(&video->spinlock, flags); + + /* release the clear frames */ + video->n_clear_frames -= n_recv; + + /* advance the clear frame cursor */ + video->first_clear_frame = (video->first_clear_frame + n_recv) % video->n_frames; + + /* reset dropped_frames */ + video->dropped_frames = 0; + + spin_unlock_irqrestore(&video->spinlock, flags); + + ret = 0; + break; + } + + case DV1394_START_RECEIVE: { + + if( !video_card_initialized(video) ) { + ret = do_dv1394_init_default(video); + if(ret) + goto out; + } + + receive_packets(video, video->frames[video->first_clear_frame]); + + ret = 0; + break; + } + + case DV1394_INIT: { + struct dv1394_init init; + if(arg == (unsigned long) NULL) { + ret = do_dv1394_init_default(video); + } else { + if(copy_from_user(&init, (void*)arg, sizeof(init))) { + ret = -EFAULT; + goto out; + } + ret = do_dv1394_init(video, &init); + } + break; + } + + case DV1394_SHUTDOWN: + ret = do_dv1394_shutdown(video, 0); + break; + + + case DV1394_GET_STATUS: { + struct dv1394_status status; + + if( !video_card_initialized(video) ) { + ret = -EINVAL; + goto out; + } + + status.init.api_version = DV1394_API_VERSION; + status.init.channel = video->channel; + status.init.n_frames = video->n_frames; + status.init.format = video->pal_or_ntsc; + status.init.cip_n = video->cip_n; + status.init.cip_d = video->cip_d; + status.init.syt_offset = video->syt_offset; + + status.first_clear_frame = video->first_clear_frame; + + /* the rest of the fields need to be locked against the interrupt */ + spin_lock_irqsave(&video->spinlock, flags); + + status.active_frame = video->active_frame; + status.n_clear_frames = video->n_clear_frames; + + status.dropped_frames = video->dropped_frames; + + /* reset dropped_frames */ + video->dropped_frames = 0; + + spin_unlock_irqrestore(&video->spinlock, flags); + + if(copy_to_user((void*)arg, &status, sizeof(status))) { + ret = -EFAULT; + goto out; + } + + ret = 0; + break; + } + + default: + break; + } + + out: + up(&video->sem); + return ret; +} + + + +/*** DEVICE FILE INTERFACE CONTINUED ***************************************/ + +static int dv1394_open(struct inode *inode, struct file *file) +{ + struct video_card *video = NULL; + + /* if the device was opened through devfs, then file->private_data + has already been set to video by devfs */ + if(file->private_data) { + video = (struct video_card*) file->private_data; + + } else { + /* look up the card by ID */ + + struct list_head *lh; + unsigned long flags; + + spin_lock_irqsave(&dv1394_cards_lock, flags); + if(!list_empty(&dv1394_cards)) { + struct video_card *p; + list_for_each(lh, &dv1394_cards) { + p = list_entry(lh, struct video_card, list); + if(p->id == ieee1394_file_to_instance(file)) { + video = p; + break; + } + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if(!video) { + debug_printk("dv1394: OHCI card %d not found", ieee1394_file_to_instance(file)); + return -ENODEV; + } + + file->private_data = (void*) video; + } + +#ifndef DV1394_ALLOW_MORE_THAN_ONE_OPEN + + if( test_and_set_bit(0, &video->open) ) { + /* video is already open by someone else */ + return -EBUSY; + } + +#endif + + V22_COMPAT_MOD_INC_USE_COUNT; + return 0; +} + + +static int dv1394_release(struct inode *inode, struct file *file) +{ + struct video_card *video = file_to_video_card(file); + + /* OK to free the DMA buffer, no more mappings can exist */ + do_dv1394_shutdown(video, 1); + + /* clean up async I/O users */ + dv1394_fasync(-1, file, 0); + + /* give someone else a turn */ + clear_bit(0, &video->open); + + V22_COMPAT_MOD_DEC_USE_COUNT; + return 0; +} + + +/*** PROC_FS INTERFACE ******************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *dv1394_procfs_entry; + +static int dv1394_procfs_read( char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct video_card *video = (struct video_card*) data; + + V22_COMPAT_MOD_INC_USE_COUNT; + snprintf( page, count, + "\ +dv1394 settings for host %d:\n\ +----------------------------\n\ +format=%s\n\ +cip_n=%lu\n\ +cip_d=%lu\n\ +syt_offset=%u\n", + video->id, + (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"), + video->cip_n, video->cip_d, video->syt_offset ); + V22_COMPAT_MOD_DEC_USE_COUNT; + return strlen(page); +} +#endif /* CONFIG_PROC_FS */ + +/* lifted from the stallion.c driver */ +#undef TOLOWER +#define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x)) +static unsigned long atol(char *str) +{ + unsigned long val; + int base, c; + char *sp; + + val = 0; + sp = str; + if ((*sp == '0') && (*(sp+1) == 'x')) { + base = 16; + sp += 2; + } else if (*sp == '0') { + base = 8; + sp++; + } else { + base = 10; + } + + for (; (*sp != 0); sp++) { + c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0'); + if ((c < 0) || (c >= base)) { + printk(KERN_ERR "dv1394: atol() invalid argument %s\n", str); + val = 0; + break; + } + val = (val * base) + c; + } + return(val); +} + +#ifdef CONFIG_PROC_FS +static int dv1394_procfs_write( struct file *file, + const char *buffer, unsigned long count, void *data) +{ + int len = 0; + char new_value[64]; + char *pos; + struct video_card *video = (struct video_card*) data; + + V22_COMPAT_MOD_INC_USE_COUNT; + + if (count > 64) + len = 64; + else + len = count; + + if (copy_from_user( new_value, buffer, len)) { + V22_COMPAT_MOD_DEC_USE_COUNT; + return -EFAULT; + } + + pos = strchr(new_value, '='); + if (pos != NULL) { + int val_len = len - (pos-new_value) - 1; + char buf[64]; + memset(buf, 0, 64); + strncpy(buf, pos+1, val_len); + if (buf[val_len-1] == '\n') buf[val_len-1] = 0; + + if (strnicmp( new_value, "format", (pos-new_value)) == 0) { + if (strnicmp( buf, "NTSC", val_len) == 0) + video->pal_or_ntsc = DV1394_NTSC; + else if (strnicmp( buf, "PAL", val_len) == 0) + video->pal_or_ntsc = DV1394_PAL; + + } else if (strnicmp( new_value, "cip_n", (pos-new_value)) == 0) { + video->cip_n = atol(buf); + } else if (strnicmp( new_value, "cip_d", (pos-new_value)) == 0) { + video->cip_d = atol(buf); + } else if (strnicmp( new_value, "syt_offset", (pos-new_value)) == 0) { + video->syt_offset = atol(buf); + } + } + + V22_COMPAT_MOD_DEC_USE_COUNT; + return len; +} + +static int dv1394_procfs_add_entry(struct video_card *video) +{ + struct proc_dir_entry *procfs_entry = NULL; + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", video->id); + + procfs_entry = create_proc_entry( buf, 0666, dv1394_procfs_entry); + if (procfs_entry == NULL) { + printk(KERN_ERR "dv1394: unable to create /proc/bus/ieee1394/dv/X\n"); + return -ENOMEM; + } + procfs_entry->owner = THIS_MODULE; + procfs_entry->data = video; + procfs_entry->read_proc = dv1394_procfs_read; + procfs_entry->write_proc = dv1394_procfs_write; + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +/*** DEVICE DRIVER HANDLERS ************************************************/ + +static void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent, void *data) +{ + int wake = 0; + struct video_card *video = (struct video_card*) data; + + irq_printk("INTERRUPT! Video = %08lx Iso event Recv: %08x Xmit: %08x\n", + (unsigned long) video, isoRecvIntEvent, isoXmitIntEvent); + irq_printk("ContextControl = %08x, CommandPtr = %08x\n", + reg_read(video->ohci, video->ohci_IsoXmitContextControlSet), + reg_read(video->ohci, video->ohci_IsoXmitCommandPtr) + ); + + + if( (video->ohci_it_ctx != -1) && + (isoXmitIntEvent & (1 << video->ohci_it_ctx)) && + (reg_read(video->ohci, video->ohci_IsoXmitContextControlSet) & (1 << 10)) ) { + + struct frame *f; + unsigned int frame, i; + + spin_lock(&video->spinlock); + if(video->active_frame == -1) + frame = 0; + else + frame = video->active_frame; + + /* check all the DMA-able frames */ + for(i = 0; i < video->n_frames; i++, frame = (frame+1) % video->n_frames) { + + irq_printk("IRQ checking frame %d...", frame); + f = video->frames[frame]; + if(f->state != FRAME_READY) { + irq_printk("clear, skipping\n"); + /* we don't own this frame */ + continue; + } + + irq_printk("DMA\n"); + + /* check the frame begin semaphore to see if we can free the previous frame */ + if( *(f->frame_begin_timestamp) ) { + int prev_frame; + struct frame *prev_f; + + + + /* don't reset, need this later *(f->frame_begin_timestamp) = 0; */ + irq_printk(" BEGIN\n"); + + prev_frame = frame - 1; + if(prev_frame == -1) + prev_frame += video->n_frames; + prev_f = video->frames[prev_frame]; + + /* make sure we can actually garbage collect + this frame */ + if( (prev_f->state == FRAME_READY) && + prev_f->done && (!f->done) ) + { + frame_reset(prev_f); + video->n_clear_frames++; + wake = 1; + video->active_frame = frame; + + irq_printk(" BEGIN - freeing previous frame %d, new active frame is %d\n", prev_frame, frame); + } else { + irq_printk(" BEGIN - can't free yet\n"); + } + + f->done = 1; + } + + + /* see if we need to set the timestamp for the next frame */ + if( *(f->mid_frame_timestamp) ) { + struct frame *next_frame; + u32 ts_cyc, ts_off; + + *(f->mid_frame_timestamp) = 0; + + irq_printk(" MIDDLE - first packet was sent at cycle %4u (%2u), assigned timestamp was (%2u) %4u\n", + *(f->frame_begin_timestamp) & 0x1FFF, *(f->frame_begin_timestamp) & 0xF, + f->assigned_timestamp >> 12, f->assigned_timestamp & 0xFFF); + + /* prepare next frame and assign timestamp */ + next_frame = video->frames[ (frame+1) % video->n_frames ]; + + if(next_frame->state == FRAME_READY) { + irq_printk(" MIDDLE - next frame is ready, good\n"); + } else { + debug_printk("dv1394: Underflow! At least one frame has been dropped.\n"); + next_frame = f; + } + + /* set the timestamp to the timestamp of the last frame sent, + plus the length of the last frame sent, plus the syt latency */ + ts_cyc = *(f->frame_begin_timestamp) & 0xF; + /* advance one frame, plus syt latency (typically 2-3) */ + ts_cyc += f->n_packets + video->syt_offset ; + + ts_off = 0; + + ts_cyc += ts_off/3072; + ts_off %= 3072; + + next_frame->assigned_timestamp = ((ts_cyc&0xF) << 12) + ts_off; + if(next_frame->cip_syt1) { + next_frame->cip_syt1->b[6] = next_frame->assigned_timestamp >> 8; + next_frame->cip_syt1->b[7] = next_frame->assigned_timestamp & 0xFF; + } + if(next_frame->cip_syt2) { + next_frame->cip_syt2->b[6] = next_frame->assigned_timestamp >> 8; + next_frame->cip_syt2->b[7] = next_frame->assigned_timestamp & 0xFF; + } + + } + + /* see if the frame looped */ + if( *(f->frame_end_timestamp) ) { + + *(f->frame_end_timestamp) = 0; + + debug_printk(" END - the frame looped at least once\n"); + + video->dropped_frames++; + } + + + + } /* for(each frame) */ + + spin_unlock(&video->spinlock); + + } /* end XMIT portion */ + + /***** RECEIVE INTERRUPT and DMA ACTIVE *****/ + + else if( (video->ohci_ir_ctx != -1) && + (isoRecvIntEvent & (1 << video->ohci_ir_ctx)) && + (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) { + + int sof=0; /* start-of-frame flag */ + struct frame *f; + + spin_lock(&video->spinlock); + + irq_printk("received packet %02d, timestamp=%04x, length=%04x, sof=%02x%02x\n", video->current_packet, + video->packet_buffer[video->current_packet].timestamp, video->packet_buffer[video->current_packet].data_length, + video->packet_buffer[video->current_packet].data[0], video->packet_buffer[video->current_packet].data[1]); + + f = video->frames[video->active_frame]; + + /* exclude empty packet */ + if (video->packet_buffer[video->current_packet].data_length > 8) { + + /* check for start of frame */ + sof = (video->packet_buffer[video->current_packet].data[0] == 0x1f && + video->packet_buffer[video->current_packet].data[1] == 0x07); + + if (!video->first_frame) { + if (sof) { + video->first_frame = 1; + } + + } else if (sof) { + /* close current frame */ + frame_reset(f); /* f->state = STATE_CLEAR */ + video->n_clear_frames++; + if (video->n_clear_frames > video->n_frames) { + video->n_clear_frames = video->n_frames; + video->dropped_frames++; + } + if (video->first_clear_frame == -1) + video->first_clear_frame = video->active_frame; + + /* get the next frame */ + video->active_frame = (video->active_frame + 1) % video->n_frames; + f = video->frames[video->active_frame]; + + irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n", + video->active_frame, video->n_clear_frames, video->first_clear_frame); + } + if (video->first_frame) { + if (sof) { + /* open next frame */ + f->state = FRAME_READY; + } + + /* copy to buffer */ + if (f->n_packets > (video->frame_size / 480)) { + printk(KERN_ERR "frame buffer overflow during receive\n"); + } + + /* make sure we are seeing the latest changes to packet_buffer */ + pci_dma_sync_single(video->ohci->dev, + video->packet_buffer_dma, + video->packet_buffer_size, + PCI_DMA_FROMDEVICE); + + frame_put_packet( f, &video->packet_buffer[video->current_packet]); + + } /* first_frame */ + + } /* not empty packet */ + + /* advance packet_buffer cursor */ + video->current_packet = (video->current_packet + 1) % MAX_PACKET_BUFFER; + + spin_unlock(&video->spinlock); + + wake = 1; /* why the hell not? */ + + } /* receive interrupt */ + + if(wake) { + + /* send SIGIO */ + + if(isoRecvIntEvent & (1)) + kill_fasync(&video->fasync, SIGIO, POLL_IN); + + if(isoXmitIntEvent & (1)) + kill_fasync(&video->fasync, SIGIO, POLL_OUT); + + /* wake readers/writers/ioctl'ers */ + wake_up_interruptible(&video->waitq); + } +} + +static struct file_operations dv1394_fops= +{ + OWNER_THIS_MODULE + poll: dv1394_poll, + ioctl: dv1394_ioctl, + mmap: dv1394_mmap, + open: dv1394_open, + write: dv1394_write, + read: dv1394_read, + release: dv1394_release, + fasync: dv1394_fasync, +}; + + +static int dv1394_init(struct ti_ohci *ohci) +{ + struct video_card *video; + unsigned long flags; + char buf[16]; + int i; + + video = kmalloc(sizeof(struct video_card), GFP_KERNEL); + if(!video) { + printk(KERN_ERR "dv1394: cannot allocate video_card\n"); + goto err; + } + + memset(video, 0, sizeof(struct video_card)); + + if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) { + printk(KERN_ERR "dv1394: ohci1394_hook_irq() failed\n"); + goto err_free; + } + + + video->ohci = ohci; + video->id = ohci->id; + + if ( dv1394_procfs_add_entry(video) < 0 ) + goto err_free; + + video->ohci_it_ctx = -1; + video->ohci_ir_ctx = -1; + + video->ohci_IsoXmitContextControlSet = 0; + video->ohci_IsoXmitContextControlClear = 0; + video->ohci_IsoXmitCommandPtr = 0; + + video->ohci_IsoRcvContextControlSet = 0; + video->ohci_IsoRcvContextControlClear = 0; + video->ohci_IsoRcvCommandPtr = 0; + video->ohci_IsoRcvContextMatch = 0; + + video->n_frames = 0; /* flag that video is not initialized */ + video->channel = -1; + video->active_frame = -1; + + /* initialize the following for proc_fs */ + video->pal_or_ntsc = DV1394_NTSC; + video->cip_n = 0; /* 0 = use builtin default */ + video->cip_d = 0; + video->syt_offset = 0; + + for(i = 0; i < DV1394_MAX_FRAMES; i++) + video->frames[i] = NULL; + + video->user_buf = NULL; + video->user_buf_size = 0; + + clear_bit(0, &video->open); + spin_lock_init(&video->spinlock); + init_MUTEX(&video->sem); + init_waitqueue_head(&video->waitq); + video->fasync = NULL; + + spin_lock_irqsave(&dv1394_cards_lock, flags); + INIT_LIST_HEAD(&video->list); + list_add_tail(&video->list, &dv1394_cards); + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + snprintf(buf, sizeof(buf), "%d", video->id); + + video->devfs_handle = devfs_register(dv1394_devfs_handle, + buf, DEVFS_FL_NONE, + IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_DV1394*16 + video->id, + S_IFCHR | S_IRUGO | S_IWUGO, + &dv1394_fops, + (void*) video); + + debug_printk("dv1394: dv1394_init() OK on ID %d\n", ohci->id); + + return 0; + + err_free: + kfree(video); + err: + return -1; +} + +static void dv1394_un_init(struct video_card *video) +{ + unsigned long flags; + + /* obviously nobody has the driver open at this point */ + do_dv1394_shutdown(video, 1); + ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video); + if(video->devfs_handle) + devfs_unregister(video->devfs_handle); + + spin_lock_irqsave(&dv1394_cards_lock, flags); + list_del(&video->list); + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + kfree(video); +} + + +static void dv1394_remove_host (struct hpsb_host *host) +{ + struct ti_ohci *ohci; + struct video_card *video = NULL; + unsigned long flags; + struct list_head *lh; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + ohci = (struct ti_ohci *)host->hostdata; + + + /* find the corresponding video_card */ + spin_lock_irqsave(&dv1394_cards_lock, flags); + if(!list_empty(&dv1394_cards)) { + struct video_card *p; + list_for_each(lh, &dv1394_cards) { + p = list_entry(lh, struct video_card, list); + if(p->id == ohci->id) { + video = p; + break; + } + } + } + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + + if(video) { + char buf[16]; + dv1394_un_init(video); + snprintf( buf, sizeof(buf), "%i", video->id); +#ifdef CONFIG_PROC_FS + remove_proc_entry( buf, dv1394_procfs_entry); +#endif + } +} + +static void dv1394_add_host (struct hpsb_host *host) +{ + struct ti_ohci *ohci; + + /* We only work with the OHCI-1394 driver */ + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) + return; + + ohci = (struct ti_ohci *)host->hostdata; + + dv1394_init(ohci); +} + +static struct hpsb_highlevel_ops hl_ops = { + add_host: dv1394_add_host, + remove_host: dv1394_remove_host, +}; + + +/*** KERNEL MODULE HANDLERS ************************************************/ + +MODULE_AUTHOR("Dan Maas , Dan Dennedy "); +MODULE_DESCRIPTION("driver for DV input/output on OHCI board"); +MODULE_SUPPORTED_DEVICE("dv1394"); +MODULE_LICENSE("GPL"); + +static void __exit dv1394_exit_module(void) +{ + hpsb_unregister_highlevel (hl_handle); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394); + devfs_unregister(dv1394_devfs_handle); +#ifdef CONFIG_PROC_FS + remove_proc_entry( "dv", ieee1394_procfs_entry); +#endif +} + +static int __init dv1394_init_module(void) +{ + if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_DV1394, + THIS_MODULE, &dv1394_fops)) { + printk(KERN_ERR "dv1394: unable to register character device\n"); + return -EIO; + } + + dv1394_devfs_handle = devfs_mk_dir(ieee1394_devfs_handle, "dv", NULL); + +#ifdef CONFIG_PROC_FS + dv1394_procfs_entry = proc_mkdir( "dv", ieee1394_procfs_entry); + if (dv1394_procfs_entry == NULL) { + printk(KERN_ERR "dv1394: unable to create /proc/ieee1394/dv\n"); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394); + devfs_unregister(dv1394_devfs_handle); + return -ENOMEM; + } + dv1394_procfs_entry->owner = THIS_MODULE; +#endif + + hl_handle = hpsb_register_highlevel ("dv1394", &hl_ops); + if (hl_handle == NULL) { + printk(KERN_ERR "dv1394: hpsb_register_highlevel failed\n"); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394); + devfs_unregister(dv1394_devfs_handle); +#ifdef CONFIG_PROC_FS + remove_proc_entry( "dv", ieee1394_procfs_entry); +#endif + return -ENOMEM; + } + + return 0; +} + +module_init(dv1394_init_module); +module_exit(dv1394_exit_module); + diff -urN linux-2.5.3-pre2/drivers/ieee1394/dv1394.h linux/drivers/ieee1394/dv1394.h --- linux-2.5.3-pre2/drivers/ieee1394/dv1394.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/ieee1394/dv1394.h Mon Jan 21 17:23:22 2002 @@ -0,0 +1,277 @@ +/* + * dv1394.h - DV input/output over IEEE 1394 on OHCI chips + * Copyright (C)2001 Daniel Maas + * receive, proc_fs by Dan Dennedy + * + * based on: + * video1394.h - driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux + * Peter Schlaile + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + */ + +#ifndef _DV_1394_H +#define _DV_1394_H + +/* This is the public user-space interface. Try not to break it. */ + +#define DV1394_API_VERSION 0x20011127 + +/* ******************** + ** ** + ** DV1394 API ** + ** ** + ******************** + + There are two methods of operating the DV1394 DV output device. + + 1) + + The simplest is an interface based on write(): simply write + full DV frames of data to the device, and they will be transmitted + as quickly as possible. The FD may be set for non-blocking I/O, + in which case you can use select() or poll() to wait for output + buffer space. + + To set the DV output parameters (e.g. whether you want NTSC or PAL + video), use the DV1394_INIT ioctl, passing in the parameters you + want in a struct dv1394_init. + + Example 1: + To play a raw .DV file: cat foo.DV > /dev/dv1394 + (cat will use write() internally) + + Example 2: + static struct dv1394_init init = { + 0x63, (broadcast channel) + 4, (four-frame ringbuffer) + DV1394_NTSC, (send NTSC video) + 0, 0 (default empty packet rate) + } + + ioctl(fd, DV1394_INIT, &init); + + while(1) { + read( , buf, DV1394_NTSC_FRAME_SIZE ); + write( , buf, DV1394_NTSC_FRAME_SIZE ); + } + + 2) + + For more control over buffering, and to avoid unnecessary copies + of the DV data, you can use the more sophisticated the mmap() interface. + First, call the DV1394_INIT ioctl to specify your parameters, + including the number of frames in the ringbuffer. Then, calling mmap() + on the dv1394 device will give you direct access to the ringbuffer + from which the DV card reads your frame data. + + The ringbuffer is simply one large, contiguous region of memory + containing two or more frames of packed DV data. Each frame of DV data + is 120000 bytes (NTSC) or 144000 bytes (PAL). + + Fill one or more frames in the ringbuffer, then use the DV1394_SUBMIT_FRAMES + ioctl to begin I/O. You can use either the DV1394_WAIT_FRAMES ioctl + or select()/poll() to wait until the frames are transmitted. Next, you'll + need to call the DV1394_GET_STATUS ioctl to determine which ringbuffer + frames are clear (ready to be filled with new DV data). Finally, use + DV1394_SUBMIT_FRAMES again to send the new data to the DV output. + + + Example: here is what a four-frame ringbuffer might look like + during DV transmission: + + + frame 0 frame 1 frame 2 frame 3 + + *--------------------------------------* + | CLEAR | DV data | DV data | CLEAR | + *--------------------------------------* + + + transmission goes in this direction --->>> + + + The DV hardware is currently transmitting the data in frame 1. + Once frame 1 is finished, it will automatically transmit frame 2. + (if frame 2 finishes before frame 3 is submitted, the device + will continue to transmit frame 2, and will increase the dropped_frames + counter each time it repeats the transmission). + + + If you called DV1394_GET_STATUS at this instant, you would + receive the following values: + + n_frames = 4 + active_frame = 1 + first_clear_frame = 3 + n_clear_frames = 2 + + At this point, you should write new DV data into frame 3 and optionally + frame 0. Then call DV1394_SUBMIT_FRAMES to inform the device that + it may transmit the new frames. + +*/ + + +/* maximum number of frames in the ringbuffer */ +#define DV1394_MAX_FRAMES 32 + +/* number of *full* isochronous packets per DV frame */ +#define DV1394_NTSC_PACKETS_PER_FRAME 250 +#define DV1394_PAL_PACKETS_PER_FRAME 300 + +/* size of one frame's worth of DV data, in bytes */ +#define DV1394_NTSC_FRAME_SIZE (480 * DV1394_NTSC_PACKETS_PER_FRAME) +#define DV1394_PAL_FRAME_SIZE (480 * DV1394_PAL_PACKETS_PER_FRAME) + + +/* ioctl() commands */ + +enum { + /* I don't like using 0 as a valid ioctl() */ + DV1394_INVALID = 0, + + + /* get the driver ready to transmit video. + pass a struct dv1394_init* as the parameter (see below), + or NULL to get default parameters */ + DV1394_INIT, + + + /* stop transmitting video and free the ringbuffer */ + DV1394_SHUTDOWN, + + + /* submit N new frames to be transmitted, where + the index of the first new frame is first_clear_buffer, + and the index of the last new frame is + (first_clear_buffer + N) % n_frames */ + DV1394_SUBMIT_FRAMES, + + + /* block until N buffers are clear (pass N as the parameter) + Because we re-transmit the last frame on underrun, there + will at most be n_frames - 1 clear frames at any time */ + DV1394_WAIT_FRAMES, + + /* capture new frames that have been received, where + the index of the first new frame is first_clear_buffer, + and the index of the last new frame is + (first_clear_buffer + N) % n_frames */ + DV1394_RECEIVE_FRAMES, + + + DV1394_START_RECEIVE, + + + /* pass a struct dv1394_status* as the parameter (see below) */ + DV1394_GET_STATUS, +}; + + + +enum pal_or_ntsc { + DV1394_NTSC = 0, + DV1394_PAL +}; + + + + +/* this is the argument to DV1394_INIT */ +struct dv1394_init { + /* DV1394_API_VERSION */ + unsigned int api_version; + + /* isochronous transmission channel to use */ + unsigned int channel; + + /* number of frames in the ringbuffer. Must be at least 2 + and at most DV1394_MAX_FRAMES. */ + unsigned int n_frames; + + /* send/receive PAL or NTSC video format */ + enum pal_or_ntsc format; + + /* the following are used only for transmission */ + + /* set these to zero unless you want a + non-default empty packet rate (see below) */ + unsigned long cip_n; + unsigned long cip_d; + + /* set this to zero unless you want a + non-default SYT cycle offset (default = 3 cycles) */ + unsigned int syt_offset; +}; + +/* Q: What are cip_n and cip_d? */ + +/* + A: DV video streams do not utilize 100% of the potential bandwidth offered + by IEEE 1394 (FireWire). To achieve the correct rate of data transmission, + DV devices must periodically insert empty packets into the 1394 data stream. + Typically there is one empty packet per 14-16 data-carrying packets. + + Some DV devices will accept a wide range of empty packet rates, while others + require a precise rate. If the dv1394 driver produces empty packets at + a rate that your device does not accept, you may see ugly patterns on the + DV output, or even no output at all. + + The default empty packet insertion rate seems to work for many people; if + your DV output is stable, you can simply ignore this discussion. However, + we have exposed the empty packet rate as a parameter to support devices that + do not work with the default rate. + + The decision to insert an empty packet is made with a numerator/denominator + algorithm. Empty packets are produced at an average rate of CIP_N / CIP_D. + You can alter the empty packet rate by passing non-zero values for cip_n + and cip_d to the INIT ioctl. + + */ + + + +struct dv1394_status { + /* this embedded init struct returns the current dv1394 + parameters in use */ + struct dv1394_init init; + + /* the ringbuffer frame that is currently being + displayed. (-1 if the device is not transmitting anything) */ + int active_frame; + + /* index of the first buffer (ahead of active_frame) that + is ready to be filled with data */ + unsigned int first_clear_frame; + + /* how many buffers, including first_clear_buffer, are + ready to be filled with data */ + unsigned int n_clear_frames; + + /* how many times the DV output has underflowed + since the last call to DV1394_GET_STATUS */ + unsigned int dropped_frames; + + /* N.B. The dropped_frames counter is only a lower bound on the actual + number of dropped frames, with the special case that if dropped_frames + is zero, then it is guaranteed that NO frames have been dropped + since the last call to DV1394_GET_STATUS. + */ +}; + + +#endif /* _DV_1394_H */ diff -urN linux-2.5.3-pre2/drivers/ieee1394/highlevel.c linux/drivers/ieee1394/highlevel.c --- linux-2.5.3-pre2/drivers/ieee1394/highlevel.c Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/highlevel.c Mon Jan 21 17:23:22 2002 @@ -44,10 +44,11 @@ hl->op = ops; write_lock_irq(&hl_drivers_lock); - hl_all_hosts(hl, 1); list_add_tail(&hl->hl_list, &hl_drivers); write_unlock_irq(&hl_drivers_lock); + hl_all_hosts(hl->op->add_host); + return hl; } @@ -73,9 +74,11 @@ write_lock_irq(&hl_drivers_lock); list_del(&hl->hl_list); - hl_all_hosts(hl, 0); write_unlock_irq(&hl_drivers_lock); + if (hl->op->remove_host) + hl_all_hosts(hl->op->remove_host); + kfree(hl); } @@ -87,7 +90,7 @@ int retval = 0; if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) { - HPSB_ERR(__FUNCTION__ " called with invalid addresses"); + HPSB_ERR("%s called with invalid addresses", __FUNCTION__); return 0; } @@ -131,12 +134,12 @@ unsigned int channel) { if (channel > 63) { - HPSB_ERR(__FUNCTION__ " called with invalid channel"); + HPSB_ERR("%s called with invalid channel", __FUNCTION__); return; } if (host->iso_listen_count[channel]++ == 0) { - host->template->devctl(host, ISO_LISTEN_CHANNEL, channel); + host->ops->devctl(host, ISO_LISTEN_CHANNEL, channel); } } @@ -144,46 +147,58 @@ unsigned int channel) { if (channel > 63) { - HPSB_ERR(__FUNCTION__ " called with invalid channel"); + HPSB_ERR("%s called with invalid channel", __FUNCTION__); return; } if (--host->iso_listen_count[channel] == 0) { - host->template->devctl(host, ISO_UNLISTEN_CHANNEL, channel); + host->ops->devctl(host, ISO_UNLISTEN_CHANNEL, channel); } } -#define DEFINE_MULTIPLEXER(Function) \ -void highlevel_##Function(struct hpsb_host *host) \ -{ \ - struct list_head *lh; \ - void (*funcptr)(struct hpsb_host*); \ - read_lock(&hl_drivers_lock); \ - list_for_each(lh, &hl_drivers) { \ - funcptr = list_entry(lh, struct hpsb_highlevel, hl_list) \ - ->op->Function; \ - if (funcptr) funcptr(host); \ - } \ - read_unlock(&hl_drivers_lock); \ -} - -DEFINE_MULTIPLEXER(add_host) -DEFINE_MULTIPLEXER(remove_host) -DEFINE_MULTIPLEXER(host_reset) -#undef DEFINE_MULTIPLEXER - -/* Add one host to our list */ -void highlevel_add_one_host (struct hpsb_host *host) -{ - if (host->template->initialize_host) - if (!host->template->initialize_host(host)) - goto fail; - host->initialized = 1; - highlevel_add_host (host); - hpsb_reset_bus (host, LONG_RESET); -fail: - host->template->number_of_hosts++; +void highlevel_add_host(struct hpsb_host *host) +{ + struct list_head *entry; + struct hpsb_highlevel *hl; + + read_lock(&hl_drivers_lock); + list_for_each(entry, &hl_drivers) { + hl = list_entry(entry, struct hpsb_highlevel, hl_list); + + hl->op->add_host(host); + } + read_unlock(&hl_drivers_lock); +} + +void highlevel_remove_host(struct hpsb_host *host) +{ + struct list_head *entry; + struct hpsb_highlevel *hl; + + write_lock_irq(&hl_drivers_lock); + list_for_each(entry, &hl_drivers) { + hl = list_entry(entry, struct hpsb_highlevel, hl_list); + + if (hl->op->remove_host) + hl->op->remove_host(host); + } + write_unlock_irq(&hl_drivers_lock); +} + +void highlevel_host_reset(struct hpsb_host *host) +{ + struct list_head *entry; + struct hpsb_highlevel *hl; + + read_lock(&hl_drivers_lock); + list_for_each(entry, &hl_drivers) { + hl = list_entry(entry, struct hpsb_highlevel, hl_list); + + if (hl->op->host_reset) + hl->op->host_reset(host); + } + read_unlock(&hl_drivers_lock); } void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data, @@ -242,12 +257,11 @@ while (as->start <= addr) { if (as->end > addr) { - partlength = MIN((unsigned int)(as->end - addr), - length); + partlength = min(as->end - addr, (u64) length); if (as->op->read != NULL) { - rcode = as->op->read(host, nodeid, buffer, addr, - partlength); + rcode = as->op->read(host, nodeid, buffer, + addr, partlength); } else { rcode = RCODE_TYPE_ERROR; } @@ -288,12 +302,11 @@ while (as->start <= addr) { if (as->end > addr) { - partlength = MIN((unsigned int)(as->end - addr), - length); + partlength = min(as->end - addr, (u64) length); if (as->op->write != NULL) { - rcode = as->op->write(host, nodeid, destid, data, - addr, partlength); + rcode = as->op->write(host, nodeid, destid, + data, addr, partlength); } else { rcode = RCODE_TYPE_ERROR; } diff -urN linux-2.5.3-pre2/drivers/ieee1394/highlevel.h linux/drivers/ieee1394/highlevel.h --- linux-2.5.3-pre2/drivers/ieee1394/highlevel.h Thu Jul 19 17:48:15 2001 +++ linux/drivers/ieee1394/highlevel.h Mon Jan 21 17:23:22 2002 @@ -91,7 +91,6 @@ void init_hpsb_highlevel(void); void highlevel_add_host(struct hpsb_host *host); -void highlevel_add_one_host(struct hpsb_host *host); void highlevel_remove_host(struct hpsb_host *host); void highlevel_host_reset(struct hpsb_host *host); diff -urN linux-2.5.3-pre2/drivers/ieee1394/hosts.c linux/drivers/ieee1394/hosts.c --- linux-2.5.3-pre2/drivers/ieee1394/hosts.c Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/hosts.c Mon Jan 21 17:23:22 2002 @@ -11,101 +11,86 @@ */ #include - #include +#include #include -#include -#include +#include #include "ieee1394_types.h" #include "hosts.h" #include "ieee1394_core.h" #include "highlevel.h" +static struct list_head hosts = LIST_HEAD_INIT(hosts); +static struct list_head host_drivers = LIST_HEAD_INIT(host_drivers); -static LIST_HEAD(templates); -static spinlock_t templates_lock = SPIN_LOCK_UNLOCKED; +spinlock_t hosts_lock = SPIN_LOCK_UNLOCKED; +spinlock_t host_drivers_lock = SPIN_LOCK_UNLOCKED; -/* - * This function calls the add_host/remove_host hooks for every host currently - * registered. Init == TRUE means add_host. - */ -void hl_all_hosts(struct hpsb_highlevel *hl, int init) + +static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p) { - struct list_head *tlh, *hlh; - struct hpsb_host_template *tmpl; - struct hpsb_host *host; + return 0; +} - spin_lock(&templates_lock); +static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg) +{ + return -1; +} - list_for_each(tlh, &templates) { - tmpl = list_entry(tlh, struct hpsb_host_template, list); - list_for_each(hlh, &tmpl->hosts) { - host = list_entry(hlh, struct hpsb_host, list); - if (host->initialized) { - if (init) { - if (hl->op->add_host) { - hl->op->add_host(host); - } - } else { - if (hl->op->remove_host) { - hl->op->remove_host(host); - } - } - } - } - } +static struct hpsb_host_operations dummy_ops = { + transmit_packet: dummy_transmit_packet, + devctl: dummy_devctl +}; - spin_unlock(&templates_lock); -} -int hpsb_inc_host_usage(struct hpsb_host *host) +int hpsb_ref_host(struct hpsb_host *host) { - struct list_head *tlh, *hlh; - struct hpsb_host_template *tmpl; - int retval = 0; + struct list_head *lh; unsigned long flags; + int retval = 0; - spin_lock_irqsave(&templates_lock, flags); - - list_for_each(tlh, &templates) { - tmpl = list_entry(tlh, struct hpsb_host_template, list); - list_for_each(hlh, &tmpl->hosts) { - if (host == list_entry(hlh, struct hpsb_host, list)) { - tmpl->devctl(host, MODIFY_USAGE, 1); + spin_lock_irqsave(&hosts_lock, flags); + list_for_each(lh, &hosts) { + if (host == list_entry(lh, struct hpsb_host, host_list)) { + if (host->ops->devctl(host, MODIFY_USAGE, 1)) { + host->refcount++; retval = 1; - break; - } } - if (retval) break; } - - spin_unlock_irqrestore(&templates_lock, flags); + } + spin_unlock_irqrestore(&hosts_lock, flags); return retval; } -void hpsb_dec_host_usage(struct hpsb_host *host) +void hpsb_unref_host(struct hpsb_host *host) { - host->template->devctl(host, MODIFY_USAGE, 0); + unsigned long flags; + + host->ops->devctl(host, MODIFY_USAGE, 0); + + spin_lock_irqsave(&hosts_lock, flags); + host->refcount--; + + if (!host->refcount && !host->is_shutdown) + kfree(host); + spin_unlock_irqrestore(&hosts_lock, flags); } -/* - * The following function is exported for module usage. It will be called from - * the detect function of a adapter driver. - */ -struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl, - size_t hd_size) +struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra) { struct hpsb_host *h; - h = vmalloc(sizeof(struct hpsb_host) + hd_size); + h = kmalloc(sizeof(struct hpsb_host) + extra, SLAB_KERNEL); if (!h) return NULL; - memset(h, 0, sizeof(struct hpsb_host) + hd_size); + memset(h, 0, sizeof(struct hpsb_host) + extra); - atomic_set(&h->generation, 0); + h->driver = drv; + h->ops = drv->ops; + h->hostdata = h + 1; INIT_LIST_HEAD(&h->pending_packets); spin_lock_init(&h->pending_pkt_lock); @@ -113,113 +98,92 @@ sema_init(&h->tlabel_count, 64); spin_lock_init(&h->tlabel_lock); + atomic_set(&h->generation, 0); + INIT_TQUEUE(&h->timeout_tq, (void (*)(void*))abort_timedouts, h); h->topology_map = h->csr.topology_map + 3; h->speed_map = (u8 *)(h->csr.speed_map + 2); - h->template = tmpl; - if (hd_size) - h->hostdata = &h->embedded_hostdata[0]; + return h; +} + +void hpsb_add_host(struct hpsb_host *host) +{ + unsigned long flags; - list_add_tail(&h->list, &tmpl->hosts); + spin_lock_irqsave(&hosts_lock, flags); + host->driver->number_of_hosts++; + list_add_tail(&host->driver_list, &host->driver->hosts); + list_add_tail(&host->host_list, &hosts); + spin_unlock_irqrestore(&hosts_lock, flags); - return h; + highlevel_add_host(host); + host->ops->devctl(host, RESET_BUS, 0); } -static void free_all_hosts(struct hpsb_host_template *tmpl) +void hpsb_remove_host(struct hpsb_host *host) { - struct list_head *hlh, *next; - struct hpsb_host *host; + struct hpsb_host_driver *drv = host->driver; + unsigned long flags; - list_for_each_safe(hlh, next, &tmpl->hosts) { - host = list_entry(hlh, struct hpsb_host, list); - vfree(host); - } + host->is_shutdown = 1; + host->ops = &dummy_ops; + highlevel_remove_host(host); + + spin_lock_irqsave(&hosts_lock, flags); + list_del(&host->driver_list); + list_del(&host->host_list); + + drv->number_of_hosts--; + if (!host->refcount) kfree(host); + spin_unlock_irqrestore(&hosts_lock, flags); } -static void init_hosts(struct hpsb_host_template *tmpl) +struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op, + const char *name) { - int count; - struct list_head *hlh; - struct hpsb_host *host; + struct hpsb_host_driver *drv; - count = tmpl->detect_hosts(tmpl); + drv = kmalloc(sizeof(struct hpsb_host_driver), SLAB_KERNEL); + if (!drv) return NULL; - list_for_each(hlh, &tmpl->hosts) { - host = list_entry(hlh, struct hpsb_host, list); - if (tmpl->initialize_host(host)) { - host->initialized = 1; + INIT_LIST_HEAD(&drv->list); + INIT_LIST_HEAD(&drv->hosts); + drv->number_of_hosts = 0; + drv->name = name; + drv->ops = op; - highlevel_add_host(host); - hpsb_reset_bus(host, LONG_RESET); - } - } + spin_lock(&host_drivers_lock); + list_add_tail(&drv->list, &host_drivers); + spin_unlock(&host_drivers_lock); - tmpl->number_of_hosts = count; - HPSB_INFO("detected %d %s adapter%s", count, tmpl->name, - (count != 1 ? "s" : "")); + return drv; } -static void shutdown_hosts(struct hpsb_host_template *tmpl) +void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv) { - struct list_head *hlh; - struct hpsb_host *host; + spin_lock(&host_drivers_lock); + list_del(&drv->list); + spin_unlock(&host_drivers_lock); - list_for_each(hlh, &tmpl->hosts) { - host = list_entry(hlh, struct hpsb_host, list); - if (host->initialized) { - host->initialized = 0; - abort_requests(host); - - highlevel_remove_host(host); - - tmpl->release_host(host); - while (test_bit(0, &host->timeout_tq.sync)) { - schedule(); - } - } - } - free_all_hosts(tmpl); - tmpl->release_host(NULL); - - tmpl->number_of_hosts = 0; + kfree(drv); } /* - * The following two functions are exported symbols for module usage. + * This function calls the given function for every host currently registered. */ -int hpsb_register_lowlevel(struct hpsb_host_template *tmpl) +void hl_all_hosts(void (*function)(struct hpsb_host*)) { - INIT_LIST_HEAD(&tmpl->hosts); - tmpl->number_of_hosts = 0; - - spin_lock(&templates_lock); - list_add_tail(&tmpl->list, &templates); - spin_unlock(&templates_lock); - - /* PCI cards should be smart and use the PCI detection layer, and - * not this one shot deal. detect_hosts() will be obsoleted soon. */ - if (tmpl->detect_hosts != NULL) { - HPSB_DEBUG("Registered %s driver, initializing now", tmpl->name); - init_hosts(tmpl); - } - - return 0; -} - -void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl) -{ - shutdown_hosts(tmpl); + struct list_head *lh; + struct hpsb_host *host; - if (tmpl->number_of_hosts) - HPSB_PANIC("attempted to remove busy host template " - "of %s at address 0x%p", tmpl->name, tmpl); - else { - spin_lock(&templates_lock); - list_del(&tmpl->list); - spin_unlock(&templates_lock); + spin_lock_irq(&hosts_lock); + list_for_each (lh, &hosts) { + host = list_entry(lh, struct hpsb_host, host_list); + function(host); } + spin_unlock_irq(&hosts_lock); } diff -urN linux-2.5.3-pre2/drivers/ieee1394/hosts.h linux/drivers/ieee1394/hosts.h --- linux-2.5.3-pre2/drivers/ieee1394/hosts.h Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/hosts.h Mon Jan 21 17:23:22 2002 @@ -13,11 +13,15 @@ struct hpsb_packet; struct hpsb_host { -/* private fields (hosts, do not use them) */ - struct list_head list; + struct list_head host_list; + + struct hpsb_host_operations *ops; + void *hostdata; atomic_t generation; + int refcount; + struct list_head pending_packets; spinlock_t pending_pkt_lock; struct tq_struct timeout_tq; @@ -28,16 +32,8 @@ struct semaphore tlabel_count; spinlock_t tlabel_lock; - int reset_retries; - quadlet_t *topology_map; - u8 *speed_map; - struct csr_control csr; - unsigned char iso_listen_count[64]; -/* readonly fields for hosts */ - struct hpsb_host_template *template; - int node_count; /* number of identified nodes on this bus */ int selfid_count; /* total number of SelfIDs received */ @@ -45,9 +41,9 @@ nodeid_t irm_id; /* ID of this bus' isochronous resource manager */ nodeid_t busmgr_id; /* ID of this bus' bus manager */ - unsigned initialized:1; /* initialized and usable */ - unsigned in_bus_reset:1; /* in bus reset / SelfID stage */ - unsigned attempt_root:1; /* attempt to become root during next reset */ + /* this nodes state */ + unsigned in_bus_reset:1; + unsigned is_shutdown:1; /* this nodes' duties on the bus */ unsigned is_root:1; @@ -55,11 +51,15 @@ unsigned is_irm:1; unsigned is_busmgr:1; -/* fields readable and writeable by the hosts */ + int reset_retries; + quadlet_t *topology_map; + u8 *speed_map; + struct csr_control csr; + + struct hpsb_host_driver *driver; + struct list_head driver_list; - void *hostdata; struct pci_dev *pdev; - int embedded_hostdata[0]; }; @@ -88,8 +88,10 @@ * Return void. */ CANCEL_REQUESTS, - /* Decrease module usage count if arg == 0, increase otherwise. Return - * void. */ + /* Decrease host usage count if arg == 0, increase otherwise. Return + * 1 for success, 0 for failure. Increase usage may fail if the driver + * is in the process of shutting itself down. Decrease usage can not + * fail. */ MODIFY_USAGE, /* Start or stop receiving isochronous channel in arg. Return void. @@ -109,37 +111,7 @@ SHORT_RESET }; -struct hpsb_host_template { - struct list_head list; - - struct list_head hosts; - int number_of_hosts; - - /* fields above will be ignored and overwritten after registering */ - - /* This should be the name of the driver (single word) and must not be - * NULL. */ - const char *name; - - /* This function shall detect all available adapters of this type and - * call hpsb_get_host for each one. The initialize_host function will - * be called to actually set up these adapters. The number of detected - * adapters or zero if there are none must be returned. - */ - int (*detect_hosts) (struct hpsb_host_template *template); - - /* After detecting and registering hosts, this function will be called - * for every registered host. It shall set up the host to be fully - * functional for bus operations and return 0 for failure. - */ - int (*initialize_host) (struct hpsb_host *host); - - /* To unload modules, this function is provided. It shall free all - * resources this host is using (if host is not NULL) or free all - * resources globally allocated by the driver (if host is NULL). - */ - void (*release_host) (struct hpsb_host *host); - +struct hpsb_host_operations { /* This function must store a pointer to the configuration ROM into the * location referenced to by pointer and return the size of the ROM. It * may not fail. If any allocation is required, it must be done @@ -175,34 +147,43 @@ quadlet_t data, quadlet_t compare); }; +struct hpsb_host_driver { + struct list_head list; + struct list_head hosts; -/* mid level internal use */ + int number_of_hosts; + const char *name; + + struct hpsb_host_operations *ops; +}; + + +/* core internal use */ void register_builtin_lowlevels(void); /* high level internal use */ struct hpsb_highlevel; -void hl_all_hosts(struct hpsb_highlevel *hl, int init); +void hl_all_hosts(void (*function)(struct hpsb_host*)); -/* - * These functions are for lowlevel (host) driver use. - */ -int hpsb_register_lowlevel(struct hpsb_host_template *tmpl); -void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl); /* - * Get a initialized host structure with hostdata_size bytes allocated in - * embedded_hostdata for free usage. Returns NULL for failure. + * In order to prevent hosts from unloading, use hpsb_ref_host(). This prevents + * the host from going away (e.g. makes module unloading of the driver + * impossible), but still can not guarantee it (e.g. PC-Card being pulled by the + * user). hpsb_ref_host() returns false if host could not be locked. If it is + * successful, host is valid as a pointer until hpsb_unref_host() (not just + * until after remove_host). */ -struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl, - size_t hostdata_size); +int hpsb_ref_host(struct hpsb_host *host); +void hpsb_unref_host(struct hpsb_host *host); -/* - * Increase / decrease host usage counter. Increase function will return true - * only if successful (host still existed). Decrease function expects host to - * exist. - */ -int hpsb_inc_host_usage(struct hpsb_host *host); -void hpsb_dec_host_usage(struct hpsb_host *host); +struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra); +void hpsb_add_host(struct hpsb_host *host); +void hpsb_remove_host(struct hpsb_host *h); + +struct hpsb_host_driver *hpsb_register_lowlevel(struct hpsb_host_operations *op, + const char *name); +void hpsb_unregister_lowlevel(struct hpsb_host_driver *drv); #endif /* _IEEE1394_HOSTS_H */ diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394.h linux/drivers/ieee1394/ieee1394.h --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394.h Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/ieee1394.h Mon Jan 21 17:23:22 2002 @@ -39,10 +39,10 @@ #define ACK_TYPE_ERROR 0xe /* Non-standard "ACK codes" for internal use */ -#define ACKX_NONE -1 -#define ACKX_SEND_ERROR -2 -#define ACKX_ABORTED -3 -#define ACKX_TIMEOUT -4 +#define ACKX_NONE (-1) +#define ACKX_SEND_ERROR (-2) +#define ACKX_ABORTED (-3) +#define ACKX_TIMEOUT (-4) #define SPEED_100 0x0 @@ -116,7 +116,7 @@ /* * Note: these mean to be bit fields of a big endian SelfID as seen on a little - * endian machine. + * endian machine. Without swapping. */ struct selfid { diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.c linux/drivers/ieee1394/ieee1394_core.c --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.c Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/ieee1394_core.c Mon Jan 21 17:23:22 2002 @@ -18,9 +18,11 @@ #include #include #include +#include #include #include #include +#include #include "ieee1394_types.h" #include "ieee1394.h" @@ -135,12 +137,8 @@ int hpsb_reset_bus(struct hpsb_host *host, int type) { - if (!host->initialized) { - return 1; - } - if (!host->in_bus_reset) { - host->template->devctl(host, RESET_BUS, type); + host->ops->devctl(host, RESET_BUS, type); return 0; } else { return 1; @@ -151,8 +149,8 @@ int hpsb_bus_reset(struct hpsb_host *host) { if (host->in_bus_reset) { - HPSB_NOTICE(__FUNCTION__ - " called while bus reset already in progress"); + HPSB_NOTICE("%s called while bus reset already in progress", + __FUNCTION__); return 1; } @@ -171,10 +169,10 @@ * Verify num_of_selfids SelfIDs and return number of nodes. Return zero in * case verification failed. */ -static int check_selfids(struct hpsb_host *host, unsigned int num_of_selfids) +static int check_selfids(struct hpsb_host *host) { int nodeid = -1; - int rest_of_selfids = num_of_selfids; + int rest_of_selfids = host->selfid_count; struct selfid *sid = (struct selfid *)host->topology_map; struct ext_selfid *esid; int esid_seq = 23; @@ -318,15 +316,18 @@ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) { - host->node_id = 0xffc0 | phyid; - host->in_bus_reset = 0; + if (!host->in_bus_reset) + HPSB_NOTICE("SelfID completion called outside of bus reset!"); + + host->node_id = LOCAL_BUS | phyid; host->is_root = isroot; - host->node_count = check_selfids(host, host->selfid_count); + host->node_count = check_selfids(host); if (!host->node_count) { if (host->reset_retries++ < 20) { /* selfid stage did not complete without error */ HPSB_NOTICE("Error in SelfID stage, resetting"); + host->in_bus_reset = 0; hpsb_reset_bus(host, LONG_RESET); return; } else { @@ -346,8 +347,9 @@ } host->reset_retries = 0; - atomic_inc(&host->generation); - if (isroot) host->template->devctl(host, ACT_CYCLE_MASTER, 1); + if (isroot) host->ops->devctl(host, ACT_CYCLE_MASTER, 1); + atomic_inc(&host->generation); + host->in_bus_reset = 0; highlevel_host_reset(host); } @@ -402,7 +404,7 @@ { struct hpsb_host *host = packet->host; - if (!host->initialized || host->in_bus_reset + if (host->is_shutdown || host->in_bus_reset || (packet->generation != get_hpsb_generation(host))) { return 0; } @@ -431,7 +433,7 @@ } #endif - return host->template->transmit_packet(host, packet); + return host->ops->transmit_packet(host, packet); } static void send_packet_nocare(struct hpsb_packet *packet) @@ -727,7 +729,7 @@ struct list_head *lh; LIST_HEAD(llist); - host->template->devctl(host, CANCEL_REQUESTS, 0); + host->ops->devctl(host, CANCEL_REQUESTS, 0); spin_lock_irqsave(&host->pending_pkt_lock, flags); list_splice(&host->pending_packets, &llist); @@ -786,10 +788,228 @@ } +/* + * character device dispatching (see ieee1394_core.h) + * Dan Maas + */ + +static struct { + struct file_operations *file_ops; + struct module *module; +} ieee1394_chardevs[16]; + +static rwlock_t ieee1394_chardevs_lock = RW_LOCK_UNLOCKED; + +static int ieee1394_dispatch_open(struct inode *inode, struct file *file); + +static struct file_operations ieee1394_chardev_ops = { + OWNER_THIS_MODULE + open: ieee1394_dispatch_open, +}; + +devfs_handle_t ieee1394_devfs_handle; + + +/* claim a block of minor numbers */ +int ieee1394_register_chardev(int blocknum, + struct module *module, + struct file_operations *file_ops) +{ + int retval; + + if( (blocknum < 0) || (blocknum > 15) ) + return -EINVAL; + + write_lock(&ieee1394_chardevs_lock); + + if(ieee1394_chardevs[blocknum].file_ops == NULL) { + /* grab the minor block */ + ieee1394_chardevs[blocknum].file_ops = file_ops; + ieee1394_chardevs[blocknum].module = module; + + retval = 0; + + V22_COMPAT_MOD_INC_USE_COUNT; + } else { + /* block already taken */ + retval = -EBUSY; + } + + write_unlock(&ieee1394_chardevs_lock); + + return retval; +} + +/* release a block of minor numbers */ +void ieee1394_unregister_chardev(int blocknum) +{ + if( (blocknum < 0) || (blocknum > 15) ) + return; + + write_lock(&ieee1394_chardevs_lock); + + if(ieee1394_chardevs[blocknum].file_ops) { + ieee1394_chardevs[blocknum].file_ops = NULL; + ieee1394_chardevs[blocknum].module = NULL; + V22_COMPAT_MOD_DEC_USE_COUNT; + } + + write_unlock(&ieee1394_chardevs_lock); +} + +/* the point of entry for open() on any ieee1394 character device */ +static int ieee1394_dispatch_open(struct inode *inode, struct file *file) +{ + struct file_operations *file_ops; + struct module *module; + int blocknum; + int retval = -ENODEV; + + /* + Maintaining correct module reference counts is tricky here! + + For Linux v2.2: + + The task-specific driver is expected to maintain its own + reference count via V22_COMPAT_MOD_[INC,DEC]_USE_COUNT. + We don't need to do anything special. + + For Linux v2.4 and later: + + The key thing to remember is that the VFS increments the + reference count of ieee1394 before it calls + ieee1394_dispatch_open(). + + If the open() succeeds, then we need to transfer this extra + reference to the task-specific driver module (e.g. raw1394). + The VFS will deref the driver module automatically when the + file is later released. + + If the open() fails, then the VFS will drop the + reference count of whatever module file->f_op->owner points + to, immediately after this function returns. + + The comments below refer to the 2.4 case, since the 2.2 + case is trivial. + + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + /* 2.2 */ +#define INCREF(mod) do {} while (0) +#define DECREF(mod) do {} while (0) +#else + /* 2.4 */ +#define INCREF(mod_) do { struct module *mod = (struct module*) mod_; \ + if(mod != NULL) __MOD_INC_USE_COUNT(mod); } while(0) +#define DECREF(mod_) do { struct module *mod = (struct module*) mod_; \ + if(mod != NULL) __MOD_DEC_USE_COUNT(mod); } while(0) +#endif + + /* shift away lower four bits of the minor + to get the index of the ieee1394_driver + we want */ + + blocknum = (minor(inode->i_rdev) >> 4) & 0xF; + + /* printk("ieee1394_dispatch_open(%d)", blocknum); */ + + /* lock the whole kernel here, to prevent a driver from + being unloaded between the file_ops lookup and the open */ + + lock_kernel(); + + read_lock(&ieee1394_chardevs_lock); + file_ops = ieee1394_chardevs[blocknum].file_ops; + module = ieee1394_chardevs[blocknum].module; + read_unlock(&ieee1394_chardevs_lock); + + if(file_ops == NULL) { + goto out_fail; + } + + /* redirect all subsequent requests to the driver's + own file_operations */ + file->f_op = file_ops; + + /* bump the reference count of the driver that + will receive the open() */ + INCREF(module); + + /* at this point BOTH ieee1394 and the task-specific driver have + an extra reference */ + + /* follow through with the open() */ + retval = file_ops->open(inode, file); + + if(retval) { + + /* if the open() failed, then we need to drop the + extra reference we gave to the task-specific + driver */ + + DECREF(module); + goto out_fail; + + } else { + + /* if the open() succeeded, then ieee1394 will be left + with an extra module reference, so we discard it here.*/ + + DECREF(THIS_MODULE); + + /* the task-specific driver still has the extra + reference we gave it. This extra reference prevents + the module from unloading while the file is open, + and will be dropped by the VFS when the file is + released. */ + + unlock_kernel(); + return 0; + } + +out_fail: + /* point the file's f_ops back to ieee1394. The VFS will then + decrement ieee1394's reference count immediately after this + function returns. */ + + file->f_op = &ieee1394_chardev_ops; + unlock_kernel(); + return retval; + +#undef INCREF +#undef DECREF + +} + +struct proc_dir_entry *ieee1394_procfs_entry; + static int __init ieee1394_init(void) { hpsb_packet_cache = kmem_cache_create("hpsb_packet", sizeof(struct hpsb_packet), 0, 0, NULL, NULL); + + ieee1394_devfs_handle = devfs_mk_dir(NULL, "ieee1394", NULL); + + if (register_chrdev(IEEE1394_MAJOR, "ieee1394", &ieee1394_chardev_ops)) { + HPSB_ERR("unable to register character device major %d!\n", IEEE1394_MAJOR); + devfs_unregister(ieee1394_devfs_handle); + return -ENODEV; + } + +#ifdef CONFIG_PROC_FS + /* Must be done before we start everything else, since the drivers + * may use it. */ + ieee1394_procfs_entry = proc_mkdir( "ieee1394", proc_bus); + if (ieee1394_procfs_entry == NULL) { + HPSB_ERR("unable to create /proc/bus/ieee1394\n"); + unregister_chrdev(IEEE1394_MAJOR, "ieee1394"); + devfs_unregister(ieee1394_devfs_handle); + return -ENOMEM; + } + ieee1394_procfs_entry->owner = THIS_MODULE; +#endif + init_hpsb_highlevel(); init_csr(); if (!disable_nodemgr) @@ -807,6 +1027,13 @@ cleanup_csr(); kmem_cache_destroy(hpsb_packet_cache); + + unregister_chrdev(IEEE1394_MAJOR, "ieee1394"); + + /* it's ok to pass a NULL devfs_handle to devfs_unregister */ + devfs_unregister(ieee1394_devfs_handle); + + remove_proc_entry("ieee1394", proc_bus); } module_init(ieee1394_init); @@ -815,9 +1042,11 @@ /* Exported symbols */ EXPORT_SYMBOL(hpsb_register_lowlevel); EXPORT_SYMBOL(hpsb_unregister_lowlevel); -EXPORT_SYMBOL(hpsb_get_host); -EXPORT_SYMBOL(hpsb_inc_host_usage); -EXPORT_SYMBOL(hpsb_dec_host_usage); +EXPORT_SYMBOL(hpsb_alloc_host); +EXPORT_SYMBOL(hpsb_add_host); +EXPORT_SYMBOL(hpsb_remove_host); +EXPORT_SYMBOL(hpsb_ref_host); +EXPORT_SYMBOL(hpsb_unref_host); EXPORT_SYMBOL(hpsb_speedto_str); EXPORT_SYMBOL(alloc_hpsb_packet); @@ -867,7 +1096,6 @@ EXPORT_SYMBOL(highlevel_add_host); EXPORT_SYMBOL(highlevel_remove_host); EXPORT_SYMBOL(highlevel_host_reset); -EXPORT_SYMBOL(highlevel_add_one_host); EXPORT_SYMBOL(hpsb_guid_get_entry); EXPORT_SYMBOL(hpsb_nodeid_get_entry); @@ -876,3 +1104,9 @@ EXPORT_SYMBOL(hpsb_register_protocol); EXPORT_SYMBOL(hpsb_unregister_protocol); EXPORT_SYMBOL(hpsb_release_unit_directory); + +EXPORT_SYMBOL(ieee1394_register_chardev); +EXPORT_SYMBOL(ieee1394_unregister_chardev); +EXPORT_SYMBOL(ieee1394_devfs_handle); + +EXPORT_SYMBOL(ieee1394_procfs_entry); diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.h linux/drivers/ieee1394/ieee1394_core.h --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_core.h Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/ieee1394_core.h Mon Jan 21 17:23:22 2002 @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include "hosts.h" @@ -96,7 +98,6 @@ return atomic_read(&host->generation); } - /* * Queue packet for transmitting, return 0 for failure. */ @@ -151,5 +152,66 @@ */ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, int write_acked); + + +/* + * CHARACTER DEVICE DISPATCHING + * + * All ieee1394 character device drivers share the same major number + * (major 171). The 256 minor numbers are allocated to the various + * task-specific interfaces (raw1394, video1394, dv1394, etc) in + * blocks of 16. + * + * The core ieee1394.o modules handles the initial open() for all + * character devices on major 171; it then dispatches to the + * appropriate task-specific driver. + * + * Minor device number block allocations: + * + * Block 0 ( 0- 15) raw1394 + * Block 1 ( 16- 31) video1394 + * Block 2 ( 32- 47) dv1394 + * + * Blocks 3-14 free for future allocation + * + * Block 15 (240-255) reserved for drivers under development, etc. + */ + +#define IEEE1394_MAJOR 171 + +#define IEEE1394_MINOR_BLOCK_RAW1394 0 +#define IEEE1394_MINOR_BLOCK_VIDEO1394 1 +#define IEEE1394_MINOR_BLOCK_DV1394 2 +#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15 + +/* return the index (within a minor number block) of a file */ +static inline unsigned char ieee1394_file_to_instance(struct file *file) +{ + unsigned char minor = minor(file->f_dentry->d_inode->i_rdev); + + /* return lower 4 bits */ + return minor & 0xF; +} + +/* + * Task-specific drivers should call ieee1394_register_chardev() to + * request a block of 16 minor numbers. + * + * Returns 0 if the request was successful, -EBUSY if the block was + * already taken. + */ + +int ieee1394_register_chardev(int blocknum, /* 0-15 */ + struct module *module, /* THIS_MODULE */ + struct file_operations *file_ops); + +/* release a block of minor numbers */ +void ieee1394_unregister_chardev(int blocknum); + +/* the devfs handle for /dev/ieee1394; NULL if devfs is not in use */ +extern devfs_handle_t ieee1394_devfs_handle; + +/* the proc_fs entry for /proc/ieee1394 */ +extern struct proc_dir_entry *ieee1394_procfs_entry; #endif /* _IEEE1394_CORE_H */ diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_syms.c linux/drivers/ieee1394/ieee1394_syms.c --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_syms.c Sun Nov 11 10:09:32 2001 +++ linux/drivers/ieee1394/ieee1394_syms.c Wed Dec 31 16:00:00 1969 @@ -1,88 +0,0 @@ -/* - * IEEE 1394 for Linux - * - * Exported symbols for module usage. - * - * Copyright (C) 1999 Andreas E. Bombe - * - * This code is licensed under the GPL. See the file COPYING in the root - * directory of the kernel sources for details. - */ - -#include -#include -#include -#include - -#include "ieee1394_types.h" -#include "hosts.h" -#include "ieee1394_core.h" -#include "ieee1394_transactions.h" -#include "ieee1394_hotplug.h" -#include "highlevel.h" -#include "nodemgr.h" - -EXPORT_SYMBOL(hpsb_register_lowlevel); -EXPORT_SYMBOL(hpsb_unregister_lowlevel); -EXPORT_SYMBOL(hpsb_get_host); -EXPORT_SYMBOL(hpsb_inc_host_usage); -EXPORT_SYMBOL(hpsb_dec_host_usage); - -EXPORT_SYMBOL(alloc_hpsb_packet); -EXPORT_SYMBOL(free_hpsb_packet); -EXPORT_SYMBOL(hpsb_send_packet); -EXPORT_SYMBOL(hpsb_reset_bus); -EXPORT_SYMBOL(hpsb_bus_reset); -EXPORT_SYMBOL(hpsb_selfid_received); -EXPORT_SYMBOL(hpsb_selfid_complete); -EXPORT_SYMBOL(hpsb_packet_sent); -EXPORT_SYMBOL(hpsb_packet_received); - -EXPORT_SYMBOL(get_tlabel); -EXPORT_SYMBOL(free_tlabel); -EXPORT_SYMBOL(fill_async_readquad); -EXPORT_SYMBOL(fill_async_readquad_resp); -EXPORT_SYMBOL(fill_async_readblock); -EXPORT_SYMBOL(fill_async_readblock_resp); -EXPORT_SYMBOL(fill_async_writequad); -EXPORT_SYMBOL(fill_async_writeblock); -EXPORT_SYMBOL(fill_async_write_resp); -EXPORT_SYMBOL(fill_async_lock); -EXPORT_SYMBOL(fill_async_lock_resp); -EXPORT_SYMBOL(fill_iso_packet); -EXPORT_SYMBOL(fill_phy_packet); -EXPORT_SYMBOL(hpsb_make_readqpacket); -EXPORT_SYMBOL(hpsb_make_readbpacket); -EXPORT_SYMBOL(hpsb_make_writeqpacket); -EXPORT_SYMBOL(hpsb_make_writebpacket); -EXPORT_SYMBOL(hpsb_make_lockpacket); -EXPORT_SYMBOL(hpsb_make_phypacket); -EXPORT_SYMBOL(hpsb_packet_success); -EXPORT_SYMBOL(hpsb_make_packet); -EXPORT_SYMBOL(hpsb_read); -EXPORT_SYMBOL(hpsb_write); -EXPORT_SYMBOL(hpsb_lock); - -EXPORT_SYMBOL(hpsb_register_highlevel); -EXPORT_SYMBOL(hpsb_unregister_highlevel); -EXPORT_SYMBOL(hpsb_register_addrspace); -EXPORT_SYMBOL(hpsb_listen_channel); -EXPORT_SYMBOL(hpsb_unlisten_channel); -EXPORT_SYMBOL(highlevel_read); -EXPORT_SYMBOL(highlevel_write); -EXPORT_SYMBOL(highlevel_lock); -EXPORT_SYMBOL(highlevel_lock64); -EXPORT_SYMBOL(highlevel_add_host); -EXPORT_SYMBOL(highlevel_remove_host); -EXPORT_SYMBOL(highlevel_host_reset); -EXPORT_SYMBOL(highlevel_add_one_host); - -EXPORT_SYMBOL(hpsb_guid_get_entry); -EXPORT_SYMBOL(hpsb_nodeid_get_entry); -EXPORT_SYMBOL(hpsb_get_host_by_ne); -EXPORT_SYMBOL(hpsb_guid_fill_packet); -EXPORT_SYMBOL(hpsb_register_protocol); -EXPORT_SYMBOL(hpsb_unregister_protocol); -EXPORT_SYMBOL(hpsb_release_unit_directory); - -MODULE_LICENSE("GPL"); diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_transactions.c linux/drivers/ieee1394/ieee1394_transactions.c --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_transactions.c Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/ieee1394_transactions.c Mon Jan 21 17:23:22 2002 @@ -246,7 +246,7 @@ packet->node_id); return -EAGAIN; } - HPSB_PANIC("reached unreachable code 1 in " __FUNCTION__); + HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__); case ACK_BUSY_X: case ACK_BUSY_A: @@ -290,7 +290,7 @@ return -EAGAIN; } - HPSB_PANIC("reached unreachable code 2 in " __FUNCTION__); + HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__); } diff -urN linux-2.5.3-pre2/drivers/ieee1394/ieee1394_types.h linux/drivers/ieee1394/ieee1394_types.h --- linux-2.5.3-pre2/drivers/ieee1394/ieee1394_types.h Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/ieee1394_types.h Mon Jan 21 17:23:22 2002 @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -18,6 +19,16 @@ #define INIT_TQ_LINK(tq) INIT_LIST_HEAD(&(tq).list) #define INIT_TQ_HEAD(tq) INIT_LIST_HEAD(&(tq)) +#endif + +/* The great kdev_t changeover in 2.5.x */ +#include +#ifndef minor +#define minor(dev) MINOR(dev) +#endif + +#ifndef __devexit_p +#define __devexit_p(x) x #endif /* This showed up around this time */ diff -urN linux-2.5.3-pre2/drivers/ieee1394/nodemgr.c linux/drivers/ieee1394/nodemgr.c --- linux-2.5.3-pre2/drivers/ieee1394/nodemgr.c Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/nodemgr.c Mon Jan 21 17:23:22 2002 @@ -16,6 +16,10 @@ #include #include #include +#include +#ifdef CONFIG_PROC_FS +#include +#endif #include "ieee1394_types.h" #include "ieee1394.h" @@ -59,13 +63,200 @@ struct host_info { struct hpsb_host *host; - struct tq_struct task; struct list_head list; + struct completion started; + struct completion exited; + wait_queue_head_t wait; + int pid; }; +#ifdef CONFIG_PROC_FS + +static int raw1394_read_proc(char *buffer, char **start, off_t offset, + int size, int *eof, void *data ) +{ + struct list_head *lh; + struct node_entry *ne; + int disp_size = 0; + char display_str[1024]; + +#define PUTF(fmt, args...) disp_size += sprintf(display_str, fmt, ## args); strcat(buffer,display_str) + buffer[0] = '\0'; + list_for_each(lh, &node_list) { + ne = list_entry(lh, struct node_entry, list); + if (!ne) + continue; + PUTF("Node[" NODE_BUS_FMT "] GUID[%016Lx]:\n", + NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid); + if (ne->host != NULL && ne->host->node_id == ne->nodeid) { + PUTF("\tNodes connected : %d\n", ne->host->node_count); + PUTF("\tSelfIDs received: %d\n", ne->host->selfid_count); + PUTF("\tOwn ID : 0x%08x\n",ne->host->node_id); + PUTF("\tIrm ID : 0x%08x\n",ne->host->irm_id); + PUTF("\tBusMgr ID : 0x%08x\n",ne->host->busmgr_id); + PUTF("\tBR IR IC II IB\n"); + PUTF("\t%02d %02d %02d %02d %02d\n", + ne->host->in_bus_reset, ne->host->is_root, + ne->host->is_cycmst, ne->host->is_irm, + ne->host->is_busmgr); + } + PUTF("\tVendor ID: %s [0x%06x]\n", + ne->vendor_name ?: "Unknown", ne->vendor_id); + PUTF("\tCapabilities: 0x%06x\n", ne->capabilities); + PUTF("\tirmc=%d cmc=%d isc=%d bmc=%d pmc=%d cyc_clk_acc=%d max_rec=%d gen=%d lspd=%d\n", + ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc, + ne->busopt.pmc, ne->busopt.cyc_clk_acc, ne->busopt.max_rec, + ne->busopt.generation, ne->busopt.lnkspd); + } +#undef PUTF + return disp_size; +} +#endif /* CONFIG_PROC_FS */ + static void nodemgr_process_config_rom(struct node_entry *ne, quadlet_t busoptions); +static int nodemgr_read_quadlet(struct hpsb_host *host, + nodeid_t nodeid, octlet_t address, + quadlet_t *quad) +{ + int i; + int ret = 0; + + for (i = 0; i < 3; i++) { + ret = hpsb_read(host, nodeid, address, quad, 4); + if (ret != -EAGAIN) + break; + } + *quad = be32_to_cpu(*quad); + + return ret; +} + +static int nodemgr_size_text_leaf(struct hpsb_host *host, + nodeid_t nodeid, + octlet_t address) +{ + quadlet_t quad; + int size = 0; + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return -1; + if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) { + /* This is the offset. */ + address += 4 * CONFIG_ROM_VALUE(quad); + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return -1; + /* Now we got the size of the text descriptor leaf. */ + size = CONFIG_ROM_LEAF_LENGTH(quad); + } + return size; +} + +static int nodemgr_read_text_leaf(struct hpsb_host *host, + nodeid_t nodeid, + octlet_t address, + quadlet_t *quadp) +{ + quadlet_t quad; + int i, size, ret; + + if (nodemgr_read_quadlet(host, nodeid, address, &quad) + && CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF) + return -1; + + /* This is the offset. */ + address += 4 * CONFIG_ROM_VALUE(quad); + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return -1; + + /* Now we got the size of the text descriptor leaf. */ + size = CONFIG_ROM_LEAF_LENGTH(quad) - 2; + if (size <= 0) + return -1; + + address += 4; + for (i = 0; i < 2; i++, address += 4, quadp++) { + if (nodemgr_read_quadlet(host, nodeid, address, quadp)) + return -1; + } + + /* Now read the text string. */ + ret = -ENXIO; + for (; size > 0; size--, address += 4, quadp++) { + for (i = 0; i < 3; i++) { + ret = hpsb_read(host, nodeid, address, quadp, 4); + if (ret != -EAGAIN) + break; + } + if (ret) + break; + } + + return ret; +} + +static struct node_entry *nodemgr_scan_root_directory + (struct hpsb_host *host, nodeid_t nodeid) +{ + octlet_t address; + quadlet_t quad; + int length; + int code, size, total_size; + struct node_entry *ne; + + address = CSR_REGISTER_BASE + CSR_CONFIG_ROM; + + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return NULL; + address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4; + + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return NULL; + length = CONFIG_ROM_ROOT_LENGTH(quad); + address += 4; + + size = 0; + total_size = sizeof(struct node_entry); + for (; length > 0; length--, address += 4) { + if (nodemgr_read_quadlet(host, nodeid, address, &quad)) + return NULL; + code = CONFIG_ROM_KEY(quad); + + if (code == CONFIG_ROM_VENDOR_ID) { + /* Check if there is a text descriptor leaf + immediately after this. */ + length--; + if (length <= 0) + break; + address += 4; + size = nodemgr_size_text_leaf(host, nodeid, + address); + switch (size) { + case -1: + return NULL; + break; + case 0: + break; + default: + total_size += (size + 1) * sizeof (quadlet_t); + break; + } + break; + } + } + ne = kmalloc(total_size, SLAB_ATOMIC); + if (ne != NULL) { + if (size != 0) { + ne->vendor_name + = (const char *) &(ne->quadlets[2]); + ne->quadlets[size] = 0; + } + else { + ne->vendor_name = NULL; + } + } + return ne; +} static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions, struct hpsb_host *host, nodeid_t nodeid) @@ -73,7 +264,7 @@ struct node_entry *ne; unsigned long flags; - ne = kmalloc(sizeof(struct node_entry), SLAB_ATOMIC); + ne = nodemgr_scan_root_directory (host, nodeid); if (!ne) return NULL; INIT_LIST_HEAD(&ne->list); @@ -89,9 +280,10 @@ nodemgr_process_config_rom (ne, busoptions); - HPSB_DEBUG("%s added: node " NODE_BUS_FMT ", GUID %016Lx", + HPSB_DEBUG("%s added: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]", (host->node_id == nodeid) ? "Local host" : "Device", - NODE_BUS_ARGS(nodeid), (unsigned long long)guid); + NODE_BUS_ARGS(nodeid), (unsigned long long)guid, + ne->vendor_name ?: "Unknown"); return ne; } @@ -122,30 +314,107 @@ return NULL; } -int nodemgr_read_quadlet(struct node_entry *ne, - octlet_t address, quadlet_t *quad) +static struct unit_directory *nodemgr_scan_unit_directory + (struct node_entry *ne, octlet_t address) { - int i; - int ret = 0; + struct unit_directory *ud; + quadlet_t quad; + u8 flags, todo; + int length, size, total_size, count; + int vendor_name_size, model_name_size; - for (i = 0; i < 3; i++) { - ret = hpsb_read(ne->host, ne->nodeid, address, quad, 4); - if (ret != -EAGAIN) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) + return NULL; + length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ; + address += 4; + + size = 0; + total_size = sizeof (struct unit_directory); + flags = 0; + count = 0; + vendor_name_size = 0; + model_name_size = 0; + for (; length > 0; length--, address += 4) { + int code; + quadlet_t value; + +retry: + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) + return NULL; + code = CONFIG_ROM_KEY(quad); + value = CONFIG_ROM_VALUE(quad); + + todo = 0; + switch (code) { + case CONFIG_ROM_VENDOR_ID: + todo = UNIT_DIRECTORY_VENDOR_TEXT; break; - } - *quad = be32_to_cpu(*quad); - return ret; -} + case CONFIG_ROM_MODEL_ID: + todo = UNIT_DIRECTORY_MODEL_TEXT; + break; + + case CONFIG_ROM_SPECIFIER_ID: + case CONFIG_ROM_UNIT_SW_VERSION: + break; + + case CONFIG_ROM_DESCRIPTOR_LEAF: + case CONFIG_ROM_DESCRIPTOR_DIRECTORY: + /* TODO: read strings... icons? */ + break; + + default: + /* Which types of quadlets do we want to + store? Only count immediate values and + CSR offsets for now. */ + code &= CONFIG_ROM_KEY_TYPE_MASK; + if ((code & 0x80) == 0) + count++; + break; + } -#define CONFIG_ROM_VENDOR_ID 0x03 -#define CONFIG_ROM_MODEL_ID 0x17 -#define CONFIG_ROM_NODE_CAPABILITES 0x0C -#define CONFIG_ROM_UNIT_DIRECTORY 0xd1 -#define CONFIG_ROM_SPECIFIER_ID 0x12 -#define CONFIG_ROM_VERSION 0x13 -#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81 -#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1 + if (todo) { + /* Check if there is a text descriptor leaf + immediately after this. */ + length--; + if (length <= 0) + break; + address += 4; + size = nodemgr_size_text_leaf(ne->host, + ne->nodeid, + address); + if (todo | UNIT_DIRECTORY_VENDOR_TEXT) + vendor_name_size = size; + else + model_name_size = size; + switch (size) { + case -1: + return NULL; + break; + case 0: + goto retry; + break; + default: + flags |= todo; + total_size += (size + 1) * sizeof (quadlet_t); + break; + } + } + } + total_size += count * sizeof (quadlet_t); + ud = kmalloc (total_size, GFP_KERNEL); + if (ud != NULL) { + memset (ud, 0, sizeof *ud); + ud->flags = flags; + ud->count = count; + ud->vendor_name_size = vendor_name_size; + ud->model_name_size = model_name_size; + /* If there is no vendor name in the unit directory, + use the one in the root directory. */ + ud->vendor_name = ne->vendor_name; + } + return ud; +} /* This implementation currently only scans the config rom and its * immediate unit directories looking for software_id and @@ -156,41 +425,75 @@ octlet_t address) { struct unit_directory *ud; - octlet_t a; quadlet_t quad; - int length, i; + quadlet_t *infop; + int length; - if (!(ud = kmalloc (sizeof *ud, GFP_KERNEL))) + if (!(ud = nodemgr_scan_unit_directory(ne, address))) goto unit_directory_error; - memset (ud, 0, sizeof *ud); ud->ne = ne; ud->address = address; - ud->arb_count = 0; - if (nodemgr_read_quadlet(ne, address, &quad)) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) goto unit_directory_error; - length = quad >> 16; - a = address + 4; + length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ; + address += 4; - for (i = 0; i < length; i++, a += 4) { + infop = (quadlet_t *) ud->quadlets; + for (; length > 0; length--, address += 4, infop++) { int code; quadlet_t value; + quadlet_t *quadp; - if (nodemgr_read_quadlet(ne, a, &quad)) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) goto unit_directory_error; - code = quad >> 24; - value = quad & 0xffffff; + code = CONFIG_ROM_KEY(quad) ; + value = CONFIG_ROM_VALUE(quad); switch (code) { case CONFIG_ROM_VENDOR_ID: ud->vendor_id = value; ud->flags |= UNIT_DIRECTORY_VENDOR_ID; + if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) { + length--; + address += 4; + quadp = &(ud->quadlets[ud->count]); + if (nodemgr_read_text_leaf(ne->host, + ne->nodeid, + address, + quadp) == 0 + && quadp[0] == 0 + && quadp[1] == 0) { + /* We only support minimal + ASCII and English. */ + quadp[ud->vendor_name_size] = 0; + ud->vendor_name + = (const char *) &(quadp[2]); + } + } break; case CONFIG_ROM_MODEL_ID: ud->model_id = value; ud->flags |= UNIT_DIRECTORY_MODEL_ID; + if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) { + length--; + address += 4; + quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]); + if (nodemgr_read_text_leaf(ne->host, + ne->nodeid, + address, + quadp) == 0 + && quadp[0] == 0 + && quadp[1] == 0) { + /* We only support minimal + ASCII and English. */ + quadp[ud->model_name_size] = 0; + ud->model_name + = (const char *) &(quadp[2]); + } + } break; case CONFIG_ROM_SPECIFIER_ID: @@ -198,7 +501,7 @@ ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID; break; - case CONFIG_ROM_VERSION: + case CONFIG_ROM_UNIT_SW_VERSION: ud->version = value; ud->flags |= UNIT_DIRECTORY_VERSION; break; @@ -209,12 +512,13 @@ break; default: - if (ud->arb_count < 16) { - /* Place them in the arbitrary pairs */ - ud->arb_keys[ud->arb_count] = code; - ud->arb_values[ud->arb_count] = value; - ud->arb_count++; - } + /* Which types of quadlets do we want to + store? Only count immediate values and + CSR offsets for now. */ + code &= CONFIG_ROM_KEY_TYPE_MASK; + if ((code & 0x80) == 0) + *infop = quad; + break; } } @@ -233,53 +537,73 @@ #ifdef CONFIG_IEEE1394_VERBOSEDEBUG struct list_head *l; - HPSB_DEBUG("vendor_id=0x%06x, capabilities=0x%06x", - ne->vendor_id, ne->capabilities); + HPSB_DEBUG("vendor_id=0x%06x [%s], capabilities=0x%06x", + ne->vendor_id, ne->vendor_name ?: "Unknown", + ne->capabilities); list_for_each (l, &ne->unit_directories) { struct unit_directory *ud = list_entry (l, struct unit_directory, node_list); HPSB_DEBUG("unit directory:"); if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) - HPSB_DEBUG(" vendor_id=0x%06x ", ud->vendor_id); + HPSB_DEBUG(" vendor_id=0x%06x [%s]", + ud->vendor_id, + ud->vendor_name ?: "Unknown"); if (ud->flags & UNIT_DIRECTORY_MODEL_ID) - HPSB_DEBUG(" model_id=0x%06x ", ud->model_id); + HPSB_DEBUG(" model_id=0x%06x [%s]", + ud->model_id, + ud->model_name ?: "Unknown"); if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID) HPSB_DEBUG(" sw_specifier_id=0x%06x ", ud->specifier_id); if (ud->flags & UNIT_DIRECTORY_VERSION) HPSB_DEBUG(" sw_version=0x%06x ", ud->version); } -#else - return; #endif + return; } static void nodemgr_process_root_directory(struct node_entry *ne) { octlet_t address; quadlet_t quad; - int length, i; + int length; address = CSR_REGISTER_BASE + CSR_CONFIG_ROM; - if (nodemgr_read_quadlet(ne, address, &quad)) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) return; - address += 4 + (quad >> 24) * 4; + address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4; - if (nodemgr_read_quadlet(ne, address, &quad)) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) return; - length = quad >> 16; + length = CONFIG_ROM_ROOT_LENGTH(quad); address += 4; - for (i = 0; i < length; i++, address += 4) { + for (; length > 0; length--, address += 4) { int code, value; - if (nodemgr_read_quadlet(ne, address, &quad)) + if (nodemgr_read_quadlet(ne->host, ne->nodeid, address, &quad)) return; - code = quad >> 24; - value = quad & 0xffffff; + code = CONFIG_ROM_KEY(quad); + value = CONFIG_ROM_VALUE(quad); switch (code) { case CONFIG_ROM_VENDOR_ID: ne->vendor_id = value; + /* Now check if there is a vendor name text + string. */ + if (ne->vendor_name != NULL) { + length--; + address += 4; + if (nodemgr_read_text_leaf(ne->host, + ne->nodeid, + address, + ne->quadlets) + != 0 + || ne->quadlets [0] != 0 + || ne->quadlets [1] != 0) + /* We only support minimal + ASCII and English. */ + ne->vendor_name = NULL; + } break; case CONFIG_ROM_NODE_CAPABILITES: @@ -359,7 +683,7 @@ kfree(buf); kfree(envp); if (value != 0) - HPSB_DEBUG("NodeMgr: hotplug policy returned 0x%x", value); + HPSB_DEBUG("NodeMgr: hotplug policy returned %d", value); } #else @@ -369,9 +693,8 @@ { #ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled"); -#else - return; #endif + return; } #endif /* CONFIG_HOTPLUG */ @@ -582,13 +905,13 @@ struct hpsb_host *host, nodeid_t nodeid) { struct list_head *lh; + struct unit_directory *ud; - if (ne->nodeid != nodeid) + if (ne->nodeid != nodeid) { HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT, NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid)); - - ne->host = host; - ne->nodeid = nodeid; + ne->nodeid = nodeid; + } if (ne->busopt.generation != ((busoptions >> 4) & 0xf)) nodemgr_process_config_rom (ne, busoptions); @@ -597,8 +920,6 @@ atomic_set(&ne->generation, get_hpsb_generation(ne->host)); list_for_each (lh, &ne->unit_directories) { - struct unit_directory *ud; - ud = list_entry (lh, struct unit_directory, node_list); if (ud->driver != NULL && ud->driver->update != NULL) ud->driver->update(ud); @@ -613,6 +934,7 @@ int header_count; unsigned header_size; quadlet_t quad; + int ret; retry_configrom: @@ -636,9 +958,10 @@ * to work though, and we are forced to doing quadlet * sized reads. */ - if (hpsb_read(host, nodeid, base, &quad, 4)) { - HPSB_ERR("ConfigROM quadlet transaction error for node " NODE_BUS_FMT, - NODE_BUS_ARGS(nodeid)); + ret = hpsb_read(host, nodeid, base, &quad, 4); + if (ret) { + HPSB_ERR("ConfigROM quadlet transaction error (%d) for node " NODE_BUS_FMT, + ret, NODE_BUS_ARGS(nodeid)); goto retry_configrom; } buffer[header_count++] = be32_to_cpu(quad); @@ -652,9 +975,10 @@ } while (header_count <= header_size && header_count < buffer_length) { - if (hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4)) { - HPSB_ERR("ConfigROM quadlet transaction error for " NODE_BUS_FMT, - NODE_BUS_ARGS(nodeid)); + ret = hpsb_read(host, nodeid, base + (header_count<<2), &quad, 4); + if (ret) { + HPSB_ERR("ConfigROM quadlet transaction error (%d) for " NODE_BUS_FMT, + ret, NODE_BUS_ARGS(nodeid)); goto retry_configrom; } buffer[header_count++] = be32_to_cpu(quad); @@ -667,8 +991,10 @@ { unsigned long flags; - HPSB_DEBUG("Device removed: node " NODE_BUS_FMT ", GUID %016Lx", - NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid); + HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]", + (ne->host->node_id == ne->nodeid) ? "Local host" : "Device", + NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid, + ne->vendor_name ?: "Unknown"); write_lock_irqsave(&unit_directory_lock, flags); nodemgr_free_unit_directories(ne); @@ -681,52 +1007,47 @@ /* This is where we probe the nodes for their information and provided * features. */ -static void nodemgr_node_probe(void *data) +static void nodemgr_node_probe_one(struct hpsb_host *host, nodeid_t nodeid) { - struct hpsb_host *host = (struct hpsb_host *)data; - struct selfid *sid = (struct selfid *)host->topology_map; - struct list_head *lh, *next; struct node_entry *ne; - int nodecount = host->node_count; - nodeid_t nodeid = LOCAL_BUS; quadlet_t buffer[5]; octlet_t guid; - unsigned long flags; /* We need to detect when the ConfigROM's generation has changed, * so we only update the node's info when it needs to be. */ - for (; nodecount; nodecount--, nodeid++, sid++) { - /* Skip extended, and non-active node's */ - while (sid->extended) - sid++; - if (!sid->link_active) - continue; - if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2)) - continue; + if (read_businfo_block (host, nodeid, buffer, sizeof(buffer) >> 2)) + return; - if (buffer[1] != IEEE1394_BUSID_MAGIC) { - /* This isn't a 1394 device */ - HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device", - NODE_BUS_ARGS(nodeid)); - continue; - } + if (buffer[1] != IEEE1394_BUSID_MAGIC) { + /* This isn't a 1394 device */ + HPSB_ERR("Node " NODE_BUS_FMT " isn't an IEEE 1394 device", + NODE_BUS_ARGS(nodeid)); + return; + } - guid = ((u64)buffer[3] << 32) | buffer[4]; - ne = hpsb_guid_get_entry(guid); + guid = ((u64)buffer[3] << 32) | buffer[4]; + ne = hpsb_guid_get_entry(guid); - if (!ne) - nodemgr_create_node(guid, buffer[2], host, nodeid); - else - nodemgr_update_node(ne, buffer[2], host, nodeid); - } + if (!ne) + nodemgr_create_node(guid, buffer[2], host, nodeid); + else + nodemgr_update_node(ne, buffer[2], host, nodeid); + + return; +} + +static void nodemgr_node_probe_cleanup(struct hpsb_host *host) +{ + unsigned long flags; + struct list_head *lh, *next; + struct node_entry *ne; /* Now check to see if we have any nodes that aren't referenced * any longer. */ - write_lock_irqsave(&node_lock, flags); - for (lh = node_list.next; lh != &node_list; lh = next) { + write_lock_irqsave(&node_lock, flags); + list_for_each_safe(lh, next, &node_list) { ne = list_entry(lh, struct node_entry, list); - next = lh->next; /* Only checking this host */ if (ne->host != host) @@ -744,6 +1065,56 @@ return; } +static void nodemgr_node_probe(struct hpsb_host *host) +{ + int nodecount = host->node_count; + struct selfid *sid = (struct selfid *)host->topology_map; + nodeid_t nodeid = LOCAL_BUS; + + /* Scan each node on the bus */ + for (; nodecount; nodecount--, nodeid++, sid++) { + while (sid->extended) + sid++; + if (!sid->link_active) + continue; + + nodemgr_node_probe_one(host, nodeid); + } + + /* Cleanup if needed */ + nodemgr_node_probe_cleanup(host); + + return; +} + +static int nodemgr_host_thread(void *__hi) +{ + struct host_info *hi = (struct host_info *)__hi; + lock_kernel(); + + /* No userlevel access needed */ + daemonize(); + + strcpy(current->comm, "NodeMgr"); + + complete(&hi->started); + + /* Sit and wait for a signal to probe the nodes on the bus. This + * happens when we get a bus reset. */ + do { + interruptible_sleep_on(&hi->wait); + if (!signal_pending(current)) + nodemgr_node_probe(hi->host); + } while (!signal_pending(current)); + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name); +#endif + + unlock_kernel(); + + complete_and_exit(&hi->exited, 0); +} struct node_entry *hpsb_guid_get_entry(u64 guid) { @@ -802,7 +1173,18 @@ * until the first bus reset. */ hi->host = host; INIT_LIST_HEAD(&hi->list); - INIT_TQUEUE(&hi->task, nodemgr_node_probe, host); + init_completion(&hi->started); + init_completion(&hi->exited); + init_waitqueue_head(&hi->wait); + + hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + + if (hi->pid < 0) { + HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s", host->driver->name); + return; + } + + wait_for_completion(&hi->started); spin_lock_irqsave (&host_info_lock, flags); list_add_tail (&hi->list, &host_info_list); @@ -831,7 +1213,11 @@ goto done_reset_host; } - schedule_task(&hi->task); +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG + HPSB_DEBUG ("NodeMgr: Processing host reset for %s", host->driver->name); +#endif + + wake_up(&hi->wait); done_reset_host: spin_unlock_irqrestore (&host_info_lock, flags); @@ -844,40 +1230,42 @@ struct list_head *lh, *next; struct node_entry *ne; unsigned long flags; + struct host_info *hi = NULL; - /* Make sure we have no active scans */ - flush_scheduled_tasks(); + spin_lock_irqsave (&host_info_lock, flags); + list_for_each_safe(lh, next, &host_info_list) { + struct host_info *myhi = list_entry(lh, struct host_info, list); + if (myhi->host == host) { + list_del(&myhi->list); + hi = myhi; + break; + } + } - /* First remove all node entries for this host */ - write_lock_irqsave(&node_lock, flags); + if (!hi) + HPSB_ERR ("NodeMgr: host %s does not exist, cannot remove", + host->driver->name); + spin_unlock_irqrestore (&host_info_lock, flags); - for (lh = node_list.next; lh != &node_list; lh = next) { + /* Even if we fail the host_info part, remove all the node + * entries. */ + write_lock_irqsave(&node_lock, flags); + list_for_each_safe(lh, next, &node_list) { ne = list_entry(lh, struct node_entry, list); - next = lh->next; - - /* Only checking this host */ - if (ne->host != host) - continue; + if (ne->host == host) nodemgr_remove_node(ne); } write_unlock_irqrestore(&node_lock, flags); - spin_lock_irqsave (&host_info_lock, flags); - list_for_each(lh, &host_info_list) { - struct host_info *hi = list_entry(lh, struct host_info, list); - if (hi->host == host) { - list_del(&hi->list); - kfree (hi); - break; + if (hi) { + if (hi->pid >= 0) { + kill_proc(hi->pid, SIGTERM, 1); + wait_for_completion(&hi->exited); } + kfree(hi); } - if (lh == host_info_list.next) - HPSB_ERR ("NodeMgr: could not remove non-existent host"); - - spin_unlock_irqrestore (&host_info_lock, flags); - return; } @@ -889,8 +1277,14 @@ static struct hpsb_highlevel *hl; +#define PROC_ENTRY "devices" + void init_ieee1394_nodemgr(void) { +#ifdef CONFIG_PROC_FS + if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL)) + HPSB_ERR("Can't create devices procfs entry"); +#endif hl = hpsb_register_highlevel("Node manager", &nodemgr_ops); if (!hl) { HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization"); @@ -900,4 +1294,7 @@ void cleanup_ieee1394_nodemgr(void) { hpsb_unregister_highlevel(hl); +#ifdef CONFIG_PROC_FS + remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry); +#endif } diff -urN linux-2.5.3-pre2/drivers/ieee1394/nodemgr.h linux/drivers/ieee1394/nodemgr.h --- linux-2.5.3-pre2/drivers/ieee1394/nodemgr.h Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/nodemgr.h Mon Jan 21 17:23:22 2002 @@ -20,6 +20,44 @@ #ifndef _IEEE1394_NODEMGR_H #define _IEEE1394_NODEMGR_H +#define CONFIG_ROM_BUS_INFO_LENGTH(q) ((q) >> 24) +#define CONFIG_ROM_BUS_CRC_LENGTH(q) (((q) >> 16) & 0xff) +#define CONFIG_ROM_BUS_CRC(q) ((q) & 0xffff) + +#define CONFIG_ROM_ROOT_LENGTH(q) ((q) >> 16) +#define CONFIG_ROM_ROOT_CRC(q) ((q) & 0xffff) + +#define CONFIG_ROM_DIRECTORY_LENGTH(q) ((q) >> 16) +#define CONFIG_ROM_DIRECTORY_CRC(q) ((q) & 0xffff) + +#define CONFIG_ROM_LEAF_LENGTH(q) ((q) >> 16) +#define CONFIG_ROM_LEAF_CRC(q) ((q) & 0xffff) + +#define CONFIG_ROM_DESCRIPTOR_TYPE(q) ((q) >> 24) +#define CONFIG_ROM_DESCRIPTOR_SPECIFIER_ID(q) ((q) & 0xffffff) +#define CONFIG_ROM_DESCRIPTOR_WIDTH(q) ((q) >> 28) +#define CONFIG_ROM_DESCRIPTOR_CHAR_SET(q) (((q) >> 16) & 0xfff) +#define CONFIG_ROM_DESCRIPTOR_LANG(q) ((q) & 0xffff) + +#define CONFIG_ROM_KEY_ID_MASK 0x3f +#define CONFIG_ROM_KEY_TYPE_MASK 0xc0 +#define CONFIG_ROM_KEY_TYPE_IMMEDIATE 0x00 +#define CONFIG_ROM_KEY_TYPE_OFFSET 0x40 +#define CONFIG_ROM_KEY_TYPE_LEAF 0x80 +#define CONFIG_ROM_KEY_TYPE_DIRECTORY 0xc0 + +#define CONFIG_ROM_KEY(q) ((q) >> 24) +#define CONFIG_ROM_VALUE(q) ((q) & 0xffffff) + +#define CONFIG_ROM_VENDOR_ID 0x03 +#define CONFIG_ROM_MODEL_ID 0x17 +#define CONFIG_ROM_NODE_CAPABILITES 0x0C +#define CONFIG_ROM_UNIT_DIRECTORY 0xd1 +#define CONFIG_ROM_SPECIFIER_ID 0x12 +#define CONFIG_ROM_UNIT_SW_VERSION 0x13 +#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81 +#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1 + /* '1' '3' '9' '4' in ASCII */ #define IEEE1394_BUSID_MAGIC 0x31333934 @@ -42,6 +80,8 @@ #define UNIT_DIRECTORY_MODEL_ID 0x02 #define UNIT_DIRECTORY_SPECIFIER_ID 0x04 #define UNIT_DIRECTORY_VERSION 0x08 +#define UNIT_DIRECTORY_VENDOR_TEXT 0x10 +#define UNIT_DIRECTORY_MODEL_TEXT 0x20 /* * A unit directory corresponds to a protocol supported by the @@ -58,17 +98,14 @@ octlet_t address; /* Address of the unit directory on the node */ u8 flags; /* Indicates which entries were read */ quadlet_t vendor_id; - char *vendor_name; + const char *vendor_name; + int vendor_name_size; quadlet_t model_id; - char *model_name; + const char *model_name; + int model_name_size; quadlet_t specifier_id; quadlet_t version; - /* Groupings for arbitrary key/value pairs */ - int arb_count; /* Number of arbitrary key/values */ - char arb_keys[16]; /* Up to 16 keys */ - quadlet_t arb_values[16]; /* Same for values */ - struct hpsb_protocol_driver *driver; void *driver_data; @@ -77,6 +114,9 @@ /* For linking directories belonging to a node */ struct list_head node_list; + + int count; /* Number of quadlets */ + quadlet_t quadlets[0]; }; struct node_entry { @@ -91,6 +131,9 @@ u32 vendor_id; u32 capabilities; struct list_head unit_directories; + + const char *vendor_name; + quadlet_t quadlets[0]; }; static inline int hpsb_node_entry_valid(struct node_entry *ne) diff -urN linux-2.5.3-pre2/drivers/ieee1394/ohci1394.c linux/drivers/ieee1394/ohci1394.c --- linux-2.5.3-pre2/drivers/ieee1394/ohci1394.c Sun Dec 30 10:31:51 2001 +++ linux/drivers/ieee1394/ohci1394.c Mon Jan 21 17:23:22 2002 @@ -77,6 +77,12 @@ * . Config ROM generation */ +/* Issues: + * + * - devctl BUS_RESET should treat arg as reset type + * + */ + #include #include #include @@ -105,16 +111,12 @@ #include #ifdef CONFIG_ALL_PPC -#include +#include +#include #include #include #endif -/* Revert to old bus reset algorithm that works on my Pismo until - * the new one is fixed - */ -#undef BUSRESET_WORKAROUND - #include "ieee1394.h" #include "ieee1394_types.h" #include "hosts.h" @@ -139,10 +141,10 @@ #ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG #define OHCI_DMA_ALLOC(fmt, args...) \ - HPSB_ERR("%s("__FUNCTION__")alloc(%d): "fmt, OHCI1394_DRIVER_NAME, \ + HPSB_ERR("%s(%s)alloc(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \ ++global_outstanding_dmas, ## args) #define OHCI_DMA_FREE(fmt, args...) \ - HPSB_ERR("%s("__FUNCTION__")free(%d): "fmt, OHCI1394_DRIVER_NAME, \ + HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \ --global_outstanding_dmas, ## args) u32 global_outstanding_dmas = 0; #else @@ -158,35 +160,23 @@ #define PRINT(level, card, fmt, args...) \ printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args) -#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10) - -static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = { - { - class: PCI_CLASS_FIREWIRE_OHCI, - class_mask: 0x00ffffff, - vendor: PCI_ANY_ID, - device: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl); - static char version[] __devinitdata = - "v0.51 08/08/01 Ben Collins "; + "$Revision: 1.91 $ Ben Collins "; /* Module Parameters */ MODULE_PARM(attempt_root,"i"); MODULE_PARM_DESC(attempt_root, "Attempt to make the host root."); static int attempt_root = 0; -static unsigned int card_id_counter = 0; - static void dma_trm_tasklet(unsigned long data); -static void remove_card(struct ti_ohci *ohci); static void dma_trm_reset(struct dma_trm_ctx *d); +static void __devexit ohci1394_pci_remove(struct pci_dev *pdev); + +static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci, + quadlet_t isoRecvEvent, + quadlet_t isoXmitEvent); + #ifndef __LITTLE_ENDIAN /* Swap a series of quads inplace. */ static __inline__ void block_swab32(quadlet_t *data, size_t size) { @@ -314,8 +304,8 @@ if ((self_id_count & 0x80000000) || ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) { PRINT(KERN_ERR, ohci->id, - "Error in reception of SelfID packets [0x%08x/0x%08x]", - self_id_count, q0); + "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)", + self_id_count, q0, ohci->self_id_errors); /* Tip by James Goodwin : * We had an error, generate another bus reset in response. */ @@ -328,6 +318,9 @@ } return; } + + /* SelfID Ok, reset error counter. */ + ohci->self_id_errors = 0; size = ((self_id_count & 0x00001FFC) >> 2) - 1; q++; @@ -361,7 +354,7 @@ return; } -static int ohci_soft_reset(struct ti_ohci *ohci) { +static void ohci_soft_reset(struct ti_ohci *ohci) { int i; reg_write(ohci, OHCI1394_HCControlSet, 0x00010000); @@ -372,9 +365,13 @@ mdelay(1); } - DBGMSG (ohci->id, "Soft reset finished"); + /* Now reenable LPS, since that's usually what we want after a + * softreset anyway. Wait 50msec to make sure we have full link + * enabled. */ + reg_write(ohci, OHCI1394_HCControlSet, 0x00080000); + mdelay(50); - return 0; + DBGMSG (ohci->id, "Soft reset finished"); } static int run_context(struct ti_ohci *ohci, int reg, char *msg) @@ -415,8 +412,7 @@ for (i=0; inum_desc; i++) { u32 c; - c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH; + c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH; if (generate_irq) c |= DMA_CTL_IRQ; @@ -489,19 +485,14 @@ static void ohci_init_config_rom(struct ti_ohci *ohci); /* Global initialization */ -static int ohci_initialize(struct hpsb_host *host) +static void ohci_initialize(struct ti_ohci *ohci) { - struct ti_ohci *ohci=host->hostdata; - int retval, i; + int i; quadlet_t buf; spin_lock_init(&ohci->phy_reg_lock); spin_lock_init(&ohci->event_lock); - /* Soft reset */ - if ((retval = ohci_soft_reset(ohci)) < 0) - return retval; - /* Put some defaults to these undefined bus options */ buf = reg_read(ohci, OHCI1394_BusOptions); buf |= 0x60000000; /* Enable CMC and ISC */ @@ -509,19 +500,6 @@ buf &= ~0x98000000; /* Disable PMC, IRMC and BMC */ reg_write(ohci, OHCI1394_BusOptions, buf); - /* Set Link Power Status (LPS) */ - reg_write(ohci, OHCI1394_HCControlSet, 0x00080000); - - /* After enabling LPS, we need to wait for the connection - * between phy and link to be established. This should be - * signaled by the LPS bit becoming 1, but this happens - * immediately. Instead we wait for reads from LinkControl to - * give a valid result, i.e. not 0xffffffff. */ - while (reg_read(ohci, OHCI1394_LinkControlSet) == 0xffffffff) { - DBGMSG(ohci->id, "waiting for phy-link connection"); - mdelay(2); - } - /* Set the bus number */ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); @@ -558,10 +536,6 @@ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); /* Initialize IR dma */ - ohci->nb_iso_rcv_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); - DBGMSG(ohci->id, "%d iso receive contexts available", - ohci->nb_iso_rcv_ctx); for (i=0;inb_iso_rcv_ctx;i++) { reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i, 0xffffffff); @@ -580,10 +554,6 @@ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); /* Initialize IT dma */ - ohci->nb_iso_xmit_ctx = - get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); - DBGMSG(ohci->id, "%d iso transmit contexts available", - ohci->nb_iso_xmit_ctx); for (i=0;inb_iso_xmit_ctx;i++) { reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i, 0xffffffff); @@ -645,32 +615,19 @@ OHCI1394_respTxComplete | OHCI1394_reqTxComplete | OHCI1394_isochRx | - OHCI1394_isochTx | - OHCI1394_unrecoverableError - ); + OHCI1394_isochTx); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); buf = reg_read(ohci, OHCI1394_Version); - PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] MMIO=[%lx-%lx]" - " Max Packet=[%d]", ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), + PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] " + "MMIO=[%lx-%lx] Max Packet=[%d]", + ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10), ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq, pci_resource_start(ohci->dev, 0), pci_resource_start(ohci->dev, 0) + pci_resource_len(ohci->dev, 0), ohci->max_packet_size); - - return 1; -} - -static void ohci_remove(struct hpsb_host *host) -{ - struct ti_ohci *ohci; - - if (host != NULL) { - ohci = host->hostdata; - remove_card(ohci); - } } /* @@ -692,7 +649,7 @@ d->prg_cpu[idx]->begin.address = 0; d->prg_cpu[idx]->begin.branchAddress = 0; - if (d->ctx==1) { + if (d->type == DMA_CTX_ASYNC_RESP) { /* * For response packets, we need to put a timeout value in * the 16 lower bits of the status... let's try 1 sec timeout @@ -741,9 +698,9 @@ if (cross_bound((unsigned long)packet->data, packet->data_size)>0) { /* FIXME: do something about it */ - PRINT(KERN_ERR, ohci->id, __FUNCTION__ - ": packet data addr: %p size %Zd bytes " - "cross page boundary", + PRINT(KERN_ERR, ohci->id, + "%s: packet data addr: %p size %Zd bytes " + "cross page boundary", __FUNCTION__, packet->data, packet->data_size); } @@ -933,10 +890,8 @@ switch (cmd) { case RESET_BUS: DBGMSG(ohci->id, "devctl: Bus reset requested%s", - ((host->attempt_root || attempt_root) ? - " and attempting to become root" : "")); - set_phy_reg_mask (ohci, 1, 0x40 | ((host->attempt_root || attempt_root) ? - 0x80 : 0)); + attempt_root ? " and attempting to become root" : ""); + set_phy_reg_mask (ohci, 1, 0x40 | (attempt_root ? 0x80 : 0)); break; case GET_CYCLE_COUNTER: @@ -988,9 +943,9 @@ u64 mask; if (arg<0 || arg>63) { - PRINT(KERN_ERR, ohci->id, __FUNCTION__ - "IS0 listen channel %d is out of range", - arg); + PRINT(KERN_ERR, ohci->id, + "%s: IS0 listen channel %d is out of range", + __FUNCTION__, arg); return -EFAULT; } @@ -999,9 +954,9 @@ spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (ohci->ISO_channel_usage & mask) { - PRINT(KERN_ERR, ohci->id, __FUNCTION__ - "IS0 listen channel %d is already used", - arg); + PRINT(KERN_ERR, ohci->id, + "%s: IS0 listen channel %d is already used", + __FUNCTION__, arg); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); return -EFAULT; } @@ -1024,9 +979,9 @@ u64 mask; if (arg<0 || arg>63) { - PRINT(KERN_ERR, ohci->id, __FUNCTION__ - "IS0 unlisten channel %d is out of range", - arg); + PRINT(KERN_ERR, ohci->id, + "%s: IS0 unlisten channel %d is out of range", + __FUNCTION__, arg); return -EFAULT; } @@ -1035,9 +990,9 @@ spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (!(ohci->ISO_channel_usage & mask)) { - PRINT(KERN_ERR, ohci->id, __FUNCTION__ - "IS0 unlisten channel %d is not used", - arg); + PRINT(KERN_ERR, ohci->id, + "%s: IS0 unlisten channel %d is not used", + __FUNCTION__, arg); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); return -EFAULT; } @@ -1130,11 +1085,7 @@ * selfIDComplete interrupt. */ spin_lock_irqsave(&ohci->event_lock, flags); event = reg_read(ohci, OHCI1394_IntEventClear); -#ifdef BUSRESET_WORKAROUND - reg_write(ohci, OHCI1394_IntEventClear, event); -#else reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset); -#endif spin_unlock_irqrestore(&ohci->event_lock, flags); if (!event) return; @@ -1144,7 +1095,6 @@ /* Die right here an now */ if (event & OHCI1394_unrecoverableError) { PRINT(KERN_ERR, ohci->id, "Unrecoverable error, shutting down card!"); - remove_card(ohci); return; } @@ -1153,16 +1103,22 @@ * selfID phase, so we disable busReset interrupts, to * avoid burying the cpu in interrupt requests. */ spin_lock_irqsave(&ohci->event_lock, flags); -#ifdef BUSRESET_WORKAROUND - reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); -#else reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset); -#endif + if (ohci->dev->vendor == PCI_VENDOR_ID_APPLE && + ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) { + udelay(10); + while(reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) { + reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); + spin_unlock_irqrestore(&ohci->event_lock, flags); + udelay(10); + spin_lock_irqsave(&ohci->event_lock, flags); + } + } spin_unlock_irqrestore(&ohci->event_lock, flags); if (!host->in_bus_reset) { DBGMSG(ohci->id, "irq_handler: Bus reset requested%s", - ((host->attempt_root || attempt_root) ? - " and attempting to become root" : "")); + (attempt_root) ? " and attempting to become root" + : ""); /* Subsystem call */ hpsb_bus_reset(ohci->host); @@ -1233,9 +1189,9 @@ else tasklet_schedule(&d->task); } - if (ohci->video_tmpl) - ohci->video_tmpl->irq_handler(ohci->id, isoRecvIntEvent, - 0); + + ohci1394_run_irq_hooks(ohci, isoRecvIntEvent, 0); + event &= ~OHCI1394_isochRx; } if (event & OHCI1394_isochTx) { @@ -1248,9 +1204,9 @@ DBGMSG(ohci->id, "Got isochTx interrupt " "status=0x%08x isoXmitIntEvent=%08x", reg_read(ohci, d->ctrlSet), isoXmitIntEvent); - if (ohci->video_tmpl) - ohci->video_tmpl->irq_handler(ohci->id, 0, - isoXmitIntEvent); + + ohci1394_run_irq_hooks(ohci, 0, isoXmitIntEvent); + if (isoXmitIntEvent & 0x1) { if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "isochTx"); @@ -1308,12 +1264,10 @@ /* Finally, we clear the busReset event and reenable * the busReset interrupt. */ -#ifndef BUSRESET_WORKAROUND spin_lock_irqsave(&ohci->event_lock, flags); reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset); reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset); spin_unlock_irqrestore(&ohci->event_lock, flags); -#endif event &= ~OHCI1394_selfIDComplete; } @@ -1359,7 +1313,7 @@ { int length = -1; - if (d->ctx < 2) { /* Async Receive Response/Request */ + if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) { length = TCODE_SIZE[tcode]; if (length == 0) { if (offset + 12 >= d->buf_size) { @@ -1370,7 +1324,7 @@ } length += 20; } - } else if (d->ctx == 2) { /* Iso receive */ + } else if (d->type == DMA_CTX_ISO) { /* Assumption: buffer fill mode with header/trailer */ length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8; } @@ -1400,11 +1354,8 @@ offset = d->buf_offset; buf_ptr = d->buf_cpu[idx] + offset/4; - dma_cache_wback_inv(&(d->prg_cpu[idx]->status), sizeof(d->prg_cpu[idx]->status)); rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; - bytes_left = d->buf_size - rescount - offset; - dma_cache_wback_inv(buf_ptr, bytes_left); while (bytes_left > 0) { tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf; @@ -1456,7 +1407,6 @@ insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; buf_ptr = d->buf_cpu[idx]; - dma_cache_wback_inv(buf_ptr, d->buf_size); offset=0; while (split_left >= d->buf_size) { @@ -1466,7 +1416,6 @@ insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; buf_ptr = d->buf_cpu[idx]; - dma_cache_wback_inv(buf_ptr, d->buf_size); } if (split_left > 0) { @@ -1513,8 +1462,6 @@ d->ctx); #endif - dma_cache_wback_inv(&(d->prg_cpu[idx]->status), - sizeof(d->prg_cpu[idx]->status)); rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff; bytes_left = d->buf_size - rescount - offset; @@ -1653,6 +1600,11 @@ } if ((*d)->spb) kfree((*d)->spb); + /* clear ISO context usage bit */ + if ((*d)->type == DMA_CTX_ISO) { + clear_bit((*d)->ctx, &ohci->ir_ctx_usage); + } + kfree(*d); *d = NULL; @@ -1660,15 +1612,21 @@ } static struct dma_rcv_ctx * -alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, - int buf_size, int split_buf_size, - int ctrlSet, int ctrlClear, int cmdPtr) +alloc_dma_rcv_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc, + int buf_size, int split_buf_size, int context_base) { - struct dma_rcv_ctx *d=NULL; + struct dma_rcv_ctx *d; int i; - d = (struct dma_rcv_ctx *)kmalloc(sizeof(struct dma_rcv_ctx), - GFP_KERNEL); + if (type == DMA_CTX_ISO) { + /* try to claim the ISO context usage bit */ + if (test_and_set_bit(ctx, &ohci->ir_ctx_usage)) { + PRINT(KERN_ERR, ohci->id, "IR DMA context %d is not available", ctx); + return NULL; + } + } + + d = kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL); if (d == NULL) { PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_rcv_ctx"); @@ -1677,21 +1635,17 @@ memset (d, 0, sizeof (struct dma_rcv_ctx)); - d->ohci = (void *)ohci; + d->ohci = ohci; + d->type = type; d->ctx = ctx; d->num_desc = num_desc; d->buf_size = buf_size; d->split_buf_size = split_buf_size; - d->ctrlSet = ctrlSet; - d->ctrlClear = ctrlClear; - d->cmdPtr = cmdPtr; - - d->buf_cpu = NULL; - d->buf_bus = NULL; - d->prg_cpu = NULL; - d->prg_bus = NULL; - d->spb = NULL; + + d->ctrlSet = context_base + OHCI1394_ContextControlSet; + d->ctrlClear = context_base + OHCI1394_ContextControlClear; + d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); @@ -1790,36 +1744,47 @@ kfree((*d)->prg_bus); } + /* clear the ISO context usage bit */ + if ((*d)->type == DMA_CTX_ISO) { + clear_bit((*d)->ctx, &ohci->it_ctx_usage); + } + kfree(*d); *d = NULL; return 0; } static struct dma_trm_ctx * -alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, - int ctrlSet, int ctrlClear, int cmdPtr) +alloc_dma_trm_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc, + int context_base) { - struct dma_trm_ctx *d=NULL; + struct dma_trm_ctx *d; int i; - d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx), - GFP_KERNEL); + if (type == DMA_CTX_ISO) { + /* try to claim the ISO context usage bit */ + if (test_and_set_bit(ctx, &ohci->it_ctx_usage)) { + PRINT(KERN_ERR, ohci->id, "IT DMA context %d is not available", ctx); + return NULL; + } + } + + d = kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL); - if (d==NULL) { + if (d == NULL) { PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_trm_ctx"); return NULL; } memset (d, 0, sizeof (struct dma_trm_ctx)); - d->ohci = (void *)ohci; + d->ohci = ohci; + d->type = type; d->ctx = ctx; d->num_desc = num_desc; - d->ctrlSet = ctrlSet; - d->ctrlClear = ctrlClear; - d->cmdPtr = cmdPtr; - d->prg_cpu = NULL; - d->prg_bus = NULL; + d->ctrlSet = context_base + OHCI1394_ContextControlSet; + d->ctrlClear = context_base + OHCI1394_ContextControlClear; + d->cmdPtr = context_base + OHCI1394_ContextCommandPtr; d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), GFP_KERNEL); @@ -1833,7 +1798,7 @@ memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); - for (i=0; inum_desc; i++) { + for (i = 0; i < d->num_desc; i++) { d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, sizeof(struct at_dma_prg), d->prg_bus+i); @@ -1896,6 +1861,21 @@ #define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32(((key) << 24) | (val))) +static inline void cf_put_str(struct config_rom_ptr *cr, const char *str) +{ + int t; + char fourb[4]; + + while (str[0]) { + memset(fourb, 0, 4); + for (t = 0; t < 4 && str[t]; t++) + fourb[t] = str[t]; + cf_put_4bytes(cr, fourb[0], fourb[1], fourb[2], fourb[3]); + str += strlen(str) < 4 ? strlen(str) : 4; + } + return; +} + static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit) { *cr->unitdir[unit].start = @@ -1965,8 +1945,6 @@ cf_put_keyval(&cr, 0x03, 0x00005e); /* Vendor ID */ cf_put_refer(&cr, 0x81, 2); /* Textual description unit */ cf_put_keyval(&cr, 0x0c, 0x0083c0); /* Node capabilities */ - cf_put_refer(&cr, 0xd1, 3); /* IPv4 unit directory */ - cf_put_refer(&cr, 0xd1, 4); /* IPv6 unit directory */ /* NOTE: Add other unit referers here, and append at bottom */ cf_unit_end(&cr); @@ -1974,49 +1952,7 @@ cf_unit_begin(&cr, 2); cf_put_keyval(&cr, 0, 0); cf_put_1quad(&cr, 0); - cf_put_4bytes(&cr, 'L', 'i', 'n', 'u'); - cf_put_4bytes(&cr, 'x', ' ', '1', '3'); - cf_put_4bytes(&cr, '9', '4', 0x0, 0x0); - cf_unit_end(&cr); - - /* IPv4 unit directory, RFC 2734 */ - cf_unit_begin(&cr, 3); - cf_put_keyval(&cr, 0x12, 0x00005e); /* Unit spec ID */ - cf_put_refer(&cr, 0x81, 6); /* Textual description unit */ - cf_put_keyval(&cr, 0x13, 0x000001); /* Unit software version */ - cf_put_refer(&cr, 0x81, 7); /* Textual description unit */ - cf_unit_end(&cr); - - cf_unit_begin(&cr, 6); - cf_put_keyval(&cr, 0, 0); - cf_put_1quad(&cr, 0); - cf_put_4bytes(&cr, 'I', 'A', 'N', 'A'); - cf_unit_end(&cr); - - cf_unit_begin(&cr, 7); - cf_put_keyval(&cr, 0, 0); - cf_put_1quad(&cr, 0); - cf_put_4bytes(&cr, 'I', 'P', 'v', '4'); - cf_unit_end(&cr); - - /* IPv6 unit directory, draft-ietf-ipngwg-1394-01.txt */ - cf_unit_begin(&cr, 4); - cf_put_keyval(&cr, 0x12, 0x00005e); /* Unit spec ID */ - cf_put_refer(&cr, 0x81, 8); /* Textual description unit */ - cf_put_keyval(&cr, 0x13, 0x000002); /* (Proposed) Unit software version */ - cf_put_refer(&cr, 0x81, 9); /* Textual description unit */ - cf_unit_end(&cr); - - cf_unit_begin(&cr, 8); - cf_put_keyval(&cr, 0, 0); - cf_put_1quad(&cr, 0); - cf_put_4bytes(&cr, 'I', 'A', 'N', 'A'); - cf_unit_end(&cr); - - cf_unit_begin(&cr, 9); - cf_put_keyval(&cr, 0, 0); - cf_put_1quad(&cr, 0); - cf_put_4bytes(&cr, 'I', 'P', 'v', '6'); + cf_put_str(&cr, "Linux OHCI-1394"); cf_unit_end(&cr); ohci->csr_config_rom_length = cr.data - ohci->csr_config_rom_cpu; @@ -2034,13 +1970,15 @@ return ohci->csr_config_rom_length * 4; } -int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data, - quadlet_t compare, int sel) +static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg, + quadlet_t data, quadlet_t compare) { + struct ti_ohci *ohci = host->hostdata; int i; - reg_write(ohci, OHCI1394_CSRData, *data); + + reg_write(ohci, OHCI1394_CSRData, data); reg_write(ohci, OHCI1394_CSRCompareData, compare); - reg_write(ohci, OHCI1394_CSRControl, sel & 0x3); + reg_write(ohci, OHCI1394_CSRControl, reg & 0x3); for (i = 0; i < OHCI_LOOP_COUNT; i++) { if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000) @@ -2049,68 +1987,58 @@ mdelay(1); } - *data = reg_read(ohci, OHCI1394_CSRData); - return 0; + return reg_read(ohci, OHCI1394_CSRData); } -static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg, - quadlet_t data, quadlet_t compare) -{ - struct ti_ohci *ohci=host->hostdata; - - ohci_compare_swap (ohci, &data, compare, reg); - - return data; -} - -static struct hpsb_host_template ohci_template = { - name: OHCI1394_DRIVER_NAME, - initialize_host: ohci_initialize, - release_host: ohci_remove, +static struct hpsb_host_operations ohci1394_ops = { get_rom: ohci_get_rom, transmit_packet: ohci_transmit, devctl: ohci_devctl, hw_csr_reg: ohci_hw_csr_reg, }; +static struct hpsb_host_driver *ohci1394_driver; + + + +/*********************************** + * PCI Driver Interface functions * + ***********************************/ -#define FAIL(fmt, args...) \ +#define FAIL(err, fmt, args...) \ do { \ PRINT_G(KERN_ERR, fmt , ## args); \ - remove_card(ohci); \ - return 1; \ + ohci1394_pci_remove(dev); \ + return err; \ } while(0) -static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_device_id *ent) +static int __devinit ohci1394_pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) { - struct ti_ohci *ohci; /* shortcut to currently handled device */ - struct hpsb_host *host; - unsigned long ohci_base, ohci_len; + static unsigned int card_id_counter = 0; static int version_printed = 0; + struct hpsb_host *host; + struct ti_ohci *ohci; /* shortcut to currently handled device */ + unsigned long ohci_base, ohci_len; + int i; + if (version_printed++ == 0) PRINT_G(KERN_INFO, "%s", version); - if (pci_enable_device(dev)) { - /* Skip ID's that fail */ - PRINT_G(KERN_NOTICE, "Failed to enable OHCI hardware %d", - card_id_counter++); - return -ENXIO; - } + if (pci_enable_device(dev)) + FAIL(-ENXIO, "Failed to enable OHCI hardware %d", + card_id_counter++); pci_set_master(dev); - host = hpsb_get_host(&ohci_template, sizeof (struct ti_ohci)); - if (!host) { - PRINT_G(KERN_ERR, "Out of memory trying to allocate host structure"); - return -ENOMEM; - } + host = hpsb_alloc_host(ohci1394_driver, sizeof(struct ti_ohci)); + if (!host) FAIL(-ENOMEM, "Failed to allocate host structure"); + ohci = host->hostdata; - ohci->host = host; - INIT_LIST_HEAD(&ohci->list); ohci->id = card_id_counter++; ohci->dev = dev; - host->pdev = dev; ohci->host = host; + host->pdev = dev; pci_set_drvdata(dev, ohci); /* We don't want hardware swapping */ @@ -2138,8 +2066,54 @@ &ohci->csr_config_rom_bus); OHCI_DMA_ALLOC("consistent csr_config_rom"); if (ohci->csr_config_rom_cpu == NULL) - FAIL("Failed to allocate buffer config rom"); + FAIL(-ENOMEM, "Failed to allocate buffer config rom"); + + + ohci_base = pci_resource_start(dev, 0); + ohci_len = pci_resource_len(dev, 0); + + if (!request_mem_region (ohci_base, ohci_len, OHCI1394_DRIVER_NAME)) + FAIL(-ENOMEM, "MMIO resource (0x%lx@0x%lx) unavailable, aborting.", + ohci_base, ohci_len); + + ohci->registers = ioremap(ohci_base, ohci_len); + + if (ohci->registers == NULL) + FAIL(-ENXIO, "Failed to remap registers - card not accessible"); + + DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p", ohci->registers); + + + /* Start off with a softreset, to clear everything to a sane + * state. This will also set Link Power State (LPS), which we + * need in order to start accessing most of the registers. */ + ohci_soft_reset(ohci); + + /* determinte the number of available IR and IT contexts right away, + because they need to be known for alloc_dma_*_ctx() */ + ohci->nb_iso_rcv_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); + DBGMSG(ohci->id, "%d iso receive contexts available", + ohci->nb_iso_rcv_ctx); + + ohci->ir_ctx_usage = 0; + + /* set the usage bits for non-existent contexts so they can't be allocated */ + for(i = ohci->nb_iso_rcv_ctx; i < sizeof(ohci->ir_ctx_usage)*8; i++) + __set_bit(i, &ohci->ir_ctx_usage); + + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + DBGMSG(ohci->id, "%d iso transmit contexts available", + ohci->nb_iso_xmit_ctx); + + ohci->it_ctx_usage = 0; + + /* set the usage bits for non-existent contexts so they can't be allocated */ + for(i = ohci->nb_iso_xmit_ctx; i < sizeof(ohci->it_ctx_usage)*8; i++) + __set_bit(i, &ohci->it_ctx_usage); + /* * self-id dma buffer allocation */ @@ -2148,103 +2122,101 @@ &ohci->selfid_buf_bus); OHCI_DMA_ALLOC("consistent selfid_buf"); if (ohci->selfid_buf_cpu == NULL) - FAIL("Failed to allocate DMA buffer for self-id packets"); + FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets"); if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff) PRINT(KERN_INFO, ohci->id, "SelfID buffer %p is not aligned on " "8Kb boundary... may cause problems on some CXD3222 chip", ohci->selfid_buf_cpu); - ohci->it_context = - alloc_dma_trm_ctx(ohci, 2, IT_NUM_DESC, - OHCI1394_IsoXmitContextControlSet, - OHCI1394_IsoXmitContextControlClear, - OHCI1394_IsoXmitCommandPtr); - - if (ohci->it_context == NULL) - FAIL("Failed to allocate IT context"); - - ohci_base = pci_resource_start(dev, 0); - ohci_len = pci_resource_len(dev, 0); - - if (!request_mem_region (ohci_base, ohci_len, host->template->name)) - FAIL("MMIO resource (0x%lx@0x%lx) unavailable, aborting.", - ohci_base, ohci_len); - - ohci->registers = ioremap(ohci_base, ohci_len); - - if (ohci->registers == NULL) - FAIL("Failed to remap registers - card not accessible"); - - DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p", - ohci->registers); + /* No self-id errors at startup */ + ohci->self_id_errors = 0; + /* AR DMA request context allocation */ ohci->ar_req_context = - alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC, + alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC, AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE, - OHCI1394_AsReqRcvContextControlSet, - OHCI1394_AsReqRcvContextControlClear, - OHCI1394_AsReqRcvCommandPtr); + OHCI1394_AsReqRcvContextBase); if (ohci->ar_req_context == NULL) - FAIL("Failed to allocate AR Req context"); + FAIL(-ENOMEM, "Failed to allocate AR Req context"); + /* AR DMA response context allocation */ ohci->ar_resp_context = - alloc_dma_rcv_ctx(ohci, 1, AR_RESP_NUM_DESC, + alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC, AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE, - OHCI1394_AsRspRcvContextControlSet, - OHCI1394_AsRspRcvContextControlClear, - OHCI1394_AsRspRcvCommandPtr); + OHCI1394_AsRspRcvContextBase); if (ohci->ar_resp_context == NULL) - FAIL("Failed to allocate AR Resp context"); + FAIL(-ENOMEM, "Failed to allocate AR Resp context"); + /* AT DMA request context */ ohci->at_req_context = - alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC, - OHCI1394_AsReqTrContextControlSet, - OHCI1394_AsReqTrContextControlClear, - OHCI1394_AsReqTrCommandPtr); + alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC, + OHCI1394_AsReqTrContextBase); if (ohci->at_req_context == NULL) - FAIL("Failed to allocate AT Req context"); + FAIL(-ENOMEM, "Failed to allocate AT Req context"); + /* AT DMA response context */ ohci->at_resp_context = - alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC, - OHCI1394_AsRspTrContextControlSet, - OHCI1394_AsRspTrContextControlClear, - OHCI1394_AsRspTrCommandPtr); + alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC, + OHCI1394_AsRspTrContextBase); if (ohci->at_resp_context == NULL) - FAIL("Failed to allocate AT Resp context"); + FAIL(-ENOMEM, "Failed to allocate AT Resp context"); + /* IR DMA context */ ohci->ir_context = - alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC, + alloc_dma_rcv_ctx(ohci, DMA_CTX_ISO, 0, IR_NUM_DESC, IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, - OHCI1394_IsoRcvContextControlSet, - OHCI1394_IsoRcvContextControlClear, - OHCI1394_IsoRcvCommandPtr); + OHCI1394_IsoRcvContextBase); if (ohci->ir_context == NULL) - FAIL("Failed to allocate IR context"); + FAIL(-ENOMEM, "Failed to allocate IR context"); + + + /* IT DMA context allocation */ + ohci->it_context = + alloc_dma_trm_ctx(ohci, DMA_CTX_ISO, 0, IT_NUM_DESC, + OHCI1394_IsoXmitContextBase); + + if (ohci->it_context == NULL) + FAIL(-ENOMEM, "Failed to allocate IT context"); ohci->ISO_channel_usage = 0; spin_lock_init(&ohci->IR_channel_lock); + for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) { + ohci->irq_hooks[i].irq_handler = NULL; + ohci->irq_hooks[i].data = NULL; + } + if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, OHCI1394_DRIVER_NAME, ohci)) - FAIL("Failed to allocate shared interrupt %d", dev->irq); + FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq); + + ohci_initialize(ohci); /* Tell the highlevel this host is ready */ - highlevel_add_one_host (host); + hpsb_add_host(host); return 0; #undef FAIL } -static void remove_card(struct ti_ohci *ohci) +static void __devexit ohci1394_pci_remove(struct pci_dev *pdev) { + struct ti_ohci *ohci; quadlet_t buf; + ohci = pci_get_drvdata(pdev); + if (!ohci) + return; + + if (ohci->host) + hpsb_remove_host(ohci->host); + /* Soft reset before we start */ ohci_soft_reset(ohci); @@ -2308,15 +2280,48 @@ of_node = pci_device_to_OF_node(ohci->dev); if (of_node) { - feature_set_firewire_power(of_node, 0); - feature_set_firewire_cable_power(of_node, 0); + pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, of_node, 0, 0); } } #endif /* CONFIG_ALL_PPC */ pci_set_drvdata(ohci->dev, NULL); + kfree(ohci); } +#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10) + +static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = { + { + class: PCI_CLASS_FIREWIRE_OHCI, + class_mask: 0x00ffffff, + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl); + +static struct pci_driver ohci1394_pci_driver = { + name: OHCI1394_DRIVER_NAME, + id_table: ohci1394_pci_tbl, + probe: ohci1394_pci_probe, + remove: __devexit_p(ohci1394_pci_remove), +}; + + + +/*********************************** + * OHCI1394 Video Interface * + ***********************************/ + +/* essentially the only purpose of this code is to allow another + module to hook into ohci's interrupt handler */ + void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) { int i=0; @@ -2336,111 +2341,96 @@ if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg); } -int ohci1394_register_video(struct ti_ohci *ohci, - struct video_template *tmpl) +static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci, + quadlet_t isoRecvEvent, + quadlet_t isoXmitEvent) { - if (ohci->video_tmpl) - return -ENFILE; - ohci->video_tmpl = tmpl; - MOD_INC_USE_COUNT; - return 0; + int i; + for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) { + if(ohci->irq_hooks[i].irq_handler != NULL) { + ohci->irq_hooks[i].irq_handler(ohci->id, isoRecvEvent, isoXmitEvent, + ohci->irq_hooks[i].data); + } + } } - -void ohci1394_unregister_video(struct ti_ohci *ohci, - struct video_template *tmpl) + +int ohci1394_hook_irq(struct ti_ohci *ohci, + void (*irq_handler) (int, quadlet_t, quadlet_t, void *), + void *data) { - if (ohci->video_tmpl != tmpl) { - PRINT(KERN_ERR, ohci->id, - "Trying to unregister wrong video device"); - } else { - ohci->video_tmpl = NULL; - MOD_DEC_USE_COUNT; + int i; + + /* find a free slot */ + for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) { + if(ohci->irq_hooks[i].irq_handler == NULL) + break; } + + if(i >= OHCI1394_MAX_IRQ_HOOKS) + return -EBUSY; + + ohci->irq_hooks[i].irq_handler = irq_handler; + ohci->irq_hooks[i].data = data; + + /* ohci1394 will never be unloaded while an IRQ hook is + in use, because the user must reference this symbol */ + + return 0; } -#if 0 -int ohci1394_request_channel(struct ti_ohci *ohci, int channel) +void ohci1394_unhook_irq(struct ti_ohci *ohci, + void (*irq_handler) (int, quadlet_t, quadlet_t, void *), + void *data) { - int csrSel; - quadlet_t chan, data1=0, data2=0; - int timeout = 32; - - if (channel<32) { - chan = 1<id, "request_channel timeout"); - return -1; + int i; + + for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) { + if( (ohci->irq_hooks[i].irq_handler == irq_handler) && + (ohci->irq_hooks[i].data == data) ) + break; } - while (timeout--) { - if (data1 & chan) { - PRINT(KERN_INFO, ohci->id, - "request channel %d failed", channel); - return -1; - } - data2 = data1; - data1 |= chan; - if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) { - PRINT(KERN_INFO, ohci->id, "request_channel timeout"); - return -1; - } - if (data1==data2) { - PRINT(KERN_INFO, ohci->id, - "request channel %d succeded", channel); - return 0; - } + + if(i < OHCI1394_MAX_IRQ_HOOKS) { + ohci->irq_hooks[i].irq_handler = NULL; + ohci->irq_hooks[i].data = NULL; } - PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel); - return -1; } -#endif EXPORT_SYMBOL(ohci1394_stop_context); -EXPORT_SYMBOL(ohci1394_register_video); -EXPORT_SYMBOL(ohci1394_unregister_video); +EXPORT_SYMBOL(ohci1394_hook_irq); +EXPORT_SYMBOL(ohci1394_unhook_irq); -MODULE_AUTHOR("Sebastien Rougeaux "); -MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers"); -MODULE_LICENSE("GPL"); -static void __devexit ohci1394_remove_one(struct pci_dev *pdev) -{ - struct ti_ohci *ohci = pci_get_drvdata(pdev); - if (ohci) { - remove_card (ohci); - pci_set_drvdata(pdev, NULL); - } -} +/*********************************** + * General module initialization * + ***********************************/ -static struct pci_driver ohci1394_driver = { - name: OHCI1394_DRIVER_NAME, - id_table: ohci1394_pci_tbl, - probe: ohci1394_add_one, - remove: ohci1394_remove_one, -}; +MODULE_AUTHOR("Sebastien Rougeaux "); +MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers"); +MODULE_LICENSE("GPL"); static void __exit ohci1394_cleanup (void) { - hpsb_unregister_lowlevel(&ohci_template); - pci_unregister_driver(&ohci1394_driver); + pci_unregister_driver(&ohci1394_pci_driver); + hpsb_unregister_lowlevel(ohci1394_driver); } static int __init ohci1394_init(void) { int ret; - if (hpsb_register_lowlevel(&ohci_template)) { - PRINT_G(KERN_ERR, "Registering failed"); - return -ENXIO; - } - if ((ret = pci_module_init(&ohci1394_driver))) { - PRINT_G(KERN_ERR, "PCI module init failed"); - hpsb_unregister_lowlevel(&ohci_template); + + ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops, + OHCI1394_DRIVER_NAME); + if (!ohci1394_driver) { + PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed"); + return -ENOMEM; + } + + ret = pci_module_init(&ohci1394_pci_driver); + if (ret < 0) { + PRINT_G(KERN_ERR, "pci_module_init failed"); + hpsb_unregister_lowlevel(ohci1394_driver); return ret; } return ret; diff -urN linux-2.5.3-pre2/drivers/ieee1394/ohci1394.h linux/drivers/ieee1394/ohci1394.h --- linux-2.5.3-pre2/drivers/ieee1394/ohci1394.h Mon Oct 1 21:24:24 2001 +++ linux/drivers/ieee1394/ohci1394.h Mon Jan 21 17:23:22 2002 @@ -76,9 +76,13 @@ quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ }; +/* identify whether a DMA context is asynchronous or isochronous */ +enum context_type { DMA_CTX_ASYNC_REQ, DMA_CTX_ASYNC_RESP, DMA_CTX_ISO }; + /* DMA receive context */ struct dma_rcv_ctx { - void *ohci; + struct ti_ohci *ohci; + enum context_type type; int ctx; unsigned int num_desc; @@ -105,7 +109,8 @@ /* DMA transmit context */ struct dma_trm_ctx { - void *ohci; + struct ti_ohci *ohci; + enum context_type type; int ctx; unsigned int num_desc; @@ -133,18 +138,9 @@ int cmdPtr; }; -/* video device template */ -struct video_template { - void (*irq_handler) (int card, quadlet_t isoRecvEvent, - quadlet_t isoXmitEvent); -}; - - struct ti_ohci { int id; /* sequential card number */ - struct list_head list; - struct pci_dev *dev; u32 state; @@ -175,11 +171,13 @@ struct dma_rcv_ctx *ir_context; spinlock_t IR_channel_lock; int nb_iso_rcv_ctx; - + unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */ + /* iso transmit */ struct dma_trm_ctx *it_context; int nb_iso_xmit_ctx; - + unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ + u64 ISO_channel_usage; /* IEEE-1394 part follows */ @@ -192,8 +190,15 @@ int self_id_errors; - /* video device */ - struct video_template *video_tmpl; + /* IRQ hooks, for video1394 and dv1394 */ + +#define OHCI1394_MAX_IRQ_HOOKS 4 + + struct ohci1394_irq_hook { + void (*irq_handler) (int card, quadlet_t isoRecvEvent, + quadlet_t isoXmitEvent, void *data); + void *data; + } irq_hooks[OHCI1394_MAX_IRQ_HOOKS]; /* Swap the selfid buffer? */ unsigned int selfid_swap:1; @@ -230,6 +235,12 @@ /* 2 KiloBytes of register space */ #define OHCI1394_REGISTER_SIZE 0x800 +/* Offsets relative to context bases defined below */ + +#define OHCI1394_ContextControlSet 0x000 +#define OHCI1394_ContextControlClear 0x004 +#define OHCI1394_ContextCommandPtr 0x00C + /* register map */ #define OHCI1394_Version 0x000 #define OHCI1394_GUID_ROM 0x004 @@ -281,27 +292,37 @@ #define OHCI1394_PhyReqFilterLoSet 0x118 #define OHCI1394_PhyReqFilterLoClear 0x11C #define OHCI1394_PhyUpperBound 0x120 + +#define OHCI1394_AsReqTrContextBase 0x180 #define OHCI1394_AsReqTrContextControlSet 0x180 #define OHCI1394_AsReqTrContextControlClear 0x184 #define OHCI1394_AsReqTrCommandPtr 0x18C + +#define OHCI1394_AsRspTrContextBase 0x1A0 #define OHCI1394_AsRspTrContextControlSet 0x1A0 #define OHCI1394_AsRspTrContextControlClear 0x1A4 #define OHCI1394_AsRspTrCommandPtr 0x1AC + +#define OHCI1394_AsReqRcvContextBase 0x1C0 #define OHCI1394_AsReqRcvContextControlSet 0x1C0 #define OHCI1394_AsReqRcvContextControlClear 0x1C4 #define OHCI1394_AsReqRcvCommandPtr 0x1CC + +#define OHCI1394_AsRspRcvContextBase 0x1E0 #define OHCI1394_AsRspRcvContextControlSet 0x1E0 #define OHCI1394_AsRspRcvContextControlClear 0x1E4 #define OHCI1394_AsRspRcvCommandPtr 0x1EC /* Isochronous transmit registers */ /* Add (32 * n) for context n */ +#define OHCI1394_IsoXmitContextBase 0x200 #define OHCI1394_IsoXmitContextControlSet 0x200 #define OHCI1394_IsoXmitContextControlClear 0x204 #define OHCI1394_IsoXmitCommandPtr 0x20C /* Isochronous receive registers */ /* Add (32 * n) for context n */ +#define OHCI1394_IsoRcvContextBase 0x400 #define OHCI1394_IsoRcvContextControlSet 0x400 #define OHCI1394_IsoRcvContextControlClear 0x404 #define OHCI1394_IsoRcvCommandPtr 0x40C @@ -346,10 +367,13 @@ void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); struct ti_ohci *ohci1394_get_struct(int card_num); -int ohci1394_register_video(struct ti_ohci *ohci, - struct video_template *tmpl); -void ohci1394_unregister_video(struct ti_ohci *ohci, - struct video_template *tmpl); +int ohci1394_hook_irq(struct ti_ohci *ohci, + void (*irq_handler) (int, quadlet_t, quadlet_t, void *), + void *data); + +void ohci1394_unhook_irq(struct ti_ohci *ohci, + void (*irq_handler) (int, quadlet_t, quadlet_t, void *), + void *data); #endif diff -urN linux-2.5.3-pre2/drivers/ieee1394/pcilynx.c linux/drivers/ieee1394/pcilynx.c --- linux-2.5.3-pre2/drivers/ieee1394/pcilynx.c Sun Nov 11 10:20:21 2001 +++ linux/drivers/ieee1394/pcilynx.c Mon Jan 21 17:23:22 2002 @@ -42,10 +42,6 @@ #include "pcilynx.h" -#if MAX_PCILYNX_CARDS > PCILYNX_MINOR_ROM_START -#error Max number of cards is bigger than PCILYNX_MINOR_ROM_START - this does not work. -#endif - /* print general (card independent) information */ #define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args) /* print card specific information */ @@ -60,9 +56,8 @@ #endif -static struct ti_lynx cards[MAX_PCILYNX_CARDS]; -static int num_of_cards = 0; -static struct hpsb_host_template lynx_template; +static struct hpsb_host_driver *lynx_driver; +static unsigned int card_id; /* * PCL handling functions. @@ -143,10 +138,6 @@ #endif -static int add_card(struct pci_dev *dev, const struct pci_device_id *devid); -static void remove_card(struct pci_dev *dev); - - /*********************************** * IEEE-1394 functionality section * @@ -161,8 +152,9 @@ unsigned long flags; if (addr > 15) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": PHY register address %d out of range", addr); + PRINT(KERN_ERR, lynx->id, + "%s: PHY register address %d out of range", + __FUNCTION__, addr); return -1; } @@ -173,8 +165,8 @@ retval = reg_read(lynx, LINK_PHY); if (i > 10000) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": runaway loop, aborting"); + PRINT(KERN_ERR, lynx->id, "%s: runaway loop, aborting", + __FUNCTION__); retval = -1; break; } @@ -196,14 +188,14 @@ unsigned long flags; if (addr > 15) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": PHY register address %d out of range", addr); + PRINT(KERN_ERR, lynx->id, + "%s: PHY register address %d out of range", __FUNCTION__, addr); return -1; } if (val > 0xff) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": PHY register value %d out of range", val); + PRINT(KERN_ERR, lynx->id, + "%s: PHY register value %d out of range", __FUNCTION__, val); return -1; } @@ -222,8 +214,8 @@ int reg; if (page > 7) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": PHY page %d out of range", page); + PRINT(KERN_ERR, lynx->id, + "%s: PHY page %d out of range", __FUNCTION__, page); return -1; } @@ -244,8 +236,8 @@ int reg; if (port > 15) { - PRINT(KERN_ERR, lynx->id, __FUNCTION__ - ": PHY port %d out of range", port); + PRINT(KERN_ERR, lynx->id, + "%s: PHY port %d out of range", __FUNCTION__, port); return -1; } @@ -377,7 +369,7 @@ hpsb_selfid_complete(host, phyid, isroot); - if (host->in_bus_reset) return; + if (host->in_bus_reset) return; /* in bus reset again */ if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); reg_set_bits(lynx, LINK_CONTROL, @@ -440,160 +432,7 @@ } -#if 0 -static int lynx_detect(struct hpsb_host_template *tmpl) -{ - struct hpsb_host *host; - int i; - - init_driver(); - - for (i = 0; i < num_of_cards; i++) { - host = hpsb_get_host(tmpl, 0); - if (host == NULL) { - /* simply don't init more after out of mem */ - return i; - } - host->hostdata = &cards[i]; - cards[i].host = host; - } - - return num_of_cards; -} -#endif - -static int lynx_initialize(struct hpsb_host *host) -{ - struct ti_lynx *lynx = host->hostdata; - struct ti_pcl pcl; - int i; - u32 *pcli; - - lynx->selfid_size = -1; - lynx->phy_reg0 = -1; - - lynx->async.queue = NULL; - - pcl.next = pcl_bus(lynx, lynx->rcv_pcl); - put_pcl(lynx, lynx->rcv_pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; -#ifdef __BIG_ENDIAN - pcl.buffer[0].control = PCL_CMD_RCV | 16; - pcl.buffer[1].control = PCL_LAST_BUFF | 4080; -#else - pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16; - pcl.buffer[1].control = PCL_LAST_BUFF | 4080; -#endif - pcl.buffer[0].pointer = lynx->rcv_page_dma; - pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; - put_pcl(lynx, lynx->rcv_pcl, &pcl); - - pcl.next = pcl_bus(lynx, lynx->async.pcl); - pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); - put_pcl(lynx, lynx->async.pcl_start, &pcl); - - pcl.next = pcl_bus(lynx, lynx->iso_send.pcl); - pcl.async_error_next = PCL_NEXT_INVALID; - put_pcl(lynx, lynx->iso_send.pcl_start, &pcl); - - pcl.next = PCL_NEXT_INVALID; - pcl.async_error_next = PCL_NEXT_INVALID; - pcl.buffer[0].control = PCL_CMD_RCV | 4; -#ifndef __BIG_ENDIAN - pcl.buffer[0].control |= PCL_BIGENDIAN; -#endif - pcl.buffer[1].control = PCL_LAST_BUFF | 2044; - - for (i = 0; i < NUM_ISORCV_PCL; i++) { - int page = i / ISORCV_PER_PAGE; - int sec = i % ISORCV_PER_PAGE; - - pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] - + sec * MAX_ISORCV_SIZE; - pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; - put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); - } - - pcli = (u32 *)&pcl; - for (i = 0; i < NUM_ISORCV_PCL; i++) { - pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]); - } - put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); - - /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */ - reg_write(lynx, FIFO_SIZES, 0x003030a0); - /* 20 byte threshold before triggering PCI transfer */ - reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); - /* threshold on both send FIFOs before transmitting: - FIFO size - cache line size - 1 */ - i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff; - i = 0x30 - i - 1; - reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i); - - reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394); - - reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT - | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET - | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK - | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC - | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW - | LINK_INT_ATF_UNDERFLOW); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV), - DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST - | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST - | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER); - - run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - - reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4); - reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0); - reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - - run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV); - - reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID - | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN - | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN - | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX); - - if (!lynx->phyic.reg_1394a) { - /* attempt to enable contender bit -FIXME- would this work - * elsewhere? */ - reg_set_bits(lynx, GPIO_CTRL_A, 0x1); - reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); - } else { - /* set the contender bit in the extended PHY register - * set. (Should check that bis 0,1,2 (=0xE0) is set - * in register 2?) - */ - i = get_phy_reg(lynx, 4); - if (i != -1) set_phy_reg(lynx, 4, i | 0x40); - } - - return 1; -} - -static void lynx_release(struct hpsb_host *host) -{ - struct ti_lynx *lynx; - - if (host != NULL) { - lynx = host->hostdata; - remove_card(lynx->dev); - } else { -#ifdef CONFIG_IEEE1394_PCILYNX_PORTS - unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); -#endif - } -} - +/* called from subsystem core */ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) { struct ti_lynx *lynx = host->hostdata; @@ -642,6 +481,8 @@ return 1; } + +/* called from subsystem core */ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) { struct ti_lynx *lynx = host->hostdata; @@ -666,9 +507,7 @@ arg |= (retval == -1 ? 63 : retval); retval = 0; - PRINT(KERN_INFO, lynx->id, "resetting bus on request%s", - (host->attempt_root ? " and attempting to become root" - : "")); + PRINT(KERN_INFO, lynx->id, "resetting bus on request"); lynx->selfid_size = -1; lynx->phy_reg0 = -1; @@ -721,6 +560,8 @@ } else { MOD_DEC_USE_COUNT; } + + retval = 1; break; case ISO_LISTEN_CHANNEL: @@ -1010,8 +851,8 @@ membase = md->lynx->aux_port; break; default: - panic("pcilynx%d: unsupported md->type %d in " __FUNCTION__, - md->lynx->id, md->type); + panic("pcilynx%d: unsupported md->type %d in %s", + md->lynx->id, md->type, __FUNCTION__); } down(&md->lynx->mem_dma_mutex); @@ -1291,6 +1132,7 @@ } } + static void iso_rcv_bh(struct ti_lynx *lynx) { unsigned int idx; @@ -1335,44 +1177,97 @@ } +static void remove_card(struct pci_dev *dev) +{ + struct ti_lynx *lynx; + int i; + + lynx = pci_get_drvdata(dev); + if (!lynx) return; + pci_set_drvdata(dev, NULL); + + switch (lynx->state) { + case is_host: + reg_write(lynx, PCI_INT_ENABLE, 0); + hpsb_remove_host(lynx->host); + case have_intr: + reg_write(lynx, PCI_INT_ENABLE, 0); + free_irq(lynx->dev->irq, lynx); + case have_iomappings: + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); + iounmap(lynx->registers); + iounmap(lynx->local_rom); + iounmap(lynx->local_ram); + iounmap(lynx->aux_port); + case have_1394_buffers: + for (i = 0; i < ISORCV_PAGES; i++) { + if (lynx->iso_rcv.page[i]) { + pci_free_consistent(lynx->dev, PAGE_SIZE, + lynx->iso_rcv.page[i], + lynx->iso_rcv.page_dma[i]); + } + } + pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, + lynx->rcv_page_dma); + case have_aux_buf: +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer, + lynx->mem_dma_buffer_dma); +#endif + case have_pcl_mem: +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM + pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, + lynx->pcl_mem_dma); +#endif + case clear: + /* do nothing - already freed */ + ; + } + + tasklet_kill(&lynx->iso_rcv.tq); + kfree(lynx); +} + + static int __devinit add_card(struct pci_dev *dev, - const struct pci_device_id *devid) + const struct pci_device_id *devid_is_unused) { #define FAIL(fmt, args...) do { \ PRINT_G(KERN_ERR, fmt , ## args); \ - num_of_cards--; \ remove_card(dev); \ - return -1; \ + return error; \ } while (0) + struct hpsb_host *host; struct ti_lynx *lynx; /* shortcut to currently handled device */ - unsigned int i; + struct ti_pcl pcl; + u32 *pcli; + int i; + int error; - if (num_of_cards == MAX_PCILYNX_CARDS) { - PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " - "Adjust MAX_PCILYNX_CARDS in pcilynx.h.", - MAX_PCILYNX_CARDS); - return -1; - } - lynx = &cards[num_of_cards++]; + error = -ENXIO; if (pci_set_dma_mask(dev, 0xffffffff)) - FAIL("DMA address limits not supported for PCILynx hardware %d", - lynx->id); + FAIL("DMA address limits not supported for PCILynx hardware"); if (pci_enable_device(dev)) - FAIL("failed to enable PCILynx hardware %d", lynx->id); + FAIL("failed to enable PCILynx hardware"); pci_set_master(dev); - lynx->host = hpsb_get_host(&lynx_template, 0); - if (!lynx->host) - FAIL("failed to allocate host structure"); - - lynx->state = have_host_struct; - lynx->host->hostdata = lynx; - lynx->id = num_of_cards-1; + error = -ENOMEM; + + host = hpsb_alloc_host(lynx_driver, sizeof(struct ti_lynx)); + if (!host) FAIL("failed to allocate control structure memory"); + + lynx = host->hostdata; + lynx->id = card_id++; lynx->dev = dev; - lynx->host->pdev = dev; + lynx->state = clear; + lynx->host = host; + host->pdev = dev; + pci_set_drvdata(dev, lynx); lynx->lock = SPIN_LOCK_UNLOCKED; lynx->phy_reg_lock = SPIN_LOCK_UNLOCKED; @@ -1435,7 +1330,9 @@ } #endif - reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); + /* Fix buggy cards with autoboot pin not tied low: */ + reg_write(lynx, DMA0_CHAN_CTRL, 0); if (!request_irq(dev->irq, lynx_irq_handler, SA_SHIRQ, PCILYNX_DRIVER_NAME, lynx)) { @@ -1502,99 +1399,121 @@ PRINT(KERN_INFO, lynx->id, "found old 1394 PHY"); } - /* Tell the highlevel this host is ready */ - highlevel_add_one_host (lynx->host); - - return 0; -#undef FAIL -} + lynx->selfid_size = -1; + lynx->phy_reg0 = -1; -static void remove_card(struct pci_dev *dev) -{ - struct ti_lynx *lynx; - int i; + lynx->async.queue = NULL; - lynx = cards; - while (lynx->dev != dev) lynx++; + pcl.next = pcl_bus(lynx, lynx->rcv_pcl); + put_pcl(lynx, lynx->rcv_pcl_start, &pcl); - switch (lynx->state) { - case have_intr: - reg_write(lynx, PCI_INT_ENABLE, 0); - free_irq(lynx->dev->irq, lynx); - case have_iomappings: - reg_write(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET); - iounmap(lynx->registers); - iounmap(lynx->local_rom); - iounmap(lynx->local_ram); - iounmap(lynx->aux_port); - case have_1394_buffers: - for (i = 0; i < ISORCV_PAGES; i++) { - if (lynx->iso_rcv.page[i]) { - pci_free_consistent(lynx->dev, PAGE_SIZE, - lynx->iso_rcv.page[i], - lynx->iso_rcv.page_dma[i]); - } - } - pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, - lynx->rcv_page_dma); - case have_aux_buf: -#ifdef CONFIG_IEEE1394_PCILYNX_PORTS - pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer, - lynx->mem_dma_buffer_dma); + pcl.next = PCL_NEXT_INVALID; + pcl.async_error_next = PCL_NEXT_INVALID; +#ifdef __BIG_ENDIAN + pcl.buffer[0].control = PCL_CMD_RCV | 16; + pcl.buffer[1].control = PCL_LAST_BUFF | 4080; +#else + pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16; + pcl.buffer[1].control = PCL_LAST_BUFF | 4080; #endif - case have_pcl_mem: -#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM - pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, - lynx->pcl_mem_dma); + pcl.buffer[0].pointer = lynx->rcv_page_dma; + pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; + put_pcl(lynx, lynx->rcv_pcl, &pcl); + + pcl.next = pcl_bus(lynx, lynx->async.pcl); + pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); + put_pcl(lynx, lynx->async.pcl_start, &pcl); + + pcl.next = pcl_bus(lynx, lynx->iso_send.pcl); + pcl.async_error_next = PCL_NEXT_INVALID; + put_pcl(lynx, lynx->iso_send.pcl_start, &pcl); + + pcl.next = PCL_NEXT_INVALID; + pcl.async_error_next = PCL_NEXT_INVALID; + pcl.buffer[0].control = PCL_CMD_RCV | 4; +#ifndef __BIG_ENDIAN + pcl.buffer[0].control |= PCL_BIGENDIAN; #endif - case have_host_struct: - /* FIXME - verify host freeing */ - case clear:; - /* do nothing - already freed */ + pcl.buffer[1].control = PCL_LAST_BUFF | 2044; + + for (i = 0; i < NUM_ISORCV_PCL; i++) { + int page = i / ISORCV_PER_PAGE; + int sec = i % ISORCV_PER_PAGE; + + pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] + + sec * MAX_ISORCV_SIZE; + pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; + put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); } - tasklet_kill(&lynx->iso_rcv.tq); + pcli = (u32 *)&pcl; + for (i = 0; i < NUM_ISORCV_PCL; i++) { + pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]); + } + put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl); - lynx->state = clear; -} + /* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */ + reg_write(lynx, FIFO_SIZES, 0x003030a0); + /* 20 byte threshold before triggering PCI transfer */ + reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24); + /* threshold on both send FIFOs before transmitting: + FIFO size - cache line size - 1 */ + i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff; + i = 0x30 - i - 1; + reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i); -#if 0 -static int init_driver() -{ - struct pci_dev *dev = NULL; - int success = 0; + reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394); - if (num_of_cards) { - PRINT_G(KERN_DEBUG, __PRETTY_FUNCTION__ " called again"); - return 0; - } + reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT + | LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET + | LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK + | LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC + | LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW + | LINK_INT_ATF_UNDERFLOW); + + reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); + reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4); + reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0); + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV), + DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST + | DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST + | DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER); - PRINT_G(KERN_INFO, "looking for PCILynx cards"); + run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV); - while ((dev = pci_find_device(PCI_VENDOR_ID_TI, - PCI_DEVICE_ID_TI_PCILYNX, dev)) - != NULL) { - if (add_card(dev) == 0) { - success = 1; - } - } + reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0); + reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4); + reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0); + reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); - if (success == 0) { - PRINT_G(KERN_WARNING, "no operable PCILynx cards found"); - return -ENXIO; - } + run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV); -#ifdef CONFIG_IEEE1394_PCILYNX_PORTS - if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) { - PRINT_G(KERN_ERR, "allocation of char major number %d failed", - PCILYNX_MAJOR); - return -EBUSY; + reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID + | LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN + | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN + | LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX); + + if (!lynx->phyic.reg_1394a) { + /* attempt to enable contender bit -FIXME- would this work + * elsewhere? */ + reg_set_bits(lynx, GPIO_CTRL_A, 0x1); + reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); + } else { + /* set the contender bit in the extended PHY register + * set. (Should check that bis 0,1,2 (=0xE0) is set + * in register 2?) + */ + i = get_phy_reg(lynx, 4); + if (i != -1) set_phy_reg(lynx, 4, i | 0x40); } -#endif + + hpsb_add_host(host); + lynx->state = is_host; return 0; +#undef FAIL } -#endif + static size_t get_lynx_rom(struct hpsb_host *host, const quadlet_t **ptr) @@ -1603,15 +1522,6 @@ return sizeof(lynx_csr_rom); } -static struct hpsb_host_template lynx_template = { - name: PCILYNX_DRIVER_NAME, - initialize_host: lynx_initialize, - release_host: lynx_release, - get_rom: get_lynx_rom, - transmit_packet: lynx_transmit, - devctl: lynx_devctl -}; - static struct pci_device_id pci_table[] __devinitdata = { { vendor: PCI_VENDOR_ID_TI, @@ -1622,11 +1532,17 @@ { } /* Terminating entry */ }; -static struct pci_driver lynx_pcidriver = { +static struct pci_driver lynx_pci_driver = { name: PCILYNX_DRIVER_NAME, id_table: pci_table, probe: add_card, - remove: remove_card, + remove: __devexit_p(remove_card), +}; + +static struct hpsb_host_operations lynx_ops = { + get_rom: get_lynx_rom, + transmit_packet: lynx_transmit, + devctl: lynx_devctl, }; MODULE_AUTHOR("Andreas E. Bombe "); @@ -1635,30 +1551,52 @@ MODULE_SUPPORTED_DEVICE("pcilynx"); MODULE_DEVICE_TABLE(pci, pci_table); -static void __exit pcilynx_cleanup(void) -{ - pci_unregister_driver(&lynx_pcidriver); - hpsb_unregister_lowlevel(&lynx_template); - PRINT_G(KERN_INFO, "removed " PCILYNX_DRIVER_NAME " module"); -} - static int __init pcilynx_init(void) { int ret; - if (hpsb_register_lowlevel(&lynx_template)) { - PRINT_G(KERN_ERR, "registering failed"); - return -ENXIO; +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) { + PRINT_G(KERN_ERR, "allocation of char major number %d failed", + PCILYNX_MAJOR); + return -EBUSY; + } +#endif + + lynx_driver = hpsb_register_lowlevel(&lynx_ops, PCILYNX_DRIVER_NAME); + if (!lynx_driver) { + ret = -ENOMEM; + goto free_char_dev; } - ret = pci_module_init(&lynx_pcidriver); + ret = pci_module_init(&lynx_pci_driver); if (ret < 0) { PRINT_G(KERN_ERR, "PCI module init failed"); - hpsb_unregister_lowlevel(&lynx_template); + goto unregister_lowlevel; } + return 0; + + unregister_lowlevel: + hpsb_unregister_lowlevel(lynx_driver); + free_char_dev: +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif + return ret; } + +static void __exit pcilynx_cleanup(void) +{ + pci_unregister_driver(&lynx_pci_driver); + hpsb_unregister_lowlevel(lynx_driver); + +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif +} + module_init(pcilynx_init); module_exit(pcilynx_cleanup); diff -urN linux-2.5.3-pre2/drivers/ieee1394/pcilynx.h linux/drivers/ieee1394/pcilynx.h --- linux-2.5.3-pre2/drivers/ieee1394/pcilynx.h Sun Aug 12 12:39:02 2001 +++ linux/drivers/ieee1394/pcilynx.h Mon Jan 21 17:23:22 2002 @@ -40,8 +40,8 @@ u32 product; } phyic; - enum { clear, have_host_struct, have_intr, have_aux_buf, have_pcl_mem, - have_1394_buffers, have_iomappings } state; + enum { clear, have_intr, have_aux_buf, have_pcl_mem, + have_1394_buffers, have_iomappings, is_host } state; /* remapped memory spaces */ void *registers; diff -urN linux-2.5.3-pre2/drivers/ieee1394/raw1394.c linux/drivers/ieee1394/raw1394.c --- linux-2.5.3-pre2/drivers/ieee1394/raw1394.c Mon Jan 21 17:22:32 2002 +++ linux/drivers/ieee1394/raw1394.c Mon Jan 21 17:23:22 2002 @@ -467,7 +467,7 @@ hi = list_entry(lh, struct host_info, list); khl->nodes = hi->host->node_count; - strcpy(khl->name, hi->host->template->name); + strcpy(khl->name, hi->host->driver->name); khl++; } @@ -495,7 +495,7 @@ lh = lh->next; } hi = list_entry(lh, struct host_info, list); - hpsb_inc_host_usage(hi->host); + hpsb_ref_host(hi->host); list_add_tail(&fi->list, &hi->file_info_list); fi->host = hi->host; fi->state = connected; @@ -915,7 +915,7 @@ { struct file_info *fi; - if (minor(inode->i_rdev)) { + if (ieee1394_file_to_instance(file) > 0) { return -ENXIO; } @@ -983,7 +983,7 @@ list_del(&fi->list); spin_unlock_irq(&host_info_lock); - hpsb_dec_host_usage(fi->host); + hpsb_unref_host(fi->host); } kfree(fi); @@ -1017,14 +1017,18 @@ return -ENOMEM; } - devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE, - RAW1394_DEVICE_MAJOR, 0, + devfs_handle = devfs_register(NULL, + RAW1394_DEVICE_NAME, DEVFS_FL_NONE, + IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16, S_IFCHR | S_IRUSR | S_IWUSR, &file_ops, NULL); - if (devfs_register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, - &file_ops)) { - HPSB_ERR("raw1394 failed to register /dev/raw1394 device"); + if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_RAW1394, + THIS_MODULE, &file_ops)) { + HPSB_ERR("raw1394 failed to register minor device block"); + devfs_unregister(devfs_handle); + hpsb_unregister_highlevel(hl_handle); return -EBUSY; } printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME); @@ -1033,7 +1037,7 @@ static void __exit cleanup_raw1394(void) { - devfs_unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_RAW1394); devfs_unregister(devfs_handle); hpsb_unregister_highlevel(hl_handle); } diff -urN linux-2.5.3-pre2/drivers/ieee1394/sbp2.c linux/drivers/ieee1394/sbp2.c --- linux-2.5.3-pre2/drivers/ieee1394/sbp2.c Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/sbp2.c Mon Jan 21 17:23:22 2002 @@ -2,7 +2,7 @@ * sbp2.c - SBP-2 protocol driver for IEEE-1394 * * Copyright (C) 2000 James Goodwin, Filanet Corporation (www.filanet.com) - * jamesg@filanet.com + * jamesg@filanet.com (JSG) * * 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 @@ -80,7 +80,7 @@ * * The SBP-2 driver is still in an early state, but supports a variety of devices. * I have read/written many gigabytes of data from/to SBP-2 drives, and have seen - * performance of more than 16 MBytes/s on individual drives (limit of the media + * performance of more than 25 MBytes/s on individual drives (limit of the media * transfer rate). * * Following are the devices that have been tested successfully: @@ -91,7 +91,7 @@ * - LaCie IEEE-1394 hard drives (several flavors) * - QPS IEEE-1394 CD-RW/DVD drives and hard drives * - BusLink IEEE-1394 hard drives - * - Iomega IEEE-1394 Zip/Jazz drives + * - Iomega IEEE-1394 Zip/Jazz/Peerless drives * - ClubMac IEEE-1394 hard drives * - FirePower IEEE-1394 hard drives * - EzQuest IEEE-1394 hard drives and CD-RW drives @@ -104,7 +104,6 @@ * - Sony IEEE-1394 CD-RW drives * - Epson IEEE-1394 scanner * - ADS IEEE-1394 memory stick and compact flash readers - * (e.g. "insmod sbp2 mode_sense_hack=1" for mem stick and flash readers)) * - SBP-2 bridge-based devices (LSI, Oxford Semiconductor, Indigita bridges) * - Various other standard IEEE-1394 hard drives and enclosures * @@ -130,10 +129,6 @@ * add some init code to the kernel to support this... and modules are much * more flexible anyway. ;-) * - * - The scsi stack in recent kernels pass down the data transfer - * direction as scsicmd->sc_data_direction, which we should use - * instead of the sbp2scsi_direction_table. - * * * History: * @@ -222,9 +217,37 @@ * when we register our driver. This change * automtically adds hotplug support to the driver. * Kristian Hogsberg + * + * 11/17/01 - Various bugfixes/cleanups: + * * Remember to logout of device in sbp2_disconnect. + * * If we fail to reconnect to a device after bus reset + * remember to release unit directory, so the ieee1394 + * knows we no longer manage it. + * * Unregister scsi hosts in sbp2_remove_host when a + * hpsb_host goes away. + * * Remove stupid hack in sbp2_remove_host. + * * Switched to "manual" module initialization + * (i.e. not scsi_module.c) and moved sbp2_cleanup + * moved sbp2scsi_release to sbp2_module_ext. The + * release function is called once pr. registered + * scsi host, but sbp2_cleanup should only be called + * upon module unload. Moved much initialization + * from sbp2scsi_detect to sbp2_module_init. + * Kristian Hogsberg + * 01/06/02 - Misc bug fixes/enhancements: (JSG) + * * Enable use_new_eh_code for scsi stuff. + * * Do not write all ones for NULL ORB high/low fields, but + * rather leave reserved areas zeroed (per SBP2 spec). + * * Use newer scsi transfer direction passed down instead of our + * direction table. + * * Bumped login time-out to 20 seconds, as some devices are slow. + * * Fixed a couple scsi unregister bugs on module unload + * 01/13/02 - Fixed compatibility with certain SBP2 devices, such as Iomega + * 1394 devices (Peerless, Jazz). Also a bit of clean-up of the + * driver, thanks to H.J.Lu (hjl@lucon.org). Removed mode_sense_hack + * module load option, as it's been fixed in the 2.4 scsi stack. */ - - + /* * Includes @@ -244,6 +267,7 @@ #include #include #include +#include #include #include #include @@ -252,6 +276,16 @@ #include #include +#ifdef CONFIG_KBUILD_2_5 +#include +#include +#include +#else +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" +#endif + #include "ieee1394.h" #include "ieee1394_types.h" #include "ieee1394_core.h" @@ -260,9 +294,6 @@ #include "highlevel.h" #include "ieee1394_transactions.h" #include "ieee1394_hotplug.h" -#include "../scsi/scsi.h" -#include "../scsi/hosts.h" -#include "../scsi/sd.h" #include "sbp2.h" /* @@ -270,16 +301,6 @@ */ /* - * Set mode_sense_hack to 1 if you have some sort of unusual sbp2 device, - * like a 1394 memory stick reader, compact flash reader, or MO drive that - * does not support mode sense. Allows you to mount the media rw instead - * of ro. - */ -MODULE_PARM(mode_sense_hack,"i"); -MODULE_PARM_DESC(mode_sense_hack, "Emulate mode sense for devices like 1394 memory stick readers"); -static int mode_sense_hack = 0; - -/* * Change max_speed on module load if you have a bad IEEE-1394 controller * that has trouble running 2KB packets at 400mb. * @@ -334,7 +355,7 @@ */ #ifdef CONFIG_IEEE1394_SBP2_DEBUG_ORBS -#define SBP2_ORB_DEBUG(fmt, args...) HPSB_ERR("sbp2("__FUNCTION__"): "fmt, ## args) +#define SBP2_ORB_DEBUG(fmt, args...) HPSB_ERR("sbp2(%s): "fmt, __FUNCTION__, ## args) static u32 global_outstanding_command_orbs = 0; #define outstanding_orb_incr global_outstanding_command_orbs++ #define outstanding_orb_decr global_outstanding_command_orbs-- @@ -346,10 +367,10 @@ #ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA #define SBP2_DMA_ALLOC(fmt, args...) \ - HPSB_ERR("sbp2("__FUNCTION__")alloc(%d): "fmt, \ + HPSB_ERR("sbp2(%s)alloc(%d): "fmt, __FUNCTION__, \ ++global_outstanding_dmas, ## args) #define SBP2_DMA_FREE(fmt, args...) \ - HPSB_ERR("sbp2("__FUNCTION__")free(%d): "fmt, \ + HPSB_ERR("sbp2(%s)free(%d): "fmt, __FUNCTION__, \ --global_outstanding_dmas, ## args) static u32 global_outstanding_dmas = 0; #else @@ -357,7 +378,6 @@ #define SBP2_DMA_FREE(fmt, args...) #endif - #if CONFIG_IEEE1394_SBP2_DEBUG >= 2 #define SBP2_DEBUG(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) #define SBP2_INFO(fmt, args...) HPSB_ERR("sbp2: "fmt, ## args) @@ -395,12 +415,11 @@ * Globals */ -Scsi_Host_Template *global_scsi_tpnt = NULL; +static Scsi_Host_Template scsi_driver_template; static u8 sbp2_speedto_maxrec[] = { 0x7, 0x8, 0x9 }; static LIST_HEAD(sbp2_host_info_list); -static int sbp2_host_count = 0; static struct hpsb_highlevel *sbp2_hl_handle = NULL; @@ -671,13 +690,13 @@ static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id, struct sbp2scsi_host_info *hi) { - struct list_head *lh; + struct list_head *lh, *next; struct sbp2_command_info *command; unsigned long flags; sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags); if (!list_empty(&scsi_id->sbp2_command_orb_completed)) { - list_for_each(lh, &scsi_id->sbp2_command_orb_completed) { + list_for_each_safe(lh, next, &scsi_id->sbp2_command_orb_completed) { command = list_entry(lh, struct sbp2_command_info, list); /* Release our generic DMA's */ @@ -786,14 +805,26 @@ hi = (struct sbp2scsi_host_info *) command->Current_SCpnt->host->hostdata[0]; if (hi == NULL) { - printk(KERN_ERR __FUNCTION__": hi == NULL\n"); + printk(KERN_ERR "%s: hi == NULL\n", __FUNCTION__); return; } if (command->cmd_dma) { - pci_unmap_single(hi->host->pdev, command->cmd_dma, - command->dma_size, command->dma_dir); - SBP2_DMA_FREE("single bulk"); + if (command->dma_type == CMD_DMA_SINGLE) { + pci_unmap_single(hi->host->pdev, command->cmd_dma, + command->dma_size, command->dma_dir); + SBP2_DMA_FREE("single bulk"); + } else if (command->dma_type == CMD_DMA_PAGE) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) + pci_unmap_single(hi->host->pdev, command->cmd_dma, + command->dma_size, command->dma_dir); +#else + pci_unmap_page(hi->host->pdev, command->cmd_dma, + command->dma_size, command->dma_dir); +#endif /* Linux version < 2.4.13 */ + SBP2_DMA_FREE("single page"); + } /* XXX: Check for CMD_DMA_NONE bug */ + command->dma_type = CMD_DMA_NONE; command->cmd_dma = 0; } @@ -868,7 +899,6 @@ hpsb_unregister_highlevel(sbp2_hl_handle); sbp2_hl_handle = NULL; } - return; } static int sbp2_probe(struct unit_directory *ud) @@ -889,8 +919,10 @@ SBP2_DEBUG("sbp2_disconnect"); hi = sbp2_find_host_info(ud->ne->host); - if (hi != NULL) - sbp2_remove_device(hi, scsi_id); + if (hi != NULL) { + sbp2_logout_device(hi, scsi_id); + sbp2_remove_device(hi, scsi_id); + } } static void sbp2_update(struct unit_directory *ud) @@ -909,12 +941,10 @@ */ if (sbp2_login_device(hi, scsi_id)) { - /* Login failed too... so, just mark him as - * unvalidated, so that he gets cleaned up - * later. - */ + /* Login failed too, just remove the device. */ SBP2_ERR("sbp2_reconnect_device failed!"); sbp2_remove_device(hi, scsi_id); + hpsb_release_unit_directory(ud); return; } } @@ -974,11 +1004,13 @@ sbp2_spin_lock(&sbp2_host_info_lock, flags); list_add_tail(&hi->list, &sbp2_host_info_list); - sbp2_host_count++; sbp2_spin_unlock(&sbp2_host_info_lock, flags); /* Register our host with the SCSI stack. */ - sbp2scsi_register_scsi_host(hi); + hi->scsi_host = scsi_register (&scsi_driver_template, sizeof(void *)); + if (hi->scsi_host) + hi->scsi_host->hostdata[0] = (unsigned long)hi; + scsi_driver_template.present++; return; } @@ -994,22 +1026,38 @@ list_for_each (lh, &sbp2_host_info_list) { hi = list_entry(lh, struct sbp2scsi_host_info, list); - if (hi->host == host) { + if (hi->host == host) return hi; - } } - return(NULL); + return NULL; } /* - * This function is called when the host is removed + * This function returns a host info structure for a given Scsi_Host + * struct. + */ +static struct sbp2scsi_host_info *sbp2_find_host_info_scsi(struct Scsi_Host *host) +{ + struct list_head *lh; + struct sbp2scsi_host_info *hi; + + list_for_each (lh, &sbp2_host_info_list) { + hi = list_entry(lh, struct sbp2scsi_host_info, list); + if (hi->scsi_host == host) + return hi; + } + + return NULL; +} + +/* + * This function is called when a host is removed. */ static void sbp2_remove_host(struct hpsb_host *host) { struct sbp2scsi_host_info *hi; unsigned long flags; - int i; SBP2_DEBUG("sbp2_remove_host"); @@ -1017,21 +1065,7 @@ hi = sbp2_find_host_info(host); if (hi != NULL) { - /* Here's an annoying hack: we get a disconnect - * callback for each device, so this loop shouldn't be - * necessary. However, the sbp2 driver receives the - * remove_host callback before the nodemgr, so when we - * get the disconnect callback, we've already freed - * the host. Thus, we free the devices here... - */ - for (i = 0; i < SBP2SCSI_MAX_SCSI_IDS; i++) { - if (hi->scsi_id[i] != NULL) { - sbp2_logout_device(hi, hi->scsi_id[i]); - sbp2_remove_device(hi, hi->scsi_id[i]); - } - } sbp2util_remove_request_packet_pool(hi); - sbp2_host_count--; list_del(&hi->list); kfree(hi); } @@ -1203,10 +1237,7 @@ */ if (sbp2_login_device(hi, scsi_id)) { - /* - * Login failed... so, just mark him as unvalidated, so - * that he gets cleaned up later. - */ + /* Login failed, just remove the device. */ SBP2_ERR("sbp2_login_device failed"); sbp2_remove_device(hi, scsi_id); return -EBUSY; @@ -1231,11 +1262,13 @@ } /* - * This function removes (cleans-up after) any unvalidated sbp2 devices + * This function removes an sbp2 device from the sbp2scsi_host_info struct. */ static void sbp2_remove_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) { + SBP2_DEBUG("sbp2_remove_device"); + /* Complete any pending commands with selection timeout */ sbp2scsi_complete_all_commands(hi, scsi_id, DID_NO_CONNECT); @@ -1276,8 +1309,7 @@ SBP2_DMA_FREE("single logout orb"); } - SBP2_DEBUG("Unvalidated SBP-2 device removed, SCSI ID = %d", - scsi_id->id); + SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id); hi->scsi_id[scsi_id->id] = NULL; kfree(scsi_id); } @@ -1361,9 +1393,9 @@ save_flags(flags); cli(); - /* 10 second timeout */ + /* 20 second timeout */ if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) - sleep_on_timeout(&scsi_id->sbp2_login_wait, 10*HZ); + sleep_on_timeout(&scsi_id->sbp2_login_wait, 20*HZ); restore_flags(flags); SBP2_DEBUG("sbp2_login_device: initial check"); @@ -1381,7 +1413,7 @@ /* * Check status - */ + */ if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { @@ -1591,13 +1623,13 @@ ud = scsi_id->ud; /* Handle different fields in the unit directory, based on keys */ - for (i = 0; i < ud->arb_count; i++) { - switch (ud->arb_keys[i]) { + for (i = 0; i < ud->count; i++) { + switch (CONFIG_ROM_KEY(ud->quadlets[i])) { case SBP2_CSR_OFFSET_KEY: /* Save off the management agent address */ scsi_id->sbp2_management_agent_addr = - CONFIG_ROM_INITIAL_MEMORY_SPACE + - (ud->arb_values[i] << 2); + CSR_REGISTER_BASE + + (CONFIG_ROM_VALUE(ud->quadlets[i]) << 2); SBP2_DEBUG("sbp2_management_agent_addr = %x", (unsigned int) scsi_id->sbp2_management_agent_addr); @@ -1605,14 +1637,16 @@ case SBP2_COMMAND_SET_SPEC_ID_KEY: /* Command spec organization */ - scsi_id->sbp2_command_set_spec_id = ud->arb_values[i]; + scsi_id->sbp2_command_set_spec_id + = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set_spec_id = %x", (unsigned int) scsi_id->sbp2_command_set_spec_id); break; case SBP2_COMMAND_SET_KEY: /* Command set used by sbp2 device */ - scsi_id->sbp2_command_set = ud->arb_values[i]; + scsi_id->sbp2_command_set + = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set = %x", (unsigned int) scsi_id->sbp2_command_set); break; @@ -1622,7 +1656,8 @@ * Unit characterisitcs (orb related stuff * that I'm not yet paying attention to) */ - scsi_id->sbp2_unit_characteristics = ud->arb_values[i]; + scsi_id->sbp2_unit_characteristics + = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_unit_characteristics = %x", (unsigned int) scsi_id->sbp2_unit_characteristics); break; @@ -1632,7 +1667,8 @@ * Device type and lun (used for * detemining type of sbp2 device) */ - scsi_id->sbp2_device_type_and_lun = ud->arb_values[i]; + scsi_id->sbp2_device_type_and_lun + = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_device_type_and_lun = %x", (unsigned int) scsi_id->sbp2_device_type_and_lun); break; @@ -1645,7 +1681,8 @@ * bridge with 128KB max transfer size * limitation. */ - scsi_id->sbp2_firmware_revision = ud->arb_values[i]; + scsi_id->sbp2_firmware_revision + = CONFIG_ROM_VALUE(ud->quadlets[i]); if (scsi_id->sbp2_firmware_revision == SBP2_128KB_BROKEN_FIRMWARE) { SBP2_WARN("warning: Bridge chipset supports 128KB max transfer size"); @@ -1687,9 +1724,9 @@ scsi_id->max_payload_size = min(sbp2_speedto_maxrec[scsi_id->speed_code], (u8)(((be32_to_cpu(hi->host->csr.rom[2]) >> 12) & 0xf) - 1)); - SBP2_ERR("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [0x%02x/%u]", + SBP2_ERR("Node[" NODE_BUS_FMT "]: Max speed [%s] - Max payload [%u]", NODE_BUS_ARGS(scsi_id->ne->nodeid), hpsb_speedto_str[scsi_id->speed_code], - scsi_id->max_payload_size, 1 << ((u32)scsi_id->max_payload_size + 2)); + 1 << ((u32)scsi_id->max_payload_size + 2)); return(0); } @@ -1747,13 +1784,15 @@ unchar *scsi_cmd, unsigned int scsi_use_sg, unsigned int scsi_request_bufflen, - void *scsi_request_buffer, int dma_dir) + void *scsi_request_buffer, + unsigned char scsi_dir) { struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer; struct sbp2_command_orb *command_orb = &command->command_orb; struct sbp2_unrestricted_page_table *scatter_gather_element = &command->scatter_gather_element[0]; - u32 sg_count, sg_len; + int dma_dir = scsi_to_pci_dma_dir (scsi_dir); + u32 sg_count, sg_len, orb_direction; dma_addr_t sg_addr; int i; @@ -1765,25 +1804,49 @@ * that data_size becomes the number of s/g elements, and * page_size should be zero (for unrestricted). */ - command_orb->next_ORB_hi = 0xffffffff; - command_orb->next_ORB_lo = 0xffffffff; + command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1); + command_orb->next_ORB_lo = 0x0; command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size); command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code); command_orb->misc |= ORB_SET_NOTIFY(1); /* Notify us when complete */ /* + * Get the direction of the transfer. If the direction is unknown, then use our + * goofy table as a back-up. + */ + switch (scsi_dir) { + case SCSI_DATA_NONE: + orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; + break; + case SCSI_DATA_WRITE: + orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; + break; + case SCSI_DATA_READ: + orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; + break; + case SCSI_DATA_UNKNOWN: + default: + SBP2_ERR("SCSI data transfer direction not specified. " + "Update the SBP2 direction table in sbp2.h if " + "necessary for your application"); + print_command (scsi_cmd); + orb_direction = sbp2scsi_direction_table[*scsi_cmd]; + break; + } + + /* * Set-up our pagetable stuff... unfortunately, this has become * messier than I'd like. Need to clean this up a bit. ;-) */ - if (sbp2scsi_direction_table[*scsi_cmd] == ORB_DIRECTION_NO_DATA_TRANSFER) { + if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { SBP2_DEBUG("No data transfer"); /* * Handle no data transfer */ - command_orb->data_descriptor_hi = 0xffffffff; - command_orb->data_descriptor_lo = 0xffffffff; + command_orb->data_descriptor_hi = 0x0; + command_orb->data_descriptor_lo = 0x0; command_orb->misc |= ORB_SET_DIRECTION(1); } else if (scsi_use_sg) { @@ -1798,15 +1861,24 @@ SBP2_DEBUG("Only one s/g element"); command->dma_dir = dma_dir; command->dma_size = sgpnt[0].length; + command->dma_type = CMD_DMA_PAGE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) command->cmd_dma = pci_map_single (hi->host->pdev, sgpnt[0].address, command->dma_size, command->dma_dir); - SBP2_DMA_ALLOC("single scatter element"); +#else + command->cmd_dma = pci_map_page(hi->host->pdev, + sgpnt[0].page, + sgpnt[0].offset, + command->dma_size, + command->dma_dir); +#endif /* Linux version < 2.4.13 */ + SBP2_DMA_ALLOC("single page scatter element"); command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->cmd_dma; command_orb->misc |= ORB_SET_DATA_SIZE(command->dma_size); - command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); } else { int count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir); @@ -1818,7 +1890,7 @@ /* use page tables (s/g) */ command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); - command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->sge_dma; @@ -1863,6 +1935,7 @@ command->dma_dir = dma_dir; command->dma_size = scsi_request_bufflen; + command->dma_type = CMD_DMA_SINGLE; command->cmd_dma = pci_map_single (hi->host->pdev, scsi_request_buffer, command->dma_size, command->dma_dir); @@ -1877,15 +1950,15 @@ command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->cmd_dma; command_orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen); - command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); /* * Sanity, in case our direction table is not * up-to-date */ if (!scsi_request_bufflen) { - command_orb->data_descriptor_hi = 0xffffffff; - command_orb->data_descriptor_lo = 0xffffffff; + command_orb->data_descriptor_hi = 0x0; + command_orb->data_descriptor_lo = 0x0; command_orb->misc |= ORB_SET_DIRECTION(1); } @@ -1899,7 +1972,7 @@ /* Use page tables (s/g) */ command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); - command_orb->misc |= ORB_SET_DIRECTION(sbp2scsi_direction_table[*scsi_cmd]); + command_orb->misc |= ORB_SET_DIRECTION(orb_direction); /* * fill out our sbp-2 page tables (and split up @@ -1964,6 +2037,12 @@ SBP2_ORB_DEBUG("sending command orb %p, linked = %x, total orbs = %x", command_orb, command->linked, global_outstanding_command_orbs); + pci_dma_sync_single(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); /* * Check to see if there are any previous orbs to use */ @@ -2000,6 +2079,7 @@ } scsi_id->last_orb = command_orb; + scsi_id->last_orb_dma = command->command_orb_dma; } else { @@ -2014,6 +2094,9 @@ cpu_to_be32(command->command_orb_dma); /* Tells hardware that this pointer is valid */ scsi_id->last_orb->next_ORB_hi = 0x0; + pci_dma_sync_single(hi->host->pdev, scsi_id->last_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); /* * Only ring the doorbell if we need to (first parts of @@ -2041,6 +2124,7 @@ } scsi_id->last_orb = command_orb; + scsi_id->last_orb_dma = command->command_orb_dma; } return(0); @@ -2053,12 +2137,17 @@ Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { unchar *cmd = (unchar *) SCpnt->cmnd; - u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16; + unsigned int request_bufflen = SCpnt->request_bufflen; + u8 device_type + = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun); struct sbp2_command_info *command; SBP2_DEBUG("sbp2_send_command"); - SBP2_DEBUG("SCSI command = %02x", *cmd); - SBP2_DEBUG("SCSI transfer size = %x", SCpnt->request_bufflen); + SBP2_DEBUG("SCSI command:"); +#if CONFIG_IEEE1394_SBP2_DEBUG >= 2 + print_command (cmd); +#endif + SBP2_DEBUG("SCSI transfer size = %x", request_bufflen); SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg); /* @@ -2069,7 +2158,7 @@ (SCpnt->request_bufflen > SBP2_BROKEN_FIRMWARE_MAX_TRANSFER) && (device_type == TYPE_DISK) && (SCpnt->use_sg) && - (*cmd == 0x28 || *cmd == 0x2a || *cmd == 0x0a || *cmd == 0x08)) { + (*cmd == READ_6 || *cmd == READ_10 || *cmd == WRITE_6 || *cmd == WRITE_10)) { /* * Darn, a broken device. We'll need to split up the @@ -2088,14 +2177,24 @@ } /* + * The scsi stack sends down a request_bufflen which does not match the + * length field in the scsi cdb. This causes some sbp2 devices to + * reject this inquiry command. Fix is to fix request_bufflen to match + * the value in the cdb. + */ + if (*cmd == INQUIRY) { + request_bufflen = cmd[4]; + } + + /* * Now actually fill in the comamnd orb and sbp2 s/g list */ sbp2_create_command_orb(hi, scsi_id, command, cmd, SCpnt->use_sg, - SCpnt->request_bufflen, SCpnt->request_buffer, - scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + request_bufflen, SCpnt->request_buffer, + SCpnt->sc_data_direction); /* * Update our cdb if necessary (to handle sbp2 RBC command set - * differences). This is where the command set hacks go! =) + * differences). This is where the command set hacks go! =) */ if ((device_type == TYPE_DISK) || (device_type == TYPE_SDAD) || @@ -2185,7 +2284,7 @@ sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg, total_transfer, &sgpnt[current_sg], - scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + SCpnt->sc_data_direction); /* * Link up the orb, and ring the doorbell if needed @@ -2219,7 +2318,7 @@ sbp2_create_command_orb(hi, scsi_id, command, new_cmd, total_sg, total_transfer, &sgpnt[current_sg], - scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + SCpnt->sc_data_direction); /* * Link up the orb, and ring the doorbell if needed @@ -2361,15 +2460,20 @@ Scsi_Cmnd *SCpnt) { u8 *scsi_buf = SCpnt->request_buffer; - u32 device_type = (scsi_id->sbp2_device_type_and_lun & 0x00ff0000) >> 16; - + u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun); + SBP2_DEBUG("sbp2_check_sbp2_response"); switch (SCpnt->cmnd[0]) { case INQUIRY: - SBP2_DEBUG("Check Inquiry data"); + /* + * Make sure data length is ok. Minimum length is 36 bytes + */ + if (scsi_buf[4] == 0) { + scsi_buf[4] = 36 - 5; + } /* * Check for Simple Direct Access Device and change it to TYPE_DISK @@ -2485,6 +2589,12 @@ if (command) { SBP2_DEBUG("Found status for command ORB"); + pci_dma_sync_single(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); SBP2_ORB_DEBUG("matched command orb %p", &command->command_orb); outstanding_orb_decr; @@ -2498,22 +2608,27 @@ if (SCpnt && !command->linked) { /* - * Handle check conditions + * See if the target stored any scsi status information */ - if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { - - SBP2_DEBUG("CHECK CONDITION"); - + if (length > 8) { /* * Translate SBP-2 status to SCSI sense data */ scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer); + } + /* + * Handle check conditions. If there is either SBP status or SCSI status + * then we'll do a fetch agent reset and note that a check condition + * occured. + */ + if (STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc) || + scsi_status) { /* * Initiate a fetch agent reset. */ + SBP2_DEBUG("CHECK CONDITION"); sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT); - } SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb); @@ -2647,6 +2762,12 @@ SBP2_DEBUG("Found pending command to complete"); lh = scsi_id->sbp2_command_orb_inuse.next; command = list_entry(lh, struct sbp2_command_info, list); + pci_dma_sync_single(hi->host->pdev, command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single(hi->host->pdev, command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); sbp2util_mark_command_completed(scsi_id, command); if (command->Current_SCpnt && !command->linked) { void (*done)(Scsi_Cmnd *) = command->Current_done; @@ -2704,13 +2825,17 @@ /* * Debug stuff */ +#if CONFIG_IEEE1394_SBP2_DEBUG >= 1 + print_command (SCpnt->cmnd); print_sense("bh", SCpnt); +#endif break; case SBP2_SCSI_STATUS_SELECTION_TIMEOUT: SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT"); SCpnt->result = DID_NO_CONNECT << 16; + print_command (SCpnt->cmnd); break; case SBP2_SCSI_STATUS_CONDITION_MET: @@ -2718,6 +2843,7 @@ case SBP2_SCSI_STATUS_COMMAND_TERMINATED: SBP2_ERR("Bad SCSI status = %x", scsi_status); SCpnt->result = DID_ERROR << 16; + print_command (SCpnt->cmnd); break; default: @@ -2733,19 +2859,6 @@ } /* - * One more quick hack (not enabled by default). Some sbp2 devices - * do not support mode sense. Turn-on this hack to allow the - * device to pass the sd driver's write-protect test (so that you - * can mount the device rw). - */ - if (mode_sense_hack && SCpnt->result != DID_OK && SCpnt->cmnd[0] == MODE_SENSE) { - SBP2_INFO("Returning success to mode sense command"); - SCpnt->result = DID_OK; - SCpnt->sense_buffer[0] = 0; - memset (SCpnt->request_buffer, 0, 8); - } - - /* * If a bus reset is in progress and there was an error, complete * the command as busy so that it will get retried. */ @@ -2784,7 +2897,8 @@ unsigned long flags; SBP2_ERR("aborting sbp2 command"); - + print_command (SCpnt->cmnd); + if (scsi_id) { /* @@ -2797,6 +2911,14 @@ command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt); if (command) { SBP2_DEBUG("Found command to abort"); + pci_dma_sync_single(hi->host->pdev, + command->command_orb_dma, + sizeof(struct sbp2_command_orb), + PCI_DMA_BIDIRECTIONAL); + pci_dma_sync_single(hi->host->pdev, + command->sge_dma, + sizeof(command->scatter_gather_element), + PCI_DMA_BIDIRECTIONAL); sbp2util_mark_command_completed(scsi_id, command); if (command->Current_SCpnt && !command->linked) { void (*done)(Scsi_Cmnd *) = command->Current_done; @@ -2814,13 +2936,13 @@ sbp2_spin_unlock(&hi->sbp2_command_lock, flags); } - return(SCSI_ABORT_SUCCESS); + return(SUCCESS); } /* * Called by scsi stack when something has really gone wrong. */ -static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) +static int sbp2scsi_reset (Scsi_Cmnd *SCpnt) { struct sbp2scsi_host_info *hi = (struct sbp2scsi_host_info *) SCpnt->host->hostdata[0]; @@ -2831,7 +2953,7 @@ hpsb_reset_bus(hi->host, LONG_RESET); } - return(SCSI_RESET_SUCCESS); + return(SUCCESS); } /* @@ -2861,126 +2983,146 @@ } /* - * This routine is called at setup (init) and does nothing. Not used here. =) + * Called by scsi stack after scsi driver is registered */ -void sbp2scsi_setup( char *str, int *ints) +static int sbp2scsi_detect (Scsi_Host_Template *tpnt) { - SBP2_DEBUG("sbp2scsi_setup"); - return; + SBP2_DEBUG("sbp2scsi_detect"); + + /* + * Call sbp2_init to register with the ieee1394 stack. This + * results in a callback to sbp2_add_host for each ieee1394 + * host controller currently registered, and for each of those + * we register a scsi host with the scsi stack. + */ + sbp2_init(); + + /* We return the number of hosts registered. */ + return scsi_driver_template.present; } + /* - * This is our detection routine, and is where we init everything. + * Called for contents of procfs */ -static int sbp2scsi_detect (Scsi_Host_Template *tpnt) +static const char *sbp2scsi_info (struct Scsi_Host *host) { - SBP2_DEBUG("sbp2scsi_detect"); + struct sbp2scsi_host_info *hi = sbp2_find_host_info_scsi(host); + static char info[1024]; - global_scsi_tpnt = tpnt; + if (!hi) /* shouldn't happen, but... */ + return "IEEE-1394 SBP-2 protocol driver"; + + sprintf(info, "IEEE-1394 SBP-2 protocol driver\nHost Driver: %s\nSerial I/O: %s", + hi->host->driver->name, serialize_io ? "yes" : "no"); + + return info; +} + + +MODULE_AUTHOR("James Goodwin "); +MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); +MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); +MODULE_LICENSE("GPL"); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,26) +#define PROC_SCSI_SBP2 PROC_SCSI_NOT_PRESENT /* What should I use? */ +static struct proc_dir_entry proc_scsi_sbp2scsi = { + low_ino: PROC_SCSI_SBP2, + namelen: SBP2_DEVICE_NAME_SIZE, + name: SBP2_DEVICE_NAME, + mode: S_IFDIR | S_IRUGO | S_IXUGO, + nlink: 2 +}; +#endif + +/* SCSI host template */ +static Scsi_Host_Template scsi_driver_template = { + name: "IEEE-1394 SBP-2 protocol driver", + info: sbp2scsi_info, + detect: sbp2scsi_detect, + queuecommand: sbp2scsi_queuecommand, + eh_abort_handler: sbp2scsi_abort, + eh_device_reset_handler:sbp2scsi_reset, + eh_bus_reset_handler: sbp2scsi_reset, + eh_host_reset_handler: sbp2scsi_reset, + bios_param: sbp2scsi_biosparam, + can_queue: SBP2SCSI_MAX_OUTSTANDING_CMDS, + this_id: -1, + sg_tablesize: SBP2_MAX_SG_ELEMENTS, + cmd_per_lun: SBP2SCSI_MAX_CMDS_PER_LUN, + use_clustering: SBP2_CLUSTERING, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + use_new_eh_code: TRUE, +#endif + emulated: 1, #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,26) - global_scsi_tpnt->proc_name = SBP2_DEVICE_NAME; + proc_name: SBP2_DEVICE_NAME, +#else + proc_dir: &proc_scsi_sbp2scsi, #endif +}; + +static int sbp2_module_init(void) +{ + SBP2_DEBUG("sbp2_module_init"); + /* * Module load option for force one command at a time */ if (serialize_io) { SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)"); - global_scsi_tpnt->can_queue = 1; - global_scsi_tpnt->cmd_per_lun = 1; + scsi_driver_template.can_queue = 1; + scsi_driver_template.cmd_per_lun = 1; } /* - * Module load option to limit max size of requests from the scsi drivers + * Module load option to limit max size of requests from the + * scsi drivers */ if (no_large_packets) { - SBP2_ERR("Driver forced to limit max transfer size (no_large_packets = 1)"); - global_scsi_tpnt->sg_tablesize = 0x1f; - global_scsi_tpnt->use_clustering = DISABLE_CLUSTERING; + SBP2_ERR("Driver forced to limit max transfer size " + "(no_large_packets = 1)"); + scsi_driver_template.sg_tablesize = 0x1f; + scsi_driver_template.use_clustering = DISABLE_CLUSTERING; } - if (mode_sense_hack) { - SBP2_ERR("Mode sense emulation enabled (mode_sense_hack = 1)"); - } - - sbp2_init(); - - if (!sbp2_host_count) { - SBP2_ERR("Please load the lower level IEEE-1394 driver (e.g. ohci1394) before sbp2..."); + /* + * Ideally we would register our scsi_driver_template with the + * scsi stack and after that register with the ieee1394 stack + * and process the add_host callbacks. However, the detect + * function in the scsi host template requires that we find at + * least one host, so we "nest" the registrations by calling + * sbp2_init from the detect function. + */ + scsi_driver_template.module = THIS_MODULE; + if (SCSI_REGISTER_HOST(&scsi_driver_template) || + !scsi_driver_template.present) { + SBP2_ERR("Please load the lower level IEEE-1394 driver " + "(e.g. ohci1394) before sbp2..."); sbp2_cleanup(); + return -ENODEV; } - /* - * Since we are returning this count, it means that sbp2 must be - * loaded "after" the host adapter module... - */ - return(sbp2_host_count); + return 0; } -/* - * This function is called from sbp2_add_host, and is where we register - * our scsi host - */ -static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi) +static void __exit sbp2_module_exit(void) { - struct Scsi_Host *shpnt = NULL; - - SBP2_DEBUG("sbp2scsi_register_scsi_host"); - SBP2_DEBUG("sbp2scsi_host_info = %p", hi); + SBP2_DEBUG("sbp2_module_exit"); /* - * Let's register with the scsi stack + * On module unload we unregister with the ieee1394 stack + * which results in remove_host callbacks for all ieee1394 + * host controllers. In the callbacks we unregister the + * corresponding scsi hosts. */ - if (global_scsi_tpnt) { - - shpnt = scsi_register (global_scsi_tpnt, sizeof(void *)); - - /* - * If successful, save off a context (to be used when SCSI - * commands are received) - */ - if (shpnt) { - shpnt->hostdata[0] = (unsigned long)hi; - } - } - - return; -} - -/* Called when our module is released */ -static int sbp2scsi_release(struct Scsi_Host *host) -{ - SBP2_DEBUG("sbp2scsi_release"); sbp2_cleanup(); - return(0); -} - -/* Called for contents of procfs */ -static const char *sbp2scsi_info (struct Scsi_Host *host) -{ - return "IEEE-1394 SBP-2 protocol driver"; -} -MODULE_AUTHOR("James Goodwin "); -MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver"); -MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME); -MODULE_LICENSE("GPL"); + if (SCSI_UNREGISTER_HOST(&scsi_driver_template)) + SBP2_ERR("sbp2_module_exit: couldn't unregister scsi driver"); -/* SCSI host template */ -static Scsi_Host_Template driver_template = { - name: "IEEE1394 SBP-2", - detect: sbp2scsi_detect, - release: sbp2scsi_release, - info: sbp2scsi_info, - queuecommand: sbp2scsi_queuecommand, - abort: sbp2scsi_abort, - reset: sbp2scsi_reset, - bios_param: sbp2scsi_biosparam, - can_queue: SBP2SCSI_MAX_OUTSTANDING_CMDS, - this_id: -1, - sg_tablesize: SBP2_MAX_SG_ELEMENTS, - cmd_per_lun: SBP2SCSI_MAX_CMDS_PER_LUN, - use_clustering: SBP2_CLUSTERING, - emulated: 1 -}; +} -#include "../scsi/scsi_module.c" +module_init(sbp2_module_init); +module_exit(sbp2_module_exit); diff -urN linux-2.5.3-pre2/drivers/ieee1394/sbp2.h linux/drivers/ieee1394/sbp2.h --- linux-2.5.3-pre2/drivers/ieee1394/sbp2.h Wed Oct 17 14:19:20 2001 +++ linux/drivers/ieee1394/sbp2.h Mon Jan 21 17:23:22 2002 @@ -22,6 +22,15 @@ #ifndef SBP2_H #define SBP2_H +/* Some compatibility code */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#define SCSI_REGISTER_HOST(tmpl) scsi_register_module(MODULE_SCSI_HA, tmpl) +#define SCSI_UNREGISTER_HOST(tmpl) scsi_unregister_module(MODULE_SCSI_HA, tmpl) +#else +#define SCSI_REGISTER_HOST(tmpl) scsi_register_host(tmpl) +#define SCSI_UNREGISTER_HOST(tmpl) scsi_unregister_host(tmpl) +#endif + #define SBP2_DEVICE_NAME "sbp2" #define SBP2_DEVICE_NAME_SIZE 4 @@ -36,6 +45,7 @@ #define ORB_DIRECTION_READ_FROM_MEDIA 0x1 #define ORB_DIRECTION_NO_DATA_TRANSFER 0x2 +#define ORB_SET_NULL_PTR(value) ((value & 0x1) << 31) #define ORB_SET_NOTIFY(value) ((value & 0x1) << 31) #define ORB_SET_RQ_FMT(value) ((value & 0x3) << 29) #define ORB_SET_NODE_ID(value) ((value & 0xffff) << 16) @@ -199,6 +209,9 @@ #define SBP2_DEVICE_TYPE_AND_LUN_KEY 0x14 #define SBP2_FIRMWARE_REVISION_KEY 0x3c +#define SBP2_DEVICE_TYPE(q) (((q) >> 16) & 0x1f) +#define SBP2_DEVICE_LUN(q) ((q) & 0xffff) + #define SBP2_AGENT_STATE_OFFSET 0x00ULL #define SBP2_AGENT_RESET_OFFSET 0x04ULL #define SBP2_ORB_POINTER_OFFSET 0x08ULL @@ -218,15 +231,6 @@ #define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e #define SBP2_SW_VERSION_ENTRY 0x00010483 -/* - * Miscellaneous general config rom related defines - */ - -#define CONFIG_ROM_INITIAL_MEMORY_SPACE 0xfffff0000000ULL - -#define CONFIG_ROM_BASE_ADDRESS 0xfffff0000400ULL -#define CONFIG_ROM_ROOT_DIR_BASE 0xfffff0000414ULL -#define CONFIG_ROM_UNIT_DIRECTORY_OFFSET 0xfffff0000424ULL #define SBP2_128KB_BROKEN_FIRMWARE 0xa0b800 #define SBP2_BROKEN_FIRMWARE_MAX_TRANSFER 0x20000 @@ -252,7 +256,8 @@ #endif /* - * SCSI direction table... since the scsi stack doesn't specify direction... =( + * SCSI direction table... + * (now used as a back-up in case the direction passed down from above is "unknown") * * DIN = IN data direction * DOU = OUT data direction @@ -306,21 +311,27 @@ }; + +/* This is the two dma types we use for cmd_dma below */ +#define CMD_DMA_NONE 0x0 +#define CMD_DMA_PAGE 0x1 +#define CMD_DMA_SINGLE 0x2 + /* * Encapsulates all the info necessary for an outstanding command. */ struct sbp2_command_info { struct list_head list; - struct sbp2_command_orb command_orb; - dma_addr_t command_orb_dma; + struct sbp2_command_orb command_orb ____cacheline_aligned; + dma_addr_t command_orb_dma ____cacheline_aligned; Scsi_Cmnd *Current_SCpnt; void (*Current_done)(Scsi_Cmnd *); unsigned int linked; /* Also need s/g structure for each sbp2 command */ - struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS]; - dma_addr_t sge_dma; + struct sbp2_unrestricted_page_table scatter_gather_element[SBP2_MAX_SG_ELEMENTS] ____cacheline_aligned; + dma_addr_t sge_dma ____cacheline_aligned; void *sge_buffer; dma_addr_t cmd_dma; int dma_type; @@ -340,6 +351,7 @@ * Various sbp2 specific structures */ struct sbp2_command_orb *last_orb; + dma_addr_t last_orb_dma; struct sbp2_login_orb *login_orb; dma_addr_t login_orb_dma; struct sbp2_login_response *login_response; @@ -403,6 +415,13 @@ spinlock_t sbp2_request_packet_lock; /* + * This is the scsi host we register with the scsi mid level. + * We keep a reference to it here, so we can unregister it + * when the hpsb_host is removed. + */ + struct Scsi_Host *scsi_host; + + /* * Lists keeping track of inuse/free sbp2_request_packets. These structures are * used for sending out sbp2 command and agent reset packets. We initially create * a pool of request packets so that we don't have to do any kmallocs while in critical @@ -480,7 +499,8 @@ unchar *scsi_cmd, unsigned int scsi_use_sg, unsigned int scsi_request_bufflen, - void *scsi_request_buffer, int dma_dir); + void *scsi_request_buffer, + unsigned char scsi_dir); static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command); static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, @@ -498,17 +518,16 @@ /* * Scsi interface related prototypes */ -static const char *sbp2scsi_info (struct Scsi_Host *host); static int sbp2scsi_detect (Scsi_Host_Template *tpnt); +static const char *sbp2scsi_info (struct Scsi_Host *host); void sbp2scsi_setup(char *str, int *ints); static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[]); static int sbp2scsi_abort (Scsi_Cmnd *SCpnt); -static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags); +static int sbp2scsi_reset (Scsi_Cmnd *SCpnt); static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 status); static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 scsi_status, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); -static void sbp2scsi_register_scsi_host(struct sbp2scsi_host_info *hi); #endif /* SBP2_H */ diff -urN linux-2.5.3-pre2/drivers/ieee1394/video1394.c linux/drivers/ieee1394/video1394.c --- linux-2.5.3-pre2/drivers/ieee1394/video1394.c Sun Dec 30 10:31:51 2001 +++ linux/drivers/ieee1394/video1394.c Mon Jan 21 17:23:22 2002 @@ -43,9 +43,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "ieee1394.h" #include "ieee1394_types.h" @@ -56,7 +59,6 @@ #include "ohci1394.h" -#define VIDEO1394_MAJOR 172 #define ISO_CHANNELS 64 #define ISO_RECEIVE 0 #define ISO_TRANSMIT 1 @@ -78,6 +80,7 @@ struct dma_iso_ctx { struct ti_ohci *ohci; + int type; /* ISO_TRANSMIT or ISO_RECEIVE */ int ctx; int channel; int last_buffer; @@ -94,6 +97,7 @@ struct dma_cmd **ir_prg; struct it_dma_prg **it_prg; unsigned int *buffer_status; + struct timeval *buffer_time; /* time when the buffer was received */ unsigned int *last_used_cmd; /* For ISO Transmit with variable sized packets only ! */ int ctrlClear; @@ -101,8 +105,8 @@ int cmdPtr; int ctxMatch; wait_queue_head_t waitq; - spinlock_t lock; - unsigned int syt_offset; + spinlock_t lock; + unsigned int syt_offset; int flags; }; @@ -141,7 +145,7 @@ printk(level "video1394_%d: " fmt "\n" , card , ## args) static void irq_handler(int card, quadlet_t isoRecvIntEvent, - quadlet_t isoXmitIntEvent); + quadlet_t isoXmitIntEvent, void *data); static LIST_HEAD(video1394_cards); static spinlock_t video1394_cards_lock = SPIN_LOCK_UNLOCKED; @@ -149,8 +153,6 @@ static devfs_handle_t devfs_handle; static struct hpsb_highlevel *hl_handle = NULL; -static struct video_template video_tmpl = { irq_handler }; - /* Code taken from bttv.c */ /*******************************/ @@ -277,6 +279,7 @@ { int i; struct ti_ohci *ohci; + unsigned long *usage; if ((*d)==NULL) return -1; @@ -303,11 +306,19 @@ if ((*d)->buffer_status) kfree((*d)->buffer_status); + if ((*d)->buffer_time) + kfree((*d)->buffer_time); if ((*d)->last_used_cmd) kfree((*d)->last_used_cmd); if ((*d)->next_buffer) kfree((*d)->next_buffer); + usage = ((*d)->type == ISO_RECEIVE) ? &ohci->ir_ctx_usage : + &ohci->it_ctx_usage; + + /* clear the ISO context usage bit */ + clear_bit((*d)->ctx, usage); + kfree(*d); *d = NULL; @@ -315,12 +326,28 @@ } static struct dma_iso_ctx * -alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc, +alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc, int buf_size, int channel, unsigned int packet_size) { struct dma_iso_ctx *d=NULL; int i; + unsigned long *usage = (type == ISO_RECEIVE) ? &ohci->ir_ctx_usage : + &ohci->it_ctx_usage; + + /* try to claim the ISO context usage bit */ + for (i = 0; i < ohci->nb_iso_rcv_ctx; i++) { + if (!test_and_set_bit(i, usage)) { + PRINT(KERN_ERR, ohci->id, "Free iso ctx %d found", i); + break; + } + } + + if (i == ohci->nb_iso_rcv_ctx) { + PRINT(KERN_ERR, ohci->id, "No DMA contexts available"); + return NULL; + } + d = (struct dma_iso_ctx *)kmalloc(sizeof(struct dma_iso_ctx), GFP_KERNEL); if (d==NULL) { @@ -331,7 +358,8 @@ memset(d, 0, sizeof(struct dma_iso_ctx)); d->ohci = (void *)ohci; - d->ctx = ctx; + d->type = type; + d->ctx = i; d->channel = channel; d->num_desc = num_desc; d->frame_size = buf_size; @@ -436,6 +464,8 @@ d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), GFP_KERNEL); + d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval), + GFP_KERNEL); d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int), GFP_KERNEL); d->next_buffer = kmalloc(d->num_desc * sizeof(int), @@ -446,6 +476,11 @@ free_dma_iso_ctx(&d); return NULL; } + if (d->buffer_time == NULL) { + PRINT(KERN_ERR, ohci->id, "Failed to allocate buffer_time"); + free_dma_iso_ctx(&d); + return NULL; + } if (d->last_used_cmd == NULL) { PRINT(KERN_ERR, ohci->id, "Failed to allocate last_used_cmd"); free_dma_iso_ctx(&d); @@ -457,6 +492,7 @@ return NULL; } memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); + memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval)); memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int)); memset(d->next_buffer, -1, d->num_desc * sizeof(int)); @@ -554,38 +590,38 @@ } /* find which context is listening to this channel */ -int ir_ctx_listening(struct video_card *video, int channel) +static struct dma_iso_ctx ** +ir_ctx_listening(struct video_card *video, int channel) { int i; struct ti_ohci *ohci = video->ohci; - for (i=0;inb_iso_rcv_ctx-1;i++) - if (video->ir_context[i]) { - if (video->ir_context[i]->channel==channel) - return i; - } + for (i = 0; i < ohci->nb_iso_rcv_ctx-1; i++) + if (video->ir_context[i] && + video->ir_context[i]->channel == channel) + return &video->ir_context[i]; PRINT(KERN_ERR, ohci->id, "No iso context is listening to channel %d", channel); - return -1; + return NULL; } -int it_ctx_talking(struct video_card *video, int channel) +static struct dma_iso_ctx ** +it_ctx_talking(struct video_card *video, int channel) { int i; struct ti_ohci *ohci = video->ohci; - for (i=0;inb_iso_xmit_ctx;i++) - if (video->it_context[i]) { - if (video->it_context[i]->channel==channel) - return i; - } + for (i = 0; i < ohci->nb_iso_xmit_ctx; i++) + if (video->it_context[i] && + video->it_context[i]->channel==channel) + return &video->ir_context[i]; PRINT(KERN_ERR, ohci->id, "No iso context is talking to channel %d", channel); - return -1; + return NULL; } int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) @@ -603,6 +639,7 @@ if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) { reset_ir_status(d, i); d->buffer_status[i] = VIDEO1394_BUFFER_READY; + get_fast_time(&d->buffer_time[i]); } } spin_unlock(&d->lock); @@ -850,7 +887,7 @@ struct video_card *p; list_for_each(lh, &video1394_cards) { p = list_entry(lh, struct video_card, list); - if (p->id == MINOR(inode->i_rdev)) { + if (p->id == ieee1394_file_to_instance(file)) { video = p; ohci = video->ohci; break; @@ -860,7 +897,8 @@ spin_unlock_irqrestore(&video1394_cards_lock, flags); if (video == NULL) { - PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d", MINOR(inode->i_rdev)); + PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d", + __FUNCTION__, ieee1394_file_to_instance(file)); return -EFAULT; } @@ -875,9 +913,23 @@ if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; + + /* if channel < 0, find lowest available one */ + if (v.channel < 0) { + mask = (u64)0x1; + for (i=0; iISO_channel_usage & mask)) { + v.channel = i; + PRINT(KERN_INFO, ohci->id, "Found free channel %d", i); + break; + } + mask = mask << 1; + } + } + if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { PRINT(KERN_ERR, ohci->id, - "Iso channel %d out of bound", v.channel); + "Iso channel %d out of bounds", v.channel); return -EFAULT; } mask = (u64)0x1<ir_context[i] = - alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1, + alloc_dma_iso_ctx(ohci, ISO_RECEIVE, v.nb_buffers, v.buf_size, v.channel, 0); @@ -940,8 +992,8 @@ v.buf_size = video->ir_context[i]->buf_size; PRINT(KERN_INFO, ohci->id, - "iso context %d listen on channel %d", i+1, - v.channel); + "iso context %d listen on channel %d", + video->current_ctx->ctx, v.channel); } else { /* find a free iso transmit context */ @@ -955,7 +1007,7 @@ } video->it_context[i] = - alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i, + alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, v.nb_buffers, v.buf_size, v.channel, v.packet_size); @@ -986,7 +1038,6 @@ { int channel; u64 mask; - int i; if(copy_from_user(&channel, (void *)arg, sizeof(int))) return -EFAULT; @@ -1005,24 +1056,23 @@ ohci->ISO_channel_usage &= ~mask; if (cmd == VIDEO1394_UNLISTEN_CHANNEL) { - i = ir_ctx_listening(video, channel); - if (i<0) return -EFAULT; - - free_dma_iso_ctx(&video->ir_context[i]); - + struct dma_iso_ctx **d; + d = ir_ctx_listening(video, channel); + if (d == NULL) return -EFAULT; PRINT(KERN_INFO, ohci->id, "Iso context %d stop listening on channel %d", - i+1, channel); + (*d)->ctx, channel); + free_dma_iso_ctx(d); } else { - i = it_ctx_talking(video, channel); - if (i<0) return -EFAULT; - - free_dma_iso_ctx(&video->it_context[i]); - + struct dma_iso_ctx **d; + d = it_ctx_talking(video, channel); + if (d == NULL) return -EFAULT; PRINT(KERN_INFO, ohci->id, "Iso context %d stop talking on channel %d", - i, channel); + (*d)->ctx, channel); + free_dma_iso_ctx(d); + } return 0; @@ -1030,15 +1080,14 @@ case VIDEO1394_LISTEN_QUEUE_BUFFER: { struct video1394_wait v; - struct dma_iso_ctx *d; - int i; + struct dma_iso_ctx *d, **dd; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; - i = ir_ctx_listening(video, v.channel); - if (i<0) return -EFAULT; - d = video->ir_context[i]; + dd = ir_ctx_listening(video, v.channel); + if (dd == NULL) return -EFAULT; + d = *dd; if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, @@ -1091,17 +1140,18 @@ } case VIDEO1394_LISTEN_WAIT_BUFFER: + case VIDEO1394_LISTEN_POLL_BUFFER: { struct video1394_wait v; - struct dma_iso_ctx *d; + struct dma_iso_ctx *d, **dd; int i; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; - i = ir_ctx_listening(video, v.channel); - if (i<0) return -EFAULT; - d = video->ir_context[i]; + dd = ir_ctx_listening(video, v.channel); + if (dd==NULL) return -EFAULT; + d = *dd; if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, @@ -1119,6 +1169,12 @@ d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; break; case VIDEO1394_BUFFER_QUEUED: + if (cmd == VIDEO1394_LISTEN_POLL_BUFFER) { + /* for polling, return error code EINTR */ + spin_unlock_irqrestore(&d->lock, flags); + return -EINTR; + } + #if 1 while(d->buffer_status[v.buffer]!= VIDEO1394_BUFFER_READY) { @@ -1146,6 +1202,10 @@ return -EFAULT; } + /* set time of buffer */ + v.filltime = d->buffer_time[v.buffer]; +// printk("Buffer %d time %d\n", v.buffer, (d->buffer_time[v.buffer]).tv_usec); + /* * Look ahead to see how many more buffers have been received */ @@ -1167,15 +1227,14 @@ { struct video1394_wait v; struct video1394_queue_variable qv; - struct dma_iso_ctx *d; - int i; + struct dma_iso_ctx *d, **dd; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; - i = it_ctx_talking(video, v.channel); - if (i<0) return -EFAULT; - d = video->it_context[i]; + dd = it_ctx_talking(video, v.channel); + if (dd == NULL) return -EFAULT; + d = *dd; if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, @@ -1259,15 +1318,14 @@ case VIDEO1394_TALK_WAIT_BUFFER: { struct video1394_wait v; - struct dma_iso_ctx *d; - int i; + struct dma_iso_ctx *d, **dd; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; - i = it_ctx_talking(video, v.channel); - if (i<0) return -EFAULT; - d = video->it_context[i]; + dd = it_ctx_talking(video, v.channel); + if (dd == NULL) return -EFAULT; + d = *dd; if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, @@ -1328,7 +1386,7 @@ struct video_card *p; list_for_each(lh, &video1394_cards) { p = list_entry(lh, struct video_card, list); - if (p->id == MINOR(file->f_dentry->d_inode->i_rdev)) { + if (p->id == ieee1394_file_to_instance(file)) { video = p; break; } @@ -1337,8 +1395,8 @@ spin_unlock_irqrestore(&video1394_cards_lock, flags); if (video == NULL) { - PRINT_G(KERN_ERR, __FUNCTION__": Unknown video card for minor %d", - MINOR(file->f_dentry->d_inode->i_rdev)); + PRINT_G(KERN_ERR, "%s: Unknown video card for minor %d", + __FUNCTION__, ieee1394_file_to_instance(file)); return -EFAULT; } @@ -1357,7 +1415,7 @@ static int video1394_open(struct inode *inode, struct file *file) { - int i = MINOR(inode->i_rdev); + int i = ieee1394_file_to_instance(file); unsigned long flags; struct video_card *video = NULL; struct list_head *lh; @@ -1397,7 +1455,7 @@ struct video_card *p; list_for_each(lh, &video1394_cards) { p = list_entry(lh, struct video_card, list); - if (p->id == MINOR(inode->i_rdev)) { + if (p->id == ieee1394_file_to_instance(file)) { video = p; break; } @@ -1406,8 +1464,8 @@ spin_unlock_irqrestore(&video1394_cards_lock, flags); if (video == NULL) { - PRINT_G(KERN_ERR, __FUNCTION__": Unknown device for minor %d", - MINOR(inode->i_rdev)); + PRINT_G(KERN_ERR, "%s: Unknown device for minor %d", + __FUNCTION__, ieee1394_file_to_instance(file)); return 1; } @@ -1419,13 +1477,13 @@ mask = (u64)0x1<ir_context[i]->channel; if (!(ohci->ISO_channel_usage & mask)) PRINT(KERN_ERR, ohci->id, - "Channel %d is not being used", + "On release: Channel %d is not being used", video->ir_context[i]->channel); else ohci->ISO_channel_usage &= ~mask; PRINT(KERN_INFO, ohci->id, "Iso receive context %d stop listening " - "on channel %d", i+1, + "on channel %d", video->ir_context[i]->ctx, video->ir_context[i]->channel); free_dma_iso_ctx(&video->ir_context[i]); } @@ -1441,7 +1499,7 @@ ohci->ISO_channel_usage &= ~mask; PRINT(KERN_INFO, ohci->id, "Iso transmit context %d stop talking " - "on channel %d", i+1, + "on channel %d", video->it_context[i]->ctx, video->it_context[i]->channel); free_dma_iso_ctx(&video->it_context[i]); } @@ -1453,44 +1511,29 @@ } static void irq_handler(int card, quadlet_t isoRecvIntEvent, - quadlet_t isoXmitIntEvent) + quadlet_t isoXmitIntEvent, void *data) { int i; - unsigned long flags; - struct video_card *video = NULL; - struct list_head *lh; - - spin_lock_irqsave(&video1394_cards_lock, flags); - if (!list_empty(&video1394_cards)) { - struct video_card *p; - list_for_each(lh, &video1394_cards) { - p = list_entry(lh, struct video_card, list); - if (p->id == card) { - video = p; - break; - } - } - } - spin_unlock_irqrestore(&video1394_cards_lock, flags); + struct video_card *video = (struct video_card*) data; if (video == NULL) { - PRINT_G(KERN_ERR, __FUNCTION__": Unknown card number %d!!", - card); + PRINT_G(KERN_ERR, "%s: Unknown card number %d", + __FUNCTION__, card); return; } DBGMSG(card, "Iso event Recv: %08x Xmit: %08x", isoRecvIntEvent, isoXmitIntEvent); - for (i=0;iohci->nb_iso_rcv_ctx-1;i++) - if (isoRecvIntEvent & (1<<(i+1))) - wakeup_dma_ir_ctx(video->ohci, - video->ir_context[i]); - - for (i=0;iohci->nb_iso_xmit_ctx;i++) - if (isoXmitIntEvent & (1<ohci, - video->it_context[i]); + for (i = 0; i < video->ohci->nb_iso_rcv_ctx-1; i++) + if (video->ir_context[i] != NULL && + isoRecvIntEvent & (1<<(video->ir_context[i]->ctx))) + wakeup_dma_ir_ctx(video->ohci, video->ir_context[i]); + + for (i = 0; i < video->ohci->nb_iso_xmit_ctx; i++) + if (video->it_context[i] != NULL && + isoXmitIntEvent & (1<<(video->it_context[i]->ctx))) + wakeup_dma_it_ctx(video->ohci, video->it_context[i]); } static struct file_operations video1394_fops= @@ -1520,8 +1563,8 @@ list_add_tail(&video->list, &video1394_cards); spin_unlock_irqrestore(&video1394_cards_lock, flags); - if (ohci1394_register_video(ohci, &video_tmpl)<0) { - PRINT(KERN_ERR, ohci->id, "Register_video failed"); + if (ohci1394_hook_irq(ohci, irq_handler, (void*) video) != 0) { + PRINT(KERN_ERR, ohci->id, "ohci1394_hook_irq() failed"); return -1; } @@ -1555,7 +1598,8 @@ sprintf(name, "%d", video->id); video->devfs = devfs_register(devfs_handle, name, DEVFS_FL_AUTO_OWNER, - VIDEO1394_MAJOR, 0, + IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_VIDEO1394*16+video->id, S_IFCHR | S_IRUSR | S_IWUSR, &video1394_fops, NULL); @@ -1567,7 +1611,7 @@ { int i; - ohci1394_unregister_video(video->ohci, &video_tmpl); + ohci1394_unhook_irq(video->ohci, irq_handler, (void*) video); devfs_unregister(video->devfs); @@ -1595,23 +1639,21 @@ { struct ti_ohci *ohci; unsigned long flags; - struct list_head *lh; + struct list_head *lh, *next; + struct video_card *p; /* We only work with the OHCI-1394 driver */ - if (strcmp(host->template->name, OHCI1394_DRIVER_NAME)) + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) return; ohci = (struct ti_ohci *)host->hostdata; spin_lock_irqsave(&video1394_cards_lock, flags); - if (!list_empty(&video1394_cards)) { - struct video_card *p; - list_for_each(lh, &video1394_cards) { - p = list_entry(lh, struct video_card, list); - if (p ->ohci == ohci) { - remove_card(p); - break; - } + list_for_each_safe(lh, next, &video1394_cards) { + p = list_entry(lh, struct video_card, list); + if (p->ohci == ohci) { + remove_card(p); + break; } } spin_unlock_irqrestore(&video1394_cards_lock, flags); @@ -1624,7 +1666,7 @@ struct ti_ohci *ohci; /* We only work with the OHCI-1394 driver */ - if (strcmp(host->template->name, OHCI1394_DRIVER_NAME)) + if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) return; ohci = (struct ti_ohci *)host->hostdata; @@ -1649,19 +1691,19 @@ hpsb_unregister_highlevel (hl_handle); devfs_unregister(devfs_handle); - devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME); - - PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module\n"); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); + + PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); } static int __init video1394_init_module (void) { - if (devfs_register_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME, - &video1394_fops)) { - PRINT_G(KERN_ERR, "video1394: unable to get major %d\n", - VIDEO1394_MAJOR); - return -EIO; - } + if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394, + THIS_MODULE, &video1394_fops)) { + PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); + return -EIO; + } + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME, strlen(VIDEO1394_DRIVER_NAME), NULL); @@ -1673,10 +1715,11 @@ if (hl_handle == NULL) { PRINT_G(KERN_ERR, "No more memory for driver\n"); devfs_unregister(devfs_handle); - devfs_unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME); + ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); return -ENOMEM; } + PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module"); return 0; } diff -urN linux-2.5.3-pre2/drivers/ieee1394/video1394.h linux/drivers/ieee1394/video1394.h --- linux-2.5.3-pre2/drivers/ieee1394/video1394.h Sun Aug 12 12:39:02 2001 +++ linux/drivers/ieee1394/video1394.h Mon Jan 21 17:23:22 2002 @@ -35,11 +35,12 @@ VIDEO1394_LISTEN_CHANNEL = 0, VIDEO1394_UNLISTEN_CHANNEL, VIDEO1394_LISTEN_QUEUE_BUFFER, - VIDEO1394_LISTEN_WAIT_BUFFER, + VIDEO1394_LISTEN_WAIT_BUFFER, // wait until buffer is ready VIDEO1394_TALK_CHANNEL, VIDEO1394_UNTALK_CHANNEL, VIDEO1394_TALK_QUEUE_BUFFER, - VIDEO1394_TALK_WAIT_BUFFER + VIDEO1394_TALK_WAIT_BUFFER, + VIDEO1394_LISTEN_POLL_BUFFER // return immediately with -EINTR if not ready }; #define VIDEO1394_SYNC_FRAMES 0x00000001 @@ -47,7 +48,7 @@ #define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004 struct video1394_mmap { - unsigned int channel; + int channel; /* -1 to find an open channel in LISTEN/TALK */ unsigned int sync_tag; unsigned int nb_buffers; unsigned int buf_size; @@ -69,6 +70,7 @@ struct video1394_wait { unsigned int channel; unsigned int buffer; + struct timeval filltime; /* time of buffer full */ }; diff -urN linux-2.5.3-pre2/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- linux-2.5.3-pre2/drivers/isdn/Config.in Tue Jan 1 19:00:01 2002 +++ linux/drivers/isdn/Config.in Mon Jan 21 17:23:22 2002 @@ -83,6 +83,7 @@ dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL + dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL fi endmenu diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile --- linux-2.5.3-pre2/drivers/isdn/hisax/Makefile Tue Jan 1 19:00:01 2002 +++ linux/drivers/isdn/hisax/Makefile Mon Jan 21 17:23:22 2002 @@ -10,7 +10,7 @@ # Objects that export symbols. -export-objs := config.o fsm.o hisax_isac.o +export-objs := config.o fsm.o hisax_isac.o hisax_hscx.o # Multipart objects. @@ -63,6 +63,7 @@ obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o +obj-$(CONFIG_HISAX_FRITZ_CLASSIC) += hisax_isac.o hisax_hscx.o hisax_fcclassic.o CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) CFLAGS_cert.o := -DCERTIFICATION=$(CERT) diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c --- linux-2.5.3-pre2/drivers/isdn/hisax/avm_pci.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/avm_pci.c Mon Jan 21 17:23:22 2002 @@ -561,7 +561,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -577,7 +577,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); modehdlc(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -640,7 +640,7 @@ if (open_hdlcstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hdlc_l2l1; + st->l1.l2l1 = hdlc_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c --- linux-2.5.3-pre2/drivers/isdn/hisax/callc.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/callc.c Mon Jan 21 17:23:22 2002 @@ -250,7 +250,6 @@ } } - /* * Dial out */ @@ -264,7 +263,7 @@ link_debug(chanp, 0, "STAT_DCONN"); HL_LL(chanp, ISDN_STAT_DCONN); init_b_st(chanp, 0); - chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); + L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } static void @@ -281,7 +280,7 @@ lli_init_bchan_out(fi, event, arg); } else { FsmChangeState(fi, ST_OUT_DIAL); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp); + L4L3(chanp->d_st, CC_SETUP | REQUEST, chanp); } } @@ -299,7 +298,7 @@ lli_init_bchan_out(fi, event, arg); } else { FsmChangeState(fi, ST_OUT_DIAL); - chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp); + L4L3(chanp->d_st, CC_RESUME | REQUEST, chanp); } } @@ -366,33 +365,33 @@ case 1: /* OK, someone likes this call */ FsmDelTimer(&chanp->drel_timer, 61); FsmChangeState(fi, ST_IN_ALERT_SENT); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); break; case 5: /* direct redirect */ case 4: /* Proceeding desired */ FsmDelTimer(&chanp->drel_timer, 61); FsmChangeState(fi, ST_IN_PROCEED_SEND); - chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc); if (ret == 5) { memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm)); - chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); } break; case 2: /* Rejecting Call */ break; case 3: /* incomplete number */ FsmDelTimer(&chanp->drel_timer, 61); - chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); break; } } else { - chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } } @@ -403,7 +402,7 @@ struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); } static void @@ -412,7 +411,7 @@ struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_ALERT_SENT); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); } static void @@ -420,7 +419,7 @@ { struct Channel *chanp = fi->userdata; - chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc); } static void @@ -435,7 +434,7 @@ chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; init_b_st(chanp, !0); - chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); + L4L3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } static void @@ -448,9 +447,9 @@ } else { FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); #ifdef WANT_ALERT - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); #endif - chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + L4L3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); } } @@ -461,7 +460,7 @@ { struct Channel *chanp = fi->userdata; - chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc); } /* Call clearing */ @@ -493,8 +492,7 @@ FsmChangeState(fi, ST_WAIT_DRELEASE); if (chanp->proc) chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, - chanp->proc); + L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } } @@ -509,8 +507,7 @@ FsmChangeState(fi, ST_WAIT_DRELEASE); if (chanp->proc) chanp->proc->para.cause = 0x15; /* Call Rejected */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, - chanp->proc); + L4L3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); } } @@ -542,12 +539,12 @@ #ifndef ALERT_REJECT if (chanp->proc) chanp->proc->para.cause = 0x15; /* Call Rejected */ - chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); lli_dhup_close(fi, event, arg); #else FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); FsmChangeState(fi, ST_IN_ALERT_SENT); - chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + L4L3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); #endif } @@ -558,7 +555,7 @@ chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void @@ -613,7 +610,7 @@ chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BREL_DISC); - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } @@ -643,7 +640,7 @@ struct Channel *chanp = fi->userdata; chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL); lli_bhup_dhup(fi, event, arg); } @@ -656,8 +653,7 @@ lli_leased_hup(fi, chanp); } else { FsmChangeState(fi, ST_WAIT_D_REL_CNF); - chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, - chanp->proc); + L4L3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); } } @@ -768,7 +764,7 @@ struct Channel *chanp = fi->userdata; chanp->data_open = 0; - chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); + L4L3(chanp->b_st, DL_RELEASE | REQUEST, NULL); lli_bhup_fail(fi, event, arg); } @@ -942,7 +938,7 @@ if (pr == (CC_SETUP | INDICATION)) { if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) { pc->para.cause = 0x11; /* User busy */ - pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc); + L4L3(pc->st, CC_REJECT | REQUEST, pc); } else { chanp->proc = pc; pc->chan = chanp; @@ -1025,16 +1021,16 @@ if (!*stp) return -ENOMEM; (*stp)->next = NULL; - (*stp)->l1.l1l2 = dummy_pstack; (*stp)->l1.l1hw = dummy_pstack; (*stp)->l1.l1tei = dummy_pstack; + (*stp)->l1.l2l1 = dummy_pstack; + (*stp)->l2.l1l2 = dummy_pstack; (*stp)->l2.l2tei = dummy_pstack; - (*stp)->l2.l2l1 = dummy_pstack; - (*stp)->l2.l2l3 = dummy_pstack; - (*stp)->l3.l3l2 = dummy_pstack; + (*stp)->l2.l3l2 = dummy_pstack; + (*stp)->l3.l2l3 = dummy_pstack; (*stp)->l3.l3ml3 = dummy_pstack; - (*stp)->l3.l3l4 = dummy_pstack; - (*stp)->lli.l4l3 = dummy_pstack; + (*stp)->l3.l4l3 = dummy_pstack; + (*stp)->lli.l3l4 = dummy_pstack; (*stp)->ma.layer = dummy_pstack; return 0; } @@ -1073,7 +1069,7 @@ setstack_l3dc(st, chanp); st->lli.userdata = chanp; st->lli.l2writewakeup = NULL; - st->l3.l3l4 = dchan_l3l4; + st->lli.l3l4 = dchan_l3l4; return 0; } @@ -1147,8 +1143,7 @@ printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n"); if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) { printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); - csta->channel->d_st->lli.l4l3(csta->channel->d_st, - DL_ESTABLISH | REQUEST, NULL); + L4L3(csta->channel->d_st, DL_ESTABLISH | REQUEST, NULL); } return (0); } @@ -1317,7 +1312,7 @@ sprintf(tmp, "Ch%d X.75", chanp->chan); setstack_isdnl2(st, tmp); setstack_l3bc(st, chanp); - st->l2.l2l3 = lldata_handler; + st->l3.l2l3 = lldata_handler; st->lli.userdata = chanp; st->lli.l1writewakeup = NULL; st->lli.l2writewakeup = ll_writewakeup; @@ -1329,7 +1324,7 @@ case (ISDN_PROTO_L2_TRANS): case (ISDN_PROTO_L2_MODEM): case (ISDN_PROTO_L2_FAX): - st->l1.l1l2 = lltrans_handler; + st->l2.l1l2 = lltrans_handler; st->lli.userdata = chanp; st->lli.l1writewakeup = ll_writewakeup; setstack_transl2(st); @@ -1352,7 +1347,7 @@ dev_kfree_skb(skb); break; case (DL_ESTABLISH | REQUEST): - st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); + L2L1(st, PH_ACTIVATE | REQUEST, NULL); break; case (DL_RELEASE | REQUEST): break; @@ -1650,16 +1645,15 @@ HiSax_putstatus(csta, "Card", "%d channel %d set leased mode\n", csta->cardnr + 1, num + 1); - chanp->d_st->l1.l1l2 = leased_l1l2; - chanp->d_st->lli.l4l3 = leased_l4l3; - chanp->d_st->lli.l4l3(chanp->d_st, - DL_ESTABLISH | REQUEST, NULL); + chanp->d_st->l2.l1l2 = leased_l1l2; + chanp->d_st->l3.l4l3 = leased_l4l3; + L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); } break; case (6): /* set B-channel test loop */ num = *(unsigned int *) ic->parm.num; if (csta->stlist) - csta->stlist->l2.l2l1(csta->stlist, + csta->stlist->l1.l2l1(csta->stlist, PH_TESTLOOP | REQUEST, (void *) (long)num); break; case (7): /* set card in PTP mode */ @@ -1673,8 +1667,7 @@ HiSax_putstatus(csta, "set card ", "in PTP mode"); printk(KERN_DEBUG "HiSax: set card in PTP mode\n"); printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); - csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st, - DL_ESTABLISH | REQUEST, NULL); + L4L3(csta->channel[0].d_st, DL_ESTABLISH | REQUEST, NULL); } else { test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag); test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag); @@ -1698,8 +1691,7 @@ printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", num); } - chanp->d_st->lli.l4l3(chanp->d_st, - DL_ESTABLISH | REQUEST, NULL); + L4L3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); break; #ifdef MODULE case (55): @@ -1764,7 +1756,7 @@ case (ISDN_CMD_PROT_IO): for (st = csta->stlist; st; st = st->next) if (st->protocol == (ic->arg & 0xFF)) - return(st->lli.l4l3_proto(st, ic)); + return(st->l3.l4l3_proto(st, ic)); return(-EINVAL); break; default: @@ -1820,10 +1812,10 @@ if (!ack) nskb->pkt_type = PACKET_NOACK; if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I) - st->l3.l3l2(st, DL_DATA | REQUEST, nskb); + L3L2(st, DL_DATA | REQUEST, nskb); else { chanp->bcs->tx_cnt += len; - st->l2.l2l1(st, PH_DATA | REQUEST, nskb); + st->l1.l2l1(st, PH_DATA | REQUEST, nskb); } dev_kfree_skb(skb); } else diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- linux-2.5.3-pre2/drivers/isdn/hisax/config.c Tue Jan 1 19:00:01 2002 +++ linux/drivers/isdn/hisax/config.c Mon Jan 21 17:23:22 2002 @@ -1776,7 +1776,7 @@ cs->hw.hisax_d_if = hisax_d_if; cs->cardmsg = hisax_cardmsg; cs->tqueue.routine = (void *) (void *) hisax_bh; - cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1; + cs->channel[0].d_st->l1.l2l1 = hisax_d_l2l1; for (i = 0; i < 2; i++) { cs->bcs[i].BC_SetStack = hisax_bc_setstack; cs->bcs[i].BC_Close = hisax_bc_close; @@ -1823,7 +1823,7 @@ else pr = PH_DEACTIVATE | INDICATION; for (st = cs->stlist; st; st = st->next) - st->l1.l1l2(st, pr, NULL); + L1L2(st, pr, NULL); } } @@ -1876,7 +1876,7 @@ clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); for (st = cs->stlist; st; st = st->next) { if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) { - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); break; } } @@ -1901,10 +1901,10 @@ // FIXME use isdnl1? switch (pr) { case PH_ACTIVATE | INDICATION: - st->l1.l1l2(st, pr, NULL); + L1L2(st, pr, NULL); break; case PH_DEACTIVATE | INDICATION: - st->l1.l1l2(st, pr, NULL); + L1L2(st, pr, NULL); bcs->hw.b_if = NULL; break; case PH_DATA | INDICATION: @@ -1922,7 +1922,7 @@ } clear_bit(BC_FLG_BUSY, &bcs->Flag); if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) { - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } break; default: @@ -1953,7 +1953,7 @@ break; case PH_PULL | REQUEST: if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); else set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1988,7 +1988,7 @@ break; case PH_PULL | REQUEST: if (!test_bit(BC_FLG_BUSY, &bcs->Flag)) - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); else set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -2009,7 +2009,7 @@ hisax_d_if->b_if[st->l1.bc]->bcs = bcs; st->l1.bcs = bcs; - st->l2.l2l1 = hisax_b_l2l1; + st->l1.l2l1 = hisax_b_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/elsa_ser.c linux/drivers/isdn/hisax/elsa_ser.c --- linux-2.5.3-pre2/drivers/isdn/hisax/elsa_ser.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/elsa_ser.c Mon Jan 21 17:23:22 2002 @@ -591,7 +591,7 @@ } } else if (pr == (PH_ACTIVATE | REQUEST)) { test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); set_arcofi(st->l1.bcs->cs, st->l1.bc); mstartup(st->l1.bcs->cs); modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag)); @@ -617,7 +617,7 @@ case L1_MODE_TRANS: if (open_hscxstate(st->l1.hardware, bcs)) return (-1); - st->l2.l2l1 = hscx_l2l1; + st->l1.l2l1 = hscx_l2l1; break; case L1_MODE_MODEM: bcs->mode = L1_MODE_MODEM; @@ -632,7 +632,7 @@ bcs->hw.hscx.rcvidx = 0; bcs->tx_cnt = 0; bcs->cs->hw.elsa.bcs = bcs; - st->l2.l2l1 = modem_l2l1; + st->l1.l2l1 = modem_l2l1; break; } st->l1.bcs = bcs; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bds0.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/hfc_2bds0.c Mon Jan 21 17:23:22 2002 @@ -545,7 +545,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -561,7 +561,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); mode_2bs0(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -602,7 +602,7 @@ if (open_hfcstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hfc_l2l1; + st->l1.l2l1 = hfc_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); @@ -1066,7 +1066,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_2bs0.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/hfc_2bs0.c Mon Jan 21 17:23:22 2002 @@ -507,7 +507,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -523,7 +523,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); mode_hfc(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -566,7 +566,7 @@ if (open_hfcstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hfc_l2l1; + st->l1.l2l1 = hfc_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_pci.c linux/drivers/isdn/hisax/hfc_pci.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_pci.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/hfc_pci.c Mon Jan 21 17:23:22 2002 @@ -776,7 +776,7 @@ st->l1.l1hw(st, pr, arg); break; case (PH_ACTIVATE | REQUEST): - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); break; case (PH_TESTLOOP | REQUEST): if (1 & (long) arg) @@ -821,7 +821,7 @@ cs->dc.hfcpci.ph_state = 1; cs->hw.hfcpci.nt_mode = 1; cs->hw.hfcpci.nt_timer = 0; - cs->stlist->l2.l2l1 = dch_nt_l2l1; + cs->stlist->l1.l2l1 = dch_nt_l2l1; restore_flags(flags); debugl1(cs, "NT mode activated"); return (0); @@ -1211,7 +1211,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1462,7 +1462,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1478,7 +1478,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); mode_hfcpci(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -1528,7 +1528,7 @@ if (open_hfcpcistate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hfcpci_l2l1; + st->l1.l2l1 = hfcpci_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hfc_sx.c linux/drivers/isdn/hisax/hfc_sx.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hfc_sx.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/hfc_sx.c Mon Jan 21 17:23:22 2002 @@ -632,7 +632,7 @@ st->l1.l1hw(st, pr, arg); break; case (PH_ACTIVATE | REQUEST): - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); break; case (PH_TESTLOOP | REQUEST): if (1 & (long) arg) @@ -676,7 +676,7 @@ cs->dc.hfcsx.ph_state = 1; cs->hw.hfcsx.nt_mode = 1; cs->hw.hfcsx.nt_timer = 0; - cs->stlist->l2.l2l1 = dch_nt_l2l1; + cs->stlist->l1.l2l1 = dch_nt_l2l1; restore_flags(flags); debugl1(cs, "NT mode activated"); return (0); @@ -1008,7 +1008,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1248,7 +1248,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1264,7 +1264,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); mode_hfcsx(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -1314,7 +1314,7 @@ if (open_hfcsxstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hfcsx_l2l1; + st->l1.l2l1 = hfcsx_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- linux-2.5.3-pre2/drivers/isdn/hisax/hisax.h Tue Jan 1 19:00:01 2002 +++ linux/drivers/isdn/hisax/hisax.h Mon Jan 21 17:23:22 2002 @@ -203,9 +203,9 @@ long Flags; struct FsmInst l1m; struct FsmTimer timer; - void (*l1l2) (struct PStack *, int, void *); void (*l1hw) (struct PStack *, int, void *); void (*l1tei) (struct PStack *, int, void *); + void (*l2l1) (struct PStack *, int, void *); int mode, bc; int delay; }; @@ -247,8 +247,8 @@ struct sk_buff *windowar[MAX_WINDOW]; struct sk_buff_head i_queue; struct sk_buff_head ui_queue; - void (*l2l1) (struct PStack *, int, void *); - void (*l2l3) (struct PStack *, int, void *); + void (*l3l2) (struct PStack *, int, void *); + void (*l1l2) (struct PStack *, int, void *); void (*l2tei) (struct PStack *, int, void *); struct FsmInst l2m; struct FsmTimer t200, t203; @@ -258,9 +258,10 @@ }; struct Layer3 { - void (*l3l4) (struct PStack *, int, void *); + void (*l4l3) (struct PStack *, int, void *); + int (*l4l3_proto) (struct PStack *, isdn_ctrl *); void (*l3ml3) (struct PStack *, int, void *); - void (*l3l2) (struct PStack *, int, void *); + void (*l2l3) (struct PStack *, int, void *); struct FsmInst l3m; struct FsmTimer l3m_timer; struct sk_buff_head squeue; @@ -272,8 +273,7 @@ }; struct LLInterface { - void (*l4l3) (struct PStack *, int, void *); - int (*l4l3_proto) (struct PStack *, isdn_ctrl *); + void (*l3l4) (struct PStack *, int, void *); void *userdata; void (*l1writewakeup) (struct PStack *, int); void (*l2writewakeup) (struct PStack *, int); @@ -1348,3 +1348,40 @@ int TeiNew(void); void TeiFree(void); int certification_check(int output); + +static inline void +L2L1(struct PStack *st, int pr, void *arg) +{ + st->l1.l2l1(st, pr, arg); +} + +static inline void +L1L2(struct PStack *st, int pr, void *arg) +{ + st->l2.l1l2(st, pr, arg); +} + +static inline void +L3L2(struct PStack *st, int pr, void *arg) +{ + st->l2.l3l2(st, pr, arg); +} + +static inline void +L2L3(struct PStack *st, int pr, void *arg) +{ + st->l3.l2l3(st, pr, arg); +} + +static inline void +L3L4(struct PStack *st, int pr, void *arg) +{ + st->lli.l3l4(st, pr, arg); +} + +static inline void +L4L3(struct PStack *st, int pr, void *arg) +{ + st->l3.l4l3(st, pr, arg); +} + diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.c linux/drivers/isdn/hisax/hisax_fcclassic.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hisax_fcclassic.c Mon Jan 21 17:23:22 2002 @@ -0,0 +1,384 @@ +/* + * Driver for AVM Fritz!classic (ISA) ISDN card + * + * Author Kai Germaschewski + * Copyright 2001 by Kai Germaschewski + * 2001 by Karsten Keil + * + * based upon Karsten Keil's original avm_a1.c driver + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hisax_fcclassic.h" + +// debugging cruft +#define __debug_variable debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 0; +MODULE_PARM(debug, "i"); +#endif + +MODULE_AUTHOR("Kai Germaschewski /Karsten Keil "); +MODULE_DESCRIPTION("AVM Fritz!Card classic ISDN driver"); + +static int protocol = 2; /* EURO-ISDN Default */ +MODULE_PARM(protocol, "i"); + +// ---------------------------------------------------------------------- + +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 + +// ---------------------------------------------------------------------- + +static unsigned char +fcclassic_read_isac(struct isac *isac, unsigned char offset) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned char val; + + val = inb(adapter->isac_base + offset); + DBG(0x1000, " port %#x, value %#x", + offset, val); + return val; +} + +static void +fcclassic_write_isac(struct isac *isac, unsigned char offset, + unsigned char value) +{ + struct fritz_adapter *adapter = isac->priv; + + DBG(0x1000, " port %#x, value %#x", + offset, value); + outb(value, adapter->isac_base + offset); +} + +static void +fcclassic_read_isac_fifo(struct isac *isac, unsigned char * data, int size) +{ + struct fritz_adapter *adapter = isac->priv; + + insb(adapter->isac_fifo, data, size); +} + +static void +fcclassic_write_isac_fifo(struct isac *isac, unsigned char * data, int size) +{ + struct fritz_adapter *adapter = isac->priv; + + outsb(adapter->isac_fifo, data, size); +} + +static u_char +fcclassic_read_hscx(struct hscx *hscx, u_char offset) +{ + struct fritz_adapter *adapter = hscx->priv; + + return inb(adapter->hscx_base[hscx->channel] + offset); +} + +static void +fcclassic_write_hscx(struct hscx *hscx, u_char offset, u_char value) +{ + struct fritz_adapter *adapter = hscx->priv; + + outb(value, adapter->hscx_base[hscx->channel] + offset); +} + +static void +fcclassic_read_hscx_fifo(struct hscx *hscx, unsigned char * data, int size) +{ + struct fritz_adapter *adapter = hscx->priv; + + insb(adapter->hscx_fifo[hscx->channel], data, size); +} + +static void +fcclassic_write_hscx_fifo(struct hscx *hscx, unsigned char * data, int size) +{ + struct fritz_adapter *adapter = hscx->priv; + + outsb(adapter->hscx_fifo[hscx->channel], data, size); +} + +// ---------------------------------------------------------------------- + +static void +fcclassic_irq(int intno, void *dev, struct pt_regs *regs) +{ + struct fritz_adapter *adapter = dev; + unsigned char sval; + + DBG(2, ""); + while ((sval = inb(adapter->cfg_reg) & 0xf) != 0x7) { + DBG(2, "sval %#x", sval); + if (!(sval & AVM_A1_STAT_TIMER)) { + outb(0x1e, adapter->cfg_reg); + } + if (!(sval & AVM_A1_STAT_HSCX)) { + hscx_irq(adapter->hscx); + } + if (!(sval & AVM_A1_STAT_ISAC)) { + isac_irq(&adapter->isac); + } + } +} + +// ---------------------------------------------------------------------- + +static int __init +fcclassic_setup(struct fritz_adapter *adapter) +{ + u32 val = 0; + int i; + int retval; + + DBG(1,""); + + isac_init(&adapter->isac); // FIXME is this okay now + + adapter->cfg_reg = adapter->io + 0x1800; + adapter->isac_base = adapter->io + 0x1400 - 0x20; + adapter->isac_fifo = adapter->io + 0x1000; + adapter->hscx_base[0] = adapter->io + 0x0400 - 0x20; + adapter->hscx_fifo[0] = adapter->io; + adapter->hscx_base[1] = adapter->io + 0x0c00 - 0x20; + adapter->hscx_fifo[1] = adapter->io + 0x0800; + + retval = -EBUSY; + if (!request_region(adapter->cfg_reg , 8, + "fcclassic cfg")) + goto err; + if (!request_region(adapter->isac_base + 0x20 , 32, + "fcclassic isac")) + goto err_cfg_reg; + if (!request_region(adapter->isac_fifo , 1, + "fcclassic isac fifo")) + goto err_isac_base; + if (!request_region(adapter->hscx_base[0] + 0x20, 32, + "fcclassic hscx")) + goto err_isac_fifo; + if (!request_region(adapter->hscx_fifo[0] , 1, + "fcclassic hscx fifo")) + goto err_hscx_base_0; + if (!request_region(adapter->hscx_base[1] + 0x20, 32, + "fcclassic hscx")) + goto err_hscx_fifo_0; + if (!request_region(adapter->hscx_fifo[1] , 1, + "fcclassic hscx fifo")) + goto err_hscx_base_1; + retval = request_irq(adapter->irq, fcclassic_irq, 0, + "fcclassic", adapter); + if (retval) + goto err_hscx_fifo_1; + + // Reset + outb(0x00, adapter->cfg_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(200 * HZ / 1000); // 200 msec + outb(0x01, adapter->cfg_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(200 * HZ / 1000); // 200 msec + outb(0x00, adapter->cfg_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(200 * HZ / 1000); // 200 msec + + val = adapter->irq; + if (val == 9) + val = 2; + outb(val, adapter->cfg_reg + 1); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(200 * HZ / 1000); // 200 msec + outb(0x00, adapter->cfg_reg); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(200 * HZ / 1000); // 200 msec + + val = inb(adapter->cfg_reg); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + adapter->cfg_reg, val); + val = inb(adapter->cfg_reg + 3); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + adapter->cfg_reg + 3, val); + val = inb(adapter->cfg_reg + 2); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + adapter->cfg_reg + 2, val); + val = inb(adapter->cfg_reg); + printk(KERN_INFO "AVM A1: Byte at %x is %x\n", + adapter->cfg_reg, val); + + outb(0x16, adapter->cfg_reg); + outb(0x1e, adapter->cfg_reg); + + adapter->isac.priv = adapter; + adapter->isac.read_isac = &fcclassic_read_isac; + adapter->isac.write_isac = &fcclassic_write_isac; + adapter->isac.read_isac_fifo = &fcclassic_read_isac_fifo; + adapter->isac.write_isac_fifo = &fcclassic_write_isac_fifo; + isac_setup(&adapter->isac); + for (i = 0; i < 2; i++) { + hscx_init(&adapter->hscx[i]); + adapter->hscx[i].priv = adapter; + adapter->hscx[i].read_hscx = &fcclassic_read_hscx; + adapter->hscx[i].write_hscx = &fcclassic_write_hscx; + adapter->hscx[i].read_hscx_fifo = &fcclassic_read_hscx_fifo; + adapter->hscx[i].write_hscx_fifo = &fcclassic_write_hscx_fifo; + hscx_setup(&adapter->hscx[i]); + } + + return 0; + + err_hscx_fifo_1: + release_region(adapter->hscx_fifo[1] , 1); + err_hscx_base_1: + release_region(adapter->hscx_base[1] + 0x20, 32); + err_hscx_fifo_0: + release_region(adapter->hscx_fifo[0] , 1); + err_hscx_base_0: + release_region(adapter->hscx_base[0] + 0x20, 32); + err_isac_fifo: + release_region(adapter->isac_fifo , 1); + err_isac_base: + release_region(adapter->isac_base + 0x20, 32); + err_cfg_reg: + release_region(adapter->cfg_reg , 8); + err: + return retval; +} + +static void __exit fcclassic_release(struct fritz_adapter *adapter) +{ + DBG(1,""); + +// outb(0, adapter->io + AVM_STATUS0); + free_irq(adapter->irq, adapter); + release_region(adapter->hscx_fifo[1] , 1); + release_region(adapter->hscx_base[1] + 0x20, 32); + release_region(adapter->hscx_fifo[0] , 1); + release_region(adapter->hscx_base[0] + 0x20, 32); + release_region(adapter->isac_fifo , 1); + release_region(adapter->isac_base + 0x20, 32); + release_region(adapter->cfg_reg , 8); +} + +// ---------------------------------------------------------------------- + +static struct fritz_adapter * __init +new_adapter(struct pci_dev *pdev) +{ + struct fritz_adapter *adapter; + struct hisax_b_if *b_if[2]; + int i; + + adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL); + if (!adapter) + return NULL; + + memset(adapter, 0, sizeof(struct fritz_adapter)); + + SET_MODULE_OWNER(&adapter->isac.hisax_d_if); + adapter->isac.hisax_d_if.ifc.priv = &adapter->isac; + adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1; + + for (i = 0; i < 2; i++) { + // adapter->hscx[i].adapter = adapter; + adapter->hscx[i].channel = i; + adapter->hscx[i].b_if.ifc.priv = &adapter->hscx[i]; + adapter->hscx[i].b_if.ifc.l2l1 = hscx_b_l2l1; + } + pci_set_drvdata(pdev, adapter); + + for (i = 0; i < 2; i++) + b_if[i] = &adapter->hscx[i].b_if; + + hisax_register(&adapter->isac.hisax_d_if, b_if, "fcclassic", protocol); + + return adapter; +} + +static void +delete_adapter(struct fritz_adapter *adapter) +{ + hisax_unregister(&adapter->isac.hisax_d_if); + kfree(adapter); +} + +static int __init +fcclassic_probe(struct pci_dev *pdev, const struct isapnp_device_id *ent) +{ + struct fritz_adapter *adapter; + int retval; + + retval = -ENOMEM; + adapter = new_adapter(pdev); + if (!adapter) + goto err; + + adapter->io = pdev->resource[0].start; + adapter->irq = pdev->irq_resource[0].start; + + printk(KERN_INFO "hisax_fcclassic: found Fritz!Card classic at IO %#x irq %d\n", + adapter->io, adapter->irq); + + retval = fcclassic_setup(adapter); + if (retval) + goto err_free; + + return 0; + + err_free: + delete_adapter(adapter); + err: + return retval; +} + +static int __exit +fcclassic_remove(struct pci_dev *pdev) +{ + struct fritz_adapter *adapter = pci_get_drvdata(pdev); + + fcclassic_release(adapter); + delete_adapter(adapter); + + return 0; +} + +static struct pci_dev isa_dev[4]; + +static int __init +hisax_fcclassic_init(void) +{ + printk(KERN_INFO "hisax_fcclassic: Fritz!Card classic ISDN driver v0.0.1\n"); + + isa_dev[0].resource[0].start = 0x300; + isa_dev[0].irq_resource[0].start = 7; + + fcclassic_probe(isa_dev, NULL); + + return 0; +} + +static void __exit +hisax_fcclassic_exit(void) +{ + fcclassic_remove(isa_dev); +} + +module_init(hisax_fcclassic_init); +module_exit(hisax_fcclassic_exit); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.h linux/drivers/isdn/hisax/hisax_fcclassic.h --- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_fcclassic.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hisax_fcclassic.h Mon Jan 21 17:23:22 2002 @@ -0,0 +1,18 @@ +#include "hisax_if.h" +#include "hisax_isac.h" +#include "hisax_hscx.h" + +#include + +struct fritz_adapter { + unsigned int io; + unsigned int irq; + unsigned int cfg_reg; + unsigned int isac_base; + unsigned int isac_fifo; + unsigned int hscx_base[2]; + unsigned int hscx_fifo[2]; + struct isac isac; + + struct hscx hscx[2]; +}; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.c linux/drivers/isdn/hisax/hisax_hscx.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hisax_hscx.c Mon Jan 21 17:23:22 2002 @@ -0,0 +1,421 @@ +/* + * Driver for HSCX + * High-Level Serial Communcation Controller Extended + * + * Author Kai Germaschewski + * Copyright 2001 by Kai Germaschewski + * 2001 by Karsten Keil + * + * based upon Karsten Keil's original isac.c driver + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +/* TODO: + * comments in .h + */ + +#include +#include +#include +#include "hisax_hscx.h" + +// debugging cruft + +#define __debug_variable debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 1; +MODULE_PARM(debug, "i"); + +static char *HSCXVer[] = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; +#endif + +MODULE_AUTHOR("Kai Germaschewski /Karsten Keil "); +MODULE_DESCRIPTION("HSCX driver"); + +#define DBG_WARN 0x0001 +#define DBG_IRQ 0x0002 +#define DBG_L1M 0x0004 +#define DBG_PR 0x0008 +#define DBG_RFIFO 0x0100 +#define DBG_RPACKET 0x0200 +#define DBG_XFIFO 0x1000 +#define DBG_XPACKET 0x2000 + +#define HSCX_ISTA 0x20 +#define HSCX_ISTA_EXB 0x01 +#define HSCX_ISTA_EXA 0x02 +#define HSCX_ISTA_ICA 0x04 +#define HSCX_ISTA_TIN 0x08 +#define HSCX_ISTA_XPR 0x10 +#define HSCX_ISTA_RSC 0x20 +#define HSCX_ISTA_RPF 0x40 +#define HSCX_ISTA_RME 0x80 + +#define HSCX_CMDR 0x21 +#define HSCX_CMDR_RMC 0x80 +#define HSCX_CMDR_RHR 0x40 +#define HSCX_CMDR_RNR 0x20 +#define HSCX_CMDR_STI 0x10 +#define HSCX_CMDR_XTF 0x08 +#define HSCX_CMDR_XIF 0x04 +#define HSCX_CMDR_XME 0x02 +#define HSCX_CMDR_XRES 0x01 + +#define HSCX_EXIR 0x24 +#define HSCX_EXIR_XDU 0x40 + +#define HSCX_RSTA 0x27 +#define HSCX_RSTA_VFR 0x80 +#define HSCX_RSTA_RDO 0x40 +#define HSCX_RSTA_CRC 0x20 +#define HSCX_RSTA_RAB 0x10 + +#define HSCX_CCR1 0x2f +#define HSCX_CCR2 0x2c +#define HSCX_TSAR 0x31 +#define HSCX_TSAX 0x30 +#define HSCX_XCCR 0x32 +#define HSCX_RCCR 0x33 +#define HSCX_MODE 0x22 + +#define HSCX_XAD1 0x24 +#define HSCX_XAD2 0x25 +#define HSCX_RAH2 0x27 +#define HSCX_TIMR 0x23 +#define HSCX_STAR 0x21 +#define HSCX_RBCL 0x25 +#define HSCX_XBCH 0x2d +#define HSCX_VSTR 0x2e +#define HSCX_RLCR 0x2e +#define HSCX_MASK 0x20 + +static inline void B_L1L2(struct hscx *hscx, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &hscx->b_if; + + DBG(0x10, "pr %#x", pr); + ifc->l1l2(ifc, pr, arg); +} + +static void hscx_version(struct hscx *hscx) +{ + int val; + + val = hscx->read_hscx(hscx, HSCX_VSTR) & 0xf; + DBG(1, "HSCX version (%x): %s", val, HSCXVer[val]); +} + +static void hscx_empty_fifo(struct hscx *hscx, int count) +{ + u_char *ptr; + + DBG(DBG_IRQ, "count %d", count); + + if ((hscx->rcvidx + count) >= HSCX_BUFMAX) { + DBG(DBG_WARN, "overrun %d", hscx->rcvidx + count); + hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC); + hscx->rcvidx = 0; + return; + } + ptr = hscx->rcvbuf + hscx->rcvidx; + hscx->rcvidx += count; + hscx->read_hscx_fifo(hscx, ptr, count); + hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC); + DBG_PACKET(DBG_RFIFO, ptr, count); +} + +static void hscx_fill_fifo(struct hscx *hscx) +{ + int count; + unsigned char cmd; + int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32; + unsigned char *ptr; + + if (!hscx->tx_skb) + BUG(); + + count = hscx->tx_skb->len; + if (count <= 0) + BUG(); + + DBG(DBG_IRQ, "count %d", count); + + if (count > fifo_size || hscx->mode == L1_MODE_TRANS) { + count = fifo_size; + cmd = 0x8; + } else { + cmd = 0xa; + } + + ptr = hscx->tx_skb->data; + skb_pull(hscx->tx_skb, count); + hscx->tx_cnt += count; + DBG_PACKET(DBG_XFIFO, ptr, count); + hscx->write_hscx_fifo(hscx, ptr, count); + hscx->write_hscx(hscx, HSCX_CMDR, cmd); +} + +static void hscx_retransmit(struct hscx *hscx) +{ + if (!hscx->tx_skb) { + DBG(DBG_WARN, "no skb"); + return; + } + skb_push(hscx->tx_skb, hscx->tx_cnt); + hscx->tx_cnt = 0; + hscx->write_hscx(hscx, HSCX_CMDR, 0x01); +} + +static inline void hscx_rme_interrupt(struct hscx *hscx) +{ + unsigned char val; + int count; + struct sk_buff *skb; + int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32; + + val = hscx->read_hscx(hscx, HSCX_RSTA); + if ((val & (HSCX_RSTA_VFR | HSCX_RSTA_RDO | HSCX_RSTA_CRC | HSCX_RSTA_RAB) ) + != (HSCX_RSTA_VFR | HSCX_RSTA_CRC)) { + DBG(DBG_WARN, "RSTA %#x, dropped", val); + hscx->write_hscx(hscx, HSCX_CMDR, HSCX_CMDR_RMC); + goto out; + } + + count = hscx->read_hscx(hscx, HSCX_RBCL) & (fifo_size-1); + DBG(DBG_IRQ, "RBCL %#x", count); + if (count == 0) + count = fifo_size; + + hscx_empty_fifo(hscx, count); + + count = hscx->rcvidx; + if (count < 1) { + DBG(DBG_WARN, "count %d < 1", count); + goto out; + } + + skb = alloc_skb(count, GFP_ATOMIC); + if (!skb) { + DBG(DBG_WARN, "no memory, dropping\n"); + goto out; + } + memcpy(skb_put(skb, count), hscx->rcvbuf, count); + DBG_SKB(DBG_RPACKET, skb); + B_L1L2(hscx, PH_DATA | INDICATION, skb); + out: + hscx->rcvidx = 0; +} + +static inline void hscx_xpr_interrupt(struct hscx *hscx) +{ + struct sk_buff *skb; + + skb = hscx->tx_skb; + if (!skb) + return; + + if (skb->len > 0) { + hscx_fill_fifo(hscx); + return; + } + hscx->tx_cnt = 0; + hscx->tx_skb = NULL; + B_L1L2(hscx, PH_DATA | CONFIRM, (void *) skb->truesize); + dev_kfree_skb_irq(skb); +} + +static inline void hscx_exi_interrupt(struct hscx *hscx) +{ + unsigned char val; + + val = hscx->read_hscx(hscx, HSCX_EXIR); + DBG(2, "EXIR %#x", val); + + if (val & HSCX_EXIR_XDU) { + DBG(DBG_WARN, "HSCX XDU"); + if (hscx->mode == L1_MODE_TRANS) { + hscx_fill_fifo(hscx); + } else { + hscx_retransmit(hscx); + } + } +} + +static void hscx_reg_interrupt(struct hscx *hscx, unsigned char val) +{ + struct sk_buff *skb; + + if (val & HSCX_ISTA_XPR) { + DBG(DBG_IRQ, "XPR"); + hscx_xpr_interrupt(hscx); + } + if (val & HSCX_ISTA_RME) { + DBG(DBG_IRQ, "RME"); + hscx_rme_interrupt(hscx); + } + if (val & HSCX_ISTA_RPF) { + int fifo_size = test_bit(HSCX_IPAC, &hscx->flags)? 64: 32; + + DBG(DBG_IRQ, "RPF"); + hscx_empty_fifo(hscx, fifo_size); + if (hscx->mode == L1_MODE_TRANS) { + skb = dev_alloc_skb(fifo_size); + if (!skb) { + DBG(DBG_WARN, "no memory, dropping\n"); + goto out; + } + memcpy(skb_put(skb, fifo_size), hscx->rcvbuf, fifo_size); + DBG_SKB(DBG_RPACKET, skb); + B_L1L2(hscx, PH_DATA | INDICATION, skb); + out: + hscx->rcvidx = 0; + } + } +} + +void hscx_irq(struct hscx *hscx_a) +{ + struct hscx *hscx_b = hscx_a + 1; + unsigned char val; + + val = hscx_b->read_hscx(hscx_b, HSCX_ISTA); + DBG(DBG_IRQ, "ISTA B %#x", val); + + if (val & HSCX_ISTA_EXB) { + DBG(DBG_IRQ, "EXI B"); + hscx_exi_interrupt(hscx_b); + } + if (val & HSCX_ISTA_EXA) { + DBG(DBG_IRQ, "EXI A"); + hscx_exi_interrupt(hscx_a); + } + if (val & 0xf8) { + hscx_reg_interrupt(hscx_b, val); + } + if (val & HSCX_ISTA_ICA) { + val = hscx_a->read_hscx(hscx_a, HSCX_ISTA); + DBG(DBG_IRQ, "ISTA A %#x", val); + hscx_reg_interrupt(hscx_a, val); + hscx_a->write_hscx(hscx_a, HSCX_MASK, 0xff); + hscx_a->write_hscx(hscx_a, HSCX_MASK, 0x00); + } + hscx_b->write_hscx(hscx_b, HSCX_MASK, 0xff); + hscx_b->write_hscx(hscx_b, HSCX_MASK, 0x00); +} + +static void modehscx(struct hscx *hscx, int mode) +{ + int bc = hscx->channel; + + DBG(0x40, "hscx %c mode %d --> %d", + 'A' + hscx->channel, hscx->mode, mode); + + hscx->mode = mode; + hscx->write_hscx(hscx, HSCX_XAD1, 0xFF); + hscx->write_hscx(hscx, HSCX_XAD2, 0xFF); + hscx->write_hscx(hscx, HSCX_RAH2, 0xFF); + hscx->write_hscx(hscx, HSCX_XBCH, 0x0); + hscx->write_hscx(hscx, HSCX_RLCR, 0x0); + hscx->write_hscx(hscx, HSCX_CCR1, + test_bit(HSCX_IPAC, &hscx->flags) ? 0x82 : 0x85); + hscx->write_hscx(hscx, HSCX_CCR2, 0x30); + hscx->write_hscx(hscx, HSCX_XCCR, 7); + hscx->write_hscx(hscx, HSCX_RCCR, 7); + + /* Switch IOM 1 SSI */ + if (test_bit(HSCX_IOM1, &hscx->flags)) + bc = 1; + + hscx->write_hscx(hscx, HSCX_TSAX, hscx->tsaxr); + hscx->write_hscx(hscx, HSCX_TSAR, hscx->tsaxr); + + switch (mode) { + case (L1_MODE_NULL): + hscx->write_hscx(hscx, HSCX_TSAX, 0x1f); + hscx->write_hscx(hscx, HSCX_TSAR, 0x1f); + hscx->write_hscx(hscx, HSCX_MODE, 0x84); + break; + case (L1_MODE_TRANS): + hscx->write_hscx(hscx, HSCX_MODE, 0xe4); + break; + case (L1_MODE_HDLC): + hscx->write_hscx(hscx, HSCX_CCR1, test_bit(HSCX_IPAC, &hscx->flags) ? 0x8a : 0x8d); + hscx->write_hscx(hscx, HSCX_MODE, 0x8c); + break; + } + if (mode) + hscx->write_hscx(hscx, HSCX_CMDR, 0x41); + + hscx->write_hscx(hscx, HSCX_ISTA, 0x00); +} + +void hscx_init(struct hscx *hscx) +{ + if (hscx->channel) + hscx->tsaxr = 0x03; + else + hscx->tsaxr = 0x2f; +} + +void hscx_setup(struct hscx *hscx) +{ + hscx_version(hscx); + hscx->mode = -1; + modehscx(hscx, L1_MODE_NULL); +} + +void hscx_b_l2l1(struct hisax_if *ifc, int pr, void *arg) +{ + struct hscx *hscx = ifc->priv; + struct sk_buff *skb = arg; + int mode; + + DBG(0x10, "pr %#x", pr); + + switch (pr) { + case PH_DATA | REQUEST: + if (hscx->tx_skb) + BUG(); + + hscx->tx_skb = skb; + DBG_SKB(1, skb); + hscx_fill_fifo(hscx); + break; + case PH_ACTIVATE | REQUEST: + mode = (int) arg; + DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", hscx->channel + 1, mode); + modehscx(hscx, mode); + B_L1L2(hscx, PH_ACTIVATE | INDICATION, NULL); + break; + case PH_DEACTIVATE | REQUEST: + DBG(4,"B%d,PH_DEACTIVATE_REQUEST", hscx->channel + 1); + modehscx(hscx, L1_MODE_NULL); + B_L1L2(hscx, PH_DEACTIVATE | INDICATION, NULL); + break; + } +} + +static int __init hisax_hscx_init(void) +{ + printk(KERN_INFO "hisax_hscx: HSCX ISDN driver v0.1.0\n"); + return 0; +} + +static void __exit hisax_hscx_exit(void) +{ +} + +EXPORT_SYMBOL(hscx_init); +EXPORT_SYMBOL(hscx_b_l2l1); + +EXPORT_SYMBOL(hscx_setup); +EXPORT_SYMBOL(hscx_irq); + +module_init(hisax_hscx_init); +module_exit(hisax_hscx_exit); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.h linux/drivers/isdn/hisax/hisax_hscx.h --- linux-2.5.3-pre2/drivers/isdn/hisax/hisax_hscx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hisax_hscx.h Mon Jan 21 17:23:22 2002 @@ -0,0 +1,37 @@ +#ifndef __HISAX_HSCX_H__ +#define __HISAX_HSCX_H__ + +#include +#include "fsm.h" +#include "hisax_if.h" + +#define HSCX_BUFMAX 4096 + +#define HSCX_IOM1 0 +#define HSCX_IPAC 1 + +struct hscx { + void *priv; + u_long flags; + struct hisax_b_if b_if; + int mode; + int channel; + u_char tsaxr; + struct sk_buff *tx_skb; + int tx_cnt; + u_char rcvbuf[HSCX_BUFMAX]; + int rcvidx; + + u_char (*read_hscx) (struct hscx *, u_char); + void (*write_hscx) (struct hscx *, u_char, u_char); + void (*read_hscx_fifo) (struct hscx *, u_char *, int); + void (*write_hscx_fifo)(struct hscx *, u_char *, int); +}; + +void hscx_init(struct hscx *hscx); +void hscx_b_l2l1(struct hisax_if *hisax_b_if, int pr, void *arg); + +void hscx_setup(struct hscx *hscx); +void hscx_irq(struct hscx *hscx); + +#endif diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c --- linux-2.5.3-pre2/drivers/isdn/hisax/hscx.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/hscx.c Mon Jan 21 17:23:22 2002 @@ -134,7 +134,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -150,7 +150,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); modehscx(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -214,7 +214,7 @@ if (open_hscxstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = hscx_l2l1; + st->l1.l2l1 = hscx_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/icc.c linux/drivers/isdn/hisax/icc.c --- linux-2.5.3-pre2/drivers/isdn/hisax/icc.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/icc.c Mon Jan 21 17:23:22 2002 @@ -89,7 +89,7 @@ debugl1(cs, "D-Channel Busy cleared"); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + L1L2(stptr, PH_PAUSE | CONFIRM, NULL); stptr = stptr->next; } } @@ -506,7 +506,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -603,7 +603,7 @@ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + L1L2(stptr, PH_PAUSE | INDICATION, NULL); stptr = stptr->next; } } else { diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c --- linux-2.5.3-pre2/drivers/isdn/hisax/isac.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/isac.c Mon Jan 21 17:23:22 2002 @@ -93,7 +93,7 @@ debugl1(cs, "D-Channel Busy cleared"); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + L1L2(stptr, PH_PAUSE | CONFIRM, NULL); stptr = stptr->next; } } @@ -510,7 +510,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -605,7 +605,7 @@ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + L1L2(stptr, PH_PAUSE | INDICATION, NULL); stptr = stptr->next; } } else { diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c --- linux-2.5.3-pre2/drivers/isdn/hisax/isar.c Sun Sep 30 12:26:06 2001 +++ linux/drivers/isdn/hisax/isar.c Mon Jan 21 17:23:22 2002 @@ -1602,7 +1602,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -1637,7 +1637,7 @@ if (st->l1.bcs->cs->debug & L1_DEB_HSCX) debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY"); modeisar(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -1696,7 +1696,7 @@ if (open_isarstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = isar_l2l1; + st->l1.l2l1 = isar_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c --- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl1.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/isdnl1.c Mon Jan 21 17:23:22 2002 @@ -160,9 +160,9 @@ st = cs->stlist; while (st) { if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); else - st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL); + L1L2(st, PH_ACTIVATE | INDICATION, NULL); st = st->next; } } @@ -175,8 +175,8 @@ st = cs->stlist; while (st) { if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) - st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL); - st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL); + L1L2(st, PH_PAUSE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | INDICATION, NULL); st = st->next; } test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); @@ -193,7 +193,7 @@ stptr = cs->stlist; while (stptr != NULL) if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { - stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL); + L1L2(stptr, PH_PULL | CONFIRM, NULL); break; } else stptr = stptr->next; @@ -235,7 +235,7 @@ if (sapi == CTRL_SAPI) { /* sapi 0 */ while (stptr != NULL) { if ((nskb = skb_clone(skb, GFP_ATOMIC))) - stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb); + L1L2(stptr, PH_DATA | INDICATION, nskb); else printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); stptr = stptr->next; @@ -254,7 +254,7 @@ found = 0; while (stptr != NULL) if (tei == stptr->l2.tei) { - stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb); + L1L2(stptr, PH_DATA | INDICATION, skb); found = !0; break; } else @@ -277,10 +277,10 @@ } if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) { if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) { - st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); + L2L1(st, PH_DEACTIVATE | CONFIRM, NULL); } } } @@ -295,7 +295,7 @@ FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL); } while ((skb = skb_dequeue(&bcs->rqueue))) { - bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb); + L1L2(bcs->st, PH_DATA | INDICATION, skb); } } @@ -717,7 +717,7 @@ struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L1_ACTIV); - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); } static void @@ -726,7 +726,7 @@ struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L1_NULL); - st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); + L2L1(st, PH_DEACTIVATE | CONFIRM, NULL); } static struct FsmNode L1BFnList[] __initdata = @@ -801,7 +801,7 @@ debugl1(cs, "PH_ACTIVATE_REQ %s", st->l1.l1m.fsm->strState[st->l1.l1m.state]); if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) - st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + L1L2(st, PH_ACTIVATE | CONFIRM, NULL); else { test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); @@ -897,7 +897,7 @@ setstack_tei(st); setstack_manager(st); st->l1.stlistp = &(cs->stlist); - st->l2.l2l1 = dch_l2l1; + st->l1.l2l1 = dch_l2l1; if (cs->setstack_d) cs->setstack_d(st, cs); } diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c --- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/isdnl2.c Mon Jan 21 17:23:22 2002 @@ -27,14 +27,14 @@ static struct Fsm l2fsm; enum { - ST_L2_1, - ST_L2_2, - ST_L2_3, - ST_L2_4, - ST_L2_5, - ST_L2_6, - ST_L2_7, - ST_L2_8, + ST_L2_1, /* TEI unassigned */ + ST_L2_2, /* Assign awaiting TEI */ + ST_L2_3, /* Establish awaiting TEI */ + ST_L2_4, /* TEI assigned */ + ST_L2_5, /* Awaiting establishment */ + ST_L2_6, /* Awaiting release */ + ST_L2_7, /* Multiple frame established */ + ST_L2_8, /* Timer recovery */ }; #define L2_STATE_COUNT (ST_L2_8+1) @@ -219,7 +219,7 @@ { if (test_bit(FLG_LAPB, &st->l2.flag)) st->l1.bcs->tx_cnt += skb->len; - st->l2.l2l1(st, PH_DATA | REQUEST, skb); + L2L1(st, PH_DATA | REQUEST, skb); } #define enqueue_ui(a, b) enqueue_super(a, b) @@ -494,15 +494,15 @@ else pr = DL_RELEASE | INDICATION; - st->l2.l2l3(st, pr, NULL); + L2L3(st, pr, NULL); } inline void lapb_dl_release_l2l3(struct PStack *st, int f) { if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); - st->l2.l2l3(st, DL_RELEASE | f, NULL); + L2L1(st, PH_DEACTIVATE | REQUEST, NULL); + L2L3(st, DL_RELEASE | f, NULL); } static void @@ -631,7 +631,7 @@ struct sk_buff *skb = arg; skb_pull(skb, l2headersize(&st->l2, 1)); - st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); + L2L3(st, DL_UNIT_DATA | INDICATION, skb); /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * in states 1-3 for broadcast */ @@ -673,7 +673,7 @@ { struct PStack *st = fi->userdata; - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + L2L3(st, DL_RELEASE | CONFIRM, NULL); } static void @@ -714,7 +714,7 @@ FsmChangeState(fi, ST_L2_7); FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); - st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); + L2L3(st, DL_ESTABLISH | INDICATION, NULL); } static void @@ -763,11 +763,11 @@ FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) - st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); + L2L3(st, DL_ESTABLISH | INDICATION, NULL); if ((ST_L2_7==state) || (ST_L2_8 == state)) if (skb_queue_len(&st->l2.i_queue) && cansend(st)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } static void @@ -820,10 +820,10 @@ FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4); if (pr != -1) - st->l2.l2l3(st, pr, NULL); + L2L3(st, pr, NULL); if (skb_queue_len(&st->l2.i_queue) && cansend(st)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } static void @@ -866,7 +866,7 @@ if (!test_bit(FLG_L3_INIT, &st->l2.flag)) skb_queue_purge(&st->l2.i_queue); if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + L2L1(st, PH_DEACTIVATE | REQUEST, NULL); st5_dl_release_l2l3(st); FsmChangeState(fi, ST_L2_4); } @@ -962,7 +962,7 @@ skb_queue_head(&l2->i_queue, l2->windowar[p1]); l2->windowar[p1] = NULL; } - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } } @@ -1022,7 +1022,7 @@ restart_t200(st, 12); } if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } else nrerrorrecovery(fi); } @@ -1050,7 +1050,7 @@ if (test_bit(FLG_LAPB, &st->l2.flag)) st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); skb_queue_tail(&st->l2.i_queue, skb); - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } static void @@ -1099,7 +1099,7 @@ else test_and_set_bit(FLG_ACK_PEND, &l2->flag); skb_pull(skb, l2headersize(l2, 0)); - st->l2.l2l3(st, DL_DATA | INDICATION, skb); + L2L3(st, DL_DATA | INDICATION, skb); } else { /* n(s)!=v(r) */ FreeSkb(skb); @@ -1128,7 +1128,7 @@ } if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag)) enquiry_cr(st, RR, RSP, 0); } @@ -1163,7 +1163,7 @@ skb_queue_purge(&st->l2.i_queue); st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G'); if (test_bit(FLG_LAPB, &st->l2.flag)) - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + L2L1(st, PH_DEACTIVATE | REQUEST, NULL); st5_dl_release_l2l3(st); } else { st->l2.rc++; @@ -1304,14 +1304,14 @@ memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); FreeSkb(oskb); } - st->l2.l2l1(st, PH_PULL | INDICATION, skb); + L2L1(st, PH_PULL | INDICATION, skb); test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) { FsmDelTimer(&st->l2.t203, 13); FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11); } if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } static void @@ -1357,7 +1357,7 @@ invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + L2L1(st, PH_PULL | REQUEST, NULL); } else nrerrorrecovery(fi); } else { @@ -1404,7 +1404,7 @@ skb_queue_purge(&st->l2.ui_queue); st->l2.tei = -1; - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + L2L3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } @@ -1430,7 +1430,7 @@ skb_queue_purge(&st->l2.ui_queue); st->l2.tei = -1; stop_t200(st, 18); - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + L2L3(st, DL_RELEASE | CONFIRM, NULL); FsmChangeState(fi, ST_L2_1); } @@ -1445,7 +1445,7 @@ st->l2.tei = -1; stop_t200(st, 17); FsmDelTimer(&st->l2.t203, 19); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + L2L3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } @@ -1457,7 +1457,7 @@ skb_queue_purge(&st->l2.i_queue); skb_queue_purge(&st->l2.ui_queue); if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + L2L3(st, DL_RELEASE | INDICATION, NULL); } static void @@ -1480,7 +1480,7 @@ skb_queue_purge(&st->l2.ui_queue); stop_t200(st, 20); - st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + L2L3(st, DL_RELEASE | CONFIRM, NULL); FsmChangeState(fi, ST_L2_4); } @@ -1494,7 +1494,7 @@ freewin(st); stop_t200(st, 19); FsmDelTimer(&st->l2.t203, 19); - st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + L2L3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_4); } @@ -1739,12 +1739,12 @@ test_bit(FLG_ORIG, &st->l2.flag)) { test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag); } - st->l2.l2l1(st, PH_ACTIVATE, NULL); + L2L1(st, PH_ACTIVATE, NULL); } break; case (DL_RELEASE | REQUEST): if (test_bit(FLG_LAPB, &st->l2.flag)) { - st->l2.l2l1(st, PH_DEACTIVATE, NULL); + L2L1(st, PH_DEACTIVATE, NULL); } FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg); break; @@ -1784,8 +1784,8 @@ void setstack_isdnl2(struct PStack *st, char *debug_id) { - st->l1.l1l2 = isdnl2_l1l2; - st->l3.l3l2 = isdnl2_l3l2; + st->l2.l1l2 = isdnl2_l1l2; + st->l2.l3l2 = isdnl2_l3l2; skb_queue_head_init(&st->l2.i_queue); skb_queue_head_init(&st->l2.ui_queue); @@ -1813,13 +1813,13 @@ switch (pr) { case (DL_DATA | REQUEST): case (DL_UNIT_DATA | REQUEST): - st->l2.l2l1(st, PH_DATA | REQUEST, arg); + L2L1(st, PH_DATA | REQUEST, arg); break; case (DL_ESTABLISH | REQUEST): - st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); + L2L1(st, PH_ACTIVATE | REQUEST, NULL); break; case (DL_RELEASE | REQUEST): - st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + L2L1(st, PH_DEACTIVATE | REQUEST, NULL); break; } } @@ -1827,7 +1827,7 @@ void setstack_transl2(struct PStack *st) { - st->l3.l3l2 = transl2_l3l2; + st->l2.l3l2 = transl2_l3l2; } void diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.h linux/drivers/isdn/hisax/isdnl2.h --- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl2.h Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/isdnl2.h Mon Jan 21 17:23:22 2002 @@ -23,3 +23,4 @@ #define RSP 1 #define LC_FLUSH_WAIT 1 + diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c --- linux-2.5.3-pre2/drivers/isdn/hisax/isdnl3.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/isdnl3.c Mon Jan 21 17:23:22 2002 @@ -163,7 +163,7 @@ static void L3ExpireTimer(struct L3Timer *t) { - t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); + t->pc->st->l3.l4l3(t->pc->st, t->event, t->pc); } void @@ -355,7 +355,7 @@ st->l3.l3m.printdebug = l3m_debug; FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer); strcpy(st->l3.debug_id, "L3DC "); - st->lli.l4l3_proto = no_l3_proto_spec; + st->l3.l4l3_proto = no_l3_proto_spec; #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -373,13 +373,13 @@ } else #endif if (st->protocol == ISDN_PTYPE_LEASED) { - st->lli.l4l3 = no_l3_proto; - st->l2.l2l3 = no_l3_proto; + st->l3.l4l3 = no_l3_proto; + st->l3.l2l3 = no_l3_proto; st->l3.l3ml3 = no_l3_proto; printk(KERN_INFO "HiSax: Leased line mode\n"); } else { - st->lli.l4l3 = no_l3_proto; - st->l2.l2l3 = no_l3_proto; + st->l3.l4l3 = no_l3_proto; + st->l3.l2l3 = no_l3_proto; st->l3.l3ml3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : @@ -393,7 +393,7 @@ void isdnl3_trans(struct PStack *st, int pr, void *arg) { - st->l3.l3l2(st, pr, arg); + L3L2(st, pr, arg); } void @@ -424,7 +424,7 @@ st->l3.l3m.userint = 0; st->l3.l3m.printdebug = l3m_debug; strcpy(st->l3.debug_id, "L3BC "); - st->lli.l4l3 = isdnl3_trans; + st->l3.l4l3 = isdnl3_trans; } #define DREL_TIMER_VALUE 40000 @@ -435,7 +435,7 @@ struct PStack *st = fi->userdata; FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); - st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL); + L3L2(st, DL_ESTABLISH | REQUEST, NULL); } static void @@ -447,7 +447,7 @@ FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { - st->l3.l3l2(st, DL_DATA | REQUEST, skb); + L3L2(st, DL_DATA | REQUEST, skb); dequeued++; } if ((!st->l3.proc) && dequeued) { @@ -468,7 +468,7 @@ FsmDelTimer(&st->l3.l3m_timer, 51); FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { - st->l3.l3l2(st, DL_DATA | REQUEST, skb); + L3L2(st, DL_DATA | REQUEST, skb); dequeued++; } if ((!st->l3.proc) && dequeued) { @@ -512,7 +512,7 @@ FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); } else { FsmChangeState(fi, ST_L3_LC_REL_WAIT); - st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + L3L2(st, DL_RELEASE | REQUEST, NULL); } } @@ -565,7 +565,7 @@ switch (pr) { case (DL_DATA | REQUEST): if (st->l3.l3m.state == ST_L3_LC_ESTAB) { - st->l3.l3l2(st, pr, arg); + L3L2(st, pr, arg); } else { struct sk_buff *skb = arg; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/jade.c linux/drivers/isdn/hisax/jade.c --- linux-2.5.3-pre2/drivers/isdn/hisax/jade.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/jade.c Mon Jan 21 17:23:22 2002 @@ -177,7 +177,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -193,7 +193,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); modejade(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -258,7 +258,7 @@ if (open_jadestate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = jade_l2l1; + st->l1.l2l1 = jade_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c --- linux-2.5.3-pre2/drivers/isdn/hisax/l3_1tr6.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/l3_1tr6.c Mon Jan 21 17:23:22 2002 @@ -223,7 +223,7 @@ l3_debug(pc->st, tmp); } newl3state(pc, 6); - pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + L3L4(pc->st, CC_SETUP | INDICATION, pc); } else release_l3_process(pc); } @@ -253,7 +253,7 @@ } dev_kfree_skb(skb); L3AddTimer(&pc->timer, T304, CC_T304); - pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + L3L4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void @@ -285,7 +285,7 @@ dev_kfree_skb(skb); L3AddTimer(&pc->timer, T310, CC_T310); newl3state(pc, 3); - pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); + L3L4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void @@ -296,7 +296,7 @@ dev_kfree_skb(skb); L3DelTimer(&pc->timer); /* T304 */ newl3state(pc, 4); - pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); + L3L4(pc->st, CC_ALERTING | INDICATION, pc); } static void @@ -316,7 +316,7 @@ } if (tmpcharge > pc->para.chargeinfo) { pc->para.chargeinfo = tmpcharge; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + L3L4(pc->st, CC_CHARGE | INDICATION, pc); } if (pc->st->l3.debug & L3_DEB_CHARGE) { sprintf(tmp, "charging info %d", pc->para.chargeinfo); @@ -349,7 +349,7 @@ newl3state(pc, 10); dev_kfree_skb(skb); pc->para.chargeinfo = 0; - pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); + L3L4(pc->st, CC_SETUP | CONFIRM, pc); } static void @@ -379,7 +379,7 @@ StopAllL3Timer(pc); newl3state(pc, 0); l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } @@ -392,7 +392,7 @@ StopAllL3Timer(pc); newl3state(pc, 0); pc->para.cause = NO_CAUSE; - pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + L3L4(pc->st, CC_RELEASE | CONFIRM, pc); release_l3_process(pc); } @@ -414,7 +414,7 @@ } if (tmpcharge > pc->para.chargeinfo) { pc->para.chargeinfo = tmpcharge; - pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + L3L4(pc->st, CC_CHARGE | INDICATION, pc); } if (pc->st->l3.debug & L3_DEB_CHARGE) { sprintf(tmp, "charging info %d", pc->para.chargeinfo); @@ -447,7 +447,7 @@ } dev_kfree_skb(skb); newl3state(pc, 12); - pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + L3L4(pc->st, CC_DISCONNECT | INDICATION, pc); } @@ -464,7 +464,7 @@ newl3state(pc, 10); pc->para.chargeinfo = 0; L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); + L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void @@ -573,7 +573,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 0xE6; l3_1tr6_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -618,7 +618,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 0xE6; l3_1tr6_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -627,7 +627,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 0xE6; l3_1tr6_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); + L3L4(pc->st, CC_CONNECT_ERR, pc); } static void @@ -643,7 +643,7 @@ l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + L3L4(pc->st, CC_RELEASE_ERR, pc); release_l3_process(pc); } @@ -652,7 +652,7 @@ { pc->para.cause = CAUSE_LocalProcErr; l3_1tr6_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -661,7 +661,7 @@ newl3state(pc, 0); pc->para.cause = 0x1b; /* Destination out of order */ pc->para.loc = 0; - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } @@ -946,8 +946,8 @@ { char tmp[64]; - st->lli.l4l3 = down1tr6; - st->l2.l2l3 = up1tr6; + st->l3.l4l3 = down1tr6; + st->l3.l2l3 = up1tr6; st->l3.l3ml3 = man1tr6; st->l3.N303 = 0; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c --- linux-2.5.3-pre2/drivers/isdn/hisax/l3dss1.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/l3dss1.c Mon Jan 21 17:23:22 2002 @@ -449,7 +449,7 @@ pc->prot.dss1.remote_result = 0; /* success */ pc->prot.dss1.invoke_id = 0; pc->redir_result = pc->prot.dss1.remote_result; - st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */ + L3L4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */ else l3_debug(st,"return error unknown identifier"); break; @@ -494,7 +494,7 @@ pc->prot.dss1.remote_result = err_ret; /* result */ pc->prot.dss1.invoke_id = 0; pc->redir_result = pc->prot.dss1.remote_result; - st->l3.l3l4(st, CC_REDIR | INDICATION, pc); + L3L4(st, CC_REDIR | INDICATION, pc); } /* Deflection error */ else l3_debug(st,"return result unknown identifier"); @@ -983,7 +983,7 @@ pc->para.cause = NO_CAUSE; StopAllL3Timer(pc); newl3state(pc, 0); - pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + L3L4(pc->st, CC_RELEASE | CONFIRM, pc); dss1_release_l3_process(pc); } @@ -1473,7 +1473,7 @@ L3AddTimer(&pc->timer, T310, CC_T310); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); + L3L4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void @@ -1512,7 +1512,7 @@ L3AddTimer(&pc->timer, T304, CC_T304); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + L3L4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void @@ -1544,7 +1544,7 @@ if (cause) newl3state(pc, 19); if (11 != ret) - pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + L3L4(pc->st, CC_DISCONNECT | INDICATION, pc); else if (!cause) l3dss1_release_req(pc, pr, NULL); if (cause) { @@ -1570,7 +1570,7 @@ /* here should inserted COLP handling KKe */ if (ret) l3dss1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); + L3L4(pc->st, CC_SETUP | CONFIRM, pc); } static void @@ -1588,7 +1588,7 @@ newl3state(pc, 4); if (ret) l3dss1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); + L3L4(pc->st, CC_ALERTING | INDICATION, pc); } static void @@ -1754,7 +1754,7 @@ newl3state(pc, 6); if (err) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, err); - pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + L3L4(pc->st, CC_SETUP | INDICATION, pc); } static void @@ -1833,7 +1833,7 @@ L3DelTimer(&pc->timer); if (ret) l3dss1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); + L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void @@ -1860,7 +1860,7 @@ return; memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); dss1_release_l3_process(pc); } @@ -1894,7 +1894,7 @@ l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause); else l3dss1_message(pc, MT_RELEASE_COMPLETE); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); dss1_release_l3_process(pc); } @@ -1916,7 +1916,7 @@ { newl3state(pc, 9); l3dss1_message(pc, MT_CALL_PROCEEDING); - pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); + L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc); } static void @@ -2009,7 +2009,7 @@ if (err) l3dss1_std_ie_err(pc, err); if (ERR_IE_COMPREHENSION != err) - pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc); + L3L4(pc->st, CC_PROGRESS | INDICATION, pc); } static void @@ -2050,7 +2050,7 @@ if (err) l3dss1_std_ie_err(pc, err); if (ERR_IE_COMPREHENSION != err) - pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc); + L3L4(pc->st, CC_NOTIFY | INDICATION, pc); } static void @@ -2082,7 +2082,7 @@ if ((p = findie(p, skb->len, 0x70, 0))) { iecpy(tmp, p, 1); strcat(pc->para.setup.eazmsn, tmp); - pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + L3L4(pc->st, CC_MORE_INFO | INDICATION, pc); } L3AddTimer(&pc->timer, T302, CC_T302); } @@ -2297,11 +2297,11 @@ /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 * set down layer 3 without sending any message */ - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); dss1_release_l3_process(pc); } else { - pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); + L3L4(pc->st, CC_IGNORE | INDICATION, pc); } } @@ -2317,7 +2317,7 @@ pc->para.loc = 0; pc->para.cause = 28; /* invalid number */ l3dss1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2330,7 +2330,7 @@ } else { L3DelTimer(&pc->timer); l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102); - pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); + L3L4(pc->st, CC_NOSETUP_RSP, pc); dss1_release_l3_process(pc); } } @@ -2342,7 +2342,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } @@ -2382,7 +2382,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2392,7 +2392,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3dss1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); + L3L4(pc->st, CC_CONNECT_ERR, pc); } static void @@ -2408,7 +2408,7 @@ l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + L3L4(pc->st, CC_RELEASE_ERR, pc); dss1_release_l3_process(pc); } @@ -2418,7 +2418,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ - pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + L3L4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 19); l3dss1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_1); @@ -2430,7 +2430,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ - pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + L3L4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); } @@ -2438,7 +2438,7 @@ l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); dss1_release_l3_process(pc); } @@ -2494,7 +2494,7 @@ * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); dss1_release_l3_process(pc); } @@ -2553,7 +2553,7 @@ L3DelTimer(&pc->timer); newl3state(pc, 0); pc->para.cause = NO_CAUSE; - pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); + L3L4(pc->st, CC_SUSPEND | CONFIRM, pc); /* We don't handle suspend_ack for IE errors now */ if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) if (pc->debug & L3_DEB_WARN) @@ -2583,7 +2583,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + L3L4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); @@ -2647,7 +2647,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); + L3L4(pc->st, CC_RESUME | CONFIRM, pc); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); @@ -2675,7 +2675,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + L3L4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 0); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3dss1_std_ie_err(pc, ret); @@ -2713,9 +2713,9 @@ up = pc->st->l3.proc; while (up) { if ((ri & 7) == 7) - up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + L4L3(up->st, CC_RESTART | REQUEST, up); else if (up->para.bchannel == chan) - up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + L4L3(up->st, CC_RESTART | REQUEST, up); up = up->next; } p = tmp; @@ -2742,7 +2742,7 @@ pc->para.cause = 0x29; /* Temporary failure */ pc->para.loc = 0; l3dss1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2751,7 +2751,7 @@ newl3state(pc, 0); pc->para.cause = 0x1b; /* Destination out of order */ pc->para.loc = 0; - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } @@ -3219,9 +3219,9 @@ char tmp[64]; int i; - st->lli.l4l3 = dss1down; - st->lli.l4l3_proto = l3dss1_cmd_global; - st->l2.l2l3 = dss1up; + st->l3.l4l3 = dss1down; + st->l3.l4l3_proto = l3dss1_cmd_global; + st->l3.l2l3 = dss1up; st->l3.l3ml3 = dss1man; st->l3.N303 = 1; st->prot.dss1.last_invoke_id = 0; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/l3ni1.c linux/drivers/isdn/hisax/l3ni1.c --- linux-2.5.3-pre2/drivers/isdn/hisax/l3ni1.c Sun Sep 30 12:26:05 2001 +++ linux/drivers/isdn/hisax/l3ni1.c Mon Jan 21 17:23:22 2002 @@ -377,7 +377,7 @@ pc->prot.ni1.remote_result = 0; /* success */ pc->prot.ni1.invoke_id = 0; pc->redir_result = pc->prot.ni1.remote_result; - st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */ + L3L4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */ else l3_debug(st,"return error unknown identifier"); break; @@ -422,7 +422,7 @@ pc->prot.ni1.remote_result = err_ret; /* result */ pc->prot.ni1.invoke_id = 0; pc->redir_result = pc->prot.ni1.remote_result; - st->l3.l3l4(st, CC_REDIR | INDICATION, pc); + L3L4(st, CC_REDIR | INDICATION, pc); } /* Deflection error */ else l3_debug(st,"return result unknown identifier"); @@ -932,7 +932,7 @@ pc->para.cause = NO_CAUSE; StopAllL3Timer(pc); newl3state(pc, 0); - pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + L3L4(pc->st, CC_RELEASE | CONFIRM, pc); ni1_release_l3_process(pc); } @@ -1326,7 +1326,7 @@ L3AddTimer(&pc->timer, T310, CC_T310); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); + L3L4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void @@ -1365,7 +1365,7 @@ L3AddTimer(&pc->timer, T304, CC_T304); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + L3L4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void @@ -1397,7 +1397,7 @@ if (cause) newl3state(pc, 19); if (11 != ret) - pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); + L3L4(pc->st, CC_DISCONNECT | INDICATION, pc); else if (!cause) l3ni1_release_req(pc, pr, NULL); if (cause) { @@ -1423,7 +1423,7 @@ /* here should inserted COLP handling KKe */ if (ret) l3ni1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); + L3L4(pc->st, CC_SETUP | CONFIRM, pc); } static void @@ -1441,7 +1441,7 @@ newl3state(pc, 4); if (ret) l3ni1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); + L3L4(pc->st, CC_ALERTING | INDICATION, pc); } static void @@ -1607,7 +1607,7 @@ newl3state(pc, 6); if (err) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, err); - pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + L3L4(pc->st, CC_SETUP | INDICATION, pc); } static void @@ -1688,7 +1688,7 @@ L3DelTimer(&pc->timer); if (ret) l3ni1_std_ie_err(pc, ret); - pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); + L3L4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void @@ -1715,7 +1715,7 @@ return; memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); ni1_release_l3_process(pc); } @@ -1749,7 +1749,7 @@ l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause); else l3ni1_message(pc, MT_RELEASE_COMPLETE); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); ni1_release_l3_process(pc); } @@ -1771,7 +1771,7 @@ { newl3state(pc, 9); l3ni1_message(pc, MT_CALL_PROCEEDING); - pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); + L3L4(pc->st, CC_PROCEED_SEND | INDICATION, pc); } static void @@ -1864,7 +1864,7 @@ if (err) l3ni1_std_ie_err(pc, err); if (ERR_IE_COMPREHENSION != err) - pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc); + L3L4(pc->st, CC_PROGRESS | INDICATION, pc); } static void @@ -1905,7 +1905,7 @@ if (err) l3ni1_std_ie_err(pc, err); if (ERR_IE_COMPREHENSION != err) - pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc); + L3L4(pc->st, CC_NOTIFY | INDICATION, pc); } static void @@ -1937,7 +1937,7 @@ if ((p = findie(p, skb->len, 0x70, 0))) { iecpy(tmp, p, 1); strcat(pc->para.setup.eazmsn, tmp); - pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); + L3L4(pc->st, CC_MORE_INFO | INDICATION, pc); } L3AddTimer(&pc->timer, T302, CC_T302); } @@ -2152,11 +2152,11 @@ /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 * set down layer 3 without sending any message */ - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); ni1_release_l3_process(pc); } else { - pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); + L3L4(pc->st, CC_IGNORE | INDICATION, pc); } } @@ -2172,7 +2172,7 @@ pc->para.loc = 0; pc->para.cause = 28; /* invalid number */ l3ni1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2185,7 +2185,7 @@ } else { L3DelTimer(&pc->timer); l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102); - pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); + L3L4(pc->st, CC_NOSETUP_RSP, pc); ni1_release_l3_process(pc); } } @@ -2197,7 +2197,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3ni1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } @@ -2237,7 +2237,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3ni1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2247,7 +2247,7 @@ pc->para.loc = 0; pc->para.cause = 102; l3ni1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); + L3L4(pc->st, CC_CONNECT_ERR, pc); } static void @@ -2263,7 +2263,7 @@ l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + L3L4(pc->st, CC_RELEASE_ERR, pc); ni1_release_l3_process(pc); } @@ -2273,7 +2273,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ - pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + L3L4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 19); l3ni1_message(pc, MT_RELEASE); L3AddTimer(&pc->timer, T308, CC_T308_1); @@ -2285,7 +2285,7 @@ L3DelTimer(&pc->timer); pc->para.cause = 102; /* Timer expiry */ pc->para.loc = 0; /* local */ - pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + L3L4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); } @@ -2293,7 +2293,7 @@ l3ni1_restart(struct l3_process *pc, u_char pr, void *arg) { L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); ni1_release_l3_process(pc); } @@ -2349,7 +2349,7 @@ * if received MT_STATUS with cause == 111 and call * state == 0, then we must set down layer 3 */ - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); newl3state(pc, 0); ni1_release_l3_process(pc); } @@ -2408,7 +2408,7 @@ L3DelTimer(&pc->timer); newl3state(pc, 0); pc->para.cause = NO_CAUSE; - pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); + L3L4(pc->st, CC_SUSPEND | CONFIRM, pc); /* We don't handle suspend_ack for IE errors now */ if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE))) if (pc->debug & L3_DEB_WARN) @@ -2438,7 +2438,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + L3L4(pc->st, CC_SUSPEND_ERR, pc); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, ret); @@ -2502,7 +2502,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); + L3L4(pc->st, CC_RESUME | CONFIRM, pc); newl3state(pc, 10); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, ret); @@ -2530,7 +2530,7 @@ return; } L3DelTimer(&pc->timer); - pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + L3L4(pc->st, CC_RESUME_ERR, pc); newl3state(pc, 0); if (ret) /* STATUS for none mandatory IE errors after actions are taken */ l3ni1_std_ie_err(pc, ret); @@ -2568,9 +2568,9 @@ up = pc->st->l3.proc; while (up) { if ((ri & 7) == 7) - up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + L4L3(up->st, CC_RESTART | REQUEST, up); else if (up->para.bchannel == chan) - up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + L4L3(up->st, CC_RESTART | REQUEST, up); up = up->next; } @@ -2598,7 +2598,7 @@ pc->para.cause = 0x29; /* Temporary failure */ pc->para.loc = 0; l3ni1_disconnect_req(pc, pr, NULL); - pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); + L3L4(pc->st, CC_SETUP_ERR, pc); } static void @@ -2607,7 +2607,7 @@ newl3state(pc, 0); pc->para.cause = 0x1b; /* Destination out of order */ pc->para.loc = 0; - pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + L3L4(pc->st, CC_RELEASE | INDICATION, pc); release_l3_process(pc); } @@ -2642,7 +2642,7 @@ { printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn ); newl3state( pc, 0 ); - pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL ); + L3L2( pc->st, DL_RELEASE | REQUEST, NULL ); return; } @@ -2667,7 +2667,7 @@ L3DelTimer( &pc->timer ); L3AddTimer( &pc->timer, TSPID, CC_TSPID ); - pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb ); + L3L2( pc->st, DL_DATA | REQUEST, skb ); } static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg ) @@ -2700,7 +2700,7 @@ printk( KERN_ERR "SPID not accepted\n" ); newl3state( pc, 0 ); - pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL ); + L3L2( pc->st, DL_RELEASE | REQUEST, NULL ); } } @@ -3170,9 +3170,9 @@ char tmp[64]; int i; - st->lli.l4l3 = ni1down; - st->lli.l4l3_proto = l3ni1_cmd_global; - st->l2.l2l3 = ni1up; + st->l3.l4l3 = ni1down; + st->l3.l4l3_proto = l3ni1_cmd_global; + st->l3.l2l3 = ni1up; st->l3.l3ml3 = ni1man; st->l3.N303 = 1; st->prot.ni1.last_invoke_id = 0; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c --- linux-2.5.3-pre2/drivers/isdn/hisax/netjet.c Sun Sep 30 12:26:06 2001 +++ linux/drivers/isdn/hisax/netjet.c Mon Jan 21 17:23:22 2002 @@ -847,7 +847,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -863,7 +863,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); mode_tiger(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -924,7 +924,7 @@ if (open_tigerstate(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = tiger_l2l1; + st->l1.l2l1 = tiger_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481.h linux/drivers/isdn/hisax/st5481.h --- linux-2.5.3-pre2/drivers/isdn/hisax/st5481.h Sun Jan 6 18:49:21 2002 +++ linux/drivers/isdn/hisax/st5481.h Mon Jan 21 17:23:22 2002 @@ -478,7 +478,7 @@ if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb) static void __attribute__((unused)) -dump_iso_packet(const char *name,urb_t *urb) +dump_iso_packet(const char *name, struct urb *urb) { int i,j; int len,ofs; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481_d.c linux/drivers/isdn/hisax/st5481_d.c --- linux-2.5.3-pre2/drivers/isdn/hisax/st5481_d.c Sat Dec 8 20:28:45 2001 +++ linux/drivers/isdn/hisax/st5481_d.c Mon Jan 21 17:23:22 2002 @@ -297,7 +297,7 @@ unsigned int num_packets, packet_offset; int len, buf_size, bytes_sent; struct sk_buff *skb; - iso_packet_descriptor_t *desc; + struct usb_iso_packet_descriptor *desc; if (d_out->fsm.state != ST_DOUT_NORMAL) return; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/st5481_usb.c linux/drivers/isdn/hisax/st5481_usb.c --- linux-2.5.3-pre2/drivers/isdn/hisax/st5481_usb.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/isdn/hisax/st5481_usb.c Mon Jan 21 17:23:22 2002 @@ -235,7 +235,7 @@ struct usb_interface_descriptor *altsetting; struct usb_endpoint_descriptor *endpoint; int status; - urb_t *urb; + struct urb *urb; u_char *buf; DBG(1,""); @@ -560,7 +560,7 @@ */ int st5481_isoc_flatten(struct urb *urb) { - iso_packet_descriptor_t *pipd,*pend; + struct usb_iso_packet_descriptor *pipd,*pend; unsigned char *src,*dst; unsigned int len; diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c --- linux-2.5.3-pre2/drivers/isdn/hisax/tei.c Sun Sep 30 12:26:06 2001 +++ linux/drivers/isdn/hisax/tei.c Mon Jan 21 17:23:22 2002 @@ -120,7 +120,7 @@ bp[2] = ri & 0xff; bp[3] = m_id; bp[4] = (tei << 1) | 1; - st->l2.l2l1(st, PH_DATA | REQUEST, skb); + L2L1(st, PH_DATA | REQUEST, skb); } static void @@ -166,7 +166,7 @@ } else if (ri == st->ma.ri) { FsmDelTimer(&st->ma.t202, 1); FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); - st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei); + L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei); cs = (struct IsdnCardState *) st->l1.hardware; cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); } @@ -240,7 +240,7 @@ if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { FsmDelTimer(&st->ma.t202, 5); FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); - st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + L3L2(st, MDL_REMOVE | REQUEST, 0); cs = (struct IsdnCardState *) st->l1.hardware; cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); } @@ -276,7 +276,7 @@ FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3); } else { st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed"); - st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0); + L3L2(st, MDL_ERROR | RESPONSE, 0); cs = (struct IsdnCardState *) st->l1.hardware; cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); FsmChangeState(fi, ST_TEI_NOP); @@ -299,7 +299,7 @@ } else { st->ma.tei_m.printdebug(&st->ma.tei_m, "verify req for tei %d failed", st->l2.tei); - st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + L3L2(st, MDL_REMOVE | REQUEST, 0); cs = (struct IsdnCardState *) st->l1.hardware; cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); FsmChangeState(fi, ST_TEI_NOP); @@ -372,7 +372,7 @@ if (st->ma.debug) st->ma.tei_m.printdebug(&st->ma.tei_m, "fixed assign tei %d", st->l2.tei); - st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei); + L3L2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei); cs = (struct IsdnCardState *) st->l1.hardware; cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); } diff -urN linux-2.5.3-pre2/drivers/isdn/hisax/w6692.c linux/drivers/isdn/hisax/w6692.c --- linux-2.5.3-pre2/drivers/isdn/hisax/w6692.c Sun Sep 30 12:26:06 2001 +++ linux/drivers/isdn/hisax/w6692.c Mon Jan 21 17:23:22 2002 @@ -108,7 +108,7 @@ debugl1(cs, "D-Channel Busy cleared"); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + L1L2(stptr, PH_PAUSE | CONFIRM, NULL); stptr = stptr->next; } } @@ -614,7 +614,7 @@ #endif if (!cs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -687,7 +687,7 @@ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); stptr = cs->stlist; while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + L1L2(stptr, PH_PAUSE | INDICATION, NULL); stptr = stptr->next; } } else { @@ -772,7 +772,7 @@ case (PH_PULL | REQUEST): if (!st->l1.bcs->tx_skb) { test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + L1L2(st, PH_PULL | CONFIRM, NULL); } else test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; @@ -788,7 +788,7 @@ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); W6692Bmode(st->l1.bcs, 0, st->l1.bc); - st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + L1L2(st, PH_DEACTIVATE | CONFIRM, NULL); break; } } @@ -852,7 +852,7 @@ if (open_w6692state(st->l1.hardware, bcs)) return (-1); st->l1.bcs = bcs; - st->l2.l2l1 = W6692_l2l1; + st->l1.l2l1 = W6692_l2l1; setstack_manager(st); bcs->st = st; setstack_l1_B(st); diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- linux-2.5.3-pre2/drivers/isdn/isdn_common.c Mon Jan 21 17:22:32 2002 +++ linux/drivers/isdn/isdn_common.c Mon Jan 21 17:23:22 2002 @@ -1124,6 +1124,18 @@ #undef cfg } +static struct file_operations isdn_status_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + read: isdn_status_read, + write: isdn_status_write, + poll: isdn_status_poll, + ioctl: isdn_status_ioctl, + open: isdn_status_open, + release: isdn_status_release, +}; + /* * /dev/isdnctrlX */ @@ -1650,155 +1662,62 @@ #undef cfg } -/* - * - */ - -static ssize_t -isdn_read(struct file *file, char *buf, size_t count, loff_t * off) -{ - uint minor = minor(file->f_dentry->d_inode->i_rdev); - - if (minor < ISDN_MINOR_CTRL) - return -ENODEV; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_read(file, buf, count, off); - -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_read(file, buf, count, off); -#endif - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_read(file, buf, count, off); - - return -ENODEV; -} - -static ssize_t -isdn_write(struct file *file, const char *buf, size_t count, loff_t * off) +static struct file_operations isdn_ctrl_fops = { - uint minor = minor(file->f_dentry->d_inode->i_rdev); - - if (minor < ISDN_MINOR_CTRL) - return -ENODEV; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_write(file, buf, count, off); - -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_write(file, buf, count, off); -#endif - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_write(file, buf, count, off); - - return -ENODEV; -} - -static unsigned int -isdn_poll(struct file *file, poll_table * wait) -{ - unsigned int minor = minor(file->f_dentry->d_inode->i_rdev); - - if (minor < ISDN_MINOR_CTRL) - return POLLERR; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_poll(file, wait); - -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_poll(file, wait); -#endif - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_poll(file, wait); - - return POLLERR; -} - -static int -isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) -{ - uint minor = minor(inode->i_rdev); - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_ioctl(inode, file, cmd, arg); - - if (minor < ISDN_MINOR_CTRL) - return -ENODEV; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_ioctl(inode, file, cmd, arg); - -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_ioctl(inode, file, cmd, arg); -#endif - - return -ENODEV; -} + owner: THIS_MODULE, + llseek: no_llseek, + read: isdn_ctrl_read, + write: isdn_ctrl_write, + poll: isdn_ctrl_poll, + ioctl: isdn_ctrl_ioctl, + open: isdn_ctrl_open, + release: isdn_ctrl_release, +}; /* - * Open the device code. + * file_operations for major 43, /dev/isdn* + * stolen from drivers/char/misc.c */ -static int -isdn_open(struct inode *ino, struct file *filep) -{ - uint minor = minor(ino->i_rdev); - - if (minor < ISDN_MINOR_CTRL) - return -ENODEV; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_open(ino, filep); - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_open(ino, filep); - -#ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_open(ino, filep); -#endif - - return -ENODEV; -} static int -isdn_close(struct inode *ino, struct file *filep) +isdn_open(struct inode * inode, struct file * file) { - uint minor = minor(ino->i_rdev); - - if (minor == ISDN_MINOR_STATUS) - return isdn_status_release(ino, filep); - - if (minor < ISDN_MINOR_CTRL) - return -ENODEV; - - if (minor <= ISDN_MINOR_CTRLMAX) - return isdn_ctrl_release(ino, filep); - + int minor = minor(inode->i_rdev); + int err = -ENODEV; + struct file_operations *old_fops, *new_fops = NULL; + + if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) + new_fops = fops_get(&isdn_ctrl_fops); #ifdef CONFIG_ISDN_PPP - if (minor <= ISDN_MINOR_PPPMAX) - return isdn_ppp_release(ino, filep); + else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX) + new_fops = fops_get(&isdn_ppp_fops); #endif + else if (minor == ISDN_MINOR_STATUS) + new_fops = fops_get(&isdn_status_fops); + + if (!new_fops) + goto out; - return -ENODEV; + err = 0; + old_fops = file->f_op; + file->f_op = new_fops; + if (file->f_op->open) { + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + } + fops_put(old_fops); + + out: + return err; } static struct file_operations isdn_fops = { owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_read, - write: isdn_write, - poll: isdn_poll, - ioctl: isdn_ioctl, open: isdn_open, - release: isdn_close, }; char * @@ -2253,7 +2172,7 @@ sprintf (buf, "isdn%d", k); dev->devfs_handle_isdnX[k] = devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, - ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, + ISDN_MAJOR, k,0600 | S_IFCHR, &isdn_fops, NULL); sprintf (buf, "isdnctrl%d", k); dev->devfs_handle_isdnctrlX[k] = diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- linux-2.5.3-pre2/drivers/isdn/isdn_ppp.c Mon Jan 21 17:22:32 2002 +++ linux/drivers/isdn/isdn_ppp.c Mon Jan 21 17:23:22 2002 @@ -9,7 +9,7 @@ * */ -#include +#include #include #include #include @@ -269,7 +269,7 @@ * isdn_ppp_open */ -int +static int isdn_ppp_open(struct inode *ino, struct file *file) { uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP; @@ -322,7 +322,7 @@ /* * release ippp device */ -int +static int isdn_ppp_release(struct inode *ino, struct file *file) { uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP; @@ -418,7 +418,7 @@ /* * ippp device ioctl */ -int +static int isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long val; @@ -579,7 +579,7 @@ return 0; } -unsigned int +static unsigned int isdn_ppp_poll(struct file *file, poll_table * wait) { unsigned int mask; @@ -690,8 +690,8 @@ * reports, that there is data */ -int -isdn_ppp_read(struct file *file, char *buf, int count, loff_t *off) +static int +isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off) { struct ippp_struct *is; struct ippp_buf_queue *b; @@ -745,8 +745,8 @@ * ipppd wanna write a packet to the card .. non-blocking */ -int -isdn_ppp_write(struct file *file, const char *buf, int count, loff_t *off) +static int +isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off) { isdn_net_local *lp; struct ippp_struct *is; @@ -829,6 +829,18 @@ unlock_kernel(); return retval; } + +struct file_operations isdn_ppp_fops = +{ + owner: THIS_MODULE, + llseek: no_llseek, + read: isdn_ppp_read, + write: isdn_ppp_write, + poll: isdn_ppp_poll, + ioctl: isdn_ppp_ioctl, + open: isdn_ppp_open, + release: isdn_ppp_release, +}; /* * init memory, structures etc. diff -urN linux-2.5.3-pre2/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h --- linux-2.5.3-pre2/drivers/isdn/isdn_ppp.h Mon Jan 21 17:22:32 2002 +++ linux/drivers/isdn/isdn_ppp.h Mon Jan 21 17:23:22 2002 @@ -12,12 +12,7 @@ #include /* for PPP_PROTOCOL */ #include /* for isdn_ppp info */ -extern int isdn_ppp_open(struct inode *, struct file *); -extern int isdn_ppp_release(struct inode *, struct file *); -extern int isdn_ppp_read(struct file *, char *, int, loff_t *off); -extern int isdn_ppp_write(struct file *, const char *, int, loff_t *off); -extern int isdn_ppp_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *); +extern struct file_operations isdn_ppp_fops; extern int isdn_ppp_init(void); extern void isdn_ppp_cleanup(void); diff -urN linux-2.5.3-pre2/drivers/media/video/cpia_usb.c linux/drivers/media/video/cpia_usb.c --- linux-2.5.3-pre2/drivers/media/video/cpia_usb.c Thu Oct 25 13:53:47 2001 +++ linux/drivers/media/video/cpia_usb.c Mon Jan 21 17:23:22 2002 @@ -46,7 +46,7 @@ struct cpia_sbuf { char *data; - urb_t *urb; + struct urb *urb; }; #define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) @@ -168,7 +168,7 @@ static int cpia_usb_open(void *privdata) { struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - urb_t *urb; + struct urb *urb; int ret, retval = 0, fx, err; if (!ucpia) diff -urN linux-2.5.3-pre2/drivers/net/Config.in linux/drivers/net/Config.in --- linux-2.5.3-pre2/drivers/net/Config.in Mon Jan 21 17:22:32 2002 +++ linux/drivers/net/Config.in Mon Jan 21 17:23:22 2002 @@ -292,7 +292,6 @@ bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO if [ "$CONFIG_NET_RADIO" = "y" ]; then dep_tristate ' STRIP (Metricom starmode radio IP)' CONFIG_STRIP $CONFIG_INET - tristate ' AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN tristate ' Aironet Arlan 655 & IC2200 DS support' CONFIG_ARLAN tristate ' Aironet 4500/4800 series adapters' CONFIG_AIRONET4500 dep_tristate ' Aironet 4500/4800 ISA/PCI/PNP/365 support ' CONFIG_AIRONET4500_NONCS $CONFIG_AIRONET4500 diff -urN linux-2.5.3-pre2/drivers/net/Makefile linux/drivers/net/Makefile --- linux-2.5.3-pre2/drivers/net/Makefile Mon Dec 10 11:17:40 2001 +++ linux/drivers/net/Makefile Mon Jan 21 17:23:22 2002 @@ -166,7 +166,6 @@ obj-$(CONFIG_EEXPRESS_PRO) += eepro.o obj-$(CONFIG_8139CP) += 8139cp.o obj-$(CONFIG_8139TOO) += 8139too.o -obj-$(CONFIG_WAVELAN) += wavelan.o obj-$(CONFIG_ARLAN) += arlan.o arlan-proc.o obj-$(CONFIG_ZNET) += znet.o obj-$(CONFIG_LAN_SAA9730) += saa9730.o diff -urN linux-2.5.3-pre2/drivers/net/au1000_eth.c linux/drivers/net/au1000_eth.c --- linux-2.5.3-pre2/drivers/net/au1000_eth.c Thu Jan 10 16:23:45 2002 +++ linux/drivers/net/au1000_eth.c Mon Jan 21 17:23:22 2002 @@ -1141,6 +1141,8 @@ //data[3] = mdio_read(ioaddr, data[0], data[1]); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; //mdio_write(ioaddr, data[0], data[1], data[2]); return 0; default: diff -urN linux-2.5.3-pre2/drivers/net/dl2k.c linux/drivers/net/dl2k.c --- linux-2.5.3-pre2/drivers/net/dl2k.c Thu Jan 10 16:23:45 2002 +++ linux/drivers/net/dl2k.c Mon Jan 21 17:23:23 2002 @@ -1040,6 +1040,8 @@ miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num); break; case SIOCDEVPRIVATE + 2: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value); break; case SIOCDEVPRIVATE + 3: diff -urN linux-2.5.3-pre2/drivers/net/i82586.h linux/drivers/net/i82586.h --- linux-2.5.3-pre2/drivers/net/i82586.h Tue Feb 13 13:15:05 2001 +++ linux/drivers/net/i82586.h Wed Dec 31 16:00:00 1969 @@ -1,413 +0,0 @@ -/* - * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor. - * - * See: - * Intel Microcommunications 1991 - * p1-1 to p1-37 - * Intel order No. 231658 - * ISBN 1-55512-119-5 - * - * Unfortunately, the above chapter mentions neither - * the System Configuration Pointer (SCP) nor the - * Intermediate System Configuration Pointer (ISCP), - * so we probably need to look elsewhere for the - * whole story -- some recommend the "Intel LAN - * Components manual" but I have neither a copy - * nor a full reference. But "elsewhere" may be - * in the same publication... - * The description of a later device, the - * "82596CA High-Performance 32-Bit Local Area Network - * Coprocessor", (ibid. p1-38 to p1-109) does mention - * the SCP and ISCP and also has an i82586 compatibility - * mode. Even more useful is "AP-235 An 82586 Data Link - * Driver" (ibid. p1-337 to p1-417). - */ - -#define I82586_MEMZ (64 * 1024) - -#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t)) - -#define ADDR_LEN 6 -#define I82586NULL 0xFFFF - -#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0) - -/* - * System Configuration Pointer (SCP). - */ -typedef struct scp_t scp_t; -struct scp_t -{ - unsigned short scp_sysbus; /* 82586 bus width: */ -#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */ -#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */ - unsigned short scp_junk[2]; /* Unused */ - unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */ - unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */ -}; - -/* - * Intermediate System Configuration Pointer (ISCP). - */ -typedef struct iscp_t iscp_t; -struct iscp_t -{ - unsigned short iscp_busy; /* set by CPU before first CA, */ - /* cleared by 82586 after read. */ - unsigned short iscp_offset; /* offset of SCB */ - unsigned short iscp_basel; /* base of SCB */ - unsigned short iscp_baseh; /* " */ -}; - -/* - * System Control Block (SCB). - * The 82586 writes its status to scb_status and then - * raises an interrupt to alert the CPU. - * The CPU writes a command to scb_command and - * then issues a Channel Attention (CA) to alert the 82586. - */ -typedef struct scb_t scb_t; -struct scb_t -{ - unsigned short scb_status; /* Status of 82586 */ -#define SCB_ST_INT (0xF << 12) /* Some of: */ -#define SCB_ST_CX (0x1 << 15) /* Cmd completed */ -#define SCB_ST_FR (0x1 << 14) /* Frame received */ -#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */ -#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */ -#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */ -#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */ -#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */ -#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */ -#define SCB_ST_CUS_ACTV (2 << 8) /* Active */ -#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */ -#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */ -#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */ -#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */ -#define SCB_ST_RUS_NRES (2 << 4) /* No resources */ -#define SCB_ST_RUS_RDY (4 << 4) /* Ready */ - unsigned short scb_command; /* Next command */ -#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */ -#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */ -#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */ -#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */ -#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */ -#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */ -#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */ -#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */ -#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */ -#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */ -#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */ -#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */ -#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */ -#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */ -#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */ -#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */ -#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */ -#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */ - unsigned short scb_cbl_offset; /* Offset of first command unit */ - /* Action Command */ - unsigned short scb_rfa_offset; /* Offset of first Receive */ - /* Frame Descriptor in the */ - /* Receive Frame Area */ - unsigned short scb_crcerrs; /* Properly aligned frames */ - /* received with a CRC error */ - unsigned short scb_alnerrs; /* Misaligned frames received */ - /* with a CRC error */ - unsigned short scb_rscerrs; /* Frames lost due to no space */ - unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */ -}; - -#define scboff(p,f) toff(scb_t, p, f) - -/* - * The eight Action Commands. - */ -typedef enum acmd_e acmd_e; -enum acmd_e -{ - acmd_nop = 0, /* Do nothing */ - acmd_ia_setup = 1, /* Load an (ethernet) address into the */ - /* 82586 */ - acmd_configure = 2, /* Update the 82586 operating parameters */ - acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */ - /* addresses into the 82586 */ - acmd_transmit = 4, /* Transmit a frame */ - acmd_tdr = 5, /* Perform a Time Domain Reflectometer */ - /* test on the serial link */ - acmd_dump = 6, /* Copy 82586 registers to memory */ - acmd_diagnose = 7, /* Run an internal self test */ -}; - -/* - * Generic Action Command header. - */ -typedef struct ach_t ach_t; -struct ach_t -{ - unsigned short ac_status; /* Command status: */ -#define AC_SFLD_C (0x1 << 15) /* Command completed */ -#define AC_SFLD_B (0x1 << 14) /* Busy executing */ -#define AC_SFLD_OK (0x1 << 13) /* Completed error free */ -#define AC_SFLD_A (0x1 << 12) /* Command aborted */ -#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */ -#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */ - /* during transmission */ -#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */ - /* (stopped) lost CTS */ -#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */ - /* (stopped) slow DMA */ -#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */ - /* other link traffic */ -#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */ - /* detect after last tx */ -#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */ - /* excessive collisions */ -#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */ - unsigned short ac_command; /* Command specifier: */ -#define AC_CFLD_EL (0x1 << 15) /* End of command list */ -#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */ -#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */ -#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */ - unsigned short ac_link; /* Next Action Command */ -}; - -#define acoff(p,f) toff(ach_t, p, f) - -/* - * The Nop Action Command. - */ -typedef struct ac_nop_t ac_nop_t; -struct ac_nop_t -{ - ach_t nop_h; -}; - -/* - * The IA-Setup Action Command. - */ -typedef struct ac_ias_t ac_ias_t; -struct ac_ias_t -{ - ach_t ias_h; - unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */ -}; - -/* - * The Configure Action Command. - */ -typedef struct ac_cfg_t ac_cfg_t; -struct ac_cfg_t -{ - ach_t cfg_h; - unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */ -#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0) - unsigned char cfg_fifolim; /* FIFO threshold */ -#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0) - unsigned char cfg_byte8; -#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */ -#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */ - /* external sync. */ - unsigned char cfg_byte9; -#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */ -#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */ -#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */ -#define AC_CFG_PLEN_2 0 /* 2 bytes */ -#define AC_CFG_PLEN_4 1 /* 4 bytes */ -#define AC_CFG_PLEN_8 2 /* 8 bytes */ -#define AC_CFG_PLEN_16 3 /* 16 bytes */ -#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */ - /* explicit in buffers */ -#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */ - unsigned char cfg_byte10; -#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */ - /* backoff method */ -#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */ -#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */ - unsigned char cfg_ifs; /* Interframe spacing */ - unsigned char cfg_slotl; /* Slot time (low byte) */ - unsigned char cfg_byte13; -#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */ -#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */ - unsigned char cfg_byte14; -#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */ -#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */ -#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */ -#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */ -#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */ -#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */ -#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */ -#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */ - unsigned char cfg_byte15; -#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */ - /* detect source */ -#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */ - /* filter in bit times */ -#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */ - /* sense source */ -#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */ - /* filter in bit times */ - unsigned short cfg_min_frm_len; -#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */ -}; - -/* - * The MC-Setup Action Command. - */ -typedef struct ac_mcs_t ac_mcs_t; -struct ac_mcs_t -{ - ach_t mcs_h; - unsigned short mcs_cnt; /* No. of bytes of MC addresses */ -#if 0 - unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */ - ... -#endif -}; - -#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ - -/* - * The Transmit Action Command. - */ -typedef struct ac_tx_t ac_tx_t; -struct ac_tx_t -{ - ach_t tx_h; - unsigned short tx_tbd_offset; /* Address of list of buffers. */ -#if 0 -Linux packets are passed down with the destination MAC address -and length/type field already prepended to the data, -so we do not need to insert it. Consistent with this -we must also set the AC_CFG_ALOC(..) flag during the -ac_cfg_t action command. - unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */ - unsigned short tx_length; /* The frame length */ -#endif /* 0 */ -}; - -/* - * The Time Domain Reflectometer Action Command. - */ -typedef struct ac_tdr_t ac_tdr_t; -struct ac_tdr_t -{ - ach_t tdr_h; - unsigned short tdr_result; /* Result. */ -#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */ -#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */ -#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */ -#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */ -#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */ - /* site in transmit */ - /* clock cycles */ -}; - -/* - * The Dump Action Command. - */ -typedef struct ac_dmp_t ac_dmp_t; -struct ac_dmp_t -{ - ach_t dmp_h; - unsigned short dmp_offset; /* Result. */ -}; - -/* - * Size of the result of the dump command. - */ -#define DUMPBYTES 170 - -/* - * The Diagnose Action Command. - */ -typedef struct ac_dgn_t ac_dgn_t; -struct ac_dgn_t -{ - ach_t dgn_h; -}; - -/* - * Transmit Buffer Descriptor (TBD). - */ -typedef struct tbd_t tbd_t; -struct tbd_t -{ - unsigned short tbd_status; /* Written by the CPU */ -#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */ - /* last for this frame */ -#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */ - /* bytes in this buffer */ - unsigned short tbd_next_bd_offset; /* Next in list */ - unsigned short tbd_bufl; /* Buffer address (low) */ - unsigned short tbd_bufh; /* " " (high) */ -}; - -/* - * Receive Buffer Descriptor (RBD). - */ -typedef struct rbd_t rbd_t; -struct rbd_t -{ - unsigned short rbd_status; /* Written by the 82586 */ -#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */ - /* last for this frame */ -#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */ -#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */ - /* bytes in this buffer */ - unsigned short rbd_next_rbd_offset; /* Next rbd in list */ - unsigned short rbd_bufl; /* Data pointer (low) */ - unsigned short rbd_bufh; /* " " (high) */ - unsigned short rbd_el_size; /* EL+Data buf. size */ -#define RBD_EL (0x1 << 15) /* This BD is the */ - /* last in the list */ -#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */ - /* buffer can hold */ -}; - -#define rbdoff(p,f) toff(rbd_t, p, f) - -/* - * Frame Descriptor (FD). - */ -typedef struct fd_t fd_t; -struct fd_t -{ - unsigned short fd_status; /* Written by the 82586 */ -#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */ -#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */ -#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */ -#define FD_STATUS_S11 (0x1 << 11) /* CRC error */ -#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */ -#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */ -#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */ -#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */ -#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */ - unsigned short fd_command; /* Command */ -#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */ -#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */ - unsigned short fd_link_offset; /* Next FD */ - unsigned short fd_rbd_offset; /* First RBD (data) */ - /* Prepared by CPU, */ - /* updated by 82586 */ -#if 0 -I think the rest is unused since we -have set AC_CFG_ALOC(..). However, just -in case, we leave the space. -#endif /* 0 */ - unsigned char fd_dest[ADDR_LEN]; /* Destination address */ - /* Written by 82586 */ - unsigned char fd_src[ADDR_LEN]; /* Source address */ - /* Written by 82586 */ - unsigned short fd_length; /* Frame length or type */ - /* Written by 82586 */ -}; - -#define fdoff(p,f) toff(fd_t, p, f) - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * For more details, see wavelan.c. - */ diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in --- linux-2.5.3-pre2/drivers/net/pcmcia/Config.in Mon Nov 12 09:35:43 2001 +++ linux/drivers/net/pcmcia/Config.in Mon Jan 21 17:23:23 2002 @@ -28,8 +28,6 @@ bool ' Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then 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 dep_tristate ' Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA fi fi diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile --- linux-2.5.3-pre2/drivers/net/pcmcia/Makefile Mon Nov 12 09:35:43 2001 +++ linux/drivers/net/pcmcia/Makefile Mon Jan 21 17:23:23 2002 @@ -27,8 +27,6 @@ # 16-bit wireless client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o -obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o -obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o # Cardbus client drivers diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/i82593.h linux/drivers/net/pcmcia/i82593.h --- linux-2.5.3-pre2/drivers/net/pcmcia/i82593.h Fri Mar 2 11:02:15 2001 +++ linux/drivers/net/pcmcia/i82593.h Wed Dec 31 16:00:00 1969 @@ -1,224 +0,0 @@ -/* - * 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 -urN linux-2.5.3-pre2/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c --- linux-2.5.3-pre2/drivers/net/pcmcia/netwave_cs.c Fri Oct 12 14:21:18 2001 +++ linux/drivers/net/pcmcia/netwave_cs.c Wed Dec 31 16:00:00 1969 @@ -1,1600 +0,0 @@ -/********************************************************************* - * - * 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 -#include - -#ifdef CONFIG_NET_PCMCIA_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 ((32*HZ)/100) - -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 */ - -/* - 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; - -/* 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(struct net_device *); - -/* Statistics */ -static void update_stats(struct net_device *dev); -static struct net_device_stats *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; - -/* - 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. -*/ - -/* Wireless Extension Backward compatibility - Jean II - * If the new wireless device private ioctl range is not defined, - * default to standard device private ioctl range */ -#ifndef SIOCIWFIRSTPRIV -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif /* SIOCIWFIRSTPRIV */ - -#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */ -/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 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_link_t link; - struct net_device dev; - dev_node_t node; - u_char *ramBase; - int timeoutCounter; - int lastExec; - struct timer_list watchdog; /* To avoid blocking state */ - struct site_survey nss; - struct net_device_stats stats; -#ifdef WIRELESS_EXT - struct iw_statistics iw_stats; /* Wireless stats */ -#endif -} netwave_private; - -#ifdef NETWAVE_STATS -static struct net_device_stats *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_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 */ - priv = kmalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) return NULL; - memset(priv, 0, sizeof(*priv)); - link = &priv->link; dev = &priv->dev; - link->priv = dev->priv = priv; - 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; - - /* 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; - - dev->tx_timeout = &netwave_watchdog; - dev->watchdog_timeo = TX_TIMEOUT; - - ether_setup(dev); - dev->open = &netwave_open; - dev->stop = &netwave_close; - 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) -{ - netwave_private *priv = link->priv; - dev_link_t **linkp; - - DEBUG(0, "netwave_detach(0x%p)\n", link); - - /* - 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(). - */ - del_timer(&link->release); - 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->dev) - unregister_netdev(&priv->dev); - kfree(priv); - -} /* 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 (very important for backward compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(&range, 0, sizeof(range)); - -#if WIRELESS_EXT > 10 - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; /* Nothing for us in v10 and v11 */ -#endif /* WIRELESS_EXT > 10 */ - - /* 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 = link->handle; - netwave_private *priv = link->priv; - struct net_device *dev = &priv->dev; - tuple_t tuple; - cisparse_t parse; - int i, j, last_ret, last_fn; - u_char buf[64]; - win_req_t req; - memreq_t mem; - u_char *ramBase = NULL; - - 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 (i = 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; - if (register_netdev(dev) != 0) { - printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); - goto failed; - } - - strcpy(priv->node.dev_name, dev->name); - link->dev = &priv->node; - link->state &= ~DEV_CONFIG_PENDING; - - /* 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); -} /* 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; - netwave_private *priv = 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(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_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; - netwave_private *priv = link->priv; - struct net_device *dev = &priv->dev; - - 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) { - netif_device_detach(dev); - mod_timer(&link->release, jiffies + HZ/20); - } - 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) - netif_device_detach(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) { - netwave_reset(dev); - netif_device_attach(dev); - } - } - 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; - - /* 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); - restore_flags(flags); - 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); - - 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... - */ - - netif_stop_queue(dev); - - { - 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? */ - netif_start_queue(dev); - } - 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 transferred 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 = dev->priv; - dev_link_t *link = &priv->link; - int i; - - if (!netif_device_present(dev)) - return; - - 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 (!DEV_OK(link)) { - DEBUG(1, "netwave_interrupt: 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)); - - netif_wake_queue(dev); - } - /* TxBA, this would trigger on all error packets received */ - /* if (status & 0x01) { - DEBUG(4, "Transmit buffers available, %x\n", status); - } - */ - } -} /* 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(struct net_device *dev) { - - DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name); - netwave_reset(dev); - dev->trans_start = jiffies; - netif_wake_queue(dev); -} /* netwave_watchdog */ - -static struct net_device_stats *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); - - dev->last_rx = jiffies; - priv->stats.rx_packets++; - priv->stats.rx_bytes += rcvLen; - - /* 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"); - } - return 0; -} - -static int netwave_open(struct net_device *dev) { - netwave_private *priv = dev->priv; - dev_link_t *link = &priv->link; - - DEBUG(1, "netwave_open: starting.\n"); - - if (!DEV_OK(link)) - return -ENODEV; - - link->open++; - MOD_INC_USE_COUNT; - - netif_start_queue(dev); - netwave_reset(dev); - - return 0; -} - -static int netwave_close(struct net_device *dev) { - netwave_private *priv = (netwave_private *)dev->priv; - dev_link_t *link = &priv->link; - - DEBUG(1, "netwave_close: finishing.\n"); - - link->open--; - netif_stop_queue(dev); - if (link->state & DEV_STALE_CONFIG) - mod_timer(&link->release, jiffies + HZ/20); - - 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; - 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); -} -MODULE_LICENSE("GPL"); diff -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan.h linux/drivers/net/pcmcia/wavelan.h --- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan.h Mon Jan 21 17:22:32 2002 +++ linux/drivers/net/pcmcia/wavelan.h Wed Dec 31 16:00:00 1969 @@ -1,386 +0,0 @@ -/* - * 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 controller (see definitions in - * file i82593.h). - * - * The main difference between the pcmcia hardware and the ISA one is - * the Ethernet Controller (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 -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.c linux/drivers/net/pcmcia/wavelan_cs.c --- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.c Mon Jan 21 17:22:32 2002 +++ linux/drivers/net/pcmcia/wavelan_cs.c Wed Dec 31 16:00:00 1969 @@ -1,4837 +0,0 @@ -/* - * 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. - * (note : inline, so optimised away) - */ -static inline void -wv_splhi(net_local * lp, - unsigned long * pflags) -{ - spin_lock_irqsave(&lp->spinlock, *pflags); - /* Note : above does the cli(); itself */ -} - -/*------------------------------------------------------------------*/ -/* - * Wrapper for re-enabling interrupts. - */ -static inline void -wv_splx(net_local * lp, - unsigned long * pflags) -{ - spin_unlock_irqrestore(&lp->spinlock, *pflags); - - /* Note : enabling interrupts on the hardware is done in wv_ru_start() - * via : outb(OP1_INT_ENABLE, LCCR(base)); - */ -} - -/*------------------------------------------------------------------*/ -/* - * 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 *******************/ -/* - * Useful 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 */ - mdelay(1); -} /* 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)) - mdelay(1); - } - - /* 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 */ - mdelay(10); - 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 unsupported 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=(long)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 flags; - -#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 */ - wv_splhi(lp, &flags); - - 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); - - if(mode==NWID_PROMISC) - lp->cell_search=1; - else - lp->cell_search=0; - - /* ReEnable interrupts & restore flags */ - wv_splx(lp, &flags); -} - -/* 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 flags; - - 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 */ - wv_splhi(lp, &flags); - - 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(lp, &flags); - - 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 0 - /* Some people don't need this, some other may need it */ - nwid=nwid^ntohs(beacon->domain_id); -#endif - -#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 *************************/ -/* - * Useful subroutines to manage the Ethernet controller - */ - -/*------------------------------------------------------------------*/ -/* - * Routine to synchronously send a command to the i82593 chip. - * Should be called with interrupts disabled. - * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(), - * wv_82593_config() & wv_diag()) - */ -static int -wv_82593_cmd(device * dev, - char * str, - int cmd, - int result) -{ - ioaddr_t base = dev->base_addr; - int status; - int wait_completed; - long spin; - - /* Spin until the chip finishes executing its current command (if any) */ - spin = 1000; - do - { - /* Time calibration of the loop */ - udelay(10); - - /* Read the interrupt register */ - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); - - /* If the interrupt hasn't be posted */ - if(spin <= 0) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n", - str, status); -#endif - return(FALSE); - } - - /* Issue the command to the controller */ - outb(cmd, LCCR(base)); - - /* If we don't have to check the result of the command - * Note : this mean that the irq handler will deal with that */ - if(result == SR0_NO_RESULT) - return(TRUE); - - /* We are waiting for command completion */ - wait_completed = TRUE; - - /* Busy wait while the LAN controller executes the command. */ - spin = 1000; - do - { - /* Time calibration of the loop */ - udelay(10); - - /* Read the interrupt register */ - outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); - status = inb(LCSR(base)); - - /* Check if there was an interrupt posted */ - if((status & SR0_INTERRUPT)) - { - /* Acknowledge the interrupt */ - outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); - - /* Check if interrupt is a command completion */ - if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) && - ((status & SR0_BOTH_RX_TX) != 0x0) && - !(status & SR0_RECEPTION)) - { - /* Signal command completion */ - wait_completed = FALSE; - } - else - { - /* Note : Rx interrupts will be handled later, because we can - * handle multiple Rx packets at once */ -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_INFO "wv_82593_cmd: not our interrupt\n"); -#endif - } - } - } - while(wait_completed && (spin-- > 0)); - - /* If the interrupt hasn't be posted */ - if(wait_completed) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n", - str, status); -#endif - return(FALSE); - } - - /* Check the return code returned by the card (see above) against - * the expected return code provided by the caller */ - if((status & SR0_EVENT_MASK) != result) - { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n", - str, 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) -{ - int ret = FALSE; - - if(wv_82593_cmd(dev, "wv_diag(): diagnose", - OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)) - ret = TRUE; - -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n"); -#endif - return(ret); -} /* 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; - - /* 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; - unsigned long flags; - - /* Arm the flag, will be cleard in wv_82593_config() */ - lp->reconfig_82593 = TRUE; - - /* Check if we can do it now ! */ - if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev))) - { - wv_splhi(lp, &flags); /* Disable interrupts */ - wv_82593_config(dev); - wv_splx(lp, &flags); /* Re-enable interrupts */ - } - else - { -#ifdef DEBUG_IOCTL_INFO - printk(KERN_DEBUG - "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n", - dev->name, dev->state, link->open); -#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; - } - - wv_splhi(lp, &flags); - - /* 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 */ - - wv_splx(lp, &flags); - - 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(" state=%lX,", dev->state); - 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 flags; - 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 */ - wv_splhi(lp, &flags); - - /* 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(!capable(CAP_NET_ADMIN)) - { - 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 (very important for backward compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know about to zero */ - memset(&range, 0, sizeof(range)); - -#if WIRELESS_EXT > 10 - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; /* Nothing for us in v10 and v11 */ -#endif /* WIRELESS_EXT > 10 */ - - /* 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 > 11 - range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range.avg_qual.level = 30; - range.avg_qual.noise = 8; -#endif /* WIRELESS_EXT > 11 */ - -#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(!capable(CAP_NET_ADMIN)) - { - 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(!capable(CAP_NET_ADMIN)) - { - 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(lp, &flags); - -#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 flags; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); -#endif - - /* Disable interrupts & save flags */ - wv_splhi(lp, &flags); - - 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(lp, &flags); - -#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 - 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 */ - -#ifdef WIRELESS_SPY - 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 */ - dev->last_rx = jiffies; - lp->stats.rx_packets++; - lp->stats.rx_bytes += sksize; - -#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()) - * Note : the spinlock is already grabbed for us and irq are disabled. - */ -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 flags; - 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 - - wv_splhi(lp, &flags); - - /* 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; - - wv_splx(lp, &flags); - -#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 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; - unsigned long flags; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, - (unsigned) skb); -#endif - - /* - * Block a timer-based transmit from overlapping a previous transmit. - * In other words, prevent reentering this routine. - */ - netif_stop_queue(dev); - - /* If somebody has asked to reconfigure the controller, - * we can do it now */ - if(lp->reconfig_82593) - { - wv_splhi(lp, &flags); /* Disable interrupts */ - wv_82593_config(dev); - wv_splx(lp, &flags); /* Re-enable interrupts */ - /* Note : the configure procedure was totally synchronous, - * so the Tx buffer is now free */ - } - -#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; - net_local * lp = (net_local *) dev->priv; - unsigned long flags; - int status; - int spin; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); -#endif - - wv_splhi(lp, &flags); - - /* 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 = 300; - do - { - udelay(10); - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0)); - - /* Now, spin until the chip finishes executing its current command */ - do - { - udelay(10); - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - } - while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); - - wv_splx(lp, &flags); - - /* If there was a problem */ - if(spin <= 0) - { -#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; - unsigned long flags; - -#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; - - wv_splhi(lp, &flags); - - /* 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)); - -#if 0 - /* XXX the i82593 manual page 6-4 seems to indicate that the stop register - should be set as below */ - /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/ -#elif 0 - /* but I set it 0 instead */ - lp->stop = 0; -#else - /* but I set it to 3 bytes per packet less than 8K */ - lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; -#endif - 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 spin = 10000; - - /* spin until the chip starts receiving */ - do - { - outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); - status = inb(LCSR(base)); - if(spin-- <= 0) - 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 - - wv_splx(lp, &flags); - -#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 controller (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; - int ret = TRUE; - -#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)) - ret = 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)) - ret = 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)) - ret = FALSE; - lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */ - } - - /* Job done, clear the flag */ - lp->reconfig_82593 = FALSE; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name); -#endif - return(ret); -} - -/*------------------------------------------------------------------*/ -/* - * 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; - unsigned long flags; - int ret = FALSE; - -#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; - - /* Disable interrupts */ - wv_splhi(lp, &flags); - - /* Disguised goto ;-) */ - do - { - /* 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 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 - break; - } - - /* initialize the modem */ - if(wv_mmc_init(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n", - dev->name); -#endif - break; - } - - /* reset the LAN controller (i82593) */ - outb(OP0_RESET, LCCR(base)); - mdelay(1); /* A bit crude ! */ - - /* Initialize the LAN controller */ - if(wv_82593_config(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", - dev->name); -#endif - break; - } - - /* Diagnostic */ - if(wv_diag(dev) == FALSE) - { -#ifdef DEBUG_CONFIG_ERRORS - printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n", - dev->name); -#endif - break; - } - - /* - * insert code for loopback test here - */ - - /* The device is now configured */ - lp->configured = 1; - ret = TRUE; - } - while(0); - - /* Re-enable interrupts */ - wv_splx(lp, &flags); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); -#endif - return(ret); -} - -/*------------------------------------------------------------------*/ -/* - * 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 - - 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 small 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 = req.Size = 0; - 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, req.Size); - 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; - netif_start_queue(dev); - -#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; - } - - strcpy(((net_local *) dev->priv)->node.dev_name, dev->name); - 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_STALE_CONFIG); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); -#endif -} /* wv_pcmcia_release */ - -/*------------------------------------------------------------------*/ -/* - * Sometimes, wavelan_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 transferred 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; - -#ifdef DEBUG_INTERRUPT_INFO - /* Check state of our spinlock (it should be cleared) */ - if(spin_is_locked(&lp->spinlock)) - printk(KERN_DEBUG - "%s: wavelan_interrupt(): spinlock is already locked !!!\n", - dev->name); -#endif - - /* Prevent reentrancy. We need to do that because we may have - * multiple interrupt handler running concurently. - * It is safe because wv_splhi() disable interrupts before aquiring - * the spinlock. */ - spin_lock(&lp->spinlock); - - /* 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; - } - - /* ----------------- 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 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 - - /* 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++; - - netif_wake_queue(dev); - 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 */ - } - } /* while(1) */ - - spin_unlock(&lp->spinlock); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); -#endif -} /* wv_interrupt */ - -/*------------------------------------------------------------------*/ -/* - * Watchdog: when we start a transmission, a timer is set for us in the - * kernel. If the transmission completes, this timer is disabled. If - * the timer expires, we are called and we try to unlock the hardware. - * - * Note : This watchdog is move clever than the one in the ISA driver, - * because it try to abort the current command before reseting - * everything... - * On the other hand, it's a bit simpler, because we don't have to - * deal with the multiple Tx buffers... - */ -static void -wavelan_watchdog(device * dev) -{ - net_local * lp = (net_local *) dev->priv; - ioaddr_t base = dev->base_addr; - unsigned long flags; - int aborted = FALSE; - -#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 - - wv_splhi(lp, &flags); - - /* Ask to abort the current command */ - outb(OP0_ABORT, LCCR(base)); - - /* Wait for the end of the command (a bit hackish) */ - if(wv_82593_cmd(dev, "wavelan_watchdog(): abort", - OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED)) - aborted = TRUE; - - /* Release spinlock here so that wv_hw_reset() can grab it */ - wv_splx(lp, &flags); - - /* Check if we were successful in aborting it */ - if(!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... */ - netif_wake_queue(dev); - -#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 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 */ - netif_start_queue(dev); - - /* 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; - 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 */ - - link->open--; - MOD_DEC_USE_COUNT; - - /* If the card is still present */ - if(netif_running(dev)) - { - netif_stop_queue(dev); - - /* 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; -} - -/*------------------------------------------------------------------*/ -/* - * 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); - if (!link) return NULL; - 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); - if (!dev) { - kfree(link); - return NULL; - } - 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); - if (!lp) { - kfree(link); - kfree(dev); - return NULL; - } - memset(lp, 0x00, sizeof(net_local)); - - /* Init specific data */ - lp->configured = 0; - lp->reconfig_82593 = FALSE; - lp->nresets = 0; - /* Multicast stuff */ - lp->promiscuous = 0; - lp->allmulticast = 0; - lp->mc_count = 0; - - /* Init spinlock */ - spin_lock_init(&lp->spinlock); - - /* back links */ - lp->link = link; - lp->dev = dev; - - /* Standard setup for generic data */ - ether_setup(dev); - - /* wavelan NET3 callbacks */ - 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 */ - - /* Set the watchdog timer */ - dev->tx_timeout = &wavelan_watchdog; - dev->watchdog_timeo = WATCHDOG_JIFFIES; - -#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 */ - 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 */ - netif_device_detach(dev); - - /* 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) - netif_device_detach(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) /* If RESET -> True, If RESUME -> False ??? */ - { - wv_hw_reset(dev); - netif_device_attach(dev); - } - } - 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 -urN linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.h linux/drivers/net/pcmcia/wavelan_cs.h --- linux-2.5.3-pre2/drivers/net/pcmcia/wavelan_cs.h Mon Jan 21 17:22:32 2002 +++ linux/drivers/net/pcmcia/wavelan_cs.h Wed Dec 31 16:00:00 1969 @@ -1,825 +0,0 @@ -/* - * 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 - * - * SMP - * --- - * We now are SMP compliant (I eventually fixed the remaining bugs). - * The driver has been tested on a dual P6-150 and survived my usual - * set of torture tests. - * Anyway, I spent enough time chasing interrupt re-entrancy during - * errors or reconfigure, and I designed the locked/unlocked sections - * of the driver with great care, and with the recent addition of - * the spinlock (thanks to the new API), we should be quite close to - * the truth. - * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), - * but better safe than sorry (especially at 2 Mb/s ;-). - * - * I have also looked into disabling only our interrupt on the card - * (via HACR) instead of all interrupts in the processor (via cli), - * so that other driver are not impacted, and it look like it's - * possible, but it's very tricky to do right (full of races). As - * the gain would be mostly for SMP systems, it can wait... - * - * 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 controller - */ - -/* --------------------------- 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 -> 2.2 - pcmcia 2.8.18+) - * with an HP OmniBook 4000 and then a 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 - * - Suppress 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 Suppress "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) - * - * Changes made for release in 3.1.12 : - * ---------------------------------- - * - reworked wv_82593_cmd to avoid using the IRQ handler and doing - * ugly things with interrupts. - * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog - * - Update to new network API (softnet - 2.3.43) : - * o replace dev->tbusy (David + me) - * o replace dev->tstart (David + me) - * o remove dev->interrupt (David) - * o add SMP locking via spinlock in splxx (me) - * o add spinlock in interrupt handler (me) - * o use kernel watchdog instead of ours (me) - * o verify that all the changes make sense and work (me) - * - Re-sync kernel/pcmcia versions (not much actually) - * - A few other cleanups (David & me)... - * - * Changes made for release in 3.1.22 : - * ---------------------------------- - * - Check that SMP works, remove annoying log message - * - * Changes made for release in 3.1.24 : - * ---------------------------------- - * - Fix unfrequent card lockup when watchdog was reseting the hardware : - * o control first busy loop in wv_82593_cmd() - * o Extend spinlock protection in wv_hw_config() - * - * 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 -#include -#include - -#ifdef CONFIG_NET_PCMCIA_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 transmitted 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 transmitted 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 : v23 (SMP + wireless extensions) 20/12/00\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 ------------------------ */ - -/* Wireless Extension Backward compatibility - Jean II - * If the new wireless device private ioctl range is not defined, - * default to standard device private ioctl range */ -#ifndef SIOCIWFIRSTPRIV -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif /* SIOCIWFIRSTPRIV */ - -#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ -#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ -#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ -#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ - -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 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... */ - spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - 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 controller */ - u_char promiscuous; /* Promiscuous mode */ - u_char allmulticast; /* All Multicast mode */ - int mc_count; /* Number of multicast addresses */ - - 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 void - wv_splhi(net_local *, /* Disable interrupts */ - unsigned long *); /* flags */ -static inline void - wv_splx(net_local *, /* ReEnable interrupts */ - unsigned long *); /* 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 controller */ -/* ------------------- 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(device *); /* Transmission watchdog */ -/* ------------------- CONFIGURATION CALLBACKS ------------------- */ -static int - wavelan_open(device *), /* Open the device */ - wavelan_close(device *); /* Close the device */ -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 */ - -/* - * 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 */ - -MODULE_LICENSE("GPL"); - -#endif /* WAVELAN_CS_H */ - diff -urN linux-2.5.3-pre2/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- linux-2.5.3-pre2/drivers/net/wavelan.c Wed Jan 9 14:54:38 2002 +++ linux/drivers/net/wavelan.c Wed Dec 31 16:00:00 1969 @@ -1,4342 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * Original copyright follows (also see the end of this file). - * See wavelan.p.h for details. - * - * - * - * AT&T GIS (nee NCR) WaveLAN card: - * An Ethernet-like radio transceiver - * controlled by an Intel 82586 coprocessor. - */ - -#include "wavelan.p.h" /* Private header */ - -/************************* MISC SUBROUTINES **************************/ -/* - * Subroutines which won't fit in one of the following category - * (WaveLAN modem or i82586) - */ - -/*------------------------------------------------------------------*/ -/* - * Wrapper for disabling interrupts and locking the driver. - * (note : inline, so optimised away) - */ -static inline void wv_splhi(net_local * lp, - unsigned long * pflags) -{ - spin_lock_irqsave(&lp->spinlock, *pflags); - /* Note : above does the cli(); itself */ -} - -/*------------------------------------------------------------------*/ -/* - * Wrapper for re-enabling interrupts and un-locking the driver. - */ -static inline void wv_splx(net_local * lp, - unsigned long * pflags) -{ - spin_unlock_irqrestore(&lp->spinlock, *pflags); -} - -/*------------------------------------------------------------------*/ -/* - * Translate irq number to PSA irq parameter - */ -static u8 wv_irq_to_psa(int irq) -{ - if (irq < 0 || irq >= NELS(irqvals)) - return 0; - - return irqvals[irq]; -} - -/*------------------------------------------------------------------*/ -/* - * Translate PSA irq parameter to irq number - */ -static int __init wv_psa_to_irq(u8 irqval) -{ - int irq; - - for (irq = 0; irq < NELS(irqvals); irq++) - if (irqvals[irq] == irqval) - return irq; - - return -1; -} - -#ifdef STRUCT_CHECK -/*------------------------------------------------------------------*/ -/* - * Sanity routine to verify the sizes of the various WaveLAN interface - * structures. - */ -static char *wv_struct_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"); - SC(ha_t, HA_SIZE, "ha_t"); - -#undef SC - - return ((char *) NULL); -} /* wv_struct_check */ -#endif /* STRUCT_CHECK */ - -/********************* HOST ADAPTER SUBROUTINES *********************/ -/* - * Useful subroutines to manage the WaveLAN ISA interface - * - * One major difference with the PCMCIA hardware (except the port mapping) - * is that we have to keep the state of the Host Control Register - * because of the interrupt enable & bus size flags. - */ - -/*------------------------------------------------------------------*/ -/* - * Read from card's Host Adaptor Status Register. - */ -static inline u16 hasr_read(unsigned long ioaddr) -{ - return (inw(HASR(ioaddr))); -} /* hasr_read */ - -/*------------------------------------------------------------------*/ -/* - * Write to card's Host Adapter Command Register. - */ -static inline void hacr_write(unsigned long ioaddr, u16 hacr) -{ - outw(hacr, HACR(ioaddr)); -} /* 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(unsigned long ioaddr, u16 hacr) -{ - hacr_write(ioaddr, hacr); - /* delay might only be needed sometimes */ - mdelay(1); -} /* hacr_write_slow */ - -/*------------------------------------------------------------------*/ -/* - * Set the channel attention bit. - */ -static inline void set_chan_attn(unsigned long ioaddr, u16 hacr) -{ - hacr_write(ioaddr, hacr | HACR_CA); -} /* set_chan_attn */ - -/*------------------------------------------------------------------*/ -/* - * Reset, and then set host adaptor into default mode. - */ -static inline void wv_hacr_reset(unsigned long ioaddr) -{ - hacr_write_slow(ioaddr, HACR_RESET); - hacr_write(ioaddr, HACR_DEFAULT); -} /* wv_hacr_reset */ - -/*------------------------------------------------------------------*/ -/* - * Set the I/O transfer over the ISA bus to 8-bit mode - */ -static inline void wv_16_off(unsigned long ioaddr, u16 hacr) -{ - hacr &= ~HACR_16BITS; - hacr_write(ioaddr, hacr); -} /* wv_16_off */ - -/*------------------------------------------------------------------*/ -/* - * Set the I/O transfer over the ISA bus to 8-bit mode - */ -static inline void wv_16_on(unsigned long ioaddr, u16 hacr) -{ - hacr |= HACR_16BITS; - hacr_write(ioaddr, hacr); -} /* wv_16_on */ - -/*------------------------------------------------------------------*/ -/* - * Disable interrupts on the WaveLAN hardware. - * (called by wv_82586_stop()) - */ -static inline void wv_ints_off(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - - lp->hacr &= ~HACR_INTRON; - hacr_write(ioaddr, lp->hacr); -} /* wv_ints_off */ - -/*------------------------------------------------------------------*/ -/* - * Enable interrupts on the WaveLAN hardware. - * (called by wv_hw_reset()) - */ -static inline void wv_ints_on(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - - lp->hacr |= HACR_INTRON; - hacr_write(ioaddr, lp->hacr); -} /* wv_ints_on */ - -/******************* MODEM MANAGEMENT SUBROUTINES *******************/ -/* - * Useful subroutines to manage the modem of the WaveLAN - */ - -/*------------------------------------------------------------------*/ -/* - * Read the Parameter Storage Area from the WaveLAN card's memory - */ -/* - * Read bytes from the PSA. - */ -static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */ - u8 * b, /* buffer to fill */ - int n) -{ /* size to read */ - wv_16_off(ioaddr, hacr); - - while (n-- > 0) { - outw(o, PIOR2(ioaddr)); - o++; - *b++ = inb(PIOP2(ioaddr)); - } - - wv_16_on(ioaddr, hacr); -} /* psa_read */ - -/*------------------------------------------------------------------*/ -/* - * Write the Parameter Storage Area to the WaveLAN card's memory. - */ -static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */ - u8 * b, /* Buffer in memory */ - int n) -{ /* Length of buffer */ - int count = 0; - - wv_16_off(ioaddr, hacr); - - while (n-- > 0) { - outw(o, PIOR2(ioaddr)); - o++; - - outb(*b, PIOP2(ioaddr)); - b++; - - /* Wait for the memory to finish its write cycle */ - count = 0; - while ((count++ < 100) && - (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1); - } - - wv_16_on(ioaddr, hacr); -} /* 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 inline u16 psa_crc(u8 * psa, /* The PSA */ - int size) -{ /* Number of short for CRC */ - int byte_cnt; /* Loop on the PSA */ - u16 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, unsigned long ioaddr, u16 hacr) -{ -#ifdef SET_PSA_CRC - psa_t psa; - u16 crc; - - /* read the parameter storage area */ - psa_read(ioaddr, hacr, 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(ioaddr, hacr, (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(unsigned long ioaddr, u16 o, u8 d) -{ - /* Wait for MMC to go idle */ - while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); - - outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr)); -} - -/*------------------------------------------------------------------*/ -/* - * Routine to write bytes to the Modem Management Controller. - * We start at the end because it is the way it should be! - */ -static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n) -{ - o += n; - b += n; - - while (n-- > 0) - mmc_out(ioaddr, --o, *(--b)); -} /* mmc_write */ - -/*------------------------------------------------------------------*/ -/* - * Read a byte from the MMC. - * Optimised version for 1 byte, avoid using memory. - */ -static inline u8 mmc_in(unsigned long ioaddr, u16 o) -{ - while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); - outw(o << 1, MMCR(ioaddr)); - - while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); - return (u8) (inw(MMCR(ioaddr)) >> 8); -} - -/*------------------------------------------------------------------*/ -/* - * 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 at the end because it is the way it should be! - */ -static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n) -{ - o += n; - b += n; - - while (n-- > 0) - *(--b) = mmc_in(ioaddr, --o); -} /* mmc_read */ - -/*------------------------------------------------------------------*/ -/* - * Get the type of encryption available. - */ -static inline int mmc_encr(unsigned long ioaddr) -{ /* I/O port of the card */ - int temp; - - temp = mmc_in(ioaddr, 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(unsigned long ioaddr, /* 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(ioaddr, mmroff(0, mmr_fee_status)) & - MMR_FEE_STATUS_BUSY)) udelay(delay); -} - -/*------------------------------------------------------------------*/ -/* - * Read bytes from the Frequency EEPROM (frequency select cards). - */ -static void fee_read(unsigned long ioaddr, /* I/O port of the card */ - u16 o, /* destination offset */ - u16 * b, /* data buffer */ - int n) -{ /* number of registers */ - b += n; /* Position at the end of the area */ - - /* Write the address */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while (n-- > 0) { - /* Write the read command */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ); - - /* Wait until EEPROM is ready (should be quick). */ - fee_wait(ioaddr, 10, 100); - - /* Read the value. */ - *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) | - mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); - } -} - -#ifdef WIRELESS_EXT /* if the wireless extension exists 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(unsigned long ioaddr, /* I/O port of the card */ - u16 o, /* destination offset */ - u16 * 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(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); - - fee_wait(ioaddr, 10, 100); - - /* Read the protected register. */ - printk("Protected 2: %02X-%02X\n", - mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)), - mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); -#endif /* DOESNT_SEEM_TO_WORK */ - - /* Enable protected register. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); - - fee_wait(ioaddr, 10, 100); - - /* Unprotect area. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); -#ifdef DOESNT_SEEM_TO_WORK /* disabled */ - /* or use: */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); -#endif /* DOESNT_SEEM_TO_WORK */ - - fee_wait(ioaddr, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ - - /* Write enable. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); - - fee_wait(ioaddr, 10, 100); - - /* Write the EEPROM address. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); - - /* Loop on all buffer */ - while (n-- > 0) { - /* Write the value. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); - mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF); - - /* Write the write command. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_WRITE); - - /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */ - mdelay(10); - fee_wait(ioaddr, 10, 100); - } - - /* Write disable. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); - - fee_wait(ioaddr, 10, 100); - -#ifdef EEPROM_IS_PROTECTED /* disabled */ - /* Reprotect EEPROM. */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); - - fee_wait(ioaddr, 10, 100); -#endif /* EEPROM_IS_PROTECTED */ -} -#endif /* WIRELESS_EXT */ - -/************************ I82586 SUBROUTINES *************************/ -/* - * Useful subroutines to manage the Ethernet controller - */ - -/*------------------------------------------------------------------*/ -/* - * Read bytes from the on-board RAM. - * Why does inlining this function make it fail? - */ -static /*inline */ void obram_read(unsigned long ioaddr, - u16 o, u8 * b, int n) -{ - outw(o, PIOR1(ioaddr)); - insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); -} - -/*------------------------------------------------------------------*/ -/* - * Write bytes to the on-board RAM. - */ -static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n) -{ - outw(o, PIOR1(ioaddr)); - outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); -} - -/*------------------------------------------------------------------*/ -/* - * Acknowledge the reading of the status issued by the i82586. - */ -static void wv_ack(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - u16 scb_cs; - int i; - - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - scb_cs &= SCB_ST_INT; - - if (scb_cs == 0) - return; - - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - udelay(100); - -#ifdef DEBUG_CONFIG_ERROR - if (i <= 0) - printk(KERN_INFO - "%s: wv_ack(): board not accepting command.\n", - dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Set channel attention bit and busy wait until command has - * completed, then acknowledge completion of the command. - */ -static inline int wv_synchronous_cmd(device * dev, const char *str) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - u16 scb_cmd; - ach_t cb; - int i; - - scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cmd, sizeof(scb_cmd)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, - sizeof(cb)); - if (cb.ac_status & AC_SFLD_C) - break; - - udelay(10); - } - udelay(100); - - if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO "%s: %s failed; status = 0x%x\n", - dev->name, str, cb.ac_status); -#endif -#ifdef DEBUG_I82586_SHOW - wv_scb_show(ioaddr); -#endif - return -1; - } - - /* Ack the status */ - wv_ack(dev); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Configuration commands completion interrupt. - * Check if done, and if OK. - */ -static inline int -wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp) -{ - unsigned short mcs_addr; - unsigned short status; - int ret; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name); -#endif - - mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t) - + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t); - - /* Read the status of the last command (set mc list). */ - obram_read(ioaddr, acoff(mcs_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - - /* If not completed -> exit */ - if ((status & AC_SFLD_C) == 0) - ret = 0; /* Not ready to be scrapped */ - else { -#ifdef DEBUG_CONFIG_ERROR - unsigned short cfg_addr; - unsigned short ias_addr; - - /* Check mc_config command */ - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n", - dev->name, status); - - /* check ia-config command */ - ias_addr = mcs_addr - sizeof(ac_ias_t); - obram_read(ioaddr, acoff(ias_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n", - dev->name, status); - - /* Check config command. */ - cfg_addr = ias_addr - sizeof(ac_cfg_t); - obram_read(ioaddr, acoff(cfg_addr, ac_status), - (unsigned char *) &status, sizeof(status)); - if ((status & AC_SFLD_OK) != AC_SFLD_OK) - printk(KERN_INFO - "%s: wv_config_complete(): configure failed; status = 0x%x\n", - dev->name, status); -#endif /* DEBUG_CONFIG_ERROR */ - - ret = 1; /* Ready to be scrapped */ - } - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, - ret); -#endif - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Command completion interrupt. - * Reclaim as many freed tx buffers as we can. - * (called in wavelan_interrupt()). - * Note : the spinlock is already grabbed for us. - */ -static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp) -{ - int nreaped = 0; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name); -#endif - - /* Loop on all the transmit buffers */ - while (lp->tx_first_in_use != I82586NULL) { - unsigned short tx_status; - - /* Read the first transmit buffer */ - obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), - (unsigned char *) &tx_status, - sizeof(tx_status)); - - /* If not completed -> exit */ - if ((tx_status & AC_SFLD_C) == 0) - break; - - /* Hack for reconfiguration */ - if (tx_status == 0xFFFF) - if (!wv_config_complete(dev, ioaddr, lp)) - break; /* Not completed */ - - /* We now remove this buffer */ - nreaped++; - --lp->tx_n_in_use; - -/* -if (lp->tx_n_in_use > 0) - printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); -*/ - - /* Was it the last one? */ - if (lp->tx_n_in_use <= 0) - lp->tx_first_in_use = I82586NULL; - else { - /* Next one in the chain */ - lp->tx_first_in_use += TXBLOCKZ; - if (lp->tx_first_in_use >= - OFFSET_CU + - NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -= - NTXBLOCKS * TXBLOCKZ; - } - - /* Hack for reconfiguration */ - if (tx_status == 0xFFFF) - continue; - - /* Now, check status of the finished command */ - if (tx_status & AC_SFLD_OK) { - int ncollisions; - - lp->stats.tx_packets++; - ncollisions = tx_status & AC_SFLD_MAXCOL; - lp->stats.collisions += ncollisions; -#ifdef DEBUG_TX_INFO - if (ncollisions > 0) - printk(KERN_DEBUG - "%s: wv_complete(): tx completed after %d collisions.\n", - dev->name, ncollisions); -#endif - } else { - lp->stats.tx_errors++; - if (tx_status & AC_SFLD_S10) { - lp->stats.tx_carrier_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: no CS.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S9) { - lp->stats.tx_carrier_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: lost CTS.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S8) { - lp->stats.tx_fifo_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: slow DMA.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S6) { - lp->stats.tx_heartbeat_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: heart beat.\n", - dev->name); -#endif - } - if (tx_status & AC_SFLD_S5) { - lp->stats.tx_aborted_errors++; -#ifdef DEBUG_TX_FAIL - printk(KERN_DEBUG - "%s: wv_complete(): tx error: too many collisions.\n", - dev->name); -#endif - } - } - -#ifdef DEBUG_TX_INFO - printk(KERN_DEBUG - "%s: wv_complete(): tx completed, tx_status 0x%04x\n", - dev->name, tx_status); -#endif - } - -#ifdef DEBUG_INTERRUPT_INFO - if (nreaped > 1) - printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", - dev->name, nreaped); -#endif - - /* - * Inform upper layers. - */ - if (lp->tx_n_in_use < NTXBLOCKS - 1) { - netif_wake_queue(dev); - } -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name); -#endif - return nreaped; -} - -/*------------------------------------------------------------------*/ -/* - * Reconfigure the i82586, or at least ask for it. - * Because wv_82586_config uses a transmission buffer, we must do it - * when we are sure that there is one left, 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 - * delays sometimes. - */ -static inline void wv_82586_reconfig(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long flags; - - /* Arm the flag, will be cleard in wv_82586_config() */ - lp->reconfig_82586 = 1; - - /* Check if we can do it now ! */ - if((netif_running(dev)) && !(netif_queue_stopped(dev))) { - wv_splhi(lp, &flags); - /* May fail */ - wv_82586_config(dev); - wv_splx(lp, &flags); - } - else { -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wv_82586_reconfig(): delayed (state = %lX)\n", - dev->name, dev->state); -#endif - } -} - -/********************* DEBUG & INFO SUBROUTINES *********************/ -/* - * This routine is used in the code to show information for debugging. - * Most of the time, it dumps the contents 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 needs to be completed. - */ -static void wv_mmc_show(device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = (net_local *) dev->priv; - mmr_t m; - - /* Basic check */ - if (hasr_read(ioaddr) & HASR_NO_CLK) { - printk(KERN_WARNING - "%s: wv_mmc_show: modem not connected\n", - dev->name); - return; - } - - /* Read the mmc */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m)); - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); - -#ifdef WIRELESS_EXT /* if wireless extension exists 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 algorithm: %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_I82586_SHOW -/*------------------------------------------------------------------*/ -/* - * Print the last block of the i82586 memory. - */ -static void wv_scb_show(unsigned long ioaddr) -{ - scb_t scb; - - obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - printk(KERN_DEBUG "##### WaveLAN system control block: #####\n"); - - printk(KERN_DEBUG "status: "); - printk("stat 0x%x[%s%s%s%s] ", - (scb. - scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | - SCB_ST_RNR)) >> 12, - (scb. - scb_status & SCB_ST_CX) ? "command completion interrupt," : - "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "", - (scb. - scb_status & SCB_ST_CNA) ? "command unit not active," : "", - (scb. - scb_status & SCB_ST_RNR) ? "receiving unit not ready," : - ""); - printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8, - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_IDLE) ? "idle" : "", - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_SUSP) ? "suspended" : "", - ((scb.scb_status & SCB_ST_CUS) == - SCB_ST_CUS_ACTV) ? "active" : ""); - printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4, - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_IDLE) ? "idle" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_SUSP) ? "suspended" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_NRES) ? "no resources" : "", - ((scb.scb_status & SCB_ST_RUS) == - SCB_ST_RUS_RDY) ? "ready" : ""); - - printk(KERN_DEBUG "command: "); - printk("ack 0x%x[%s%s%s%s] ", - (scb. - scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | - SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12, - (scb. - scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "", - (scb. - scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "", - (scb. - scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "", - (scb. - scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""); - printk("cuc 0x%x[%s%s%s%s%s] ", - (scb.scb_command & SCB_CMD_CUC) >> 8, - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_NOP) ? "nop" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_GO) ? "start cbl_offset" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_RES) ? "resume execution" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_SUS) ? "suspend execution" : "", - ((scb.scb_command & SCB_CMD_CUC) == - SCB_CMD_CUC_ABT) ? "abort execution" : ""); - printk("ruc 0x%x[%s%s%s%s%s]\n", - (scb.scb_command & SCB_CMD_RUC) >> 4, - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_NOP) ? "nop" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_GO) ? "start rfa_offset" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_RES) ? "resume reception" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_SUS) ? "suspend reception" : "", - ((scb.scb_command & SCB_CMD_RUC) == - SCB_CMD_RUC_ABT) ? "abort reception" : ""); - - printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset); - printk("rfa_offset 0x%x\n", scb.scb_rfa_offset); - - printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs); - printk("alnerrs %d ", scb.scb_alnerrs); - printk("rscerrs %d ", scb.scb_rscerrs); - printk("ovrnerrs %d\n", scb.scb_ovrnerrs); -} - -/*------------------------------------------------------------------*/ -/* - * Print the formatted status of the i82586's receive unit. - */ -static void wv_ru_show(device * dev) -{ - /* net_local *lp = (net_local *) dev->priv; */ - - printk(KERN_DEBUG - "##### WaveLAN i82586 receiver unit status: #####\n"); - printk(KERN_DEBUG "ru:"); - /* - * Not implemented yet - */ - printk("\n"); -} /* wv_ru_show */ - -/*------------------------------------------------------------------*/ -/* - * Display info about one control block of the i82586 memory. - */ -static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p) -{ - unsigned long ioaddr; - ac_tx_t actx; - - ioaddr = dev->base_addr; - - printk("%d: 0x%x:", i, p); - - obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx)); - printk(" status=0x%x,", actx.tx_h.ac_status); - printk(" command=0x%x,", actx.tx_h.ac_command); - - /* - { - tbd_t tbd; - - obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd)); - printk(" tbd_status=0x%x,", tbd.tbd_status); - } - */ - - printk("|"); -} - -/*------------------------------------------------------------------*/ -/* - * Print status of the command unit of the i82586. - */ -static void wv_cu_show(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned int i; - u16 p; - - printk(KERN_DEBUG - "##### WaveLAN i82586 command unit status: #####\n"); - - printk(KERN_DEBUG); - for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) { - wv_cu_show_one(dev, lp, i, p); - - p += TXBLOCKZ; - if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - p -= NTXBLOCKS * TXBLOCKZ; - } - printk("\n"); -} -#endif /* DEBUG_I82586_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(" state=%lX,", dev->state); - 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:"); - printk(" tx_n_in_use=%d,", lp->tx_n_in_use); - printk(" hacr=0x%x,", lp->hacr); - printk(" rx_head=0x%x,", lp->rx_head); - printk(" rx_last=0x%x,", lp->rx_last); - printk(" tx_first_free=0x%x,", lp->tx_first_free); - printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use); - 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(u8 * 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 are lots of flags for configuring it to your liking. - */ -static inline void wv_init_info(device * dev) -{ - short ioaddr = dev->base_addr; - net_local *lp = (net_local *) dev->priv; - psa_t psa; - int i; - - /* Read the parameter storage area */ - psa_read(ioaddr, lp->hacr, 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_I82586_SHOW - wv_cu_show(dev); -#endif - -#ifdef DEBUG_BASIC_SHOW - /* Now, let's go for the basic stuff. */ - printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr); - for (i = 0; i < WAVELAN_ADDR_SIZE; i++) - printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]); - printk(", IRQ %d", dev->irq); - - /* 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(ioaddr, 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(ioaddr, 0x00, &freq, 1); - - /* Print frequency */ - printk(", 2.00, %ld", (freq >> 6) + 2400L); - - /* Hack! */ - if (freq & 0x20) - printk(".5"); - } else { - printk(", PC"); - switch (psa.psa_comp_number) { - case PSA_COMP_PC_AT_915: - case PSA_COMP_PC_AT_2400: - printk("-AT"); - break; - case PSA_COMP_PC_MC_915: - case PSA_COMP_PC_MC_2400: - printk("-MC"); - break; - case PSA_COMP_PCMCIA_915: - printk("MCIA"); - break; - default: - printk("?"); - } - printk(", "); - 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 different - * 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 - - /* Are we asking for promiscuous mode, - * or all multicast addresses (we don't have that!) - * or too many multicast addresses for the hardware filter? */ - if ((dev->flags & IFF_PROMISC) || - (dev->flags & IFF_ALLMULTI) || - (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) { - /* - * Enable promiscuous mode: receive all packets. - */ - if (!lp->promiscuous) { - lp->promiscuous = 1; - lp->mc_count = 0; - - wv_82586_reconfig(dev); - - /* Tell the kernel that we are doing a really bad job. */ - dev->flags |= IFF_PROMISC; - } - } else - /* Are there 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 || (dev->mc_count != lp->mc_count)) -#endif - { - lp->promiscuous = 0; - lp->mc_count = dev->mc_count; - - wv_82586_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->mc_count = 0; - - wv_82586_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); - - /* Reconfigure the beast. */ - wv_82586_reconfig(dev); - - return 0; -} -#endif /* SET_MAC_ADDRESS */ - -#ifdef WIRELESS_EXT /* if wireless extensions exist in the kernel */ - -/*------------------------------------------------------------------*/ -/* - * Frequency setting (for hardware capable 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(unsigned long ioaddr, /* 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 */ - /* Theoretically, 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 22 MHz wide, so some of the channels - * will interfere. */ - if ((frequency->e == 0) && (frequency->m < BAND_NUM)) { - /* Get frequency offset. */ - freq = channel_bands[frequency->m] >> 1; - } - - /* Verify that the frequency is allowed. */ - if (freq != 0L) { - u16 table[10]; /* Authorized frequency table */ - - /* Read the frequency table. */ - fee_read(ioaddr, 0x71, 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 to see whether 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 - * and 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(ioaddr, 0x00, area, 16); - - /* Read the DAC. */ - fee_read(ioaddr, 0x60, dac, 2); - - /* Read the new power adjust value. */ - fee_read(ioaddr, 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); - - /* Other parts 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(ioaddr, 0x00, area, 16); - - /* Write the DAC. */ - fee_write(ioaddr, 0x60, dac, 2); - - /* We now should verify here that the writing of the EEPROM went OK. */ - - /* Reread the first area. */ - fee_read(ioaddr, 0x00, area_verify, 16); - - /* Reread the DAC. */ - fee_read(ioaddr, 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 - * synthesizers (from the EEPROM - area 1) - * Note: as the EEPROM is automatically decremented, we set the end - * if the area... */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait until the download is finished. */ - fee_wait(ioaddr, 100, 100); - - /* We must now download the power adjust value (gain) to - * the synthesizers (from the EEPROM - area 7 - DAC). */ - mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61); - mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), - MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); - - /* Wait for the download to finish. */ - fee_wait(ioaddr, 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(unsigned long ioaddr, /* I/O port of the card */ - iw_freq * list, /* List of frequencies to fill */ - int max) -{ /* Maximum number of frequencies */ - u16 table[10]; /* Authorized frequency table */ - long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ - int i; /* index in the table */ - int c = 0; /* Channel number */ - - /* Read the frequency table. */ - fee_read(ioaddr, 0x71 /* frequency table */ , table, 10); - - /* Check 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))) { - /* Compute approximate channel number */ - while ((((channel_bands[c] >> 1) - 24) < freq) && - (c < NELS(channel_bands))) - c++; - list[i].i = c; /* Set the list index */ - - /* 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 our list, and if they match, get the statistics. - * Sorry, but this function really needs the wireless extensions. - */ -static inline void wl_spy_gather(device * dev, u8 * mac, /* MAC address */ - u8 * stats) -{ /* Statistics to gather */ - net_local *lp = (net_local *) dev->priv; - int i; - - /* Check 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 calculates a histogram of 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, u8 * stats) -{ /* Statistics to gather */ - net_local *lp = (net_local *) dev->priv; - u8 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 for configuration and information. - * It is here that the wireless extensions are treated (iwconfig). - */ -static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is applied */ - struct ifreq *rq, /* data passed */ - int cmd) -{ /* ioctl number */ - unsigned long ioaddr = 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 flags; - int ret = 0; - int err = 0; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, - cmd); -#endif - - /* Disable interrupts and save flags. */ - wv_splhi(lp, &flags); - - /* Look what is the request */ - switch (cmd) { - /* --------------- WIRELESS EXTENSIONS --------------- */ - - case SIOCGIWNAME: - strcpy(wrq->u.name, "WaveLAN"); - break; - - case SIOCSIWNWID: - /* Set NWID in WaveLAN. */ - 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; - psa.psa_nwid_select = 0x01; - psa_write(ioaddr, lp->hacr, - (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(ioaddr, - (char *) &m.w.mmw_netw_id_l - - (char *) &m, - (unsigned char *) &m.w.mmw_netw_id_l, 2); - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); - } else { - /* Disable NWID in the psa. */ - psa.psa_nwid_select = 0x00; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_nwid_select - - (char *) &psa, - (unsigned char *) &psa.psa_nwid_select, - 1); - - /* Disable NWID in the mmc (no filtering). */ - mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), - MMW_LOOPT_SEL_DIS_NWID); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - break; - - case SIOCGIWNWID: - /* Read the NWID. */ - psa_read(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 3); - 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 */ - break; - - case SIOCSIWFREQ: - /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) - ret = wv_set_frequency(ioaddr, &(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(ioaddr, 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(ioaddr, 0x00, &freq, 1); - wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; - wrq->u.freq.e = 1; - } else { - psa_read(ioaddr, lp->hacr, - (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. */ - /* 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; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), - psa.psa_thr_pre_set); - break; - - case SIOCGIWSENS: - /* Read the level threshold. */ - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F; - wrq->u.sens.fixed = 1; - break; - - case SIOCSIWENCODE: - /* Set encryption key */ - if (!mmc_encr(ioaddr)) { - 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 */ - wv_splx(lp, &flags); - err = copy_from_user(psa.psa_encryption_key, - wrq->u.encoding.pointer, - wrq->u.encoding.length); - wv_splhi(lp, &flags); - if (err) { - ret = -EFAULT; - break; - } - - psa.psa_encryption_select = 1; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 8 + 1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), - MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); - mmc_write(ioaddr, 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(ioaddr, lp->hacr, - (char *) &psa.psa_encryption_select - - (char *) &psa, - (unsigned char *) &psa. - psa_encryption_select, 1); - - mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); - } - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - break; - - case SIOCGIWENCODE: - /* Read the encryption key */ - if (!mmc_encr(ioaddr)) { - ret = -EOPNOTSUPP; - break; - } - - /* only super-user can see encryption key */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - - /* Basic checking... */ - if (wrq->u.encoding.pointer != (caddr_t) 0) { - /* Verify the user buffer */ - ret = - verify_area(VERIFY_WRITE, - wrq->u.encoding.pointer, 8); - if (ret) - break; - - psa_read(ioaddr, lp->hacr, - (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(ioaddr); - - /* Copy the key to the user buffer */ - wrq->u.encoding.length = 8; - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.encoding.pointer, - psa.psa_encryption_key, 8)) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - break; - - case SIOCGIWRANGE: - /* basic checking */ - if (wrq->u.data.pointer != (caddr_t) 0) { - struct iw_range range; - - /* Set the length (very important for backward - * compatibility) */ - wrq->u.data.length = sizeof(struct iw_range); - - /* Set all the info we don't care or don't know - * about to zero */ - memset(&range, 0, sizeof(range)); - - /* Set the Wireless Extension versions */ - range.we_version_compiled = WIRELESS_EXT; - range.we_version_source = 9; - - /* Set information in the range struct. */ - range.throughput = 1.6 * 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(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - range.num_channels = 10; - range.num_frequency = - wv_frequency_list(ioaddr, 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; - range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ - /* Need to get better values for those two */ - range.avg_qual.level = 30; - range.avg_qual.noise = 8; - - range.num_bitrates = 1; - range.bitrate[0] = 2000000; /* 2 Mb/s */ - - /* Encryption supported ? */ - if (mmc_encr(ioaddr)) { - 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; - } - - /* Copy structure to the user buffer. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - &range, - sizeof(struct iw_range))) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - 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" }, - }; - - /* Set the number of available ioctls. */ - wrq->u.data.length = 4; - - /* Copy structure to the user buffer. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - (u8 *) priv, - sizeof(priv))) - ret = -EFAULT; - wv_splhi(lp, &flags); - } - 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; - - /* Are there are addresses to copy? */ - if (lp->spy_number > 0) { - struct sockaddr address[IW_MAX_SPY]; - int i; - - /* Copy addresses to the driver. */ - wv_splx(lp, &flags); - err = copy_from_user(address, - wrq->u.data.pointer, - sizeof(struct sockaddr) - * lp->spy_number); - wv_splhi(lp, &flags); - if (err) { - 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; - - /* Does 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 = AF_UNIX; - } - - /* Copy addresses to the user buffer. */ - wv_splx(lp, &flags); - err = copy_to_user(wrq->u.data.pointer, - address, - sizeof(struct sockaddr) - * lp->spy_number); - - /* Copy stats to the user buffer (just after). */ - err |= copy_to_user(wrq->u.data.pointer - + (sizeof(struct sockaddr) - * lp->spy_number), - lp->spy_stat, - sizeof(iw_qual) * lp->spy_number); - wv_splhi(lp, &flags); - if (err) { - 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 (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - psa.psa_quality_thr = *(wrq->u.name) & 0x0F; - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); - mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), - psa.psa_quality_thr); - break; - - case SIOCGIPQTHR: - psa_read(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - *(wrq->u.name) = psa.psa_quality_thr & 0x0F; - break; - -#ifdef HISTOGRAM - case SIOCSIPHISTO: - /* Verify that the user is root. */ - if (!capable(CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - - /* Check the number of intervals. */ - if (wrq->u.data.length > 16) { - ret = -E2BIG; - break; - } - lp->his_number = wrq->u.data.length; - - /* Are there addresses to copy? */ - if (lp->his_number > 0) { - /* Copy interval ranges to the driver */ - wv_splx(lp, &flags); - err = copy_from_user(lp->his_range, - wrq->u.data.pointer, - sizeof(char) * lp->his_number); - wv_splhi(lp, &flags); - if (err) { - 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. */ - wv_splx(lp, &flags); - if (copy_to_user(wrq->u.data.pointer, - lp->his_sum, - sizeof(long) * lp->his_number); - ret = -EFAULT; - wv_splhi(lp, &flags); - - } /* if(pointer != NULL) */ - break; -#endif /* HISTOGRAM */ - - /* ------------------- OTHER IOCTL ------------------- */ - - default: - ret = -EOPNOTSUPP; - } /* switch (cmd) */ - - /* Enable interrupts and restore flags. */ - wv_splx(lp, &flags); - -#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) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = (net_local *) dev->priv; - mmr_t m; - iw_stats *wstats; - unsigned long flags; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", - dev->name); -#endif - - /* Check */ - if (lp == (net_local *) NULL) - return (iw_stats *) NULL; - - /* Disable interrupts and save flags. */ - wv_splhi(lp, &flags); - - wstats = &lp->wstats; - - /* Get data from the mmc. */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - - mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); - mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, - 2); - mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, - 4); - - mmc_out(ioaddr, 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; - - /* Enable interrupts and restore flags. */ - wv_splx(lp, &flags); - -#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 deals with receiving the packets. - * The interrupt handler gets an interrupt when a packet has been - * successfully received and calls this part. - */ - -/*------------------------------------------------------------------*/ -/* - * This routine does the actual copying 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, u16 buf_off, int sksize) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - struct sk_buff *skb; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", - dev->name, buf_off, sksize); -#endif - - /* Allocate buffer for the data */ - if ((skb = dev_alloc_skb(sksize)) == (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++; - return; - } - - skb->dev = dev; - - /* Copy the packet to the buffer. */ - obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); - skb->protocol = eth_type_trans(skb, dev); - -#ifdef DEBUG_RX_INFO - wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); -#endif /* DEBUG_RX_INFO */ - - /* Statistics-gathering and associated stuff. - * It seem a bit messy with all the define, but it's really simple... */ -#if defined(WIRELESS_SPY) || defined(HISTOGRAM) - if ( -#ifdef WIRELESS_SPY - (lp->spy_number > 0) || -#endif /* WIRELESS_SPY */ -#ifdef HISTOGRAM - (lp->his_number > 0) || -#endif /* HISTOGRAM */ - 0) { - u8 stats[3]; /* signal level, noise level, signal quality */ - - /* Read signal level, silence level and signal quality bytes. */ - /* Note: in the PCMCIA hardware, these are part of the frame. It seems - * that for the ISA hardware, it's nowhere to be found in the frame, - * so I'm obliged to do this (it has a side effect on /proc/net/wireless). - * Any ideas? - */ - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); - mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3); - mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); - -#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 - - /* Spying stuff */ -#ifdef WIRELESS_SPY - wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, - stats); -#endif /* WIRELESS_SPY */ -#ifdef HISTOGRAM - wl_his_gather(dev, stats); -#endif /* HISTOGRAM */ - } -#endif /* defined(WIRELESS_SPY) || defined(HISTOGRAM) */ - - /* - * Hand the packet to the network module. - */ - netif_rx(skb); - - /* Keep statistics up to date */ - dev->last_rx = jiffies; - lp->stats.rx_packets++; - lp->stats.rx_bytes += sksize; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Transfer as many packets as we can - * from the device RAM. - * (called in wavelan_interrupt()). - * Note : the spinlock is already grabbed for us. - */ -static inline void wv_receive(device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = (net_local *) dev->priv; - fd_t fd; - rbd_t rbd; - int nreaped = 0; - -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name); -#endif - - /* Loop on each received packet. */ - for (;;) { - obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, - sizeof(fd)); - - /* Note about the status : - * It start up to be 0 (the value we set). Then, when the RU - * grab the buffer to prepare for reception, it sets the - * FD_STATUS_B flag. When the RU has finished receiving the - * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate - * completion and set the other flags to indicate the eventual - * errors. FD_STATUS_OK indicates that the reception was OK. - */ - - /* If the current frame is not complete, we have reached the end. */ - if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) - break; /* This is how we exit the loop. */ - - nreaped++; - - /* Check whether frame was correctly received. */ - if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) { - /* Does the frame contain a pointer to the data? Let's check. */ - if (fd.fd_rbd_offset != I82586NULL) { - /* Read the receive buffer descriptor */ - obram_read(ioaddr, fd.fd_rbd_offset, - (unsigned char *) &rbd, - sizeof(rbd)); - -#ifdef DEBUG_RX_ERROR - if ((rbd.rbd_status & RBD_STATUS_EOF) != - RBD_STATUS_EOF) printk(KERN_INFO - "%s: wv_receive(): missing EOF flag.\n", - dev->name); - - if ((rbd.rbd_status & RBD_STATUS_F) != - RBD_STATUS_F) printk(KERN_INFO - "%s: wv_receive(): missing F flag.\n", - dev->name); -#endif /* DEBUG_RX_ERROR */ - - /* Read the packet and transmit to Linux */ - wv_packet_read(dev, rbd.rbd_bufl, - rbd. - rbd_status & - RBD_STATUS_ACNT); - } -#ifdef DEBUG_RX_ERROR - else /* if frame has no data */ - printk(KERN_INFO - "%s: wv_receive(): frame has no data.\n", - dev->name); -#endif - } else { /* If reception was no successful */ - - lp->stats.rx_errors++; - -#ifdef DEBUG_RX_INFO - printk(KERN_DEBUG - "%s: wv_receive(): frame not received successfully (%X).\n", - dev->name, fd.fd_status); -#endif - -#ifdef DEBUG_RX_ERROR - if ((fd.fd_status & FD_STATUS_S6) != 0) - printk(KERN_INFO - "%s: wv_receive(): no EOF flag.\n", - dev->name); -#endif - - if ((fd.fd_status & FD_STATUS_S7) != 0) { - lp->stats.rx_length_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): frame too short.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S8) != 0) { - lp->stats.rx_over_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): rx DMA overrun.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S9) != 0) { - lp->stats.rx_fifo_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): ran out of resources.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S10) != 0) { - lp->stats.rx_frame_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): alignment error.\n", - dev->name); -#endif - } - - if ((fd.fd_status & FD_STATUS_S11) != 0) { - lp->stats.rx_crc_errors++; -#ifdef DEBUG_RX_FAIL - printk(KERN_DEBUG - "%s: wv_receive(): CRC error.\n", - dev->name); -#endif - } - } - - fd.fd_status = 0; - obram_write(ioaddr, fdoff(lp->rx_head, fd_status), - (unsigned char *) &fd.fd_status, - sizeof(fd.fd_status)); - - fd.fd_command = FD_COMMAND_EL; - obram_write(ioaddr, fdoff(lp->rx_head, fd_command), - (unsigned char *) &fd.fd_command, - sizeof(fd.fd_command)); - - fd.fd_command = 0; - obram_write(ioaddr, fdoff(lp->rx_last, fd_command), - (unsigned char *) &fd.fd_command, - sizeof(fd.fd_command)); - - lp->rx_last = lp->rx_head; - lp->rx_head = fd.fd_link_offset; - } /* for(;;) -> loop on all frames */ - -#ifdef DEBUG_RX_INFO - if (nreaped > 1) - printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", - dev->name, nreaped); -#endif -#ifdef DEBUG_RX_TRACE - printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name); -#endif -} - -/*********************** PACKET TRANSMISSION ***********************/ -/* - * This part deals with sending packets through the WaveLAN. - * - */ - -/*------------------------------------------------------------------*/ -/* - * This routine fills in the appropriate registers and memory - * locations on the WaveLAN card and starts the card off on - * the transmit. - * - * The principle: - * Each block contains a transmit command, a NOP command, - * a transmit block descriptor and a buffer. - * The CU read the transmit block which point to the tbd, - * read the tbd and the content of the buffer. - * When it has finish with it, it goes to the next command - * which in our case is the NOP. The NOP points on itself, - * so the CU stop here. - * When we add the next block, we modify the previous nop - * to make it point on the new tx command. - * Simple, isn't it ? - * - * (called in wavelan_packet_xmit()) - */ -static inline int wv_packet_write(device * dev, void *buf, short length) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned short txblock; - unsigned short txpred; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short buf_addr; - ac_tx_t tx; - ac_nop_t nop; - tbd_t tbd; - int clen = length; - unsigned long flags; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, - length); -#endif - - /* Do we need some padding? */ - if (clen < ETH_ZLEN) - clen = ETH_ZLEN; - - wv_splhi(lp, &flags); - - /* Check nothing bad has happened */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { -#ifdef DEBUG_TX_ERROR - printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n", - dev->name); -#endif - wv_splx(lp, &flags); - return 1; - } - - /* Calculate addresses of next block and previous block. */ - txblock = lp->tx_first_free; - txpred = txblock - TXBLOCKZ; - if (txpred < OFFSET_CU) - txpred += NTXBLOCKS * TXBLOCKZ; - lp->tx_first_free += TXBLOCKZ; - if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; - - lp->tx_n_in_use++; - - /* Calculate addresses of the different parts of the block. */ - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - buf_addr = tbd_addr + sizeof(tbd); - - /* - * Transmit command - */ - tx.tx_h.ac_status = 0; - obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), - (unsigned char *) &tx.tx_h.ac_status, - sizeof(tx.tx_h.ac_status)); - - /* - * NOP command - */ - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* - * Transmit buffer descriptor - */ - tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen); - tbd.tbd_next_bd_offset = I82586NULL; - tbd.tbd_bufl = buf_addr; - tbd.tbd_bufh = 0; - obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd)); - - /* - * Data - */ - obram_write(ioaddr, buf_addr, buf, length); - - /* - * Overwrite the predecessor NOP link - * so that it points to this txblock. - */ - nop_addr = txpred + sizeof(tx); - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = txblock; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Keep stats up to date. */ - lp->stats.tx_bytes += length; - - if (lp->tx_first_in_use == I82586NULL) - lp->tx_first_in_use = txblock; - - if (lp->tx_n_in_use < NTXBLOCKS - 1) - netif_wake_queue(dev); - - wv_splx(lp, &flags); - -#ifdef DEBUG_TX_INFO - wv_packet_info((u8 *) 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 - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine is called when we want to send a packet (NET3 callback) - * In this routine, we check if 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; - unsigned long flags; - -#ifdef DEBUG_TX_TRACE - printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, - (unsigned) skb); -#endif - - /* - * Block a timer-based transmit from overlapping. - * In other words, prevent reentering this routine. - */ - netif_stop_queue(dev); - - /* If somebody has asked to reconfigure the controller, - * we can do it now. - */ - if (lp->reconfig_82586) { - wv_splhi(lp, &flags); - wv_82586_config(dev); - wv_splx(lp, &flags); - /* Check that we can continue */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) - return 1; - } -#ifdef DEBUG_TX_ERROR - if (skb->next) - printk(KERN_INFO "skb has next\n"); -#endif - - /* Write packet on the card */ - if(wv_packet_write(dev, skb->data, skb->len)) - return 1; /* We failed */ - - 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 does the real job of starting and configuring the hardware. - */ - -/*--------------------------------------------------------------------*/ -/* - * Routine to initialize the Modem Management Controller. - * (called by wv_hw_reset()) - */ -static inline int wv_mmc_init(device * dev) -{ - unsigned long ioaddr = dev->base_addr; - net_local *lp = (net_local *) dev->priv; - psa_t psa; - mmw_t m; - int configured; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); -#endif - - /* Read the parameter storage area. */ - psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); - -#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 later (with iwconfig). */ - psa.psa_nwid[0] = 0; - psa.psa_nwid[1] = 0; - - /* no NWID checking since NWID is not set */ - 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(ioaddr, lp->hacr, - (char *) psa.psa_nwid - (char *) &psa, - (unsigned char *) psa.psa_nwid, 4); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_thr_pre_set - (char *) &psa, - (unsigned char *) &psa.psa_thr_pre_set, 1); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_quality_thr - (char *) &psa, - (unsigned char *) &psa.psa_quality_thr, 1); - psa_write(ioaddr, lp->hacr, - (char *) &psa.psa_conf_status - (char *) &psa, - (unsigned char *) &psa.psa_conf_status, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, lp->hacr); -#endif - } - - /* 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_freeze = 0; - 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_decay_prm = 0; - m.mmw_decay_updat_prm = 0; - - /* Write all info to MMC. */ - mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m)); - - /* The following code starts 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 up a bit and added 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 verifies that it is able to read a sensible - * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID - * is 0xA (Xilinx version) or 0xB (Ariadne version). - * My test is more crude but does work. */ - if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & - (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { - /* We must download the frequency parameters to the - * synthesizers (from the EEPROM - area 1) - * Note: as the EEPROM is automatically 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(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, - (unsigned char *) &m.mmw_fee_ctrl, 2); - - /* Wait until the download is finished. */ - fee_wait(ioaddr, 100, 100); - -#ifdef DEBUG_CONFIG_INFO - /* The frequency was in the last word downloaded. */ - mmc_read(ioaddr, (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 synthesizers (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(ioaddr, (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 0; -} - -/*------------------------------------------------------------------*/ -/* - * Construct the fd and rbd structures. - * Start the receive unit. - * (called by wv_hw_reset()) - */ -static inline int wv_ru_start(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - u16 scb_cs; - fd_t fd; - rbd_t rbd; - u16 rx; - u16 rx_next; - int i; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); -#endif - - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY) - return 0; - - lp->rx_head = OFFSET_RU; - - for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) { - rx_next = - (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ; - - fd.fd_status = 0; - fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0; - fd.fd_link_offset = rx_next; - fd.fd_rbd_offset = rx + sizeof(fd); - obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd)); - - rbd.rbd_status = 0; - rbd.rbd_next_rbd_offset = I82586NULL; - rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd); - rbd.rbd_bufh = 0; - rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ); - obram_write(ioaddr, rx + sizeof(fd), - (unsigned char *) &rbd, sizeof(rbd)); - - lp->rx_last = rx; - } - - obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), - (unsigned char *) &lp->rx_head, sizeof(lp->rx_head)); - - scb_cs = SCB_CMD_RUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_ru_start(): board not accepting command.\n", - dev->name); -#endif - return -1; - } -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Initialise the transmit blocks. - * Start the command unit executing the NOP - * self-loop of the first transmit block. - * - * Here we create the list of send buffers used to transmit packets - * between the PC and the command unit. For each buffer, we create a - * buffer descriptor (pointing on the buffer), a transmit command - * (pointing to the buffer descriptor) and a NOP command. - * The transmit command is linked to the NOP, and the NOP to itself. - * When we will have finished executing the transmit command, we will - * then loop on the NOP. By releasing the NOP link to a new command, - * we may send another buffer. - * - * (called by wv_hw_reset()) - */ -static inline int wv_cu_start(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - int i; - u16 txblock; - u16 first_nop; - u16 scb_cs; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name); -#endif - - lp->tx_first_free = OFFSET_CU; - lp->tx_first_in_use = I82586NULL; - - for (i = 0, txblock = OFFSET_CU; - i < NTXBLOCKS; i++, txblock += TXBLOCKZ) { - ac_tx_t tx; - ac_nop_t nop; - tbd_t tbd; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short buf_addr; - - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - buf_addr = tbd_addr + sizeof(tbd); - - tx.tx_h.ac_status = 0; - tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I; - tx.tx_h.ac_link = nop_addr; - tx.tx_tbd_offset = tbd_addr; - obram_write(ioaddr, tx_addr, (unsigned char *) &tx, - sizeof(tx)); - - nop.nop_h.ac_status = 0; - nop.nop_h.ac_command = acmd_nop; - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, nop_addr, (unsigned char *) &nop, - sizeof(nop)); - - tbd.tbd_status = TBD_STATUS_EOF; - tbd.tbd_next_bd_offset = I82586NULL; - tbd.tbd_bufl = buf_addr; - tbd.tbd_bufh = 0; - obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, - sizeof(tbd)); - } - - first_nop = - OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t); - obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), - (unsigned char *) &first_nop, sizeof(first_nop)); - - scb_cs = SCB_CMD_CUC_GO; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - - set_chan_attn(ioaddr, lp->hacr); - - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cs, sizeof(scb_cs)); - if (scb_cs == 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_cu_start(): board not accepting command.\n", - dev->name); -#endif - return -1; - } - - lp->tx_n_in_use = 0; - netif_start_queue(dev); -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine does a standard configuration of the WaveLAN - * controller (i82586). - * - * It initialises the scp, iscp and scb structure - * The first two are just pointers to the next. - * The last one is used for basic configuration and for basic - * communication (interrupt status). - * - * (called by wv_hw_reset()) - */ -static inline int wv_82586_start(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - scp_t scp; /* system configuration pointer */ - iscp_t iscp; /* intermediate scp */ - scb_t scb; /* system control block */ - ach_t cb; /* Action command header */ - u8 zeroes[512]; - int i; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name); -#endif - - /* - * Clear the onboard RAM. - */ - memset(&zeroes[0], 0x00, sizeof(zeroes)); - for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes)) - obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes)); - - /* - * Construct the command unit structures: - * scp, iscp, scb, cb. - */ - memset(&scp, 0x00, sizeof(scp)); - scp.scp_sysbus = SCP_SY_16BBUS; - scp.scp_iscpl = OFFSET_ISCP; - obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp, - sizeof(scp)); - - memset(&iscp, 0x00, sizeof(iscp)); - iscp.iscp_busy = 1; - iscp.iscp_offset = OFFSET_SCB; - obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, - sizeof(iscp)); - - /* Our first command is to reset the i82586. */ - memset(&scb, 0x00, sizeof(scb)); - scb.scb_command = SCB_CMD_RESET; - scb.scb_cbl_offset = OFFSET_CU; - scb.scb_rfa_offset = OFFSET_RU; - obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - set_chan_attn(ioaddr, lp->hacr); - - /* Wait for command to finish. */ - for (i = 1000; i > 0; i--) { - obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, - sizeof(iscp)); - - if (iscp.iscp_busy == (unsigned short) 0) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): iscp_busy timeout.\n", - dev->name); -#endif - return -1; - } - - /* Check command completion. */ - for (i = 15; i > 0; i--) { - obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, - sizeof(scb)); - - if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA)) - break; - - udelay(10); - } - - if (i <= 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", - dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); -#endif - return -1; - } - - wv_ack(dev); - - /* Set the action command header. */ - memset(&cb, 0x00, sizeof(cb)); - cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose); - cb.ac_link = OFFSET_CU; - obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); - - if (wv_synchronous_cmd(dev, "diag()") == -1) - return -1; - - obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); - if (cb.ac_status & AC_SFLD_FAIL) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wv_82586_start(): i82586 Self Test failed.\n", - dev->name); -#endif - return -1; - } -#ifdef DEBUG_I82586_SHOW - wv_scb_show(ioaddr); -#endif - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * This routine does a standard configuration of the WaveLAN - * controller (i82586). - * - * This routine is a violent hack. We use the first free transmit block - * to make our configuration. In the buffer area, we create the three - * configuration commands (linked). We make the previous NOP point to - * the beginning of the buffer instead of the tx command. After, we go - * as usual to the NOP command. - * Note that only the last command (mc_set) will generate an interrupt. - * - * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit()) - */ -static void wv_82586_config(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned short txblock; - unsigned short txpred; - unsigned short tx_addr; - unsigned short nop_addr; - unsigned short tbd_addr; - unsigned short cfg_addr; - unsigned short ias_addr; - unsigned short mcs_addr; - ac_tx_t tx; - ac_nop_t nop; - ac_cfg_t cfg; /* Configure action */ - ac_ias_t ias; /* IA-setup action */ - ac_mcs_t mcs; /* Multicast setup */ - struct dev_mc_list *dmi; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); -#endif - - /* Check nothing bad has happened */ - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n", - dev->name); -#endif - return; - } - - /* Calculate addresses of next block and previous block. */ - txblock = lp->tx_first_free; - txpred = txblock - TXBLOCKZ; - if (txpred < OFFSET_CU) - txpred += NTXBLOCKS * TXBLOCKZ; - lp->tx_first_free += TXBLOCKZ; - if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) - lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; - - lp->tx_n_in_use++; - - /* Calculate addresses of the different parts of the block. */ - tx_addr = txblock; - nop_addr = tx_addr + sizeof(tx); - tbd_addr = nop_addr + sizeof(nop); - cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */ - ias_addr = cfg_addr + sizeof(cfg); - mcs_addr = ias_addr + sizeof(ias); - - /* - * Transmit command - */ - tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */ - obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), - (unsigned char *) &tx.tx_h.ac_status, - sizeof(tx.tx_h.ac_status)); - - /* - * NOP command - */ - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = nop_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Create a configure action. */ - memset(&cfg, 0x00, sizeof(cfg)); - - /* - * For Linux we invert AC_CFG_ALOC() so as to conform - * to the way that net packets reach us from above. - * (See also ac_tx_t.) - * - * Updated from Wavelan Manual WCIN085B - */ - cfg.cfg_byte_cnt = - AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); - cfg.cfg_fifolim = AC_CFG_FIFOLIM(4); - cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0); - cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | - AC_CFG_ILPBCK(0) | - AC_CFG_PRELEN(AC_CFG_PLEN_2) | - AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); - cfg.cfg_byte10 = AC_CFG_BOFMET(1) | - AC_CFG_ACR(6) | AC_CFG_LINPRIO(0); - cfg.cfg_ifs = 0x20; - cfg.cfg_slotl = 0x0C; - cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0); - cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | - AC_CFG_BTSTF(0) | - AC_CFG_CRC16(0) | - AC_CFG_NCRC(0) | - AC_CFG_TNCRS(1) | - AC_CFG_MANCH(0) | - AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous); - cfg.cfg_byte15 = AC_CFG_ICDS(0) | - AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0); -/* - cfg.cfg_min_frm_len = AC_CFG_MNFRM(64); -*/ - cfg.cfg_min_frm_len = AC_CFG_MNFRM(8); - - cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure); - cfg.cfg_h.ac_link = ias_addr; - obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg)); - - /* Set up the MAC address */ - memset(&ias, 0x00, sizeof(ias)); - ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup); - ias.ias_h.ac_link = mcs_addr; - memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0], - sizeof(ias.ias_addr)); - obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias)); - - /* Initialize adapter's Ethernet multicast addresses */ - memset(&mcs, 0x00, sizeof(mcs)); - mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup); - mcs.mcs_h.ac_link = nop_addr; - mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count; - obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs)); - - /* Any address to set? */ - if (lp->mc_count) { - for (dmi = dev->mc_list; dmi; dmi = dmi->next) - outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr, - WAVELAN_ADDR_SIZE >> 1); - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wv_82586_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 - } - - /* - * Overwrite the predecessor NOP link - * so that it points to the configure action. - */ - nop_addr = txpred + sizeof(tx); - nop.nop_h.ac_status = 0; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), - (unsigned char *) &nop.nop_h.ac_status, - sizeof(nop.nop_h.ac_status)); - nop.nop_h.ac_link = cfg_addr; - obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), - (unsigned char *) &nop.nop_h.ac_link, - sizeof(nop.nop_h.ac_link)); - - /* Job done, clear the flag */ - lp->reconfig_82586 = 0; - - if (lp->tx_first_in_use == I82586NULL) - lp->tx_first_in_use = txblock; - - if (lp->tx_n_in_use == (NTXBLOCKS - 1)) - netif_stop_queue(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * This routine, called by wavelan_close(), gracefully stops the - * WaveLAN controller (i82586). - * (called by wavelan_close()) - */ -static inline void wv_82586_stop(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - u16 scb_cmd; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name); -#endif - - /* Suspend both command unit and receive unit. */ - scb_cmd = - (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & - SCB_CMD_RUC_SUS); - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &scb_cmd, sizeof(scb_cmd)); - set_chan_attn(ioaddr, lp->hacr); - - /* No more interrupts */ - wv_ints_off(dev); - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Totally reset the WaveLAN and restart it. - * Performs the following actions: - * 1. A power reset (reset DMA) - * 2. Initialize the radio modem (using wv_mmc_init) - * 3. Reset & Configure LAN controller (using wv_82586_start) - * 4. Start the LAN controller's command unit - * 5. Start the LAN controller's receive unit - * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open()) - */ -static int wv_hw_reset(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long ioaddr = dev->base_addr; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* Increase the number of resets done. */ - lp->nresets++; - - wv_hacr_reset(ioaddr); - lp->hacr = HACR_DEFAULT; - - if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0)) - return -1; - - /* Enable the card to send interrupts. */ - wv_ints_on(dev); - - /* Start card functions */ - if (wv_cu_start(dev) < 0) - return -1; - - /* Setup the controller and parameters */ - wv_82586_config(dev); - - /* Finish configuration with the receive unit */ - if (wv_ru_start(dev) < 0) - return -1; - -#ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Check if there is a WaveLAN at the specific base address. - * As a side effect, this reads the MAC address. - * (called in wavelan_probe() and init_module()) - */ -static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac) -{ - int i; /* Loop counter */ - - /* Check if the base address if available. */ - if (check_region(ioaddr, sizeof(ha_t))) - return -EADDRINUSE; /* ioaddr already used */ - - /* Reset host interface */ - wv_hacr_reset(ioaddr); - - /* Read the MAC address from the parameter storage area. */ - psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), - mac, 6); - - /* - * Check the first three octets of the address for the manufacturer's code. - * Note: if this can't find your WaveLAN card, you've got a - * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on - * how to configure your card. - */ - for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++) - if ((mac[0] == MAC_ADDRESSES[i][0]) && - (mac[1] == MAC_ADDRESSES[i][1]) && - (mac[2] == MAC_ADDRESSES[i][2])) - return 0; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_WARNING - "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n", - ioaddr, mac[0], mac[1], mac[2]); -#endif - return -ENODEV; -} - -/************************ INTERRUPT HANDLING ************************/ - -/* - * This function is the interrupt handler for the WaveLAN card. This - * routine will be called whenever: - */ -static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - device *dev; - unsigned long ioaddr; - net_local *lp; - u16 hasr; - u16 status; - u16 ack_cmd; - - dev = dev_id; - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); -#endif - - lp = (net_local *) dev->priv; - ioaddr = dev->base_addr; - -#ifdef DEBUG_INTERRUPT_INFO - /* Check state of our spinlock */ - if(spin_is_locked(&lp->spinlock)) - printk(KERN_DEBUG - "%s: wavelan_interrupt(): spinlock is already locked !!!\n", - dev->name); -#endif - - /* Prevent reentrancy. We need to do that because we may have - * multiple interrupt handler running concurrently. - * It is safe because wv_splhi() disables interrupts before acquiring - * the spinlock. */ - spin_lock(&lp->spinlock); - - /* Check modem interrupt */ - if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) { - u8 dce_status; - - /* - * Interrupt from the modem management controller. - * This will clear it -- ignored for now. - */ - mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, - sizeof(dce_status)); -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", - dev->name, dce_status); -#endif - } - - /* Check if not controller interrupt */ - if ((hasr & HASR_82586_INTR) == 0) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): interrupt not coming from i82586\n", - dev->name); -#endif - spin_unlock (&lp->spinlock); - return; - } - - /* Read interrupt data. */ - obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), - (unsigned char *) &status, sizeof(status)); - - /* - * Acknowledge the interrupt(s). - */ - ack_cmd = status & SCB_ST_INT; - obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), - (unsigned char *) &ack_cmd, sizeof(ack_cmd)); - set_chan_attn(ioaddr, lp->hacr); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n", - dev->name, status); -#endif - - /* Command completed. */ - if ((status & SCB_ST_CX) == SCB_ST_CX) { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_interrupt(): command completed.\n", - dev->name); -#endif - wv_complete(dev, ioaddr, lp); - } - - /* Frame received. */ - if ((status & SCB_ST_FR) == SCB_ST_FR) { -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_interrupt(): received packet.\n", - dev->name); -#endif - wv_receive(dev); - } - - /* Check the state of the command unit. */ - if (((status & SCB_ST_CNA) == SCB_ST_CNA) || - (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && - (netif_running(dev)))) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): CU inactive -- restarting\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* Check the state of the command unit. */ - if (((status & SCB_ST_RNR) == SCB_ST_RNR) || - (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && - (netif_running(dev)))) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): RU not ready -- restarting\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* Release spinlock */ - spin_unlock (&lp->spinlock); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); -#endif -} - -/*------------------------------------------------------------------*/ -/* - * Watchdog: when we start a transmission, a timer is set for us in the - * kernel. If the transmission completes, this timer is disabled. If - * the timer expires, we are called and we try to unlock the hardware. - */ -static void wavelan_watchdog(device * dev) -{ - net_local * lp = (net_local *)dev->priv; - u_long ioaddr = dev->base_addr; - unsigned long flags; - unsigned int nreaped; - -#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 - - /* Check that we came here for something */ - if (lp->tx_n_in_use <= 0) { - return; - } - - wv_splhi(lp, &flags); - - /* Try to see if some buffers are not free (in case we missed - * an interrupt */ - nreaped = wv_complete(dev, ioaddr, lp); - -#ifdef DEBUG_INTERRUPT_INFO - printk(KERN_DEBUG - "%s: wavelan_watchdog(): %d reaped, %d remain.\n", - dev->name, nreaped, lp->tx_n_in_use); -#endif - -#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_I82586_SHOW - wv_cu_show(dev); -#endif - - /* If no buffer has been freed */ - if (nreaped == 0) { -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_watchdog(): cleanup failed, trying reset\n", - dev->name); -#endif - wv_hw_reset(dev); - } - - /* At this point, we should have some free Tx buffer ;-) */ - if (lp->tx_n_in_use < NTXBLOCKS - 1) - netif_wake_queue(dev); - - wv_splx(lp, &flags); - -#ifdef DEBUG_INTERRUPT_TRACE - printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); -#endif -} - -/********************* CONFIGURATION CALLBACKS *********************/ -/* - * Here are the functions called by the Linux networking code (NET3) - * for initialization, configuration and deinstallations of the - * WaveLAN ISA hardware. - */ - -/*------------------------------------------------------------------*/ -/* - * Configure and start up the WaveLAN PCMCIA adaptor. - * Called by NET3 when it "opens" the device. - */ -static int wavelan_open(device * dev) -{ - net_local * lp = (net_local *)dev->priv; - unsigned long flags; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - /* Check irq */ - if (dev->irq == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", - dev->name); -#endif - return -ENXIO; - } - - if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) - { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", - dev->name); -#endif - return -EAGAIN; - } - - wv_splhi(lp, &flags); - - if (wv_hw_reset(dev) != -1) { - netif_start_queue(dev); - } else { - free_irq(dev->irq, dev); -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_open(): impossible to start the card\n", - dev->name); -#endif - wv_splx(lp, &flags); - return -EAGAIN; - } - wv_splx(lp, &flags); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Shut down the WaveLAN ISA card. - * Called by NET3 when it "closes" the device. - */ -static int wavelan_close(device * dev) -{ - net_local *lp = (net_local *) dev->priv; - unsigned long flags; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, - (unsigned int) dev); -#endif - - netif_stop_queue(dev); - - /* - * Flush the Tx and disable Rx. - */ - wv_splhi(lp, &flags); - wv_82586_stop(dev); - wv_splx(lp, &flags); - - free_irq(dev->irq, dev); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Probe an I/O address, and if the WaveLAN is there configure the - * device structure - * (called by wavelan_probe() and via init_module()). - */ -static int __init wavelan_config(device * dev) -{ - unsigned long ioaddr = dev->base_addr; - u8 irq_mask; - int irq; - net_local *lp; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n", - dev->name, (unsigned int) dev, ioaddr); -#endif - - /* Check IRQ argument on command line. */ - if (dev->irq != 0) { - irq_mask = wv_irq_to_psa(dev->irq); - - if (irq_mask == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "%s: wavelan_config(): invalid IRQ %d ignored.\n", - dev->name, dev->irq); -#endif - dev->irq = 0; - } else { -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: wavelan_config(): changing IRQ to %d\n", - dev->name, dev->irq); -#endif - psa_write(ioaddr, HACR_DEFAULT, - psaoff(0, psa_int_req_no), &irq_mask, 1); - /* update the Wavelan checksum */ - update_psa_checksum(dev, ioaddr, HACR_DEFAULT); - wv_hacr_reset(ioaddr); - } - } - - psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), - &irq_mask, 1); - if ((irq = wv_psa_to_irq(irq_mask)) == -1) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_INFO - "%s: wavelan_config(): could not wavelan_map_irq(%d).\n", - dev->name, irq_mask); -#endif - return -EAGAIN; - } - - dev->irq = irq; - - if (!request_region(ioaddr, sizeof(ha_t), "wavelan")) - return -EBUSY; - - dev->mem_start = 0x0000; - dev->mem_end = 0x0000; - dev->if_port = 0; - - /* Initialize device structures */ - dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL); - if (dev->priv == NULL) { - release_region(ioaddr, sizeof(ha_t)); - return -ENOMEM; - } - memset(dev->priv, 0x00, sizeof(net_local)); - lp = (net_local *) dev->priv; - - /* Back link to the device structure. */ - lp->dev = dev; - /* Add the device at the beginning of the linked list. */ - lp->next = wavelan_list; - wavelan_list = lp; - - lp->hacr = HACR_DEFAULT; - - /* Multicast stuff */ - lp->promiscuous = 0; - lp->mc_count = 0; - - /* Init spinlock */ - spin_lock_init(&lp->spinlock); - - /* - * Fill in the fields of the device structure - * with generic Ethernet values. - */ - ether_setup(dev); - - SET_MODULE_OWNER(dev); - 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; - dev->tx_timeout = &wavelan_watchdog; - dev->watchdog_timeo = WATCHDOG_JIFFIES; -#ifdef SET_MAC_ADDRESS - dev->set_mac_address = &wavelan_set_mac_address; -#endif /* SET_MAC_ADDRESS */ - -#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */ - dev->do_ioctl = wavelan_ioctl; - dev->get_wireless_stats = wavelan_get_wireless_stats; -#endif - - dev->mtu = WAVELAN_MTU; - - /* Display nice information. */ - wv_init_info(dev); - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name); -#endif - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Check for a network adaptor of this type. Return '0' iff one - * exists. There seem to be different interpretations of - * the initial value of dev->base_addr. - * We follow the example in drivers/net/ne.c. - * (called in "Space.c") - */ -int __init wavelan_probe(device * dev) -{ - short base_addr; - mac_addr mac; /* MAC address (check existence of WaveLAN) */ - int i; - int r; - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG - "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", - dev->name, (unsigned int) dev, - (unsigned int) dev->base_addr); -#endif - -#ifdef STRUCT_CHECK - if (wv_struct_check() != (char *) NULL) { - printk(KERN_WARNING - "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n", - dev->name, wv_struct_check()); - return -ENODEV; - } -#endif /* STRUCT_CHECK */ - - /* Check the value of the command line parameter for base address. */ - base_addr = dev->base_addr; - - /* Don't probe at all. */ - if (base_addr < 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "%s: wavelan_probe(): invalid base address\n", - dev->name); -#endif - return -ENXIO; - } - - /* Check a single specified location. */ - if (base_addr > 0x100) { - /* Check if there is something at this base address */ - if ((r = wv_check_ioaddr(base_addr, mac)) == 0) { - memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ - r = wavelan_config(dev); - } -#ifdef DEBUG_CONFIG_INFO - if (r != 0) - printk(KERN_DEBUG - "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n", - dev->name, base_addr); -#endif - -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); -#endif - return r; - } - - /* Scan all possible addresses of the WaveLAN hardware. */ - for (i = 0; i < NELS(iobase); i++) { - /* Check whether there is something at this base address. */ - if (wv_check_ioaddr(iobase[i], mac) == 0) { - dev->base_addr = iobase[i]; /* Copy base address. */ - memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ - if (wavelan_config(dev) == 0) { -#ifdef DEBUG_CALLBACK_TRACE - printk(KERN_DEBUG - "%s: <-wavelan_probe()\n", - dev->name); -#endif - return 0; - } - } - } - - /* We may have touched base_addr. Another driver may not like it. */ - dev->base_addr = base_addr; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n", - dev->name); -#endif - - return -ENODEV; -} - -/****************************** MODULE ******************************/ -/* - * Module entry point: insertion and removal - */ - -#ifdef MODULE -/*------------------------------------------------------------------*/ -/* - * Insertion of the module - * I'm now quite proud of the multi-device support. - */ -int init_module(void) -{ - mac_addr mac; /* MAC address (check WaveLAN existence) */ - int ret = -EIO; /* Return error if no cards found */ - int i; - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "-> init_module()\n"); -#endif - - /* If probing is asked */ - if (io[0] == 0) { -#ifdef DEBUG_CONFIG_ERROR - printk(KERN_WARNING - "WaveLAN init_module(): doing device probing (bad !)\n"); - printk(KERN_WARNING - "Specify base addresses while loading module to correct the problem\n"); -#endif - - /* Copy the basic set of address to be probed. */ - for (i = 0; i < NELS(iobase); i++) - io[i] = iobase[i]; - } - - - /* Loop on all possible base addresses. */ - i = -1; - while ((io[++i] != 0) && (i < NELS(io))) { - /* Check if there is something at this base address. */ - if (wv_check_ioaddr(io[i], mac) == 0) { - device *dev; - - /* Create device and set basic arguments. */ - dev = - kmalloc(sizeof(struct net_device), GFP_KERNEL); - if (dev == NULL) { - ret = -ENOMEM; - break; - } - memset(dev, 0x00, sizeof(struct net_device)); - memcpy(dev->name, name[i], IFNAMSIZ); /* Copy name */ - dev->base_addr = io[i]; - dev->irq = irq[i]; - dev->init = &wavelan_config; - memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ - - /* Try to create the device. */ - if (register_netdev(dev) != 0) { - /* Deallocate everything. */ - /* Note: if dev->priv is mallocated, there is no way to fail. */ - kfree(dev); - } else { - /* If at least one device OK, we do not fail */ - ret = 0; - } - } /* if there is something at the address */ - } /* Loop on all addresses. */ - -#ifdef DEBUG_CONFIG_ERROR - if (wavelan_list == (net_local *) NULL) - printk(KERN_WARNING - "WaveLAN init_module(): no device found\n"); -#endif - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "<- init_module()\n"); -#endif - return ret; -} - -/*------------------------------------------------------------------*/ -/* - * Removal of the module - */ -void cleanup_module(void) -{ -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "-> cleanup_module()\n"); -#endif - - /* Loop on all devices and release them. */ - while (wavelan_list != (net_local *) NULL) { - device *dev = wavelan_list->dev; - -#ifdef DEBUG_CONFIG_INFO - printk(KERN_DEBUG - "%s: cleanup_module(): removing device at 0x%x\n", - dev->name, (unsigned int) dev); -#endif - - /* Release the ioport region. */ - release_region(dev->base_addr, sizeof(ha_t)); - - /* Definitely remove the device. */ - unregister_netdev(dev); - - /* Unlink the device. */ - wavelan_list = wavelan_list->next; - - /* Free pieces. */ - kfree(dev->priv); - kfree(dev); - } - -#ifdef DEBUG_MODULE_TRACE - printk(KERN_DEBUG "<- cleanup_module()\n"); -#endif -} -#endif /* MODULE */ -MODULE_LICENSE("GPL"); - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * This software was developed as a component of the - * Linux operating system. - * It is based on other device drivers and information - * either written or supplied by: - * Ajay Bakre (bakre@paul.rutgers.edu), - * Donald Becker (becker@scyld.com), - * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), - * Anders Klemets (klemets@it.kth.se), - * Vladimir V. Kolpakov (w@stier.koenig.ru), - * Marc Meertens (Marc.Meertens@Utrecht.NCR.com), - * Pauline Middelink (middelin@polyware.iaf.nl), - * Robert Morris (rtm@das.harvard.edu), - * Jean Tourrilhes (jt@hplb.hpl.hp.com), - * Girish Welling (welling@paul.rutgers.edu), - * - * Thanks go also to: - * James Ashton (jaa101@syseng.anu.edu.au), - * Alan Cox (alan@redhat.com), - * Allan Creighton (allanc@cs.usyd.edu.au), - * Matthew Geier (matthew@cs.usyd.edu.au), - * Remo di Giovanni (remo@cs.usyd.edu.au), - * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), - * Vipul Gupta (vgupta@cs.binghamton.edu), - * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), - * Tim Nicholson (tim@cs.usyd.edu.au), - * Ian Parkin (ian@cs.usyd.edu.au), - * John Rosenberg (johnr@cs.usyd.edu.au), - * George Rossi (george@phm.gov.au), - * Arthur Scott (arthur@cs.usyd.edu.au), - * Peter Storey, - * for their assistance and advice. - * - * Please send bug reports, updates, comments to: - * - * Bruce Janson Email: bruce@cs.usyd.edu.au - * Basser Department of Computer Science Phone: +61-2-9351-3423 - * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838 - */ diff -urN linux-2.5.3-pre2/drivers/net/wavelan.h linux/drivers/net/wavelan.h --- linux-2.5.3-pre2/drivers/net/wavelan.h Tue Feb 13 13:15:05 2001 +++ linux/drivers/net/wavelan.h Wed Dec 31 16:00:00 1969 @@ -1,370 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * Original copyright follows. See wavelan.p.h for details. - * - * This file contains the declarations for the WaveLAN hardware. Note that - * the WaveLAN ISA includes a i82586 controller (see definitions in - * file i82586.h). - * - * The main difference between the ISA hardware and the PCMCIA one is - * the Ethernet controller (i82586 instead of i82593). - * The i82586 allows multiple transmit buffers. The PSA needs to be accessed - * through the host interface. - */ - -#ifndef _WAVELAN_H -#define _WAVELAN_H - -/************************** MAGIC NUMBERS ***************************/ - -/* Detection of the WaveLAN card is done by reading the MAC - * address from the card and checking it. If you have a non-AT&T - * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson), - * you might need to modify this part to accommodate your hardware. - */ -static const 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! */ -}; - -#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */ - -#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */ - -#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU) - -/* - * 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 - */ -static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, - 0xD0, 0xF0, 0xF8, 0x150 }; - -/* Frequencies of the 1.0 modem (fixed frequencies). - * Use to map the PSA `subband' to a frequency - * Note : all frequencies apart from the first one need to be multiplied by 10 - */ -static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; - - - -/*************************** PC INTERFACE ****************************/ - -/* - * Host Adaptor structure. - * (base is board port address). - */ -typedef union hacs_u hacs_u; -union hacs_u -{ - unsigned short hu_command; /* Command register */ -#define HACR_RESET 0x0001 /* Reset board */ -#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */ -#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */ -#define HACR_OUT0 0x0008 /* General purpose output pin 0 */ - /* not used - must be 1 */ -#define HACR_OUT1 0x0010 /* General purpose output pin 1 */ - /* not used - must be 1 */ -#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */ -#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */ -#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */ - unsigned short hu_status; /* Status Register */ -#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */ -#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */ -#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */ -#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */ -}; - -typedef struct ha_t ha_t; -struct ha_t -{ - hacs_u ha_cs; /* Command and status registers */ -#define ha_command ha_cs.hu_command -#define ha_status ha_cs.hu_status - unsigned short ha_mmcr; /* Modem Management Ctrl Register */ - unsigned short ha_pior0; /* Program I/O Address Register Port 0 */ - unsigned short ha_piop0; /* Program I/O Port 0 */ - unsigned short ha_pior1; /* Program I/O Address Register Port 1 */ - unsigned short ha_piop1; /* Program I/O Port 1 */ - unsigned short ha_pior2; /* Program I/O Address Register Port 2 */ - unsigned short ha_piop2; /* Program I/O Port 2 */ -}; - -#define HA_SIZE 16 - -#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0) -#define HACR(p) hoff(p, ha_command) -#define HASR(p) hoff(p, ha_status) -#define MMCR(p) hoff(p, ha_mmcr) -#define PIOR0(p) hoff(p, ha_pior0) -#define PIOP0(p) hoff(p, ha_piop0) -#define PIOR1(p) hoff(p, ha_pior1) -#define PIOP1(p) hoff(p, ha_piop1) -#define PIOR2(p) hoff(p, ha_pior2) -#define PIOP2(p) hoff(p, ha_piop2) - -/* - * Program I/O Mode Register values. - */ -#define STATIC_PIO 0 /* Mode 1: static mode */ - /* RAM access ??? */ -#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */ - /* RAM access ??? */ -#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */ - /* RAM access ??? */ -#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */ - /* Parameter access. */ -#define PIO_MASK 3 /* register mask */ -#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2)) - -#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2)) -#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE) - -/************************** MEMORY LAYOUT **************************/ - -/* - * Onboard 64 k RAM layout. - * (Offsets from 0x0000.) - */ -#define OFFSET_RU 0x0000 /* 75% memory */ -#define OFFSET_CU 0xC000 /* 25% memory */ -#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t)) -#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t)) -#define OFFSET_SCP I82586_SCP_ADDR - -#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ) -#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ) - -#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ) -#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ) - -/********************** PARAMETER STORAGE AREA **********************/ - -/* - * Parameter Storage Area (PSA). - */ -typedef struct psa_t psa_t; -struct psa_t -{ - 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] Compatibility 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 */ -}; - -#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 or 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, no 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 or unfreeze 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 parameters */ - 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 address in protect register */ -#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ - /* Never issue the PRDS command: 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) */ -}; - -#define MMW_SIZE 37 - -#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 NWIDs rxd (low) */ - unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */ - /* Warning: read high-order octet first! */ - unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */ - unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs 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) */ -}; - -#define MMR_SIZE 36 - -#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 */ - -/* - * This software may only be used and distributed - * according to the terms of the GNU General Public License. - * - * For more details, see wavelan.c. - */ diff -urN linux-2.5.3-pre2/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h --- linux-2.5.3-pre2/drivers/net/wavelan.p.h Fri Oct 12 14:21:18 2001 +++ linux/drivers/net/wavelan.p.h Wed Dec 31 16:00:00 1969 @@ -1,713 +0,0 @@ -/* - * WaveLAN ISA driver - * - * Jean II - HPLB '96 - * - * Reorganisation and extension of the driver. - * - * This file contains all definitions and declarations necessary for the - * WaveLAN ISA driver. This file is a private header, so it should - * be included only in wavelan.c! - */ - -#ifndef WAVELAN_P_H -#define WAVELAN_P_H - -/************************** DOCUMENTATION ***************************/ -/* - * This driver provides a Linux interface to the WaveLAN ISA hardware. - * The WaveLAN is a product of Lucent (http://www.wavelan.com/). - * This division was formerly part of NCR and then AT&T. - * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean. - * - * To learn how to use this driver, read the NET3 HOWTO. - * If you want to exploit the many other functionalities, read the comments - * in the code. - * - * This driver is the result of the effort of many people (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 - * - * SMP - * --- - * We now are SMP compliant (I eventually fixed the remaining bugs). - * The driver has been tested on a dual P6-150 and survived my usual - * set of torture tests. - * Anyway, I spent enough time chasing interrupt re-entrancy during - * errors or reconfigure, and I designed the locked/unlocked sections - * of the driver with great care, and with the recent addition of - * the spinlock (thanks to the new API), we should be quite close to - * the truth. - * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), - * but better safe than sorry (especially at 2 Mb/s ;-). - * - * I have also looked into disabling only our interrupt on the card - * (via HACR) instead of all interrupts in the processor (via cli), - * so that other driver are not impacted, and it look like it's - * possible, but it's very tricky to do right (full of races). As - * the gain would be mostly for SMP systems, it can wait... - * - * 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 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.o is too darned big - * --------------------------- - * That's true! There is a very simple way to reduce the driver - * object by 33%! Comment out the following line: - * #include - * Other compile options can also reduce the size of it... - * - * MAC address and hardware detection: - * ----------------------------------- - * The detection code for the WaveLAN checks that the first three - * octets of the MAC address fit the company code. This type of - * detection works well for AT&T cards (because the AT&T code is - * hardcoded in wavelan.h), but of course will fail for other - * manufacturers. - * - * 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, etc.) - * 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 and verify - * 4) Send me the MAC code. I will include it in the next version. - * - */ - -/* --------------------- WIRELESS EXTENSIONS --------------------- */ -/* - * This driver is the first to support "wireless extensions". - * This set of extensions provides a standard way to control the wireless - * characteristics of the hardware. Applications such as mobile IP may - * take advantage of it. - * - * 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.c: actual code for the driver: C functions - * - * wavelan.p.h: private header: local types and variables for driver - * - * wavelan.h: description of the hardware interface and structs - * - * i82586.h: description of the Ethernet controller - */ - -/* --------------------------- HISTORY --------------------------- */ -/* - * This is based on information in the drivers' headers. It may not be - * accurate, and I guarantee only my best effort. - * - * The history of the WaveLAN drivers is as complicated as the history of - * the WaveLAN itself (NCR -> AT&T -> Lucent). - * - * It all started with Anders Klemets - * writing a WaveLAN ISA driver for the Mach microkernel. Girish - * Welling had also worked on it. - * Keith Moore modified this for the PCMCIA hardware. - * - * Robert Morris ported these two drivers to BSDI - * and added specific PCMCIA support (there is currently no equivalent - * of the PCMCIA package under BSD). - * - * Jim Binkley ported both BSDI drivers to FreeBSD. - * - * Bruce Janson ported the BSDI ISA driver to Linux. - * - * Anthony D. Joseph started to modify Bruce's driver - * (with help of the BSDI PCMCIA driver) for PCMCIA. - * Yunzhou Li finished this work. - * Joe Finney patched the driver to start - * 2.00 cards correctly (2.4 GHz with frequency selection). - * David Hinds integrated the whole in his - * PCMCIA package (and bug corrections). - * - * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some - * patches to the PCMCIA driver. Later, I added code in the ISA driver - * for Wireless Extensions and full support of frequency selection - * cards. Then, I did the same to the PCMCIA driver, and did some - * reorganisation. Finally, I came back to the ISA driver to - * upgrade it at the same level as the PCMCIA one and reorganise - * the code. - * Loeke Brederveld from Lucent has given me - * much needed information on the WaveLAN hardware. - */ - -/* The original copyrights and literature mention others' names and - * credits. I don't know what their part in this development was. - */ - -/* By the way, for the copyright and legal stuff: - * almost everybody wrote code under the GNU or BSD license (or similar), - * and want their original copyright to remain somewhere in the - * code (for myself, I go with the GPL). - * Nobody wants to take responsibility for anything, except the fame. - */ - -/* --------------------------- CREDITS --------------------------- */ -/* - * This software was developed as a component of the - * Linux operating system. - * It is based on other device drivers and information - * either written or supplied by: - * Ajay Bakre , - * Donald Becker , - * Loeke Brederveld , - * Brent Elphick , - * Anders Klemets , - * Vladimir V. Kolpakov , - * Marc Meertens , - * Pauline Middelink , - * Robert Morris , - * Jean Tourrilhes , - * Girish Welling , - * Clark Woodworth - * Yongguang Zhang - * - * Thanks go also to: - * James Ashton , - * Alan Cox , - * Allan Creighton , - * Matthew Geier , - * Remo di Giovanni , - * Eckhard Grah , - * Vipul Gupta , - * Mark Hagan , - * Tim Nicholson , - * Ian Parkin , - * John Rosenberg , - * George Rossi , - * Arthur Scott , - * Stanislav Sinyagin - * and Peter Storey for their assistance and advice. - * - * Additional Credits: - * - * My development has been done initially under Debian 1.1 (Linux 2.0.x) - * and now under Debian 2.2, initially with an HP Vectra XP/60, and now - * an HP Vectra XP/90. - * - */ - -/* ------------------------- IMPROVEMENTS ------------------------- */ -/* - * I proudly present: - * - * Changes made in first pre-release: - * ---------------------------------- - * - reorganisation of the code, function name change - * - creation of private header (wavelan.p.h) - * - reorganised debug messages - * - more comments, history, etc. - * - mmc_init: configure the PSA if not done - * - mmc_init: correct default value of level threshold for PCMCIA - * - mmc_init: 2.00 detection better code for 2.00 initialization - * - better info at startup - * - IRQ setting (note: this setting is permanent) - * - watchdog: change strategy (and solve module removal problems) - * - add wireless extensions (ioctl and get_wireless_stats) - * get/set nwid/frequency on fly, info for /proc/net/wireless - * - more wireless extensions: SETSPY and GETSPY - * - make wireless extensions optional - * - private ioctl to set/get quality and level threshold, histogram - * - remove /proc/net/wavelan - * - suppress useless stuff from lp (net_local) - * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) - * - add message level (debug stuff in /var/adm/debug and errors not - * displayed at console and still in /var/adm/messages) - * - multi device support - * - start fixing the probe (init code) - * - more inlines - * - man page - * - many other minor details and cleanups - * - * Changes made in second pre-release: - * ----------------------------------- - * - clean up init code (probe and module init) - * - better multiple device support (module) - * - name assignment (module) - * - * Changes made in third pre-release: - * ---------------------------------- - * - be more conservative on timers - * - preliminary support for multicast (I still lack some details) - * - * Changes made in fourth pre-release: - * ----------------------------------- - * - multicast (revisited and finished) - * - avoid reset in set_multicast_list (a really big hack) - * if somebody could apply this code for other i82586 based drivers - * - share onboard memory 75% RU and 25% CU (instead of 50/50) - * - * Changes made for release in 2.1.15: - * ----------------------------------- - * - change the detection code for multi manufacturer code support - * - * Changes made for release in 2.1.17: - * ----------------------------------- - * - update to wireless extensions changes - * - silly bug in card initial configuration (psa_conf_status) - * - * Changes made for release in 2.1.27 & 2.0.30: - * -------------------------------------------- - * - small bug in debug code (probably not the last one...) - * - remove extern keyword for wavelan_probe() - * - level threshold is now a standard wireless extension (version 4 !) - * - modules parameters types (new module interface) - * - * Changes made for release in 2.1.36: - * ----------------------------------- - * - byte count stats (courtesy of David Hinds) - * - remove dev_tint stuff (courtesy of David Hinds) - * - encryption setting from Brent Elphick (thanks a lot!) - * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) - * - * Other changes (not by me) : - * ------------------------- - * - Spelling and gramar "rectification". - * - * Changes made for release in 2.0.37 & 2.2.2 : - * ------------------------------------------ - * - Correct status in /proc/net/wireless - * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray) - * - Module init code don't fail if we found at least one card in - * the address list (Karlis Peisenieks) - * - Missing parenthesis (Christopher Peterson) - * - Correct i82586 configuration parameters - * - Encryption initialisation bug (Robert McCormack) - * - New mac addresses detected in the probe - * - Increase watchdog for busy environments - * - * Changes made for release in 2.0.38 & 2.2.7 : - * ------------------------------------------ - * - Correct the reception logic to better report errors and avoid - * sending bogus packet up the stack - * - Delay RU config to avoid corrupting first received packet - * - Change config completion code (to actually check something) - * - Avoid reading out of bound in skbuf to transmit - * - Rectify a lot of (useless) debugging code - * - Change the way to `#ifdef SET_PSA_CRC' - * - * Changes made for release in 2.2.11 & 2.3.13 : - * ------------------------------------------- - * - 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 2.2.14 & 2.3.23 : - * ------------------------------------------- - * - Fix check for root permission (break instead of exit) - * - New nwid & encoding setting (Wireless Extension 9) - * - * Changes made for release in 2.3.49 : - * ---------------------------------- - * - Indentation reformating (Alan) - * - Update to new network API (softnet - 2.3.43) : - * o replace dev->tbusy (Alan) - * o replace dev->tstart (Alan) - * o remove dev->interrupt (Alan) - * o add SMP locking via spinlock in splxx (me) - * o add spinlock in interrupt handler (me) - * o use kernel watchdog instead of ours (me) - * o increase watchdog timeout (kernel is more sensitive) (me) - * o verify that all the changes make sense and work (me) - * - Fixup a potential gotcha when reconfiguring and thighten a bit - * the interactions with Tx queue. - * - * Changes made for release in 2.4.0 : - * --------------------------------- - * - Fix spinlock stupid bugs that I left in. The driver is now SMP - * compliant and doesn't lockup at startup. - * - * Wishes & dreams: - * ---------------- - * - roaming (see Pcmcia driver) - */ - -/***************************** INCLUDES *****************************/ - -#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 /* Wireless extensions */ - -/* WaveLAN declarations */ -#include "i82586.h" -#include "wavelan.h" - -/************************** DRIVER OPTIONS **************************/ -/* - * `#define' or `#undef' the following constant to change the behaviour - * of the driver... - */ -#undef SET_PSA_CRC /* Calculate and set the CRC on 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 extensions exist in the kernel */ -/* Warning: this stuff will slow down the driver. */ -#define WIRELESS_SPY /* Enable spying addresses. */ -#undef HISTOGRAM /* Enable histogram of signal 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 and 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_ERROR /* errors on configuration */ -#undef DEBUG_TX_TRACE /* transmission calls */ -#undef DEBUG_TX_INFO /* header of the transmitted 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 received packet */ -#undef DEBUG_RX_FAIL /* Normal failure conditions */ -#define DEBUG_RX_ERROR /* Unexpected conditions */ - -#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */ -#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */ -#undef DEBUG_IOCTL_INFO /* various debugging 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 unused fields too. */ -#undef DEBUG_I82586_SHOW /* Show i82586 status. */ -#undef DEBUG_DEVICE_SHOW /* Show device parameters. */ - -/************************ CONSTANTS & MACROS ************************/ - -#ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n"; -#endif - -/* Watchdog temporisation */ -#define WATCHDOG_JIFFIES (512*HZ/100) - -/* Macro to get the number of elements in an array */ -#define NELS(a) (sizeof(a) / sizeof(a[0])) - -/* ------------------------ PRIVATE IOCTL ------------------------ */ - -#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ -#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ -#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */ -#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */ - -#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ -#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ - -/****************************** 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 keeps data in two structures: "device" - * keeps the generic data (same format for everybody) and "net_local" keeps - * additional specific data. - * Note that some of this specific data is in fact generic (en_stats, for - * example). - */ -struct net_local -{ - net_local * next; /* linked list of the devices */ - device * dev; /* reverse link */ - spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - en_stats stats; /* Ethernet interface statistics */ - int nresets; /* number of hardware resets */ - u_char reconfig_82586; /* We need to reconfigure the controller. */ - u_char promiscuous; /* promiscuous mode */ - int mc_count; /* number of multicast addresses */ - u_short hacr; /* current host interface state */ - - int tx_n_in_use; - u_short rx_head; - u_short rx_last; - u_short tx_first_free; - u_short tx_first_in_use; - -#ifdef WIRELESS_EXT - iw_stats wstats; /* Wireless-specific statistics */ -#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 */ -}; - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- MISC. SUBROUTINES ------------------------ */ -static inline void - wv_splhi(net_local *, /* Disable interrupts, lock driver */ - unsigned long *); /* flags */ -static inline void - wv_splx(net_local *, /* Enable interrupts, unlock driver */ - unsigned long *); /* flags */ -static u_char - wv_irq_to_psa(int); -static int - wv_psa_to_irq(u_char); -/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ -static inline u_short /* 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_short), /* data */ - hacr_write_slow(u_long, - u_short), - set_chan_attn(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_hacr_reset(u_long), /* ioaddr */ - wv_16_off(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_16_on(u_long, /* ioaddr */ - u_short), /* hacr */ - wv_ints_off(device *), - wv_ints_on(device *); -/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ -static void - psa_read(u_long, /* Read the Parameter Storage Area. */ - u_short, /* hacr */ - int, /* offset in PSA */ - u_char *, /* buffer to fill */ - int), /* size to read */ - psa_write(u_long, /* Write to the PSA. */ - u_short, /* hacr */ - 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); /* 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 */ -/* ---------------------- I82586 SUBROUTINES ----------------------- */ -static /*inline*/ void - obram_read(u_long, /* ioaddr */ - u_short, /* o */ - u_char *, /* b */ - int); /* n */ -static inline void - obram_write(u_long, /* ioaddr */ - u_short, /* o */ - u_char *, /* b */ - int); /* n */ -static void - wv_ack(device *); -static inline int - wv_synchronous_cmd(device *, - const char *), - wv_config_complete(device *, - u_long, - net_local *); -static int - wv_complete(device *, - u_long, - net_local *); -static inline void - wv_82586_reconfig(device *); -/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ -#ifdef DEBUG_I82586_SHOW -static void - wv_scb_show(unsigned short); -#endif -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 */ -static void - wavelan_set_multicast_list(device *); -/* ----------------------- PACKET RECEPTION ----------------------- */ -static inline void - wv_packet_read(device *, /* Read a packet from a frame. */ - u_short, - int), - wv_receive(device *); /* Read all packets waiting. */ -/* --------------------- PACKET TRANSMISSION --------------------- */ -static inline int - 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. */ - wv_ru_start(device *), /* Start the i82586 receiver unit. */ - wv_cu_start(device *), /* Start the i82586 command unit. */ - wv_82586_start(device *); /* Start the i82586. */ -static void - wv_82586_config(device *); /* Configure the i82586. */ -static inline void - wv_82586_stop(device *); -static int - wv_hw_reset(device *), /* Reset the WaveLAN hardware. */ - wv_check_ioaddr(u_long, /* ioaddr */ - u_char *); /* mac address (read) */ -/* ---------------------- INTERRUPT HANDLING ---------------------- */ -static void - wavelan_interrupt(int, /* interrupt handler */ - void *, - struct pt_regs *); -static void - wavelan_watchdog(device *); /* transmission watchdog */ -/* ------------------- CONFIGURATION CALLBACKS ------------------- */ -static int - wavelan_open(device *), /* Open the device. */ - wavelan_close(device *), /* Close the device. */ - wavelan_config(device *); /* Configure one device. */ -extern int - wavelan_probe(device *); /* See Space.c. */ - -/**************************** VARIABLES ****************************/ - -/* - * This is the root of the linked list of WaveLAN drivers - * It is use to verify that we don't reuse the same base address - * for two different drivers and to clean up when removing the module. - */ -static net_local * wavelan_list = (net_local *) NULL; - -/* - * This table is used to translate the PSA value to IRQ number - * and vice versa. - */ -static u_char irqvals[] = -{ - 0, 0, 0, 0x01, - 0x02, 0x04, 0, 0x08, - 0, 0, 0x10, 0x20, - 0x40, 0, 0, 0x80, -}; - -/* - * Table of the available I/O addresses (base addresses) for WaveLAN - */ -static unsigned short iobase[] = -{ -#if 0 - /* Leave out 0x3C0 for now -- seems to clash with some video - * controllers. - * Leave out the others too -- we will always use 0x390 and leave - * 0x300 for the Ethernet device. - * Jean II: 0x3E0 is fine as well. - */ - 0x300, 0x390, 0x3E0, 0x3C0 -#endif /* 0 */ - 0x390, 0x3E0 -}; - -#ifdef MODULE -/* Parameters set by insmod */ -static int io[4]; -static int irq[4]; -static char name[4][IFNAMSIZ]; -MODULE_PARM(io, "1-4i"); -MODULE_PARM(irq, "1-4i"); -MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ)); -MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required"); -MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)"); -MODULE_PARM_DESC(name, "WaveLAN interface neme(s)"); -#endif /* MODULE */ - -#endif /* WAVELAN_P_H */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/Config.in linux/drivers/net/wireless/Config.in --- linux-2.5.3-pre2/drivers/net/wireless/Config.in Tue Oct 9 15:13:03 2001 +++ linux/drivers/net/wireless/Config.in Mon Jan 21 17:23:23 2002 @@ -2,6 +2,12 @@ # Wireless LAN device configuration # +comment 'Wireless ISA/PCI cards support' + +# Good old obsolete Wavelan. +tristate ' AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support' CONFIG_WAVELAN + +# 802.11b cards if [ "$CONFIG_ISA" = "y" -o "$CONFIG_PCI" = "y" ]; then tristate ' Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards' CONFIG_AIRO fi @@ -18,8 +24,13 @@ # If Pcmcia is compiled in, offer Pcmcia cards... if [ "$CONFIG_PCMCIA" != "n" ]; then - comment 'Wireless Pcmcia cards support' + comment 'Wireless Pcmcia/Cardbus cards support' + +# Obsolete cards + dep_tristate ' Xircom Netwave AirSurfer Pcmcia wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA + dep_tristate ' AT&T/Lucent old Wavelan Pcmcia wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA +# 802.11b cards dep_tristate ' Hermes PCMCIA card support' CONFIG_PCMCIA_HERMES $CONFIG_HERMES tristate ' Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards' CONFIG_AIRO_CS fi diff -urN linux-2.5.3-pre2/drivers/net/wireless/Makefile linux/drivers/net/wireless/Makefile --- linux-2.5.3-pre2/drivers/net/wireless/Makefile Tue Oct 9 15:13:03 2001 +++ linux/drivers/net/wireless/Makefile Mon Jan 21 17:23:23 2002 @@ -14,6 +14,11 @@ # Things that need to export symbols export-objs := airo.o orinoco.o hermes.o +# Obsolete cards +obj-$(CONFIG_WAVELAN) += wavelan.o +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o + obj-$(CONFIG_HERMES) += orinoco.o hermes.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o obj-$(CONFIG_APPLE_AIRPORT) += airport.o diff -urN linux-2.5.3-pre2/drivers/net/wireless/i82586.h linux/drivers/net/wireless/i82586.h --- linux-2.5.3-pre2/drivers/net/wireless/i82586.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/i82586.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,413 @@ +/* + * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor. + * + * See: + * Intel Microcommunications 1991 + * p1-1 to p1-37 + * Intel order No. 231658 + * ISBN 1-55512-119-5 + * + * Unfortunately, the above chapter mentions neither + * the System Configuration Pointer (SCP) nor the + * Intermediate System Configuration Pointer (ISCP), + * so we probably need to look elsewhere for the + * whole story -- some recommend the "Intel LAN + * Components manual" but I have neither a copy + * nor a full reference. But "elsewhere" may be + * in the same publication... + * The description of a later device, the + * "82596CA High-Performance 32-Bit Local Area Network + * Coprocessor", (ibid. p1-38 to p1-109) does mention + * the SCP and ISCP and also has an i82586 compatibility + * mode. Even more useful is "AP-235 An 82586 Data Link + * Driver" (ibid. p1-337 to p1-417). + */ + +#define I82586_MEMZ (64 * 1024) + +#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t)) + +#define ADDR_LEN 6 +#define I82586NULL 0xFFFF + +#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * System Configuration Pointer (SCP). + */ +typedef struct scp_t scp_t; +struct scp_t +{ + unsigned short scp_sysbus; /* 82586 bus width: */ +#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */ +#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */ + unsigned short scp_junk[2]; /* Unused */ + unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */ + unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */ +}; + +/* + * Intermediate System Configuration Pointer (ISCP). + */ +typedef struct iscp_t iscp_t; +struct iscp_t +{ + unsigned short iscp_busy; /* set by CPU before first CA, */ + /* cleared by 82586 after read. */ + unsigned short iscp_offset; /* offset of SCB */ + unsigned short iscp_basel; /* base of SCB */ + unsigned short iscp_baseh; /* " */ +}; + +/* + * System Control Block (SCB). + * The 82586 writes its status to scb_status and then + * raises an interrupt to alert the CPU. + * The CPU writes a command to scb_command and + * then issues a Channel Attention (CA) to alert the 82586. + */ +typedef struct scb_t scb_t; +struct scb_t +{ + unsigned short scb_status; /* Status of 82586 */ +#define SCB_ST_INT (0xF << 12) /* Some of: */ +#define SCB_ST_CX (0x1 << 15) /* Cmd completed */ +#define SCB_ST_FR (0x1 << 14) /* Frame received */ +#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */ +#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */ +#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */ +#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */ +#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */ +#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */ +#define SCB_ST_CUS_ACTV (2 << 8) /* Active */ +#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */ +#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */ +#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */ +#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */ +#define SCB_ST_RUS_NRES (2 << 4) /* No resources */ +#define SCB_ST_RUS_RDY (4 << 4) /* Ready */ + unsigned short scb_command; /* Next command */ +#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */ +#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */ +#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */ +#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */ +#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */ +#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */ +#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */ +#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */ +#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */ +#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */ +#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */ +#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */ +#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */ +#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */ +#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */ +#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */ +#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */ +#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */ + unsigned short scb_cbl_offset; /* Offset of first command unit */ + /* Action Command */ + unsigned short scb_rfa_offset; /* Offset of first Receive */ + /* Frame Descriptor in the */ + /* Receive Frame Area */ + unsigned short scb_crcerrs; /* Properly aligned frames */ + /* received with a CRC error */ + unsigned short scb_alnerrs; /* Misaligned frames received */ + /* with a CRC error */ + unsigned short scb_rscerrs; /* Frames lost due to no space */ + unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */ +}; + +#define scboff(p,f) toff(scb_t, p, f) + +/* + * The eight Action Commands. + */ +typedef enum acmd_e acmd_e; +enum acmd_e +{ + acmd_nop = 0, /* Do nothing */ + acmd_ia_setup = 1, /* Load an (ethernet) address into the */ + /* 82586 */ + acmd_configure = 2, /* Update the 82586 operating parameters */ + acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */ + /* addresses into the 82586 */ + acmd_transmit = 4, /* Transmit a frame */ + acmd_tdr = 5, /* Perform a Time Domain Reflectometer */ + /* test on the serial link */ + acmd_dump = 6, /* Copy 82586 registers to memory */ + acmd_diagnose = 7, /* Run an internal self test */ +}; + +/* + * Generic Action Command header. + */ +typedef struct ach_t ach_t; +struct ach_t +{ + unsigned short ac_status; /* Command status: */ +#define AC_SFLD_C (0x1 << 15) /* Command completed */ +#define AC_SFLD_B (0x1 << 14) /* Busy executing */ +#define AC_SFLD_OK (0x1 << 13) /* Completed error free */ +#define AC_SFLD_A (0x1 << 12) /* Command aborted */ +#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */ +#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */ + /* during transmission */ +#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */ + /* (stopped) lost CTS */ +#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */ + /* (stopped) slow DMA */ +#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */ + /* other link traffic */ +#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */ + /* detect after last tx */ +#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */ + /* excessive collisions */ +#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */ + unsigned short ac_command; /* Command specifier: */ +#define AC_CFLD_EL (0x1 << 15) /* End of command list */ +#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */ +#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */ +#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */ + unsigned short ac_link; /* Next Action Command */ +}; + +#define acoff(p,f) toff(ach_t, p, f) + +/* + * The Nop Action Command. + */ +typedef struct ac_nop_t ac_nop_t; +struct ac_nop_t +{ + ach_t nop_h; +}; + +/* + * The IA-Setup Action Command. + */ +typedef struct ac_ias_t ac_ias_t; +struct ac_ias_t +{ + ach_t ias_h; + unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */ +}; + +/* + * The Configure Action Command. + */ +typedef struct ac_cfg_t ac_cfg_t; +struct ac_cfg_t +{ + ach_t cfg_h; + unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */ +#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0) + unsigned char cfg_fifolim; /* FIFO threshold */ +#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0) + unsigned char cfg_byte8; +#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */ +#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */ + /* external sync. */ + unsigned char cfg_byte9; +#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */ +#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */ +#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */ +#define AC_CFG_PLEN_2 0 /* 2 bytes */ +#define AC_CFG_PLEN_4 1 /* 4 bytes */ +#define AC_CFG_PLEN_8 2 /* 8 bytes */ +#define AC_CFG_PLEN_16 3 /* 16 bytes */ +#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */ + /* explicit in buffers */ +#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */ + unsigned char cfg_byte10; +#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */ + /* backoff method */ +#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */ +#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */ + unsigned char cfg_ifs; /* Interframe spacing */ + unsigned char cfg_slotl; /* Slot time (low byte) */ + unsigned char cfg_byte13; +#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */ +#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */ + unsigned char cfg_byte14; +#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */ +#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */ +#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */ +#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */ +#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */ +#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */ +#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */ +#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */ + unsigned char cfg_byte15; +#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */ + /* detect source */ +#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */ + /* filter in bit times */ +#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */ + /* sense source */ +#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */ + /* filter in bit times */ + unsigned short cfg_min_frm_len; +#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */ +}; + +/* + * The MC-Setup Action Command. + */ +typedef struct ac_mcs_t ac_mcs_t; +struct ac_mcs_t +{ + ach_t mcs_h; + unsigned short mcs_cnt; /* No. of bytes of MC addresses */ +#if 0 + unsigned char mcs_data[ADDR_LEN]; /* The first MC address .. */ + ... +#endif +}; + +#define I82586_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ + +/* + * The Transmit Action Command. + */ +typedef struct ac_tx_t ac_tx_t; +struct ac_tx_t +{ + ach_t tx_h; + unsigned short tx_tbd_offset; /* Address of list of buffers. */ +#if 0 +Linux packets are passed down with the destination MAC address +and length/type field already prepended to the data, +so we do not need to insert it. Consistent with this +we must also set the AC_CFG_ALOC(..) flag during the +ac_cfg_t action command. + unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */ + unsigned short tx_length; /* The frame length */ +#endif /* 0 */ +}; + +/* + * The Time Domain Reflectometer Action Command. + */ +typedef struct ac_tdr_t ac_tdr_t; +struct ac_tdr_t +{ + ach_t tdr_h; + unsigned short tdr_result; /* Result. */ +#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */ +#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */ +#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */ +#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */ +#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */ + /* site in transmit */ + /* clock cycles */ +}; + +/* + * The Dump Action Command. + */ +typedef struct ac_dmp_t ac_dmp_t; +struct ac_dmp_t +{ + ach_t dmp_h; + unsigned short dmp_offset; /* Result. */ +}; + +/* + * Size of the result of the dump command. + */ +#define DUMPBYTES 170 + +/* + * The Diagnose Action Command. + */ +typedef struct ac_dgn_t ac_dgn_t; +struct ac_dgn_t +{ + ach_t dgn_h; +}; + +/* + * Transmit Buffer Descriptor (TBD). + */ +typedef struct tbd_t tbd_t; +struct tbd_t +{ + unsigned short tbd_status; /* Written by the CPU */ +#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */ + /* last for this frame */ +#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */ + /* bytes in this buffer */ + unsigned short tbd_next_bd_offset; /* Next in list */ + unsigned short tbd_bufl; /* Buffer address (low) */ + unsigned short tbd_bufh; /* " " (high) */ +}; + +/* + * Receive Buffer Descriptor (RBD). + */ +typedef struct rbd_t rbd_t; +struct rbd_t +{ + unsigned short rbd_status; /* Written by the 82586 */ +#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */ + /* last for this frame */ +#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */ +#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */ + /* bytes in this buffer */ + unsigned short rbd_next_rbd_offset; /* Next rbd in list */ + unsigned short rbd_bufl; /* Data pointer (low) */ + unsigned short rbd_bufh; /* " " (high) */ + unsigned short rbd_el_size; /* EL+Data buf. size */ +#define RBD_EL (0x1 << 15) /* This BD is the */ + /* last in the list */ +#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */ + /* buffer can hold */ +}; + +#define rbdoff(p,f) toff(rbd_t, p, f) + +/* + * Frame Descriptor (FD). + */ +typedef struct fd_t fd_t; +struct fd_t +{ + unsigned short fd_status; /* Written by the 82586 */ +#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */ +#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */ +#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */ +#define FD_STATUS_S11 (0x1 << 11) /* CRC error */ +#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */ +#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */ +#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */ +#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */ +#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */ + unsigned short fd_command; /* Command */ +#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */ +#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */ + unsigned short fd_link_offset; /* Next FD */ + unsigned short fd_rbd_offset; /* First RBD (data) */ + /* Prepared by CPU, */ + /* updated by 82586 */ +#if 0 +I think the rest is unused since we +have set AC_CFG_ALOC(..). However, just +in case, we leave the space. +#endif /* 0 */ + unsigned char fd_dest[ADDR_LEN]; /* Destination address */ + /* Written by 82586 */ + unsigned char fd_src[ADDR_LEN]; /* Source address */ + /* Written by 82586 */ + unsigned short fd_length; /* Frame length or type */ + /* Written by 82586 */ +}; + +#define fdoff(p,f) toff(fd_t, p, f) + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * For more details, see wavelan.c. + */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/i82593.h linux/drivers/net/wireless/i82593.h --- linux-2.5.3-pre2/drivers/net/wireless/i82593.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/i82593.h Mon Jan 21 17:23:23 2002 @@ -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 -urN linux-2.5.3-pre2/drivers/net/wireless/netwave_cs.c linux/drivers/net/wireless/netwave_cs.c --- linux-2.5.3-pre2/drivers/net/wireless/netwave_cs.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/netwave_cs.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,1600 @@ +/********************************************************************* + * + * 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 +#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 ((32*HZ)/100) + +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 */ + +/* + 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; + +/* 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(struct net_device *); + +/* Statistics */ +static void update_stats(struct net_device *dev); +static struct net_device_stats *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; + +/* + 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. +*/ + +/* Wireless Extension Backward compatibility - Jean II + * If the new wireless device private ioctl range is not defined, + * default to standard device private ioctl range */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ + +#define SIOCGIPSNAP SIOCIWFIRSTPRIV /* Site Survey Snapshot */ +/*#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 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_link_t link; + struct net_device dev; + dev_node_t node; + u_char *ramBase; + int timeoutCounter; + int lastExec; + struct timer_list watchdog; /* To avoid blocking state */ + struct site_survey nss; + struct net_device_stats stats; +#ifdef WIRELESS_EXT + struct iw_statistics iw_stats; /* Wireless stats */ +#endif +} netwave_private; + +#ifdef NETWAVE_STATS +static struct net_device_stats *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_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 */ + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) return NULL; + memset(priv, 0, sizeof(*priv)); + link = &priv->link; dev = &priv->dev; + link->priv = dev->priv = priv; + 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; + + /* 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; + + dev->tx_timeout = &netwave_watchdog; + dev->watchdog_timeo = TX_TIMEOUT; + + ether_setup(dev); + dev->open = &netwave_open; + dev->stop = &netwave_close; + 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) +{ + netwave_private *priv = link->priv; + dev_link_t **linkp; + + DEBUG(0, "netwave_detach(0x%p)\n", link); + + /* + 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(). + */ + del_timer(&link->release); + 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->dev) + unregister_netdev(&priv->dev); + kfree(priv); + +} /* 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 (very important for backward compatibility) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(&range, 0, sizeof(range)); + +#if WIRELESS_EXT > 10 + /* Set the Wireless Extension versions */ + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 9; /* Nothing for us in v10 and v11 */ +#endif /* WIRELESS_EXT > 10 */ + + /* 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 = link->handle; + netwave_private *priv = link->priv; + struct net_device *dev = &priv->dev; + tuple_t tuple; + cisparse_t parse; + int i, j, last_ret, last_fn; + u_char buf[64]; + win_req_t req; + memreq_t mem; + u_char *ramBase = NULL; + + 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 (i = 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; + if (register_netdev(dev) != 0) { + printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); + goto failed; + } + + strcpy(priv->node.dev_name, dev->name); + link->dev = &priv->node; + link->state &= ~DEV_CONFIG_PENDING; + + /* 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); +} /* 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; + netwave_private *priv = 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(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_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; + netwave_private *priv = link->priv; + struct net_device *dev = &priv->dev; + + 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) { + netif_device_detach(dev); + mod_timer(&link->release, jiffies + HZ/20); + } + 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) + netif_device_detach(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) { + netwave_reset(dev); + netif_device_attach(dev); + } + } + 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; + + /* 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); + restore_flags(flags); + 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); + + 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... + */ + + netif_stop_queue(dev); + + { + 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? */ + netif_start_queue(dev); + } + 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 transferred 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 = dev->priv; + dev_link_t *link = &priv->link; + int i; + + if (!netif_device_present(dev)) + return; + + 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 (!DEV_OK(link)) { + DEBUG(1, "netwave_interrupt: 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)); + + netif_wake_queue(dev); + } + /* TxBA, this would trigger on all error packets received */ + /* if (status & 0x01) { + DEBUG(4, "Transmit buffers available, %x\n", status); + } + */ + } +} /* 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(struct net_device *dev) { + + DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name); + netwave_reset(dev); + dev->trans_start = jiffies; + netif_wake_queue(dev); +} /* netwave_watchdog */ + +static struct net_device_stats *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); + + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += rcvLen; + + /* 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"); + } + return 0; +} + +static int netwave_open(struct net_device *dev) { + netwave_private *priv = dev->priv; + dev_link_t *link = &priv->link; + + DEBUG(1, "netwave_open: starting.\n"); + + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + netwave_reset(dev); + + return 0; +} + +static int netwave_close(struct net_device *dev) { + netwave_private *priv = (netwave_private *)dev->priv; + dev_link_t *link = &priv->link; + + DEBUG(1, "netwave_close: finishing.\n"); + + link->open--; + netif_stop_queue(dev); + if (link->state & DEV_STALE_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + + 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; + 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); +} +MODULE_LICENSE("GPL"); diff -urN linux-2.5.3-pre2/drivers/net/wireless/todo.txt linux/drivers/net/wireless/todo.txt --- linux-2.5.3-pre2/drivers/net/wireless/todo.txt Mon May 7 19:42:14 2001 +++ linux/drivers/net/wireless/todo.txt Mon Jan 21 17:23:23 2002 @@ -4,21 +4,26 @@ 1) Bring other kernel Wireless LAN drivers here Already done : o hermes.c/orinoco.c -> Wavelan IEEE driver + Airport driver - Drivers I have control over : + o airo.c/airo_cs.c -> Ben's Aironet driver o wavelan.c -> old Wavelan ISA driver - o wavelan_cs.c -> old Wavelan Pcmcia driver (warning : header) + o wavelan_cs.c -> old Wavelan Pcmcia driver o netwave_cs.c -> Netwave Pcmcia driver Drivers likely to go : o ray_cs.c -> Raytheon/Aviator driver (maintainer MIA) Drivers I have absolutely no control over : o arlan.c -> old Aironet Arlan 655 (need to ask Elmer) o aironet4500_xxx.c -> Elmer's Aironet driver (need to ask Elmer) - o airo.c/airo_cs.c -> Ben's Aironet driver (not yet in kernel) o strip.c -> Metricom's stuff. Not a wlan. Hum... ETA : Kernel 2.5.X 2) Bring new Wireless LAN driver not yet in the kernel there See my web page for details + +3) Misc + o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete + o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete + o Use new Probe/module stuff in wavelan.c + o New Wireless Extension API (pending) Jean II diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.c linux/drivers/net/wireless/wavelan.c --- linux-2.5.3-pre2/drivers/net/wireless/wavelan.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,4342 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follows (also see the end of this file). + * See wavelan.p.h for details. + * + * + * + * AT&T GIS (nee NCR) WaveLAN card: + * An Ethernet-like radio transceiver + * controlled by an Intel 82586 coprocessor. + */ + +#include "wavelan.p.h" /* Private header */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (WaveLAN modem or i82586) + */ + +/*------------------------------------------------------------------*/ +/* + * Wrapper for disabling interrupts and locking the driver. + * (note : inline, so optimised away) + */ +static inline void wv_splhi(net_local * lp, + unsigned long * pflags) +{ + spin_lock_irqsave(&lp->spinlock, *pflags); + /* Note : above does the cli(); itself */ +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for re-enabling interrupts and un-locking the driver. + */ +static inline void wv_splx(net_local * lp, + unsigned long * pflags) +{ + spin_unlock_irqrestore(&lp->spinlock, *pflags); +} + +/*------------------------------------------------------------------*/ +/* + * Translate irq number to PSA irq parameter + */ +static u8 wv_irq_to_psa(int irq) +{ + if (irq < 0 || irq >= NELS(irqvals)) + return 0; + + return irqvals[irq]; +} + +/*------------------------------------------------------------------*/ +/* + * Translate PSA irq parameter to irq number + */ +static int __init wv_psa_to_irq(u8 irqval) +{ + int irq; + + for (irq = 0; irq < NELS(irqvals); irq++) + if (irqvals[irq] == irqval) + return irq; + + return -1; +} + +#ifdef STRUCT_CHECK +/*------------------------------------------------------------------*/ +/* + * Sanity routine to verify the sizes of the various WaveLAN interface + * structures. + */ +static char *wv_struct_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"); + SC(ha_t, HA_SIZE, "ha_t"); + +#undef SC + + return ((char *) NULL); +} /* wv_struct_check */ +#endif /* STRUCT_CHECK */ + +/********************* HOST ADAPTER SUBROUTINES *********************/ +/* + * Useful subroutines to manage the WaveLAN ISA interface + * + * One major difference with the PCMCIA hardware (except the port mapping) + * is that we have to keep the state of the Host Control Register + * because of the interrupt enable & bus size flags. + */ + +/*------------------------------------------------------------------*/ +/* + * Read from card's Host Adaptor Status Register. + */ +static inline u16 hasr_read(unsigned long ioaddr) +{ + return (inw(HASR(ioaddr))); +} /* hasr_read */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. + */ +static inline void hacr_write(unsigned long ioaddr, u16 hacr) +{ + outw(hacr, HACR(ioaddr)); +} /* 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(unsigned long ioaddr, u16 hacr) +{ + hacr_write(ioaddr, hacr); + /* delay might only be needed sometimes */ + mdelay(1); +} /* hacr_write_slow */ + +/*------------------------------------------------------------------*/ +/* + * Set the channel attention bit. + */ +static inline void set_chan_attn(unsigned long ioaddr, u16 hacr) +{ + hacr_write(ioaddr, hacr | HACR_CA); +} /* set_chan_attn */ + +/*------------------------------------------------------------------*/ +/* + * Reset, and then set host adaptor into default mode. + */ +static inline void wv_hacr_reset(unsigned long ioaddr) +{ + hacr_write_slow(ioaddr, HACR_RESET); + hacr_write(ioaddr, HACR_DEFAULT); +} /* wv_hacr_reset */ + +/*------------------------------------------------------------------*/ +/* + * Set the I/O transfer over the ISA bus to 8-bit mode + */ +static inline void wv_16_off(unsigned long ioaddr, u16 hacr) +{ + hacr &= ~HACR_16BITS; + hacr_write(ioaddr, hacr); +} /* wv_16_off */ + +/*------------------------------------------------------------------*/ +/* + * Set the I/O transfer over the ISA bus to 8-bit mode + */ +static inline void wv_16_on(unsigned long ioaddr, u16 hacr) +{ + hacr |= HACR_16BITS; + hacr_write(ioaddr, hacr); +} /* wv_16_on */ + +/*------------------------------------------------------------------*/ +/* + * Disable interrupts on the WaveLAN hardware. + * (called by wv_82586_stop()) + */ +static inline void wv_ints_off(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + + lp->hacr &= ~HACR_INTRON; + hacr_write(ioaddr, lp->hacr); +} /* wv_ints_off */ + +/*------------------------------------------------------------------*/ +/* + * Enable interrupts on the WaveLAN hardware. + * (called by wv_hw_reset()) + */ +static inline void wv_ints_on(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + + lp->hacr |= HACR_INTRON; + hacr_write(ioaddr, lp->hacr); +} /* wv_ints_on */ + +/******************* MODEM MANAGEMENT SUBROUTINES *******************/ +/* + * Useful subroutines to manage the modem of the WaveLAN + */ + +/*------------------------------------------------------------------*/ +/* + * Read the Parameter Storage Area from the WaveLAN card's memory + */ +/* + * Read bytes from the PSA. + */ +static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */ + u8 * b, /* buffer to fill */ + int n) +{ /* size to read */ + wv_16_off(ioaddr, hacr); + + while (n-- > 0) { + outw(o, PIOR2(ioaddr)); + o++; + *b++ = inb(PIOP2(ioaddr)); + } + + wv_16_on(ioaddr, hacr); +} /* psa_read */ + +/*------------------------------------------------------------------*/ +/* + * Write the Parameter Storage Area to the WaveLAN card's memory. + */ +static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */ + u8 * b, /* Buffer in memory */ + int n) +{ /* Length of buffer */ + int count = 0; + + wv_16_off(ioaddr, hacr); + + while (n-- > 0) { + outw(o, PIOR2(ioaddr)); + o++; + + outb(*b, PIOP2(ioaddr)); + b++; + + /* Wait for the memory to finish its write cycle */ + count = 0; + while ((count++ < 100) && + (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1); + } + + wv_16_on(ioaddr, hacr); +} /* 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 inline u16 psa_crc(u8 * psa, /* The PSA */ + int size) +{ /* Number of short for CRC */ + int byte_cnt; /* Loop on the PSA */ + u16 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, unsigned long ioaddr, u16 hacr) +{ +#ifdef SET_PSA_CRC + psa_t psa; + u16 crc; + + /* read the parameter storage area */ + psa_read(ioaddr, hacr, 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(ioaddr, hacr, (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(unsigned long ioaddr, u16 o, u8 d) +{ + /* Wait for MMC to go idle */ + while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); + + outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr)); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to write bytes to the Modem Management Controller. + * We start at the end because it is the way it should be! + */ +static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n) +{ + o += n; + b += n; + + while (n-- > 0) + mmc_out(ioaddr, --o, *(--b)); +} /* mmc_write */ + +/*------------------------------------------------------------------*/ +/* + * Read a byte from the MMC. + * Optimised version for 1 byte, avoid using memory. + */ +static inline u8 mmc_in(unsigned long ioaddr, u16 o) +{ + while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); + outw(o << 1, MMCR(ioaddr)); + + while (inw(HASR(ioaddr)) & HASR_MMC_BUSY); + return (u8) (inw(MMCR(ioaddr)) >> 8); +} + +/*------------------------------------------------------------------*/ +/* + * 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 at the end because it is the way it should be! + */ +static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n) +{ + o += n; + b += n; + + while (n-- > 0) + *(--b) = mmc_in(ioaddr, --o); +} /* mmc_read */ + +/*------------------------------------------------------------------*/ +/* + * Get the type of encryption available. + */ +static inline int mmc_encr(unsigned long ioaddr) +{ /* I/O port of the card */ + int temp; + + temp = mmc_in(ioaddr, 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(unsigned long ioaddr, /* 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(ioaddr, mmroff(0, mmr_fee_status)) & + MMR_FEE_STATUS_BUSY)) udelay(delay); +} + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the Frequency EEPROM (frequency select cards). + */ +static void fee_read(unsigned long ioaddr, /* I/O port of the card */ + u16 o, /* destination offset */ + u16 * b, /* data buffer */ + int n) +{ /* number of registers */ + b += n; /* Position at the end of the area */ + + /* Write the address */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while (n-- > 0) { + /* Write the read command */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ); + + /* Wait until EEPROM is ready (should be quick). */ + fee_wait(ioaddr, 10, 100); + + /* Read the value. */ + *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) | + mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); + } +} + +#ifdef WIRELESS_EXT /* if the wireless extension exists 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(unsigned long ioaddr, /* I/O port of the card */ + u16 o, /* destination offset */ + u16 * 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(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); + + fee_wait(ioaddr, 10, 100); + + /* Read the protected register. */ + printk("Protected 2: %02X-%02X\n", + mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)), + mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); +#endif /* DOESNT_SEEM_TO_WORK */ + + /* Enable protected register. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); + + fee_wait(ioaddr, 10, 100); + + /* Unprotect area. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* or use: */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); +#endif /* DOESNT_SEEM_TO_WORK */ + + fee_wait(ioaddr, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ + + /* Write enable. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); + + fee_wait(ioaddr, 10, 100); + + /* Write the EEPROM address. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while (n-- > 0) { + /* Write the value. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); + mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF); + + /* Write the write command. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_WRITE); + + /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */ + mdelay(10); + fee_wait(ioaddr, 10, 100); + } + + /* Write disable. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); + + fee_wait(ioaddr, 10, 100); + +#ifdef EEPROM_IS_PROTECTED /* disabled */ + /* Reprotect EEPROM. */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); + + fee_wait(ioaddr, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ +} +#endif /* WIRELESS_EXT */ + +/************************ I82586 SUBROUTINES *************************/ +/* + * Useful subroutines to manage the Ethernet controller + */ + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the on-board RAM. + * Why does inlining this function make it fail? + */ +static /*inline */ void obram_read(unsigned long ioaddr, + u16 o, u8 * b, int n) +{ + outw(o, PIOR1(ioaddr)); + insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Write bytes to the on-board RAM. + */ +static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n) +{ + outw(o, PIOR1(ioaddr)); + outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Acknowledge the reading of the status issued by the i82586. + */ +static void wv_ack(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 scb_cs; + int i; + + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + scb_cs &= SCB_ST_INT; + + if (scb_cs == 0) + return; + + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + udelay(100); + +#ifdef DEBUG_CONFIG_ERROR + if (i <= 0) + printk(KERN_INFO + "%s: wv_ack(): board not accepting command.\n", + dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Set channel attention bit and busy wait until command has + * completed, then acknowledge completion of the command. + */ +static inline int wv_synchronous_cmd(device * dev, const char *str) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 scb_cmd; + ach_t cb; + int i; + + scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cmd, sizeof(scb_cmd)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, + sizeof(cb)); + if (cb.ac_status & AC_SFLD_C) + break; + + udelay(10); + } + udelay(100); + + if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "%s: %s failed; status = 0x%x\n", + dev->name, str, cb.ac_status); +#endif +#ifdef DEBUG_I82586_SHOW + wv_scb_show(ioaddr); +#endif + return -1; + } + + /* Ack the status */ + wv_ack(dev); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Configuration commands completion interrupt. + * Check if done, and if OK. + */ +static inline int +wv_config_complete(device * dev, unsigned long ioaddr, net_local * lp) +{ + unsigned short mcs_addr; + unsigned short status; + int ret; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name); +#endif + + mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t) + + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t); + + /* Read the status of the last command (set mc list). */ + obram_read(ioaddr, acoff(mcs_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + + /* If not completed -> exit */ + if ((status & AC_SFLD_C) == 0) + ret = 0; /* Not ready to be scrapped */ + else { +#ifdef DEBUG_CONFIG_ERROR + unsigned short cfg_addr; + unsigned short ias_addr; + + /* Check mc_config command */ + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n", + dev->name, status); + + /* check ia-config command */ + ias_addr = mcs_addr - sizeof(ac_ias_t); + obram_read(ioaddr, acoff(ias_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n", + dev->name, status); + + /* Check config command. */ + cfg_addr = ias_addr - sizeof(ac_cfg_t); + obram_read(ioaddr, acoff(cfg_addr, ac_status), + (unsigned char *) &status, sizeof(status)); + if ((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO + "%s: wv_config_complete(): configure failed; status = 0x%x\n", + dev->name, status); +#endif /* DEBUG_CONFIG_ERROR */ + + ret = 1; /* Ready to be scrapped */ + } + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, + ret); +#endif + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Command completion interrupt. + * Reclaim as many freed tx buffers as we can. + * (called in wavelan_interrupt()). + * Note : the spinlock is already grabbed for us. + */ +static int wv_complete(device * dev, unsigned long ioaddr, net_local * lp) +{ + int nreaped = 0; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name); +#endif + + /* Loop on all the transmit buffers */ + while (lp->tx_first_in_use != I82586NULL) { + unsigned short tx_status; + + /* Read the first transmit buffer */ + obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), + (unsigned char *) &tx_status, + sizeof(tx_status)); + + /* If not completed -> exit */ + if ((tx_status & AC_SFLD_C) == 0) + break; + + /* Hack for reconfiguration */ + if (tx_status == 0xFFFF) + if (!wv_config_complete(dev, ioaddr, lp)) + break; /* Not completed */ + + /* We now remove this buffer */ + nreaped++; + --lp->tx_n_in_use; + +/* +if (lp->tx_n_in_use > 0) + printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); +*/ + + /* Was it the last one? */ + if (lp->tx_n_in_use <= 0) + lp->tx_first_in_use = I82586NULL; + else { + /* Next one in the chain */ + lp->tx_first_in_use += TXBLOCKZ; + if (lp->tx_first_in_use >= + OFFSET_CU + + NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -= + NTXBLOCKS * TXBLOCKZ; + } + + /* Hack for reconfiguration */ + if (tx_status == 0xFFFF) + continue; + + /* Now, check status of the finished command */ + if (tx_status & AC_SFLD_OK) { + int ncollisions; + + lp->stats.tx_packets++; + ncollisions = tx_status & AC_SFLD_MAXCOL; + lp->stats.collisions += ncollisions; +#ifdef DEBUG_TX_INFO + if (ncollisions > 0) + printk(KERN_DEBUG + "%s: wv_complete(): tx completed after %d collisions.\n", + dev->name, ncollisions); +#endif + } else { + lp->stats.tx_errors++; + if (tx_status & AC_SFLD_S10) { + lp->stats.tx_carrier_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: no CS.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S9) { + lp->stats.tx_carrier_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: lost CTS.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S8) { + lp->stats.tx_fifo_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: slow DMA.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S6) { + lp->stats.tx_heartbeat_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: heart beat.\n", + dev->name); +#endif + } + if (tx_status & AC_SFLD_S5) { + lp->stats.tx_aborted_errors++; +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG + "%s: wv_complete(): tx error: too many collisions.\n", + dev->name); +#endif + } + } + +#ifdef DEBUG_TX_INFO + printk(KERN_DEBUG + "%s: wv_complete(): tx completed, tx_status 0x%04x\n", + dev->name, tx_status); +#endif + } + +#ifdef DEBUG_INTERRUPT_INFO + if (nreaped > 1) + printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", + dev->name, nreaped); +#endif + + /* + * Inform upper layers. + */ + if (lp->tx_n_in_use < NTXBLOCKS - 1) { + netif_wake_queue(dev); + } +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name); +#endif + return nreaped; +} + +/*------------------------------------------------------------------*/ +/* + * Reconfigure the i82586, or at least ask for it. + * Because wv_82586_config uses a transmission buffer, we must do it + * when we are sure that there is one left, 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 + * delays sometimes. + */ +static inline void wv_82586_reconfig(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long flags; + + /* Arm the flag, will be cleard in wv_82586_config() */ + lp->reconfig_82586 = 1; + + /* Check if we can do it now ! */ + if((netif_running(dev)) && !(netif_queue_stopped(dev))) { + wv_splhi(lp, &flags); + /* May fail */ + wv_82586_config(dev); + wv_splx(lp, &flags); + } + else { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wv_82586_reconfig(): delayed (state = %lX)\n", + dev->name, dev->state); +#endif + } +} + +/********************* DEBUG & INFO SUBROUTINES *********************/ +/* + * This routine is used in the code to show information for debugging. + * Most of the time, it dumps the contents 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 needs to be completed. + */ +static void wv_mmc_show(device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; + mmr_t m; + + /* Basic check */ + if (hasr_read(ioaddr) & HASR_NO_CLK) { + printk(KERN_WARNING + "%s: wv_mmc_show: modem not connected\n", + dev->name); + return; + } + + /* Read the mmc */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m)); + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +#ifdef WIRELESS_EXT /* if wireless extension exists 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 algorithm: %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_I82586_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the last block of the i82586 memory. + */ +static void wv_scb_show(unsigned long ioaddr) +{ + scb_t scb; + + obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + printk(KERN_DEBUG "##### WaveLAN system control block: #####\n"); + + printk(KERN_DEBUG "status: "); + printk("stat 0x%x[%s%s%s%s] ", + (scb. + scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | + SCB_ST_RNR)) >> 12, + (scb. + scb_status & SCB_ST_CX) ? "command completion interrupt," : + "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "", + (scb. + scb_status & SCB_ST_CNA) ? "command unit not active," : "", + (scb. + scb_status & SCB_ST_RNR) ? "receiving unit not ready," : + ""); + printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8, + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_IDLE) ? "idle" : "", + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_SUSP) ? "suspended" : "", + ((scb.scb_status & SCB_ST_CUS) == + SCB_ST_CUS_ACTV) ? "active" : ""); + printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4, + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_IDLE) ? "idle" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_SUSP) ? "suspended" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_NRES) ? "no resources" : "", + ((scb.scb_status & SCB_ST_RUS) == + SCB_ST_RUS_RDY) ? "ready" : ""); + + printk(KERN_DEBUG "command: "); + printk("ack 0x%x[%s%s%s%s] ", + (scb. + scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | + SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12, + (scb. + scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "", + (scb. + scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "", + (scb. + scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "", + (scb. + scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""); + printk("cuc 0x%x[%s%s%s%s%s] ", + (scb.scb_command & SCB_CMD_CUC) >> 8, + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_NOP) ? "nop" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_GO) ? "start cbl_offset" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_RES) ? "resume execution" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_SUS) ? "suspend execution" : "", + ((scb.scb_command & SCB_CMD_CUC) == + SCB_CMD_CUC_ABT) ? "abort execution" : ""); + printk("ruc 0x%x[%s%s%s%s%s]\n", + (scb.scb_command & SCB_CMD_RUC) >> 4, + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_NOP) ? "nop" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_GO) ? "start rfa_offset" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_RES) ? "resume reception" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_SUS) ? "suspend reception" : "", + ((scb.scb_command & SCB_CMD_RUC) == + SCB_CMD_RUC_ABT) ? "abort reception" : ""); + + printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset); + printk("rfa_offset 0x%x\n", scb.scb_rfa_offset); + + printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs); + printk("alnerrs %d ", scb.scb_alnerrs); + printk("rscerrs %d ", scb.scb_rscerrs); + printk("ovrnerrs %d\n", scb.scb_ovrnerrs); +} + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the i82586's receive unit. + */ +static void wv_ru_show(device * dev) +{ + /* net_local *lp = (net_local *) dev->priv; */ + + printk(KERN_DEBUG + "##### WaveLAN i82586 receiver unit status: #####\n"); + printk(KERN_DEBUG "ru:"); + /* + * Not implemented yet + */ + printk("\n"); +} /* wv_ru_show */ + +/*------------------------------------------------------------------*/ +/* + * Display info about one control block of the i82586 memory. + */ +static void wv_cu_show_one(device * dev, net_local * lp, int i, u16 p) +{ + unsigned long ioaddr; + ac_tx_t actx; + + ioaddr = dev->base_addr; + + printk("%d: 0x%x:", i, p); + + obram_read(ioaddr, p, (unsigned char *) &actx, sizeof(actx)); + printk(" status=0x%x,", actx.tx_h.ac_status); + printk(" command=0x%x,", actx.tx_h.ac_command); + + /* + { + tbd_t tbd; + + obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd)); + printk(" tbd_status=0x%x,", tbd.tbd_status); + } + */ + + printk("|"); +} + +/*------------------------------------------------------------------*/ +/* + * Print status of the command unit of the i82586. + */ +static void wv_cu_show(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned int i; + u16 p; + + printk(KERN_DEBUG + "##### WaveLAN i82586 command unit status: #####\n"); + + printk(KERN_DEBUG); + for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) { + wv_cu_show_one(dev, lp, i, p); + + p += TXBLOCKZ; + if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + p -= NTXBLOCKS * TXBLOCKZ; + } + printk("\n"); +} +#endif /* DEBUG_I82586_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(" state=%lX,", dev->state); + 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:"); + printk(" tx_n_in_use=%d,", lp->tx_n_in_use); + printk(" hacr=0x%x,", lp->hacr); + printk(" rx_head=0x%x,", lp->rx_head); + printk(" rx_last=0x%x,", lp->rx_last); + printk(" tx_first_free=0x%x,", lp->tx_first_free); + printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use); + 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(u8 * 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 are lots of flags for configuring it to your liking. + */ +static inline void wv_init_info(device * dev) +{ + short ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; + psa_t psa; + int i; + + /* Read the parameter storage area */ + psa_read(ioaddr, lp->hacr, 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_I82586_SHOW + wv_cu_show(dev); +#endif + +#ifdef DEBUG_BASIC_SHOW + /* Now, let's go for the basic stuff. */ + printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr); + for (i = 0; i < WAVELAN_ADDR_SIZE; i++) + printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]); + printk(", IRQ %d", dev->irq); + + /* 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(ioaddr, 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(ioaddr, 0x00, &freq, 1); + + /* Print frequency */ + printk(", 2.00, %ld", (freq >> 6) + 2400L); + + /* Hack! */ + if (freq & 0x20) + printk(".5"); + } else { + printk(", PC"); + switch (psa.psa_comp_number) { + case PSA_COMP_PC_AT_915: + case PSA_COMP_PC_AT_2400: + printk("-AT"); + break; + case PSA_COMP_PC_MC_915: + case PSA_COMP_PC_MC_2400: + printk("-MC"); + break; + case PSA_COMP_PCMCIA_915: + printk("MCIA"); + break; + default: + printk("?"); + } + printk(", "); + 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 different + * 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 + + /* Are we asking for promiscuous mode, + * or all multicast addresses (we don't have that!) + * or too many multicast addresses for the hardware filter? */ + if ((dev->flags & IFF_PROMISC) || + (dev->flags & IFF_ALLMULTI) || + (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) { + /* + * Enable promiscuous mode: receive all packets. + */ + if (!lp->promiscuous) { + lp->promiscuous = 1; + lp->mc_count = 0; + + wv_82586_reconfig(dev); + + /* Tell the kernel that we are doing a really bad job. */ + dev->flags |= IFF_PROMISC; + } + } else + /* Are there 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 || (dev->mc_count != lp->mc_count)) +#endif + { + lp->promiscuous = 0; + lp->mc_count = dev->mc_count; + + wv_82586_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->mc_count = 0; + + wv_82586_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); + + /* Reconfigure the beast. */ + wv_82586_reconfig(dev); + + return 0; +} +#endif /* SET_MAC_ADDRESS */ + +#ifdef WIRELESS_EXT /* if wireless extensions exist in the kernel */ + +/*------------------------------------------------------------------*/ +/* + * Frequency setting (for hardware capable 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(unsigned long ioaddr, /* 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 */ + /* Theoretically, 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 22 MHz wide, so some of the channels + * will interfere. */ + if ((frequency->e == 0) && (frequency->m < BAND_NUM)) { + /* Get frequency offset. */ + freq = channel_bands[frequency->m] >> 1; + } + + /* Verify that the frequency is allowed. */ + if (freq != 0L) { + u16 table[10]; /* Authorized frequency table */ + + /* Read the frequency table. */ + fee_read(ioaddr, 0x71, 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 to see whether 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 + * and 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(ioaddr, 0x00, area, 16); + + /* Read the DAC. */ + fee_read(ioaddr, 0x60, dac, 2); + + /* Read the new power adjust value. */ + fee_read(ioaddr, 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); + + /* Other parts 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(ioaddr, 0x00, area, 16); + + /* Write the DAC. */ + fee_write(ioaddr, 0x60, dac, 2); + + /* We now should verify here that the writing of the EEPROM went OK. */ + + /* Reread the first area. */ + fee_read(ioaddr, 0x00, area_verify, 16); + + /* Reread the DAC. */ + fee_read(ioaddr, 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 + * synthesizers (from the EEPROM - area 1) + * Note: as the EEPROM is automatically decremented, we set the end + * if the area... */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished. */ + fee_wait(ioaddr, 100, 100); + + /* We must now download the power adjust value (gain) to + * the synthesizers (from the EEPROM - area 7 - DAC). */ + mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61); + mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait for the download to finish. */ + fee_wait(ioaddr, 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(unsigned long ioaddr, /* I/O port of the card */ + iw_freq * list, /* List of frequencies to fill */ + int max) +{ /* Maximum number of frequencies */ + u16 table[10]; /* Authorized frequency table */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ + int i; /* index in the table */ + int c = 0; /* Channel number */ + + /* Read the frequency table. */ + fee_read(ioaddr, 0x71 /* frequency table */ , table, 10); + + /* Check 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))) { + /* Compute approximate channel number */ + while ((((channel_bands[c] >> 1) - 24) < freq) && + (c < NELS(channel_bands))) + c++; + list[i].i = c; /* Set the list index */ + + /* 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 our list, and if they match, get the statistics. + * Sorry, but this function really needs the wireless extensions. + */ +static inline void wl_spy_gather(device * dev, u8 * mac, /* MAC address */ + u8 * stats) +{ /* Statistics to gather */ + net_local *lp = (net_local *) dev->priv; + int i; + + /* Check 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 calculates a histogram of 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, u8 * stats) +{ /* Statistics to gather */ + net_local *lp = (net_local *) dev->priv; + u8 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 for configuration and information. + * It is here that the wireless extensions are treated (iwconfig). + */ +static int wavelan_ioctl(struct net_device *dev, /* device on which the ioctl is applied */ + struct ifreq *rq, /* data passed */ + int cmd) +{ /* ioctl number */ + unsigned long ioaddr = 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 flags; + int ret = 0; + int err = 0; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, + cmd); +#endif + + /* Disable interrupts and save flags. */ + wv_splhi(lp, &flags); + + /* Look what is the request */ + switch (cmd) { + /* --------------- WIRELESS EXTENSIONS --------------- */ + + case SIOCGIWNAME: + strcpy(wrq->u.name, "WaveLAN"); + break; + + case SIOCSIWNWID: + /* Set NWID in WaveLAN. */ + 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; + psa.psa_nwid_select = 0x01; + psa_write(ioaddr, lp->hacr, + (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(ioaddr, + (char *) &m.w.mmw_netw_id_l - + (char *) &m, + (unsigned char *) &m.w.mmw_netw_id_l, 2); + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); + } else { + /* Disable NWID in the psa. */ + psa.psa_nwid_select = 0x00; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_nwid_select - + (char *) &psa, + (unsigned char *) &psa.psa_nwid_select, + 1); + + /* Disable NWID in the mmc (no filtering). */ + mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), + MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + break; + + case SIOCGIWNWID: + /* Read the NWID. */ + psa_read(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 3); + 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 */ + break; + + case SIOCSIWFREQ: + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(ioaddr, &(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(ioaddr, 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(ioaddr, 0x00, &freq, 1); + wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrq->u.freq.e = 1; + } else { + psa_read(ioaddr, lp->hacr, + (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. */ + /* 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; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), + psa.psa_thr_pre_set); + break; + + case SIOCGIWSENS: + /* Read the level threshold. */ + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F; + wrq->u.sens.fixed = 1; + break; + + case SIOCSIWENCODE: + /* Set encryption key */ + if (!mmc_encr(ioaddr)) { + 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 */ + wv_splx(lp, &flags); + err = copy_from_user(psa.psa_encryption_key, + wrq->u.encoding.pointer, + wrq->u.encoding.length); + wv_splhi(lp, &flags); + if (err) { + ret = -EFAULT; + break; + } + + psa.psa_encryption_select = 1; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 8 + 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(ioaddr, 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(ioaddr, lp->hacr, + (char *) &psa.psa_encryption_select - + (char *) &psa, + (unsigned char *) &psa. + psa_encryption_select, 1); + + mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + break; + + case SIOCGIWENCODE: + /* Read the encryption key */ + if (!mmc_encr(ioaddr)) { + ret = -EOPNOTSUPP; + break; + } + + /* only super-user can see encryption key */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + /* Basic checking... */ + if (wrq->u.encoding.pointer != (caddr_t) 0) { + /* Verify the user buffer */ + ret = + verify_area(VERIFY_WRITE, + wrq->u.encoding.pointer, 8); + if (ret) + break; + + psa_read(ioaddr, lp->hacr, + (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(ioaddr); + + /* Copy the key to the user buffer */ + wrq->u.encoding.length = 8; + wv_splx(lp, &flags); + if (copy_to_user(wrq->u.encoding.pointer, + psa.psa_encryption_key, 8)) + ret = -EFAULT; + wv_splhi(lp, &flags); + } + break; + + case SIOCGIWRANGE: + /* basic checking */ + if (wrq->u.data.pointer != (caddr_t) 0) { + struct iw_range range; + + /* Set the length (very important for backward + * compatibility) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know + * about to zero */ + memset(&range, 0, sizeof(range)); + + /* Set the Wireless Extension versions */ + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 9; + + /* Set information in the range struct. */ + range.throughput = 1.6 * 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(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + range.num_channels = 10; + range.num_frequency = + wv_frequency_list(ioaddr, 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; + range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range.avg_qual.level = 30; + range.avg_qual.noise = 8; + + range.num_bitrates = 1; + range.bitrate[0] = 2000000; /* 2 Mb/s */ + + /* Encryption supported ? */ + if (mmc_encr(ioaddr)) { + 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; + } + + /* Copy structure to the user buffer. */ + wv_splx(lp, &flags); + if (copy_to_user(wrq->u.data.pointer, + &range, + sizeof(struct iw_range))) + ret = -EFAULT; + wv_splhi(lp, &flags); + } + 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" }, + }; + + /* Set the number of available ioctls. */ + wrq->u.data.length = 4; + + /* Copy structure to the user buffer. */ + wv_splx(lp, &flags); + if (copy_to_user(wrq->u.data.pointer, + (u8 *) priv, + sizeof(priv))) + ret = -EFAULT; + wv_splhi(lp, &flags); + } + 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; + + /* Are there are addresses to copy? */ + if (lp->spy_number > 0) { + struct sockaddr address[IW_MAX_SPY]; + int i; + + /* Copy addresses to the driver. */ + wv_splx(lp, &flags); + err = copy_from_user(address, + wrq->u.data.pointer, + sizeof(struct sockaddr) + * lp->spy_number); + wv_splhi(lp, &flags); + if (err) { + 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; + + /* Does 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 = AF_UNIX; + } + + /* Copy addresses to the user buffer. */ + wv_splx(lp, &flags); + err = copy_to_user(wrq->u.data.pointer, + address, + sizeof(struct sockaddr) + * lp->spy_number); + + /* Copy stats to the user buffer (just after). */ + err |= copy_to_user(wrq->u.data.pointer + + (sizeof(struct sockaddr) + * lp->spy_number), + lp->spy_stat, + sizeof(iw_qual) * lp->spy_number); + wv_splhi(lp, &flags); + if (err) { + 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 (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + psa.psa_quality_thr = *(wrq->u.name) & 0x0F; + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); + mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), + psa.psa_quality_thr); + break; + + case SIOCGIPQTHR: + psa_read(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + *(wrq->u.name) = psa.psa_quality_thr & 0x0F; + break; + +#ifdef HISTOGRAM + case SIOCSIPHISTO: + /* Verify that the user is root. */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + /* Check the number of intervals. */ + if (wrq->u.data.length > 16) { + ret = -E2BIG; + break; + } + lp->his_number = wrq->u.data.length; + + /* Are there addresses to copy? */ + if (lp->his_number > 0) { + /* Copy interval ranges to the driver */ + wv_splx(lp, &flags); + err = copy_from_user(lp->his_range, + wrq->u.data.pointer, + sizeof(char) * lp->his_number); + wv_splhi(lp, &flags); + if (err) { + 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. */ + wv_splx(lp, &flags); + if (copy_to_user(wrq->u.data.pointer, + lp->his_sum, + sizeof(long) * lp->his_number); + ret = -EFAULT; + wv_splhi(lp, &flags); + + } /* if(pointer != NULL) */ + break; +#endif /* HISTOGRAM */ + + /* ------------------- OTHER IOCTL ------------------- */ + + default: + ret = -EOPNOTSUPP; + } /* switch (cmd) */ + + /* Enable interrupts and restore flags. */ + wv_splx(lp, &flags); + +#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) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; + mmr_t m; + iw_stats *wstats; + unsigned long flags; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", + dev->name); +#endif + + /* Check */ + if (lp == (net_local *) NULL) + return (iw_stats *) NULL; + + /* Disable interrupts and save flags. */ + wv_splhi(lp, &flags); + + wstats = &lp->wstats; + + /* Get data from the mmc. */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + + mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); + mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, + 2); + mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, + 4); + + mmc_out(ioaddr, 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; + + /* Enable interrupts and restore flags. */ + wv_splx(lp, &flags); + +#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 deals with receiving the packets. + * The interrupt handler gets an interrupt when a packet has been + * successfully received and calls this part. + */ + +/*------------------------------------------------------------------*/ +/* + * This routine does the actual copying 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, u16 buf_off, int sksize) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + struct sk_buff *skb; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", + dev->name, buf_off, sksize); +#endif + + /* Allocate buffer for the data */ + if ((skb = dev_alloc_skb(sksize)) == (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++; + return; + } + + skb->dev = dev; + + /* Copy the packet to the buffer. */ + obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); + skb->protocol = eth_type_trans(skb, dev); + +#ifdef DEBUG_RX_INFO + wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); +#endif /* DEBUG_RX_INFO */ + + /* Statistics-gathering and associated stuff. + * It seem a bit messy with all the define, but it's really simple... */ +#if defined(WIRELESS_SPY) || defined(HISTOGRAM) + if ( +#ifdef WIRELESS_SPY + (lp->spy_number > 0) || +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + (lp->his_number > 0) || +#endif /* HISTOGRAM */ + 0) { + u8 stats[3]; /* signal level, noise level, signal quality */ + + /* Read signal level, silence level and signal quality bytes. */ + /* Note: in the PCMCIA hardware, these are part of the frame. It seems + * that for the ISA hardware, it's nowhere to be found in the frame, + * so I'm obliged to do this (it has a side effect on /proc/net/wireless). + * Any ideas? + */ + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3); + mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +#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 + + /* Spying stuff */ +#ifdef WIRELESS_SPY + wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, + stats); +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + wl_his_gather(dev, stats); +#endif /* HISTOGRAM */ + } +#endif /* defined(WIRELESS_SPY) || defined(HISTOGRAM) */ + + /* + * Hand the packet to the network module. + */ + netif_rx(skb); + + /* Keep statistics up to date */ + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += sksize; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Transfer as many packets as we can + * from the device RAM. + * (called in wavelan_interrupt()). + * Note : the spinlock is already grabbed for us. + */ +static inline void wv_receive(device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; + fd_t fd; + rbd_t rbd; + int nreaped = 0; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name); +#endif + + /* Loop on each received packet. */ + for (;;) { + obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, + sizeof(fd)); + + /* Note about the status : + * It start up to be 0 (the value we set). Then, when the RU + * grab the buffer to prepare for reception, it sets the + * FD_STATUS_B flag. When the RU has finished receiving the + * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate + * completion and set the other flags to indicate the eventual + * errors. FD_STATUS_OK indicates that the reception was OK. + */ + + /* If the current frame is not complete, we have reached the end. */ + if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) + break; /* This is how we exit the loop. */ + + nreaped++; + + /* Check whether frame was correctly received. */ + if ((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) { + /* Does the frame contain a pointer to the data? Let's check. */ + if (fd.fd_rbd_offset != I82586NULL) { + /* Read the receive buffer descriptor */ + obram_read(ioaddr, fd.fd_rbd_offset, + (unsigned char *) &rbd, + sizeof(rbd)); + +#ifdef DEBUG_RX_ERROR + if ((rbd.rbd_status & RBD_STATUS_EOF) != + RBD_STATUS_EOF) printk(KERN_INFO + "%s: wv_receive(): missing EOF flag.\n", + dev->name); + + if ((rbd.rbd_status & RBD_STATUS_F) != + RBD_STATUS_F) printk(KERN_INFO + "%s: wv_receive(): missing F flag.\n", + dev->name); +#endif /* DEBUG_RX_ERROR */ + + /* Read the packet and transmit to Linux */ + wv_packet_read(dev, rbd.rbd_bufl, + rbd. + rbd_status & + RBD_STATUS_ACNT); + } +#ifdef DEBUG_RX_ERROR + else /* if frame has no data */ + printk(KERN_INFO + "%s: wv_receive(): frame has no data.\n", + dev->name); +#endif + } else { /* If reception was no successful */ + + lp->stats.rx_errors++; + +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG + "%s: wv_receive(): frame not received successfully (%X).\n", + dev->name, fd.fd_status); +#endif + +#ifdef DEBUG_RX_ERROR + if ((fd.fd_status & FD_STATUS_S6) != 0) + printk(KERN_INFO + "%s: wv_receive(): no EOF flag.\n", + dev->name); +#endif + + if ((fd.fd_status & FD_STATUS_S7) != 0) { + lp->stats.rx_length_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): frame too short.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S8) != 0) { + lp->stats.rx_over_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): rx DMA overrun.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S9) != 0) { + lp->stats.rx_fifo_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): ran out of resources.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S10) != 0) { + lp->stats.rx_frame_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): alignment error.\n", + dev->name); +#endif + } + + if ((fd.fd_status & FD_STATUS_S11) != 0) { + lp->stats.rx_crc_errors++; +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG + "%s: wv_receive(): CRC error.\n", + dev->name); +#endif + } + } + + fd.fd_status = 0; + obram_write(ioaddr, fdoff(lp->rx_head, fd_status), + (unsigned char *) &fd.fd_status, + sizeof(fd.fd_status)); + + fd.fd_command = FD_COMMAND_EL; + obram_write(ioaddr, fdoff(lp->rx_head, fd_command), + (unsigned char *) &fd.fd_command, + sizeof(fd.fd_command)); + + fd.fd_command = 0; + obram_write(ioaddr, fdoff(lp->rx_last, fd_command), + (unsigned char *) &fd.fd_command, + sizeof(fd.fd_command)); + + lp->rx_last = lp->rx_head; + lp->rx_head = fd.fd_link_offset; + } /* for(;;) -> loop on all frames */ + +#ifdef DEBUG_RX_INFO + if (nreaped > 1) + printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", + dev->name, nreaped); +#endif +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name); +#endif +} + +/*********************** PACKET TRANSMISSION ***********************/ +/* + * This part deals with sending packets through the WaveLAN. + * + */ + +/*------------------------------------------------------------------*/ +/* + * This routine fills in the appropriate registers and memory + * locations on the WaveLAN card and starts the card off on + * the transmit. + * + * The principle: + * Each block contains a transmit command, a NOP command, + * a transmit block descriptor and a buffer. + * The CU read the transmit block which point to the tbd, + * read the tbd and the content of the buffer. + * When it has finish with it, it goes to the next command + * which in our case is the NOP. The NOP points on itself, + * so the CU stop here. + * When we add the next block, we modify the previous nop + * to make it point on the new tx command. + * Simple, isn't it ? + * + * (called in wavelan_packet_xmit()) + */ +static inline int wv_packet_write(device * dev, void *buf, short length) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + unsigned short txblock; + unsigned short txpred; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short buf_addr; + ac_tx_t tx; + ac_nop_t nop; + tbd_t tbd; + int clen = length; + unsigned long flags; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, + length); +#endif + + /* Do we need some padding? */ + if (clen < ETH_ZLEN) + clen = ETH_ZLEN; + + wv_splhi(lp, &flags); + + /* Check nothing bad has happened */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { +#ifdef DEBUG_TX_ERROR + printk(KERN_INFO "%s: wv_packet_write(): Tx queue full.\n", + dev->name); +#endif + wv_splx(lp, &flags); + return 1; + } + + /* Calculate addresses of next block and previous block. */ + txblock = lp->tx_first_free; + txpred = txblock - TXBLOCKZ; + if (txpred < OFFSET_CU) + txpred += NTXBLOCKS * TXBLOCKZ; + lp->tx_first_free += TXBLOCKZ; + if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + + lp->tx_n_in_use++; + + /* Calculate addresses of the different parts of the block. */ + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + buf_addr = tbd_addr + sizeof(tbd); + + /* + * Transmit command + */ + tx.tx_h.ac_status = 0; + obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), + (unsigned char *) &tx.tx_h.ac_status, + sizeof(tx.tx_h.ac_status)); + + /* + * NOP command + */ + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* + * Transmit buffer descriptor + */ + tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen); + tbd.tbd_next_bd_offset = I82586NULL; + tbd.tbd_bufl = buf_addr; + tbd.tbd_bufh = 0; + obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd)); + + /* + * Data + */ + obram_write(ioaddr, buf_addr, buf, length); + + /* + * Overwrite the predecessor NOP link + * so that it points to this txblock. + */ + nop_addr = txpred + sizeof(tx); + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = txblock; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Keep stats up to date. */ + lp->stats.tx_bytes += length; + + if (lp->tx_first_in_use == I82586NULL) + lp->tx_first_in_use = txblock; + + if (lp->tx_n_in_use < NTXBLOCKS - 1) + netif_wake_queue(dev); + + wv_splx(lp, &flags); + +#ifdef DEBUG_TX_INFO + wv_packet_info((u8 *) 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 + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called when we want to send a packet (NET3 callback) + * In this routine, we check if 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; + unsigned long flags; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, + (unsigned) skb); +#endif + + /* + * Block a timer-based transmit from overlapping. + * In other words, prevent reentering this routine. + */ + netif_stop_queue(dev); + + /* If somebody has asked to reconfigure the controller, + * we can do it now. + */ + if (lp->reconfig_82586) { + wv_splhi(lp, &flags); + wv_82586_config(dev); + wv_splx(lp, &flags); + /* Check that we can continue */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) + return 1; + } +#ifdef DEBUG_TX_ERROR + if (skb->next) + printk(KERN_INFO "skb has next\n"); +#endif + + /* Write packet on the card */ + if(wv_packet_write(dev, skb->data, skb->len)) + return 1; /* We failed */ + + 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 does the real job of starting and configuring the hardware. + */ + +/*--------------------------------------------------------------------*/ +/* + * Routine to initialize the Modem Management Controller. + * (called by wv_hw_reset()) + */ +static inline int wv_mmc_init(device * dev) +{ + unsigned long ioaddr = dev->base_addr; + net_local *lp = (net_local *) dev->priv; + psa_t psa; + mmw_t m; + int configured; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); +#endif + + /* Read the parameter storage area. */ + psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); + +#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 later (with iwconfig). */ + psa.psa_nwid[0] = 0; + psa.psa_nwid[1] = 0; + + /* no NWID checking since NWID is not set */ + 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(ioaddr, lp->hacr, + (char *) psa.psa_nwid - (char *) &psa, + (unsigned char *) psa.psa_nwid, 4); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_thr_pre_set - (char *) &psa, + (unsigned char *) &psa.psa_thr_pre_set, 1); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_quality_thr - (char *) &psa, + (unsigned char *) &psa.psa_quality_thr, 1); + psa_write(ioaddr, lp->hacr, + (char *) &psa.psa_conf_status - (char *) &psa, + (unsigned char *) &psa.psa_conf_status, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, lp->hacr); +#endif + } + + /* 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_freeze = 0; + 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_decay_prm = 0; + m.mmw_decay_updat_prm = 0; + + /* Write all info to MMC. */ + mmc_write(ioaddr, 0, (u8 *) & m, sizeof(m)); + + /* The following code starts 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 up a bit and added 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 verifies that it is able to read a sensible + * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). + * My test is more crude but does work. */ + if (!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) { + /* We must download the frequency parameters to the + * synthesizers (from the EEPROM - area 1) + * Note: as the EEPROM is automatically 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(ioaddr, (char *) &m.mmw_fee_ctrl - (char *) &m, + (unsigned char *) &m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished. */ + fee_wait(ioaddr, 100, 100); + +#ifdef DEBUG_CONFIG_INFO + /* The frequency was in the last word downloaded. */ + mmc_read(ioaddr, (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 synthesizers (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(ioaddr, (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 0; +} + +/*------------------------------------------------------------------*/ +/* + * Construct the fd and rbd structures. + * Start the receive unit. + * (called by wv_hw_reset()) + */ +static inline int wv_ru_start(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 scb_cs; + fd_t fd; + rbd_t rbd; + u16 rx; + u16 rx_next; + int i; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); +#endif + + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY) + return 0; + + lp->rx_head = OFFSET_RU; + + for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) { + rx_next = + (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ; + + fd.fd_status = 0; + fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0; + fd.fd_link_offset = rx_next; + fd.fd_rbd_offset = rx + sizeof(fd); + obram_write(ioaddr, rx, (unsigned char *) &fd, sizeof(fd)); + + rbd.rbd_status = 0; + rbd.rbd_next_rbd_offset = I82586NULL; + rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd); + rbd.rbd_bufh = 0; + rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ); + obram_write(ioaddr, rx + sizeof(fd), + (unsigned char *) &rbd, sizeof(rbd)); + + lp->rx_last = rx; + } + + obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), + (unsigned char *) &lp->rx_head, sizeof(lp->rx_head)); + + scb_cs = SCB_CMD_RUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_ru_start(): board not accepting command.\n", + dev->name); +#endif + return -1; + } +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Initialise the transmit blocks. + * Start the command unit executing the NOP + * self-loop of the first transmit block. + * + * Here we create the list of send buffers used to transmit packets + * between the PC and the command unit. For each buffer, we create a + * buffer descriptor (pointing on the buffer), a transmit command + * (pointing to the buffer descriptor) and a NOP command. + * The transmit command is linked to the NOP, and the NOP to itself. + * When we will have finished executing the transmit command, we will + * then loop on the NOP. By releasing the NOP link to a new command, + * we may send another buffer. + * + * (called by wv_hw_reset()) + */ +static inline int wv_cu_start(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + int i; + u16 txblock; + u16 first_nop; + u16 scb_cs; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name); +#endif + + lp->tx_first_free = OFFSET_CU; + lp->tx_first_in_use = I82586NULL; + + for (i = 0, txblock = OFFSET_CU; + i < NTXBLOCKS; i++, txblock += TXBLOCKZ) { + ac_tx_t tx; + ac_nop_t nop; + tbd_t tbd; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short buf_addr; + + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + buf_addr = tbd_addr + sizeof(tbd); + + tx.tx_h.ac_status = 0; + tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I; + tx.tx_h.ac_link = nop_addr; + tx.tx_tbd_offset = tbd_addr; + obram_write(ioaddr, tx_addr, (unsigned char *) &tx, + sizeof(tx)); + + nop.nop_h.ac_status = 0; + nop.nop_h.ac_command = acmd_nop; + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, nop_addr, (unsigned char *) &nop, + sizeof(nop)); + + tbd.tbd_status = TBD_STATUS_EOF; + tbd.tbd_next_bd_offset = I82586NULL; + tbd.tbd_bufl = buf_addr; + tbd.tbd_bufh = 0; + obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, + sizeof(tbd)); + } + + first_nop = + OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t); + obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), + (unsigned char *) &first_nop, sizeof(first_nop)); + + scb_cs = SCB_CMD_CUC_GO; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + + set_chan_attn(ioaddr, lp->hacr); + + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cs, sizeof(scb_cs)); + if (scb_cs == 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_cu_start(): board not accepting command.\n", + dev->name); +#endif + return -1; + } + + lp->tx_n_in_use = 0; + netif_start_queue(dev); +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard configuration of the WaveLAN + * controller (i82586). + * + * It initialises the scp, iscp and scb structure + * The first two are just pointers to the next. + * The last one is used for basic configuration and for basic + * communication (interrupt status). + * + * (called by wv_hw_reset()) + */ +static inline int wv_82586_start(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + scp_t scp; /* system configuration pointer */ + iscp_t iscp; /* intermediate scp */ + scb_t scb; /* system control block */ + ach_t cb; /* Action command header */ + u8 zeroes[512]; + int i; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name); +#endif + + /* + * Clear the onboard RAM. + */ + memset(&zeroes[0], 0x00, sizeof(zeroes)); + for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes)) + obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes)); + + /* + * Construct the command unit structures: + * scp, iscp, scb, cb. + */ + memset(&scp, 0x00, sizeof(scp)); + scp.scp_sysbus = SCP_SY_16BBUS; + scp.scp_iscpl = OFFSET_ISCP; + obram_write(ioaddr, OFFSET_SCP, (unsigned char *) &scp, + sizeof(scp)); + + memset(&iscp, 0x00, sizeof(iscp)); + iscp.iscp_busy = 1; + iscp.iscp_offset = OFFSET_SCB; + obram_write(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, + sizeof(iscp)); + + /* Our first command is to reset the i82586. */ + memset(&scb, 0x00, sizeof(scb)); + scb.scb_command = SCB_CMD_RESET; + scb.scb_cbl_offset = OFFSET_CU; + scb.scb_rfa_offset = OFFSET_RU; + obram_write(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + set_chan_attn(ioaddr, lp->hacr); + + /* Wait for command to finish. */ + for (i = 1000; i > 0; i--) { + obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, + sizeof(iscp)); + + if (iscp.iscp_busy == (unsigned short) 0) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): iscp_busy timeout.\n", + dev->name); +#endif + return -1; + } + + /* Check command completion. */ + for (i = 15; i > 0; i--) { + obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, + sizeof(scb)); + + if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA)) + break; + + udelay(10); + } + + if (i <= 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", + dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); +#endif + return -1; + } + + wv_ack(dev); + + /* Set the action command header. */ + memset(&cb, 0x00, sizeof(cb)); + cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose); + cb.ac_link = OFFSET_CU; + obram_write(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); + + if (wv_synchronous_cmd(dev, "diag()") == -1) + return -1; + + obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb, sizeof(cb)); + if (cb.ac_status & AC_SFLD_FAIL) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wv_82586_start(): i82586 Self Test failed.\n", + dev->name); +#endif + return -1; + } +#ifdef DEBUG_I82586_SHOW + wv_scb_show(ioaddr); +#endif + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard configuration of the WaveLAN + * controller (i82586). + * + * This routine is a violent hack. We use the first free transmit block + * to make our configuration. In the buffer area, we create the three + * configuration commands (linked). We make the previous NOP point to + * the beginning of the buffer instead of the tx command. After, we go + * as usual to the NOP command. + * Note that only the last command (mc_set) will generate an interrupt. + * + * (called by wv_hw_reset(), wv_82586_reconfig(), wavelan_packet_xmit()) + */ +static void wv_82586_config(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + unsigned short txblock; + unsigned short txpred; + unsigned short tx_addr; + unsigned short nop_addr; + unsigned short tbd_addr; + unsigned short cfg_addr; + unsigned short ias_addr; + unsigned short mcs_addr; + ac_tx_t tx; + ac_nop_t nop; + ac_cfg_t cfg; /* Configure action */ + ac_ias_t ias; /* IA-setup action */ + ac_mcs_t mcs; /* Multicast setup */ + struct dev_mc_list *dmi; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); +#endif + + /* Check nothing bad has happened */ + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "%s: wv_82586_config(): Tx queue full.\n", + dev->name); +#endif + return; + } + + /* Calculate addresses of next block and previous block. */ + txblock = lp->tx_first_free; + txpred = txblock - TXBLOCKZ; + if (txpred < OFFSET_CU) + txpred += NTXBLOCKS * TXBLOCKZ; + lp->tx_first_free += TXBLOCKZ; + if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) + lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + + lp->tx_n_in_use++; + + /* Calculate addresses of the different parts of the block. */ + tx_addr = txblock; + nop_addr = tx_addr + sizeof(tx); + tbd_addr = nop_addr + sizeof(nop); + cfg_addr = tbd_addr + sizeof(tbd_t); /* beginning of the buffer */ + ias_addr = cfg_addr + sizeof(cfg); + mcs_addr = ias_addr + sizeof(ias); + + /* + * Transmit command + */ + tx.tx_h.ac_status = 0xFFFF; /* Fake completion value */ + obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), + (unsigned char *) &tx.tx_h.ac_status, + sizeof(tx.tx_h.ac_status)); + + /* + * NOP command + */ + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = nop_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Create a configure action. */ + memset(&cfg, 0x00, sizeof(cfg)); + + /* + * For Linux we invert AC_CFG_ALOC() so as to conform + * to the way that net packets reach us from above. + * (See also ac_tx_t.) + * + * Updated from Wavelan Manual WCIN085B + */ + cfg.cfg_byte_cnt = + AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); + cfg.cfg_fifolim = AC_CFG_FIFOLIM(4); + cfg.cfg_byte8 = AC_CFG_SAV_BF(1) | AC_CFG_SRDY(0); + cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | + AC_CFG_ILPBCK(0) | + AC_CFG_PRELEN(AC_CFG_PLEN_2) | + AC_CFG_ALOC(1) | AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); + cfg.cfg_byte10 = AC_CFG_BOFMET(1) | + AC_CFG_ACR(6) | AC_CFG_LINPRIO(0); + cfg.cfg_ifs = 0x20; + cfg.cfg_slotl = 0x0C; + cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | AC_CFG_SLTTMHI(0); + cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | + AC_CFG_BTSTF(0) | + AC_CFG_CRC16(0) | + AC_CFG_NCRC(0) | + AC_CFG_TNCRS(1) | + AC_CFG_MANCH(0) | + AC_CFG_BCDIS(0) | AC_CFG_PRM(lp->promiscuous); + cfg.cfg_byte15 = AC_CFG_ICDS(0) | + AC_CFG_CDTF(0) | AC_CFG_ICSS(0) | AC_CFG_CSTF(0); +/* + cfg.cfg_min_frm_len = AC_CFG_MNFRM(64); +*/ + cfg.cfg_min_frm_len = AC_CFG_MNFRM(8); + + cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure); + cfg.cfg_h.ac_link = ias_addr; + obram_write(ioaddr, cfg_addr, (unsigned char *) &cfg, sizeof(cfg)); + + /* Set up the MAC address */ + memset(&ias, 0x00, sizeof(ias)); + ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup); + ias.ias_h.ac_link = mcs_addr; + memcpy(&ias.ias_addr[0], (unsigned char *) &dev->dev_addr[0], + sizeof(ias.ias_addr)); + obram_write(ioaddr, ias_addr, (unsigned char *) &ias, sizeof(ias)); + + /* Initialize adapter's Ethernet multicast addresses */ + memset(&mcs, 0x00, sizeof(mcs)); + mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup); + mcs.mcs_h.ac_link = nop_addr; + mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count; + obram_write(ioaddr, mcs_addr, (unsigned char *) &mcs, sizeof(mcs)); + + /* Any address to set? */ + if (lp->mc_count) { + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + outsw(PIOP1(ioaddr), (u16 *) dmi->dmi_addr, + WAVELAN_ADDR_SIZE >> 1); + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wv_82586_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 + } + + /* + * Overwrite the predecessor NOP link + * so that it points to the configure action. + */ + nop_addr = txpred + sizeof(tx); + nop.nop_h.ac_status = 0; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), + (unsigned char *) &nop.nop_h.ac_status, + sizeof(nop.nop_h.ac_status)); + nop.nop_h.ac_link = cfg_addr; + obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), + (unsigned char *) &nop.nop_h.ac_link, + sizeof(nop.nop_h.ac_link)); + + /* Job done, clear the flag */ + lp->reconfig_82586 = 0; + + if (lp->tx_first_in_use == I82586NULL) + lp->tx_first_in_use = txblock; + + if (lp->tx_n_in_use == (NTXBLOCKS - 1)) + netif_stop_queue(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine, called by wavelan_close(), gracefully stops the + * WaveLAN controller (i82586). + * (called by wavelan_close()) + */ +static inline void wv_82586_stop(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 scb_cmd; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name); +#endif + + /* Suspend both command unit and receive unit. */ + scb_cmd = + (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & + SCB_CMD_RUC_SUS); + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &scb_cmd, sizeof(scb_cmd)); + set_chan_attn(ioaddr, lp->hacr); + + /* No more interrupts */ + wv_ints_off(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Totally reset the WaveLAN and restart it. + * Performs the following actions: + * 1. A power reset (reset DMA) + * 2. Initialize the radio modem (using wv_mmc_init) + * 3. Reset & Configure LAN controller (using wv_82586_start) + * 4. Start the LAN controller's command unit + * 5. Start the LAN controller's receive unit + * (called by wavelan_interrupt(), wavelan_watchdog() & wavelan_open()) + */ +static int wv_hw_reset(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long ioaddr = dev->base_addr; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Increase the number of resets done. */ + lp->nresets++; + + wv_hacr_reset(ioaddr); + lp->hacr = HACR_DEFAULT; + + if ((wv_mmc_init(dev) < 0) || (wv_82586_start(dev) < 0)) + return -1; + + /* Enable the card to send interrupts. */ + wv_ints_on(dev); + + /* Start card functions */ + if (wv_cu_start(dev) < 0) + return -1; + + /* Setup the controller and parameters */ + wv_82586_config(dev); + + /* Finish configuration with the receive unit */ + if (wv_ru_start(dev) < 0) + return -1; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Check if there is a WaveLAN at the specific base address. + * As a side effect, this reads the MAC address. + * (called in wavelan_probe() and init_module()) + */ +static int wv_check_ioaddr(unsigned long ioaddr, u8 * mac) +{ + int i; /* Loop counter */ + + /* Check if the base address if available. */ + if (check_region(ioaddr, sizeof(ha_t))) + return -EADDRINUSE; /* ioaddr already used */ + + /* Reset host interface */ + wv_hacr_reset(ioaddr); + + /* Read the MAC address from the parameter storage area. */ + psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), + mac, 6); + + /* + * Check the first three octets of the address for the manufacturer's code. + * Note: if this can't find your WaveLAN card, you've got a + * non-NCR/AT&T/Lucent ISA card. See wavelan.p.h for detail on + * how to configure your card. + */ + for (i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++) + if ((mac[0] == MAC_ADDRESSES[i][0]) && + (mac[1] == MAC_ADDRESSES[i][1]) && + (mac[2] == MAC_ADDRESSES[i][2])) + return 0; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_WARNING + "WaveLAN (0x%3X): your MAC address might be %02X:%02X:%02X.\n", + ioaddr, mac[0], mac[1], mac[2]); +#endif + return -ENODEV; +} + +/************************ INTERRUPT HANDLING ************************/ + +/* + * This function is the interrupt handler for the WaveLAN card. This + * routine will be called whenever: + */ +static void wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + device *dev; + unsigned long ioaddr; + net_local *lp; + u16 hasr; + u16 status; + u16 ack_cmd; + + dev = dev_id; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); +#endif + + lp = (net_local *) dev->priv; + ioaddr = dev->base_addr; + +#ifdef DEBUG_INTERRUPT_INFO + /* Check state of our spinlock */ + if(spin_is_locked(&lp->spinlock)) + printk(KERN_DEBUG + "%s: wavelan_interrupt(): spinlock is already locked !!!\n", + dev->name); +#endif + + /* Prevent reentrancy. We need to do that because we may have + * multiple interrupt handler running concurrently. + * It is safe because wv_splhi() disables interrupts before acquiring + * the spinlock. */ + spin_lock(&lp->spinlock); + + /* Check modem interrupt */ + if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) { + u8 dce_status; + + /* + * Interrupt from the modem management controller. + * This will clear it -- ignored for now. + */ + mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, + sizeof(dce_status)); +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", + dev->name, dce_status); +#endif + } + + /* Check if not controller interrupt */ + if ((hasr & HASR_82586_INTR) == 0) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): interrupt not coming from i82586\n", + dev->name); +#endif + spin_unlock (&lp->spinlock); + return; + } + + /* Read interrupt data. */ + obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), + (unsigned char *) &status, sizeof(status)); + + /* + * Acknowledge the interrupt(s). + */ + ack_cmd = status & SCB_ST_INT; + obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), + (unsigned char *) &ack_cmd, sizeof(ack_cmd)); + set_chan_attn(ioaddr, lp->hacr); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n", + dev->name, status); +#endif + + /* Command completed. */ + if ((status & SCB_ST_CX) == SCB_ST_CX) { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_interrupt(): command completed.\n", + dev->name); +#endif + wv_complete(dev, ioaddr, lp); + } + + /* Frame received. */ + if ((status & SCB_ST_FR) == SCB_ST_FR) { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_interrupt(): received packet.\n", + dev->name); +#endif + wv_receive(dev); + } + + /* Check the state of the command unit. */ + if (((status & SCB_ST_CNA) == SCB_ST_CNA) || + (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && + (netif_running(dev)))) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): CU inactive -- restarting\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* Check the state of the command unit. */ + if (((status & SCB_ST_RNR) == SCB_ST_RNR) || + (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && + (netif_running(dev)))) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): RU not ready -- restarting\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* Release spinlock */ + spin_unlock (&lp->spinlock); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Watchdog: when we start a transmission, a timer is set for us in the + * kernel. If the transmission completes, this timer is disabled. If + * the timer expires, we are called and we try to unlock the hardware. + */ +static void wavelan_watchdog(device * dev) +{ + net_local * lp = (net_local *)dev->priv; + u_long ioaddr = dev->base_addr; + unsigned long flags; + unsigned int nreaped; + +#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 + + /* Check that we came here for something */ + if (lp->tx_n_in_use <= 0) { + return; + } + + wv_splhi(lp, &flags); + + /* Try to see if some buffers are not free (in case we missed + * an interrupt */ + nreaped = wv_complete(dev, ioaddr, lp); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG + "%s: wavelan_watchdog(): %d reaped, %d remain.\n", + dev->name, nreaped, lp->tx_n_in_use); +#endif + +#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_I82586_SHOW + wv_cu_show(dev); +#endif + + /* If no buffer has been freed */ + if (nreaped == 0) { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_watchdog(): cleanup failed, trying reset\n", + dev->name); +#endif + wv_hw_reset(dev); + } + + /* At this point, we should have some free Tx buffer ;-) */ + if (lp->tx_n_in_use < NTXBLOCKS - 1) + netif_wake_queue(dev); + + wv_splx(lp, &flags); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); +#endif +} + +/********************* CONFIGURATION CALLBACKS *********************/ +/* + * Here are the functions called by the Linux networking code (NET3) + * for initialization, configuration and deinstallations of the + * WaveLAN ISA hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Configure and start up the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "opens" the device. + */ +static int wavelan_open(device * dev) +{ + net_local * lp = (net_local *)dev->priv; + unsigned long flags; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Check irq */ + if (dev->irq == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", + dev->name); +#endif + return -ENXIO; + } + + if (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0) + { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", + dev->name); +#endif + return -EAGAIN; + } + + wv_splhi(lp, &flags); + + if (wv_hw_reset(dev) != -1) { + netif_start_queue(dev); + } else { + free_irq(dev->irq, dev); +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_open(): impossible to start the card\n", + dev->name); +#endif + wv_splx(lp, &flags); + return -EAGAIN; + } + wv_splx(lp, &flags); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Shut down the WaveLAN ISA card. + * Called by NET3 when it "closes" the device. + */ +static int wavelan_close(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + unsigned long flags; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + netif_stop_queue(dev); + + /* + * Flush the Tx and disable Rx. + */ + wv_splhi(lp, &flags); + wv_82586_stop(dev); + wv_splx(lp, &flags); + + free_irq(dev->irq, dev); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Probe an I/O address, and if the WaveLAN is there configure the + * device structure + * (called by wavelan_probe() and via init_module()). + */ +static int __init wavelan_config(device * dev) +{ + unsigned long ioaddr = dev->base_addr; + u8 irq_mask; + int irq; + net_local *lp; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%lx)\n", + dev->name, (unsigned int) dev, ioaddr); +#endif + + /* Check IRQ argument on command line. */ + if (dev->irq != 0) { + irq_mask = wv_irq_to_psa(dev->irq); + + if (irq_mask == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "%s: wavelan_config(): invalid IRQ %d ignored.\n", + dev->name, dev->irq); +#endif + dev->irq = 0; + } else { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: wavelan_config(): changing IRQ to %d\n", + dev->name, dev->irq); +#endif + psa_write(ioaddr, HACR_DEFAULT, + psaoff(0, psa_int_req_no), &irq_mask, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev, ioaddr, HACR_DEFAULT); + wv_hacr_reset(ioaddr); + } + } + + psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), + &irq_mask, 1); + if ((irq = wv_psa_to_irq(irq_mask)) == -1) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO + "%s: wavelan_config(): could not wavelan_map_irq(%d).\n", + dev->name, irq_mask); +#endif + return -EAGAIN; + } + + dev->irq = irq; + + if (!request_region(ioaddr, sizeof(ha_t), "wavelan")) + return -EBUSY; + + dev->mem_start = 0x0000; + dev->mem_end = 0x0000; + dev->if_port = 0; + + /* Initialize device structures */ + dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL); + if (dev->priv == NULL) { + release_region(ioaddr, sizeof(ha_t)); + return -ENOMEM; + } + memset(dev->priv, 0x00, sizeof(net_local)); + lp = (net_local *) dev->priv; + + /* Back link to the device structure. */ + lp->dev = dev; + /* Add the device at the beginning of the linked list. */ + lp->next = wavelan_list; + wavelan_list = lp; + + lp->hacr = HACR_DEFAULT; + + /* Multicast stuff */ + lp->promiscuous = 0; + lp->mc_count = 0; + + /* Init spinlock */ + spin_lock_init(&lp->spinlock); + + /* + * Fill in the fields of the device structure + * with generic Ethernet values. + */ + ether_setup(dev); + + SET_MODULE_OWNER(dev); + 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; + dev->tx_timeout = &wavelan_watchdog; + dev->watchdog_timeo = WATCHDOG_JIFFIES; +#ifdef SET_MAC_ADDRESS + dev->set_mac_address = &wavelan_set_mac_address; +#endif /* SET_MAC_ADDRESS */ + +#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */ + dev->do_ioctl = wavelan_ioctl; + dev->get_wireless_stats = wavelan_get_wireless_stats; +#endif + + dev->mtu = WAVELAN_MTU; + + /* Display nice information. */ + wv_init_info(dev); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Check for a network adaptor of this type. Return '0' iff one + * exists. There seem to be different interpretations of + * the initial value of dev->base_addr. + * We follow the example in drivers/net/ne.c. + * (called in "Space.c") + */ +int __init wavelan_probe(device * dev) +{ + short base_addr; + mac_addr mac; /* MAC address (check existence of WaveLAN) */ + int i; + int r; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG + "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", + dev->name, (unsigned int) dev, + (unsigned int) dev->base_addr); +#endif + +#ifdef STRUCT_CHECK + if (wv_struct_check() != (char *) NULL) { + printk(KERN_WARNING + "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n", + dev->name, wv_struct_check()); + return -ENODEV; + } +#endif /* STRUCT_CHECK */ + + /* Check the value of the command line parameter for base address. */ + base_addr = dev->base_addr; + + /* Don't probe at all. */ + if (base_addr < 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "%s: wavelan_probe(): invalid base address\n", + dev->name); +#endif + return -ENXIO; + } + + /* Check a single specified location. */ + if (base_addr > 0x100) { + /* Check if there is something at this base address */ + if ((r = wv_check_ioaddr(base_addr, mac)) == 0) { + memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ + r = wavelan_config(dev); + } +#ifdef DEBUG_CONFIG_INFO + if (r != 0) + printk(KERN_DEBUG + "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n", + dev->name, base_addr); +#endif + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); +#endif + return r; + } + + /* Scan all possible addresses of the WaveLAN hardware. */ + for (i = 0; i < NELS(iobase); i++) { + /* Check whether there is something at this base address. */ + if (wv_check_ioaddr(iobase[i], mac) == 0) { + dev->base_addr = iobase[i]; /* Copy base address. */ + memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ + if (wavelan_config(dev) == 0) { +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG + "%s: <-wavelan_probe()\n", + dev->name); +#endif + return 0; + } + } + } + + /* We may have touched base_addr. Another driver may not like it. */ + dev->base_addr = base_addr; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n", + dev->name); +#endif + + return -ENODEV; +} + +/****************************** MODULE ******************************/ +/* + * Module entry point: insertion and removal + */ + +#ifdef MODULE +/*------------------------------------------------------------------*/ +/* + * Insertion of the module + * I'm now quite proud of the multi-device support. + */ +int init_module(void) +{ + mac_addr mac; /* MAC address (check WaveLAN existence) */ + int ret = -EIO; /* Return error if no cards found */ + int i; + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> init_module()\n"); +#endif + + /* If probing is asked */ + if (io[0] == 0) { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_WARNING + "WaveLAN init_module(): doing device probing (bad !)\n"); + printk(KERN_WARNING + "Specify base addresses while loading module to correct the problem\n"); +#endif + + /* Copy the basic set of address to be probed. */ + for (i = 0; i < NELS(iobase); i++) + io[i] = iobase[i]; + } + + + /* Loop on all possible base addresses. */ + i = -1; + while ((io[++i] != 0) && (i < NELS(io))) { + /* Check if there is something at this base address. */ + if (wv_check_ioaddr(io[i], mac) == 0) { + device *dev; + + /* Create device and set basic arguments. */ + dev = + kmalloc(sizeof(struct net_device), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + break; + } + memset(dev, 0x00, sizeof(struct net_device)); + memcpy(dev->name, name[i], IFNAMSIZ); /* Copy name */ + dev->base_addr = io[i]; + dev->irq = irq[i]; + dev->init = &wavelan_config; + memcpy(dev->dev_addr, mac, 6); /* Copy MAC address. */ + + /* Try to create the device. */ + if (register_netdev(dev) != 0) { + /* Deallocate everything. */ + /* Note: if dev->priv is mallocated, there is no way to fail. */ + kfree(dev); + } else { + /* If at least one device OK, we do not fail */ + ret = 0; + } + } /* if there is something at the address */ + } /* Loop on all addresses. */ + +#ifdef DEBUG_CONFIG_ERROR + if (wavelan_list == (net_local *) NULL) + printk(KERN_WARNING + "WaveLAN init_module(): no device found\n"); +#endif + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- init_module()\n"); +#endif + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Removal of the module + */ +void cleanup_module(void) +{ +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> cleanup_module()\n"); +#endif + + /* Loop on all devices and release them. */ + while (wavelan_list != (net_local *) NULL) { + device *dev = wavelan_list->dev; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG + "%s: cleanup_module(): removing device at 0x%x\n", + dev->name, (unsigned int) dev); +#endif + + /* Release the ioport region. */ + release_region(dev->base_addr, sizeof(ha_t)); + + /* Definitely remove the device. */ + unregister_netdev(dev); + + /* Unlink the device. */ + wavelan_list = wavelan_list->next; + + /* Free pieces. */ + kfree(dev->priv); + kfree(dev); + } + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- cleanup_module()\n"); +#endif +} +#endif /* MODULE */ +MODULE_LICENSE("GPL"); + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + * Ajay Bakre (bakre@paul.rutgers.edu), + * Donald Becker (becker@scyld.com), + * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), + * Anders Klemets (klemets@it.kth.se), + * Vladimir V. Kolpakov (w@stier.koenig.ru), + * Marc Meertens (Marc.Meertens@Utrecht.NCR.com), + * Pauline Middelink (middelin@polyware.iaf.nl), + * Robert Morris (rtm@das.harvard.edu), + * Jean Tourrilhes (jt@hplb.hpl.hp.com), + * Girish Welling (welling@paul.rutgers.edu), + * + * Thanks go also to: + * James Ashton (jaa101@syseng.anu.edu.au), + * Alan Cox (alan@redhat.com), + * Allan Creighton (allanc@cs.usyd.edu.au), + * Matthew Geier (matthew@cs.usyd.edu.au), + * Remo di Giovanni (remo@cs.usyd.edu.au), + * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), + * Vipul Gupta (vgupta@cs.binghamton.edu), + * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + * Tim Nicholson (tim@cs.usyd.edu.au), + * Ian Parkin (ian@cs.usyd.edu.au), + * John Rosenberg (johnr@cs.usyd.edu.au), + * George Rossi (george@phm.gov.au), + * Arthur Scott (arthur@cs.usyd.edu.au), + * Peter Storey, + * for their assistance and advice. + * + * Please send bug reports, updates, comments to: + * + * Bruce Janson Email: bruce@cs.usyd.edu.au + * Basser Department of Computer Science Phone: +61-2-9351-3423 + * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-9351-3838 + */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.h linux/drivers/net/wireless/wavelan.h --- linux-2.5.3-pre2/drivers/net/wireless/wavelan.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,370 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follows. See wavelan.p.h for details. + * + * This file contains the declarations for the WaveLAN hardware. Note that + * the WaveLAN ISA includes a i82586 controller (see definitions in + * file i82586.h). + * + * The main difference between the ISA hardware and the PCMCIA one is + * the Ethernet controller (i82586 instead of i82593). + * The i82586 allows multiple transmit buffers. The PSA needs to be accessed + * through the host interface. + */ + +#ifndef _WAVELAN_H +#define _WAVELAN_H + +/************************** MAGIC NUMBERS ***************************/ + +/* Detection of the WaveLAN card is done by reading the MAC + * address from the card and checking it. If you have a non-AT&T + * product (OEM, like DEC RoamAbout, Digital Ocean, or Epson), + * you might need to modify this part to accommodate your hardware. + */ +static const 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! */ +}; + +#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */ + +#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */ + +#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU) + +/* + * 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 + */ +static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, + 0xD0, 0xF0, 0xF8, 0x150 }; + +/* Frequencies of the 1.0 modem (fixed frequencies). + * Use to map the PSA `subband' to a frequency + * Note : all frequencies apart from the first one need to be multiplied by 10 + */ +static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; + + + +/*************************** PC INTERFACE ****************************/ + +/* + * Host Adaptor structure. + * (base is board port address). + */ +typedef union hacs_u hacs_u; +union hacs_u +{ + unsigned short hu_command; /* Command register */ +#define HACR_RESET 0x0001 /* Reset board */ +#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */ +#define HACR_16BITS 0x0004 /* 16-bit operation (0 => 8bits) */ +#define HACR_OUT0 0x0008 /* General purpose output pin 0 */ + /* not used - must be 1 */ +#define HACR_OUT1 0x0010 /* General purpose output pin 1 */ + /* not used - must be 1 */ +#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */ +#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */ +#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */ + unsigned short hu_status; /* Status Register */ +#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */ +#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */ +#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */ +#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */ +}; + +typedef struct ha_t ha_t; +struct ha_t +{ + hacs_u ha_cs; /* Command and status registers */ +#define ha_command ha_cs.hu_command +#define ha_status ha_cs.hu_status + unsigned short ha_mmcr; /* Modem Management Ctrl Register */ + unsigned short ha_pior0; /* Program I/O Address Register Port 0 */ + unsigned short ha_piop0; /* Program I/O Port 0 */ + unsigned short ha_pior1; /* Program I/O Address Register Port 1 */ + unsigned short ha_piop1; /* Program I/O Port 1 */ + unsigned short ha_pior2; /* Program I/O Address Register Port 2 */ + unsigned short ha_piop2; /* Program I/O Port 2 */ +}; + +#define HA_SIZE 16 + +#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0) +#define HACR(p) hoff(p, ha_command) +#define HASR(p) hoff(p, ha_status) +#define MMCR(p) hoff(p, ha_mmcr) +#define PIOR0(p) hoff(p, ha_pior0) +#define PIOP0(p) hoff(p, ha_piop0) +#define PIOR1(p) hoff(p, ha_pior1) +#define PIOP1(p) hoff(p, ha_piop1) +#define PIOR2(p) hoff(p, ha_pior2) +#define PIOP2(p) hoff(p, ha_piop2) + +/* + * Program I/O Mode Register values. + */ +#define STATIC_PIO 0 /* Mode 1: static mode */ + /* RAM access ??? */ +#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */ + /* RAM access ??? */ +#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */ + /* RAM access ??? */ +#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */ + /* Parameter access. */ +#define PIO_MASK 3 /* register mask */ +#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2)) + +#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2)) +#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE) + +/************************** MEMORY LAYOUT **************************/ + +/* + * Onboard 64 k RAM layout. + * (Offsets from 0x0000.) + */ +#define OFFSET_RU 0x0000 /* 75% memory */ +#define OFFSET_CU 0xC000 /* 25% memory */ +#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t)) +#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t)) +#define OFFSET_SCP I82586_SCP_ADDR + +#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ) +#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ) + +#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ) +#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ) + +/********************** PARAMETER STORAGE AREA **********************/ + +/* + * Parameter Storage Area (PSA). + */ +typedef struct psa_t psa_t; +struct psa_t +{ + 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] Compatibility 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 */ +}; + +#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 or 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, no 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 or unfreeze 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 parameters */ + 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 address in protect register */ +#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ + /* Never issue the PRDS command: 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) */ +}; + +#define MMW_SIZE 37 + +#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 NWIDs rxd (low) */ + unsigned char mmr_correct_nwid_h; /* # of correct NWIDs rxd (high) */ + /* Warning: read high-order octet first! */ + unsigned char mmr_wrong_nwid_l; /* # of wrong NWIDs rxd (low) */ + unsigned char mmr_wrong_nwid_h; /* # of wrong NWIDs 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) */ +}; + +#define MMR_SIZE 36 + +#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 */ + +/* + * This software may only be used and distributed + * according to the terms of the GNU General Public License. + * + * For more details, see wavelan.c. + */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan.p.h linux/drivers/net/wireless/wavelan.p.h --- linux-2.5.3-pre2/drivers/net/wireless/wavelan.p.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan.p.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,713 @@ +/* + * WaveLAN ISA driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * + * This file contains all definitions and declarations necessary for the + * WaveLAN ISA driver. This file is a private header, so it should + * be included only in wavelan.c! + */ + +#ifndef WAVELAN_P_H +#define WAVELAN_P_H + +/************************** DOCUMENTATION ***************************/ +/* + * This driver provides a Linux interface to the WaveLAN ISA hardware. + * The WaveLAN is a product of Lucent (http://www.wavelan.com/). + * This division was formerly part of NCR and then AT&T. + * WaveLANs are also distributed by DEC (RoamAbout DS) and Digital Ocean. + * + * To learn how to use this driver, read the NET3 HOWTO. + * If you want to exploit the many other functionalities, read the comments + * in the code. + * + * This driver is the result of the effort of many people (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 + * + * SMP + * --- + * We now are SMP compliant (I eventually fixed the remaining bugs). + * The driver has been tested on a dual P6-150 and survived my usual + * set of torture tests. + * Anyway, I spent enough time chasing interrupt re-entrancy during + * errors or reconfigure, and I designed the locked/unlocked sections + * of the driver with great care, and with the recent addition of + * the spinlock (thanks to the new API), we should be quite close to + * the truth. + * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), + * but better safe than sorry (especially at 2 Mb/s ;-). + * + * I have also looked into disabling only our interrupt on the card + * (via HACR) instead of all interrupts in the processor (via cli), + * so that other driver are not impacted, and it look like it's + * possible, but it's very tricky to do right (full of races). As + * the gain would be mostly for SMP systems, it can wait... + * + * 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 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.o is too darned big + * --------------------------- + * That's true! There is a very simple way to reduce the driver + * object by 33%! Comment out the following line: + * #include + * Other compile options can also reduce the size of it... + * + * MAC address and hardware detection: + * ----------------------------------- + * The detection code for the WaveLAN checks that the first three + * octets of the MAC address fit the company code. This type of + * detection works well for AT&T cards (because the AT&T code is + * hardcoded in wavelan.h), but of course will fail for other + * manufacturers. + * + * 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, etc.) + * 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 and verify + * 4) Send me the MAC code. I will include it in the next version. + * + */ + +/* --------------------- WIRELESS EXTENSIONS --------------------- */ +/* + * This driver is the first to support "wireless extensions". + * This set of extensions provides a standard way to control the wireless + * characteristics of the hardware. Applications such as mobile IP may + * take advantage of it. + * + * 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.c: actual code for the driver: C functions + * + * wavelan.p.h: private header: local types and variables for driver + * + * wavelan.h: description of the hardware interface and structs + * + * i82586.h: description of the Ethernet controller + */ + +/* --------------------------- HISTORY --------------------------- */ +/* + * This is based on information in the drivers' headers. It may not be + * accurate, and I guarantee only my best effort. + * + * The history of the WaveLAN drivers is as complicated as the history of + * the WaveLAN itself (NCR -> AT&T -> Lucent). + * + * It all started with Anders Klemets + * writing a WaveLAN ISA driver for the Mach microkernel. Girish + * Welling had also worked on it. + * Keith Moore modified this for the PCMCIA hardware. + * + * Robert Morris ported these two drivers to BSDI + * and added specific PCMCIA support (there is currently no equivalent + * of the PCMCIA package under BSD). + * + * Jim Binkley ported both BSDI drivers to FreeBSD. + * + * Bruce Janson ported the BSDI ISA driver to Linux. + * + * Anthony D. Joseph started to modify Bruce's driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li finished this work. + * Joe Finney patched the driver to start + * 2.00 cards correctly (2.4 GHz with frequency selection). + * David Hinds integrated the whole in his + * PCMCIA package (and bug corrections). + * + * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some + * patches to the PCMCIA driver. Later, I added code in the ISA driver + * for Wireless Extensions and full support of frequency selection + * cards. Then, I did the same to the PCMCIA driver, and did some + * reorganisation. Finally, I came back to the ISA driver to + * upgrade it at the same level as the PCMCIA one and reorganise + * the code. + * Loeke Brederveld from Lucent has given me + * much needed information on the WaveLAN hardware. + */ + +/* The original copyrights and literature mention others' names and + * credits. I don't know what their part in this development was. + */ + +/* By the way, for the copyright and legal stuff: + * almost everybody wrote code under the GNU or BSD license (or similar), + * and want their original copyright to remain somewhere in the + * code (for myself, I go with the GPL). + * Nobody wants to take responsibility for anything, except the fame. + */ + +/* --------------------------- CREDITS --------------------------- */ +/* + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + * Ajay Bakre , + * Donald Becker , + * Loeke Brederveld , + * Brent Elphick , + * Anders Klemets , + * Vladimir V. Kolpakov , + * Marc Meertens , + * Pauline Middelink , + * Robert Morris , + * Jean Tourrilhes , + * Girish Welling , + * Clark Woodworth + * Yongguang Zhang + * + * Thanks go also to: + * James Ashton , + * Alan Cox , + * Allan Creighton , + * Matthew Geier , + * Remo di Giovanni , + * Eckhard Grah , + * Vipul Gupta , + * Mark Hagan , + * Tim Nicholson , + * Ian Parkin , + * John Rosenberg , + * George Rossi , + * Arthur Scott , + * Stanislav Sinyagin + * and Peter Storey for their assistance and advice. + * + * Additional Credits: + * + * My development has been done initially under Debian 1.1 (Linux 2.0.x) + * and now under Debian 2.2, initially with an HP Vectra XP/60, and now + * an HP Vectra XP/90. + * + */ + +/* ------------------------- IMPROVEMENTS ------------------------- */ +/* + * I proudly present: + * + * Changes made in first pre-release: + * ---------------------------------- + * - reorganisation of the code, function name change + * - creation of private header (wavelan.p.h) + * - reorganised debug messages + * - more comments, history, etc. + * - mmc_init: configure the PSA if not done + * - mmc_init: correct default value of level threshold for PCMCIA + * - mmc_init: 2.00 detection better code for 2.00 initialization + * - better info at startup + * - IRQ setting (note: this setting is permanent) + * - watchdog: change strategy (and solve module removal problems) + * - add wireless extensions (ioctl and get_wireless_stats) + * get/set nwid/frequency on fly, info for /proc/net/wireless + * - more wireless extensions: SETSPY and GETSPY + * - make wireless extensions optional + * - private ioctl to set/get quality and level threshold, histogram + * - remove /proc/net/wavelan + * - suppress useless stuff from lp (net_local) + * - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) + * - add message level (debug stuff in /var/adm/debug and errors not + * displayed at console and still in /var/adm/messages) + * - multi device support + * - start fixing the probe (init code) + * - more inlines + * - man page + * - many other minor details and cleanups + * + * Changes made in second pre-release: + * ----------------------------------- + * - clean up init code (probe and module init) + * - better multiple device support (module) + * - name assignment (module) + * + * Changes made in third pre-release: + * ---------------------------------- + * - be more conservative on timers + * - preliminary support for multicast (I still lack some details) + * + * Changes made in fourth pre-release: + * ----------------------------------- + * - multicast (revisited and finished) + * - avoid reset in set_multicast_list (a really big hack) + * if somebody could apply this code for other i82586 based drivers + * - share onboard memory 75% RU and 25% CU (instead of 50/50) + * + * Changes made for release in 2.1.15: + * ----------------------------------- + * - change the detection code for multi manufacturer code support + * + * Changes made for release in 2.1.17: + * ----------------------------------- + * - update to wireless extensions changes + * - silly bug in card initial configuration (psa_conf_status) + * + * Changes made for release in 2.1.27 & 2.0.30: + * -------------------------------------------- + * - small bug in debug code (probably not the last one...) + * - remove extern keyword for wavelan_probe() + * - level threshold is now a standard wireless extension (version 4 !) + * - modules parameters types (new module interface) + * + * Changes made for release in 2.1.36: + * ----------------------------------- + * - byte count stats (courtesy of David Hinds) + * - remove dev_tint stuff (courtesy of David Hinds) + * - encryption setting from Brent Elphick (thanks a lot!) + * - 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * + * Other changes (not by me) : + * ------------------------- + * - Spelling and gramar "rectification". + * + * Changes made for release in 2.0.37 & 2.2.2 : + * ------------------------------------------ + * - Correct status in /proc/net/wireless + * - Set PSA CRC to make PtP diagnostic tool happy (Bob Gray) + * - Module init code don't fail if we found at least one card in + * the address list (Karlis Peisenieks) + * - Missing parenthesis (Christopher Peterson) + * - Correct i82586 configuration parameters + * - Encryption initialisation bug (Robert McCormack) + * - New mac addresses detected in the probe + * - Increase watchdog for busy environments + * + * Changes made for release in 2.0.38 & 2.2.7 : + * ------------------------------------------ + * - Correct the reception logic to better report errors and avoid + * sending bogus packet up the stack + * - Delay RU config to avoid corrupting first received packet + * - Change config completion code (to actually check something) + * - Avoid reading out of bound in skbuf to transmit + * - Rectify a lot of (useless) debugging code + * - Change the way to `#ifdef SET_PSA_CRC' + * + * Changes made for release in 2.2.11 & 2.3.13 : + * ------------------------------------------- + * - 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 2.2.14 & 2.3.23 : + * ------------------------------------------- + * - Fix check for root permission (break instead of exit) + * - New nwid & encoding setting (Wireless Extension 9) + * + * Changes made for release in 2.3.49 : + * ---------------------------------- + * - Indentation reformating (Alan) + * - Update to new network API (softnet - 2.3.43) : + * o replace dev->tbusy (Alan) + * o replace dev->tstart (Alan) + * o remove dev->interrupt (Alan) + * o add SMP locking via spinlock in splxx (me) + * o add spinlock in interrupt handler (me) + * o use kernel watchdog instead of ours (me) + * o increase watchdog timeout (kernel is more sensitive) (me) + * o verify that all the changes make sense and work (me) + * - Fixup a potential gotcha when reconfiguring and thighten a bit + * the interactions with Tx queue. + * + * Changes made for release in 2.4.0 : + * --------------------------------- + * - Fix spinlock stupid bugs that I left in. The driver is now SMP + * compliant and doesn't lockup at startup. + * + * Wishes & dreams: + * ---------------- + * - roaming (see Pcmcia driver) + */ + +/***************************** INCLUDES *****************************/ + +#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 /* Wireless extensions */ + +/* WaveLAN declarations */ +#include "i82586.h" +#include "wavelan.h" + +/************************** DRIVER OPTIONS **************************/ +/* + * `#define' or `#undef' the following constant to change the behaviour + * of the driver... + */ +#undef SET_PSA_CRC /* Calculate and set the CRC on 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 extensions exist in the kernel */ +/* Warning: this stuff will slow down the driver. */ +#define WIRELESS_SPY /* Enable spying addresses. */ +#undef HISTOGRAM /* Enable histogram of signal 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 and 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_ERROR /* errors on configuration */ +#undef DEBUG_TX_TRACE /* transmission calls */ +#undef DEBUG_TX_INFO /* header of the transmitted 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 received packet */ +#undef DEBUG_RX_FAIL /* Normal failure conditions */ +#define DEBUG_RX_ERROR /* Unexpected conditions */ + +#undef DEBUG_PACKET_DUMP /* Dump packet on the screen if defined to 32. */ +#undef DEBUG_IOCTL_TRACE /* misc. call by Linux */ +#undef DEBUG_IOCTL_INFO /* various debugging 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 unused fields too. */ +#undef DEBUG_I82586_SHOW /* Show i82586 status. */ +#undef DEBUG_DEVICE_SHOW /* Show device parameters. */ + +/************************ CONSTANTS & MACROS ************************/ + +#ifdef DEBUG_VERSION_SHOW +static const char *version = "wavelan.c : v23 (SMP + wireless extensions) 05/10/00\n"; +#endif + +/* Watchdog temporisation */ +#define WATCHDOG_JIFFIES (512*HZ/100) + +/* Macro to get the number of elements in an array */ +#define NELS(a) (sizeof(a) / sizeof(a[0])) + +/* ------------------------ PRIVATE IOCTL ------------------------ */ + +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ +#define SIOCSIPLTHR SIOCIWFIRSTPRIV + 2 /* Set level threshold */ +#define SIOCGIPLTHR SIOCIWFIRSTPRIV + 3 /* Get level threshold */ + +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 7 /* Get histogram values */ + +/****************************** 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 keeps data in two structures: "device" + * keeps the generic data (same format for everybody) and "net_local" keeps + * additional specific data. + * Note that some of this specific data is in fact generic (en_stats, for + * example). + */ +struct net_local +{ + net_local * next; /* linked list of the devices */ + device * dev; /* reverse link */ + spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ + en_stats stats; /* Ethernet interface statistics */ + int nresets; /* number of hardware resets */ + u_char reconfig_82586; /* We need to reconfigure the controller. */ + u_char promiscuous; /* promiscuous mode */ + int mc_count; /* number of multicast addresses */ + u_short hacr; /* current host interface state */ + + int tx_n_in_use; + u_short rx_head; + u_short rx_last; + u_short tx_first_free; + u_short tx_first_in_use; + +#ifdef WIRELESS_EXT + iw_stats wstats; /* Wireless-specific statistics */ +#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 */ +}; + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- MISC. SUBROUTINES ------------------------ */ +static inline void + wv_splhi(net_local *, /* Disable interrupts, lock driver */ + unsigned long *); /* flags */ +static inline void + wv_splx(net_local *, /* Enable interrupts, unlock driver */ + unsigned long *); /* flags */ +static u_char + wv_irq_to_psa(int); +static int + wv_psa_to_irq(u_char); +/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ +static inline u_short /* 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_short), /* data */ + hacr_write_slow(u_long, + u_short), + set_chan_attn(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_hacr_reset(u_long), /* ioaddr */ + wv_16_off(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_16_on(u_long, /* ioaddr */ + u_short), /* hacr */ + wv_ints_off(device *), + wv_ints_on(device *); +/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ +static void + psa_read(u_long, /* Read the Parameter Storage Area. */ + u_short, /* hacr */ + int, /* offset in PSA */ + u_char *, /* buffer to fill */ + int), /* size to read */ + psa_write(u_long, /* Write to the PSA. */ + u_short, /* hacr */ + 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); /* 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 */ +/* ---------------------- I82586 SUBROUTINES ----------------------- */ +static /*inline*/ void + obram_read(u_long, /* ioaddr */ + u_short, /* o */ + u_char *, /* b */ + int); /* n */ +static inline void + obram_write(u_long, /* ioaddr */ + u_short, /* o */ + u_char *, /* b */ + int); /* n */ +static void + wv_ack(device *); +static inline int + wv_synchronous_cmd(device *, + const char *), + wv_config_complete(device *, + u_long, + net_local *); +static int + wv_complete(device *, + u_long, + net_local *); +static inline void + wv_82586_reconfig(device *); +/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ +#ifdef DEBUG_I82586_SHOW +static void + wv_scb_show(unsigned short); +#endif +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 */ +static void + wavelan_set_multicast_list(device *); +/* ----------------------- PACKET RECEPTION ----------------------- */ +static inline void + wv_packet_read(device *, /* Read a packet from a frame. */ + u_short, + int), + wv_receive(device *); /* Read all packets waiting. */ +/* --------------------- PACKET TRANSMISSION --------------------- */ +static inline int + 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. */ + wv_ru_start(device *), /* Start the i82586 receiver unit. */ + wv_cu_start(device *), /* Start the i82586 command unit. */ + wv_82586_start(device *); /* Start the i82586. */ +static void + wv_82586_config(device *); /* Configure the i82586. */ +static inline void + wv_82586_stop(device *); +static int + wv_hw_reset(device *), /* Reset the WaveLAN hardware. */ + wv_check_ioaddr(u_long, /* ioaddr */ + u_char *); /* mac address (read) */ +/* ---------------------- INTERRUPT HANDLING ---------------------- */ +static void + wavelan_interrupt(int, /* interrupt handler */ + void *, + struct pt_regs *); +static void + wavelan_watchdog(device *); /* transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int + wavelan_open(device *), /* Open the device. */ + wavelan_close(device *), /* Close the device. */ + wavelan_config(device *); /* Configure one device. */ +extern int + wavelan_probe(device *); /* See Space.c. */ + +/**************************** VARIABLES ****************************/ + +/* + * This is the root of the linked list of WaveLAN drivers + * It is use to verify that we don't reuse the same base address + * for two different drivers and to clean up when removing the module. + */ +static net_local * wavelan_list = (net_local *) NULL; + +/* + * This table is used to translate the PSA value to IRQ number + * and vice versa. + */ +static u_char irqvals[] = +{ + 0, 0, 0, 0x01, + 0x02, 0x04, 0, 0x08, + 0, 0, 0x10, 0x20, + 0x40, 0, 0, 0x80, +}; + +/* + * Table of the available I/O addresses (base addresses) for WaveLAN + */ +static unsigned short iobase[] = +{ +#if 0 + /* Leave out 0x3C0 for now -- seems to clash with some video + * controllers. + * Leave out the others too -- we will always use 0x390 and leave + * 0x300 for the Ethernet device. + * Jean II: 0x3E0 is fine as well. + */ + 0x300, 0x390, 0x3E0, 0x3C0 +#endif /* 0 */ + 0x390, 0x3E0 +}; + +#ifdef MODULE +/* Parameters set by insmod */ +static int io[4]; +static int irq[4]; +static char name[4][IFNAMSIZ]; +MODULE_PARM(io, "1-4i"); +MODULE_PARM(irq, "1-4i"); +MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ)); +MODULE_PARM_DESC(io, "WaveLAN I/O base address(es),required"); +MODULE_PARM_DESC(irq, "WaveLAN IRQ number(s)"); +MODULE_PARM_DESC(name, "WaveLAN interface neme(s)"); +#endif /* MODULE */ + +#endif /* WAVELAN_P_H */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.c linux/drivers/net/wireless/wavelan_cs.c --- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan_cs.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,4837 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follow. See wavelan_cs.p.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.p.h" /* Private header */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (wavelan modem or i82593) + */ + +/*------------------------------------------------------------------*/ +/* + * Wrapper for disabling interrupts. + * (note : inline, so optimised away) + */ +static inline void +wv_splhi(net_local * lp, + unsigned long * pflags) +{ + spin_lock_irqsave(&lp->spinlock, *pflags); + /* Note : above does the cli(); itself */ +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for re-enabling interrupts. + */ +static inline void +wv_splx(net_local * lp, + unsigned long * pflags) +{ + spin_unlock_irqrestore(&lp->spinlock, *pflags); + + /* Note : enabling interrupts on the hardware is done in wv_ru_start() + * via : outb(OP1_INT_ENABLE, LCCR(base)); + */ +} + +/*------------------------------------------------------------------*/ +/* + * 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 *******************/ +/* + * Useful 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 */ + mdelay(1); +} /* 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)) + mdelay(1); + } + + /* 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 */ + mdelay(10); + 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 unsupported 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=(long)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 flags; + +#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 */ + wv_splhi(lp, &flags); + + 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); + + if(mode==NWID_PROMISC) + lp->cell_search=1; + else + lp->cell_search=0; + + /* ReEnable interrupts & restore flags */ + wv_splx(lp, &flags); +} + +/* 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 flags; + + 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 */ + wv_splhi(lp, &flags); + + 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(lp, &flags); + + 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 0 + /* Some people don't need this, some other may need it */ + nwid=nwid^ntohs(beacon->domain_id); +#endif + +#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 *************************/ +/* + * Useful subroutines to manage the Ethernet controller + */ + +/*------------------------------------------------------------------*/ +/* + * Routine to synchronously send a command to the i82593 chip. + * Should be called with interrupts disabled. + * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(), + * wv_82593_config() & wv_diag()) + */ +static int +wv_82593_cmd(device * dev, + char * str, + int cmd, + int result) +{ + ioaddr_t base = dev->base_addr; + int status; + int wait_completed; + long spin; + + /* Spin until the chip finishes executing its current command (if any) */ + spin = 1000; + do + { + /* Time calibration of the loop */ + udelay(10); + + /* Read the interrupt register */ + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); + + /* If the interrupt hasn't be posted */ + if(spin <= 0) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n", + str, status); +#endif + return(FALSE); + } + + /* Issue the command to the controller */ + outb(cmd, LCCR(base)); + + /* If we don't have to check the result of the command + * Note : this mean that the irq handler will deal with that */ + if(result == SR0_NO_RESULT) + return(TRUE); + + /* We are waiting for command completion */ + wait_completed = TRUE; + + /* Busy wait while the LAN controller executes the command. */ + spin = 1000; + do + { + /* Time calibration of the loop */ + udelay(10); + + /* Read the interrupt register */ + outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); + status = inb(LCSR(base)); + + /* Check if there was an interrupt posted */ + if((status & SR0_INTERRUPT)) + { + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + + /* Check if interrupt is a command completion */ + if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) && + ((status & SR0_BOTH_RX_TX) != 0x0) && + !(status & SR0_RECEPTION)) + { + /* Signal command completion */ + wait_completed = FALSE; + } + else + { + /* Note : Rx interrupts will be handled later, because we can + * handle multiple Rx packets at once */ +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_INFO "wv_82593_cmd: not our interrupt\n"); +#endif + } + } + } + while(wait_completed && (spin-- > 0)); + + /* If the interrupt hasn't be posted */ + if(wait_completed) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n", + str, status); +#endif + return(FALSE); + } + + /* Check the return code returned by the card (see above) against + * the expected return code provided by the caller */ + if((status & SR0_EVENT_MASK) != result) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n", + str, 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) +{ + int ret = FALSE; + + if(wv_82593_cmd(dev, "wv_diag(): diagnose", + OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)) + ret = TRUE; + +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n"); +#endif + return(ret); +} /* 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; + + /* 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; + unsigned long flags; + + /* Arm the flag, will be cleard in wv_82593_config() */ + lp->reconfig_82593 = TRUE; + + /* Check if we can do it now ! */ + if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev))) + { + wv_splhi(lp, &flags); /* Disable interrupts */ + wv_82593_config(dev); + wv_splx(lp, &flags); /* Re-enable interrupts */ + } + else + { +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG + "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n", + dev->name, dev->state, link->open); +#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; + } + + wv_splhi(lp, &flags); + + /* 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 */ + + wv_splx(lp, &flags); + + 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(" state=%lX,", dev->state); + 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 flags; + 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 */ + wv_splhi(lp, &flags); + + /* 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(!capable(CAP_NET_ADMIN)) + { + 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 (very important for backward compatibility) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(&range, 0, sizeof(range)); + +#if WIRELESS_EXT > 10 + /* Set the Wireless Extension versions */ + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 9; /* Nothing for us in v10 and v11 */ +#endif /* WIRELESS_EXT > 10 */ + + /* 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 > 11 + range.avg_qual.qual = MMR_SGNL_QUAL; /* Always max */ + /* Need to get better values for those two */ + range.avg_qual.level = 30; + range.avg_qual.noise = 8; +#endif /* WIRELESS_EXT > 11 */ + +#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(!capable(CAP_NET_ADMIN)) + { + 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(!capable(CAP_NET_ADMIN)) + { + 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(lp, &flags); + +#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 flags; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); +#endif + + /* Disable interrupts & save flags */ + wv_splhi(lp, &flags); + + 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(lp, &flags); + +#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 + 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 */ + +#ifdef WIRELESS_SPY + 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 */ + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += sksize; + +#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()) + * Note : the spinlock is already grabbed for us and irq are disabled. + */ +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 flags; + 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 + + wv_splhi(lp, &flags); + + /* 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; + + wv_splx(lp, &flags); + +#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 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; + unsigned long flags; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, + (unsigned) skb); +#endif + + /* + * Block a timer-based transmit from overlapping a previous transmit. + * In other words, prevent reentering this routine. + */ + netif_stop_queue(dev); + + /* If somebody has asked to reconfigure the controller, + * we can do it now */ + if(lp->reconfig_82593) + { + wv_splhi(lp, &flags); /* Disable interrupts */ + wv_82593_config(dev); + wv_splx(lp, &flags); /* Re-enable interrupts */ + /* Note : the configure procedure was totally synchronous, + * so the Tx buffer is now free */ + } + +#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; + net_local * lp = (net_local *) dev->priv; + unsigned long flags; + int status; + int spin; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); +#endif + + wv_splhi(lp, &flags); + + /* 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 = 300; + do + { + udelay(10); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0)); + + /* Now, spin until the chip finishes executing its current command */ + do + { + udelay(10); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + } + while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0)); + + wv_splx(lp, &flags); + + /* If there was a problem */ + if(spin <= 0) + { +#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; + unsigned long flags; + +#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; + + wv_splhi(lp, &flags); + + /* 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)); + +#if 0 + /* XXX the i82593 manual page 6-4 seems to indicate that the stop register + should be set as below */ + /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/ +#elif 0 + /* but I set it 0 instead */ + lp->stop = 0; +#else + /* but I set it to 3 bytes per packet less than 8K */ + lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; +#endif + 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 spin = 10000; + + /* spin until the chip starts receiving */ + do + { + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + if(spin-- <= 0) + 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 + + wv_splx(lp, &flags); + +#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 controller (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; + int ret = TRUE; + +#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)) + ret = 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)) + ret = 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)) + ret = FALSE; + lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */ + } + + /* Job done, clear the flag */ + lp->reconfig_82593 = FALSE; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name); +#endif + return(ret); +} + +/*------------------------------------------------------------------*/ +/* + * 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; + unsigned long flags; + int ret = FALSE; + +#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; + + /* Disable interrupts */ + wv_splhi(lp, &flags); + + /* Disguised goto ;-) */ + do + { + /* 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 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 + break; + } + + /* initialize the modem */ + if(wv_mmc_init(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n", + dev->name); +#endif + break; + } + + /* reset the LAN controller (i82593) */ + outb(OP0_RESET, LCCR(base)); + mdelay(1); /* A bit crude ! */ + + /* Initialize the LAN controller */ + if(wv_82593_config(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", + dev->name); +#endif + break; + } + + /* Diagnostic */ + if(wv_diag(dev) == FALSE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n", + dev->name); +#endif + break; + } + + /* + * insert code for loopback test here + */ + + /* The device is now configured */ + lp->configured = 1; + ret = TRUE; + } + while(0); + + /* Re-enable interrupts */ + wv_splx(lp, &flags); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); +#endif + return(ret); +} + +/*------------------------------------------------------------------*/ +/* + * 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 + + 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 small 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 = req.Size = 0; + 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, req.Size); + 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; + netif_start_queue(dev); + +#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; + } + + strcpy(((net_local *) dev->priv)->node.dev_name, dev->name); + 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_STALE_CONFIG); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); +#endif +} /* wv_pcmcia_release */ + +/*------------------------------------------------------------------*/ +/* + * Sometimes, wavelan_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 transferred 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; + +#ifdef DEBUG_INTERRUPT_INFO + /* Check state of our spinlock (it should be cleared) */ + if(spin_is_locked(&lp->spinlock)) + printk(KERN_DEBUG + "%s: wavelan_interrupt(): spinlock is already locked !!!\n", + dev->name); +#endif + + /* Prevent reentrancy. We need to do that because we may have + * multiple interrupt handler running concurently. + * It is safe because wv_splhi() disable interrupts before aquiring + * the spinlock. */ + spin_lock(&lp->spinlock); + + /* 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; + } + + /* ----------------- 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 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 + + /* 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++; + + netif_wake_queue(dev); + 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 */ + } + } /* while(1) */ + + spin_unlock(&lp->spinlock); + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif +} /* wv_interrupt */ + +/*------------------------------------------------------------------*/ +/* + * Watchdog: when we start a transmission, a timer is set for us in the + * kernel. If the transmission completes, this timer is disabled. If + * the timer expires, we are called and we try to unlock the hardware. + * + * Note : This watchdog is move clever than the one in the ISA driver, + * because it try to abort the current command before reseting + * everything... + * On the other hand, it's a bit simpler, because we don't have to + * deal with the multiple Tx buffers... + */ +static void +wavelan_watchdog(device * dev) +{ + net_local * lp = (net_local *) dev->priv; + ioaddr_t base = dev->base_addr; + unsigned long flags; + int aborted = FALSE; + +#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 + + wv_splhi(lp, &flags); + + /* Ask to abort the current command */ + outb(OP0_ABORT, LCCR(base)); + + /* Wait for the end of the command (a bit hackish) */ + if(wv_82593_cmd(dev, "wavelan_watchdog(): abort", + OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED)) + aborted = TRUE; + + /* Release spinlock here so that wv_hw_reset() can grab it */ + wv_splx(lp, &flags); + + /* Check if we were successful in aborting it */ + if(!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... */ + netif_wake_queue(dev); + +#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 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 */ + netif_start_queue(dev); + + /* 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; + 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 */ + + link->open--; + MOD_DEC_USE_COUNT; + + /* If the card is still present */ + if(netif_running(dev)) + { + netif_stop_queue(dev); + + /* 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; +} + +/*------------------------------------------------------------------*/ +/* + * 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); + if (!link) return NULL; + 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); + if (!dev) { + kfree(link); + return NULL; + } + 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); + if (!lp) { + kfree(link); + kfree(dev); + return NULL; + } + memset(lp, 0x00, sizeof(net_local)); + + /* Init specific data */ + lp->configured = 0; + lp->reconfig_82593 = FALSE; + lp->nresets = 0; + /* Multicast stuff */ + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = 0; + + /* Init spinlock */ + spin_lock_init(&lp->spinlock); + + /* back links */ + lp->link = link; + lp->dev = dev; + + /* Standard setup for generic data */ + ether_setup(dev); + + /* wavelan NET3 callbacks */ + 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 */ + + /* Set the watchdog timer */ + dev->tx_timeout = &wavelan_watchdog; + dev->watchdog_timeo = WATCHDOG_JIFFIES; + +#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 */ + 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 */ + netif_device_detach(dev); + + /* 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) + netif_device_detach(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) /* If RESET -> True, If RESUME -> False ??? */ + { + wv_hw_reset(dev); + netif_device_attach(dev); + } + } + 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 -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.h linux/drivers/net/wireless/wavelan_cs.h --- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan_cs.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,386 @@ +/* + * 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 controller (see definitions in + * file i82593.h). + * + * The main difference between the pcmcia hardware and the ISA one is + * the Ethernet Controller (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_CS_H +#define _WAVELAN_CS_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_CS_H */ diff -urN linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.p.h linux/drivers/net/wireless/wavelan_cs.p.h --- linux-2.5.3-pre2/drivers/net/wireless/wavelan_cs.p.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/wireless/wavelan_cs.p.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,825 @@ +/* + * 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_P_H +#define WAVELAN_CS_P_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 + * + * SMP + * --- + * We now are SMP compliant (I eventually fixed the remaining bugs). + * The driver has been tested on a dual P6-150 and survived my usual + * set of torture tests. + * Anyway, I spent enough time chasing interrupt re-entrancy during + * errors or reconfigure, and I designed the locked/unlocked sections + * of the driver with great care, and with the recent addition of + * the spinlock (thanks to the new API), we should be quite close to + * the truth. + * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast), + * but better safe than sorry (especially at 2 Mb/s ;-). + * + * I have also looked into disabling only our interrupt on the card + * (via HACR) instead of all interrupts in the processor (via cli), + * so that other driver are not impacted, and it look like it's + * possible, but it's very tricky to do right (full of races). As + * the gain would be mostly for SMP systems, it can wait... + * + * 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_cs.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_cs.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.p.h : Private header : local types / vars for the driver + * + * wavelan_cs.h : Description of the hardware interface & structs + * + * i82593.h : Description if the Ethernet controller + */ + +/* --------------------------- 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 -> 2.2 - pcmcia 2.8.18+) + * with an HP OmniBook 4000 and then a 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 + * - Suppress 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 Suppress "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) + * + * Changes made for release in 3.1.12 : + * ---------------------------------- + * - reworked wv_82593_cmd to avoid using the IRQ handler and doing + * ugly things with interrupts. + * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog + * - Update to new network API (softnet - 2.3.43) : + * o replace dev->tbusy (David + me) + * o replace dev->tstart (David + me) + * o remove dev->interrupt (David) + * o add SMP locking via spinlock in splxx (me) + * o add spinlock in interrupt handler (me) + * o use kernel watchdog instead of ours (me) + * o verify that all the changes make sense and work (me) + * - Re-sync kernel/pcmcia versions (not much actually) + * - A few other cleanups (David & me)... + * + * Changes made for release in 3.1.22 : + * ---------------------------------- + * - Check that SMP works, remove annoying log message + * + * Changes made for release in 3.1.24 : + * ---------------------------------- + * - Fix unfrequent card lockup when watchdog was reseting the hardware : + * o control first busy loop in wv_82593_cmd() + * o Extend spinlock protection in wv_hw_config() + * + * 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 +#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_cs.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 transmitted 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 transmitted 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 : v23 (SMP + wireless extensions) 20/12/00\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 ------------------------ */ + +/* Wireless Extension Backward compatibility - Jean II + * If the new wireless device private ioctl range is not defined, + * default to standard device private ioctl range */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ + +#define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ +#define SIOCGIPQTHR SIOCIWFIRSTPRIV + 1 /* Get quality threshold */ +#define SIOCSIPROAM SIOCIWFIRSTPRIV + 2 /* Set roaming state */ +#define SIOCGIPROAM SIOCIWFIRSTPRIV + 3 /* Get roaming state */ + +#define SIOCSIPHISTO SIOCIWFIRSTPRIV + 6 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCIWFIRSTPRIV + 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... */ + spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ + 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 controller */ + u_char promiscuous; /* Promiscuous mode */ + u_char allmulticast; /* All Multicast mode */ + int mc_count; /* Number of multicast addresses */ + + 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 void + wv_splhi(net_local *, /* Disable interrupts */ + unsigned long *); /* flags */ +static inline void + wv_splx(net_local *, /* ReEnable interrupts */ + unsigned long *); /* 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 controller */ +/* ------------------- 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(device *); /* Transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int + wavelan_open(device *), /* Open the device */ + wavelan_close(device *); /* Close the device */ +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 */ + +/* + * 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 */ + +MODULE_LICENSE("GPL"); + +#endif /* WAVELAN_CS_P_H */ + diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/audio.c linux/drivers/sound/emu10k1/audio.c --- linux-2.5.3-pre2/drivers/sound/emu10k1/audio.c Tue Oct 9 10:53:17 2001 +++ linux/drivers/sound/emu10k1/audio.c Mon Jan 21 17:23:23 2002 @@ -1098,7 +1098,7 @@ static int emu10k1_audio_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct emu10k1_card *card = NULL; struct list_head *entry; struct emu10k1_wavedevice *wave_dev; diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/midi.c linux/drivers/sound/emu10k1/midi.c --- linux-2.5.3-pre2/drivers/sound/emu10k1/midi.c Tue Oct 9 10:53:18 2001 +++ linux/drivers/sound/emu10k1/midi.c Mon Jan 21 17:23:23 2002 @@ -87,7 +87,7 @@ static int emu10k1_midi_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct emu10k1_card *card = NULL; struct emu10k1_mididevice *midi_dev; struct list_head *entry; diff -urN linux-2.5.3-pre2/drivers/sound/emu10k1/mixer.c linux/drivers/sound/emu10k1/mixer.c --- linux-2.5.3-pre2/drivers/sound/emu10k1/mixer.c Tue Oct 9 10:53:18 2001 +++ linux/drivers/sound/emu10k1/mixer.c Mon Jan 21 17:23:23 2002 @@ -640,7 +640,7 @@ static int emu10k1_mixer_open(struct inode *inode, struct file *file) { - int minor = MINOR(inode->i_rdev); + int minor = minor(inode->i_rdev); struct emu10k1_card *card = NULL; struct list_head *entry; diff -urN linux-2.5.3-pre2/drivers/usb/Makefile linux/drivers/usb/Makefile --- linux-2.5.3-pre2/drivers/usb/Makefile Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/Makefile Mon Jan 21 17:23:23 2002 @@ -46,6 +46,10 @@ obj-y += hcd/ehci-hcd.o endif +ifeq ($(CONFIG_USB_OHCI_HCD),y) + obj-y += hcd/ohci-hcd.o +endif + obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o @@ -85,6 +89,7 @@ mod-subdirs := serial hcd subdir-$(CONFIG_USB_EHCI_HCD) += hcd +subdir-$(CONFIG_USB_OHCI_HCD) += hcd subdir-$(CONFIG_USB_SERIAL) += serial subdir-$(CONFIG_USB_STORAGE) += storage diff -urN linux-2.5.3-pre2/drivers/usb/audio.c linux/drivers/usb/audio.c --- linux-2.5.3-pre2/drivers/usb/audio.c Mon Jan 21 17:22:33 2002 +++ linux/drivers/usb/audio.c Mon Jan 21 17:23:23 2002 @@ -297,13 +297,13 @@ #define FLG_CONNECTED 32 struct my_data_urb { - urb_t urb; - iso_packet_descriptor_t isoframe[DESCFRAMES]; + struct urb urb; + struct usb_iso_packet_descriptor isoframe[DESCFRAMES]; }; struct my_sync_urb { - urb_t urb; - iso_packet_descriptor_t isoframe[SYNCFRAMES]; + struct urb urb; + struct usb_iso_packet_descriptor isoframe[SYNCFRAMES]; }; diff -urN linux-2.5.3-pre2/drivers/usb/auerswald.c linux/drivers/usb/auerswald.c --- linux-2.5.3-pre2/drivers/usb/auerswald.c Tue Jan 8 13:15:52 2002 +++ linux/drivers/usb/auerswald.c Mon Jan 21 17:23:23 2002 @@ -176,7 +176,7 @@ typedef struct { struct auerchain *chain; /* pointer to the chain to which this element belongs */ - urb_t * urbp; /* pointer to attached urb */ + struct urb * urbp; /* pointer to attached urb */ void *context; /* saved URB context */ usb_complete_t complete; /* saved URB completion function */ struct list_head list; /* to include element into a list */ @@ -200,7 +200,7 @@ unsigned int len; /* number of characters in data buffer */ unsigned int retries; /* for urb retries */ struct usb_ctrlrequest *dr; /* for setup data in control messages */ - urb_t * urbp; /* USB urb */ + struct urb * urbp; /* USB urb */ struct auerbufctl *list; /* pointer to list */ struct list_head buff_list; /* reference to next buffer in list */ } auerbuf_t,*pauerbuf_t; @@ -237,7 +237,7 @@ int open_count; /* count the number of open character channels */ char dev_desc[AUSI_DLEN];/* for storing a textual description */ unsigned int maxControlLength; /* max. Length of control paket (without header) */ - urb_t * inturbp; /* interrupt urb */ + struct urb * inturbp; /* interrupt urb */ char * intbufp; /* data buffer for interrupt urb */ unsigned int irqsize; /* size of interrupt endpoint 1 */ struct auerchain controlchain; /* for chaining of control messages */ @@ -274,7 +274,7 @@ /*-------------------------------------------------------------------*/ /* Forwards */ -static void auerswald_ctrlread_complete (urb_t * urb); +static void auerswald_ctrlread_complete (struct urb * urb); static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp); @@ -283,7 +283,7 @@ /* -------------------------- */ /* completion function for chained urbs */ -static void auerchain_complete (urb_t * urb) +static void auerchain_complete (struct urb * urb) { unsigned long flags; int result; @@ -350,7 +350,7 @@ this function may be called from completion context or from user space! early = 1 -> submit in front of chain */ -static int auerchain_submit_urb_list (pauerchain_t acp, urb_t * urb, int early) +static int auerchain_submit_urb_list (pauerchain_t acp, struct urb * urb, int early) { int result; unsigned long flags; @@ -424,7 +424,7 @@ /* submit function for chained urbs this function may be called from completion context or from user space! */ -static int auerchain_submit_urb (pauerchain_t acp, urb_t * urb) +static int auerchain_submit_urb (pauerchain_t acp, struct urb * urb) { return auerchain_submit_urb_list (acp, urb, 0); } @@ -433,10 +433,10 @@ the result is 0 if the urb is cancelled, or -EINPROGRESS if USB_ASYNC_UNLINK is set and the function is successfully started. */ -static int auerchain_unlink_urb (pauerchain_t acp, urb_t * urb) +static int auerchain_unlink_urb (pauerchain_t acp, struct urb * urb) { unsigned long flags; - urb_t * urbp; + struct urb * urbp; pauerchainelement_t acep; struct list_head *tmp; @@ -492,7 +492,7 @@ static void auerchain_unlink_all (pauerchain_t acp) { unsigned long flags; - urb_t * urbp; + struct urb * urbp; pauerchainelement_t acep; dbg ("auerchain_unlink_all called"); @@ -598,7 +598,7 @@ /* completion handler for synchronous chained URBs */ -static void auerchain_blocking_completion (urb_t *urb) +static void auerchain_blocking_completion (struct urb *urb) { wait_queue_head_t *wakeup = (wait_queue_head_t *)urb->context; wake_up (wakeup); @@ -606,7 +606,7 @@ /* Starts chained urb and waits for completion or timeout */ -static int auerchain_start_wait_urb (pauerchain_t acp, urb_t *urb, int timeout, int* actual_length) +static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length) { DECLARE_WAITQUEUE (wait, current); DECLARE_WAIT_QUEUE_HEAD (wqh); @@ -675,7 +675,7 @@ { int ret; struct usb_ctrlrequest *dr; - urb_t *urb; + struct urb *urb; int length; dbg ("auerchain_control_msg"); @@ -858,7 +858,7 @@ } /* Completion of asynchronous write block */ -static void auerchar_ctrlwrite_complete (urb_t * urb) +static void auerchar_ctrlwrite_complete (struct urb * urb) { pauerbuf_t bp = (pauerbuf_t) urb->context; pauerswald_t cp = ((pauerswald_t)((char *)(bp->list)-(unsigned long)(&((pauerswald_t)0)->bufctl))); @@ -871,7 +871,7 @@ } /* Completion handler for dummy retry packet */ -static void auerswald_ctrlread_wretcomplete (urb_t * urb) +static void auerswald_ctrlread_wretcomplete (struct urb * urb) { pauerbuf_t bp = (pauerbuf_t) urb->context; pauerswald_t cp; @@ -910,7 +910,7 @@ } /* completion handler for receiving of control messages */ -static void auerswald_ctrlread_complete (urb_t * urb) +static void auerswald_ctrlread_complete (struct urb * urb) { unsigned int serviceid; pauerswald_t cp; @@ -980,7 +980,7 @@ messages from the USB device. */ /* int completion handler. */ -static void auerswald_int_complete (urb_t * urb) +static void auerswald_int_complete (struct urb * urb) { unsigned long flags; unsigned int channelid; diff -urN linux-2.5.3-pre2/drivers/usb/devio.c linux/drivers/usb/devio.c --- linux-2.5.3-pre2/drivers/usb/devio.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/devio.c Mon Jan 21 17:23:23 2002 @@ -53,7 +53,7 @@ unsigned int signr; void *userbuffer; void *userurb; - urb_t urb; + struct urb urb; }; static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) @@ -170,7 +170,7 @@ static struct async *alloc_async(unsigned int numisoframes) { - unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t); + unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor); struct async *as = kmalloc(assize, GFP_KERNEL); if (!as) return NULL; diff -urN linux-2.5.3-pre2/drivers/usb/hcd/Config.in linux/drivers/usb/hcd/Config.in --- linux-2.5.3-pre2/drivers/usb/hcd/Config.in Thu Jan 3 13:21:28 2002 +++ linux/drivers/usb/hcd/Config.in Mon Jan 21 17:23:23 2002 @@ -2,6 +2,6 @@ # USB Host Controller Drivers # dep_tristate ' EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL -# dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL +dep_tristate ' OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL # dep_tristate ' UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL diff -urN linux-2.5.3-pre2/drivers/usb/hcd/Makefile linux/drivers/usb/hcd/Makefile --- linux-2.5.3-pre2/drivers/usb/hcd/Makefile Tue Jan 8 13:15:52 2002 +++ linux/drivers/usb/hcd/Makefile Mon Jan 21 17:23:23 2002 @@ -6,7 +6,7 @@ O_TARGET := obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o -# obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o +obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o # obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o # Extract lists of the multi-part drivers. diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ehci-sched.c linux/drivers/usb/hcd/ehci-sched.c --- linux-2.5.3-pre2/drivers/usb/hcd/ehci-sched.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/hcd/ehci-sched.c Mon Jan 21 17:23:23 2002 @@ -630,7 +630,7 @@ if (!(urb->transfer_flags & EHCI_STATE_UNLINK) && ehci->hcd.state != USB_STATE_HALT) { int i; - iso_packet_descriptor_t *desc; + struct usb_iso_packet_descriptor *desc; struct ehci_itd *first_itd = urb->hcpriv; /* update status for this frame's transfers */ diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-dbg.c linux/drivers/usb/hcd/ohci-dbg.c --- linux-2.5.3-pre2/drivers/usb/hcd/ohci-dbg.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci-dbg.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,236 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * This file is licenced under GPL + * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $ + */ + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG + +#define pipestring(pipe) ({ char *temp; \ + switch (usb_pipetype (pipe)) { \ + case PIPE_CONTROL: temp = "CTRL"; break; \ + case PIPE_BULK: temp = "BULK"; break; \ + case PIPE_INTERRUPT: temp = "INTR"; break; \ + default: temp = "ISOC"; break; \ + }; temp;}) + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header + */ +static void urb_print (struct urb * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + + if (!urb->dev || !urb->dev->bus) { + dbg("%s URB: no dev", str); + return; + } + +#ifndef OHCI_VERBOSE_DEBUG + if (urb->status != 0) +#endif + dbg("%s:[%4x] dev:%d,ep=%d-%c,%s,flags:%4x,len:%d/%d,stat:%d", + str, + usb_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + pipestring (pipe), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status); + +#ifdef OHCI_VERBOSE_DEBUG + if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printk (KERN_DEBUG __FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printk (" %02x", ((__u8 *) urb->setup_packet) [i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (KERN_DEBUG __FILE__ ": data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); + printk ("%s stat:%d\n", i < len? "...": "", urb->status); + } + } +#endif +} + +static inline struct ed * +dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma); + +/* print non-empty branches of the periodic ed tree */ +void ep_print_int_eds (struct ohci_hcd *ohci, char * str) +{ + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", + str, i, i); + while (*ed_p != 0 && j--) { + struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printk ("\n"); + } +} + + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} + +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +// dump control and status registers +static void ohci_dump_status (struct ohci_hcd *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (struct ohci_hcd *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg_port (controller, "", i, temp); + } +} + +static void ohci_dump (struct ohci_hcd *controller, int verbose) +{ + dbg ("OHCI controller %s state", controller->hcd.bus_name); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif + diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-hcd.c linux/drivers/usb/hcd/ohci-hcd.c --- linux-2.5.3-pre2/drivers/usb/hcd/ohci-hcd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci-hcd.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,973 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * [ Initialisation is based on Linus' ] + * [ uhci code and gregs ohci fragments ] + * [ (C) Copyright 1999 Linus Torvalds ] + * [ (C) Copyright 1999 Gregory P. Smith] + * + * + * History: + * + * 2002/01/18 package as a patch for 2.5.3; this should match the + * 2.4.17 kernel modulo some bugs being fixed. + * + * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes + * from post-2.4.5 patches. + * 2001/09/20 USB_ZERO_PACKET support; hcca_dma portability, OPTi warning + * 2001/09/07 match PCI PM changes, errnos from Linus' tree + * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify; + * pbook pci quirks gone (please fix pbook pci sw!) (db) + * + * 2001/04/08 Identify version on module load (gb) + * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam); + pci_map_single (db) + * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db) + * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam) + * + * 2000/09/26 fixed races in removing the private portion of the urb + * 2000/09/07 disable bulk and control lists when unlinking the last + * endpoint descriptor in order to avoid unrecoverable errors on + * the Lucent chips. (rwc@sgi) + * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some + * urb unlink probs, indentation fixes + * 2000/08/11 various oops fixes mostly affecting iso and cleanup from + * device unplugs. + * 2000/06/28 use PCI hotplug framework, for better power management + * and for Cardbus support (David Brownell) + * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling + * when the controller loses power; handle UE; cleanup; ... + * + * v5.2 1999/12/07 URB 3rd preview, + * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) + * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume + * i386: HUB, Keyboard, Mouse, Printer + * + * v4.3 1999/10/27 multiple HCs, bulk_request + * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes + * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. + * v4.0 1999/08/18 + * v3.0 1999/06/25 + * v2.1 1999/05/09 code clean up + * v2.0 1999/05/04 + * v1.0 1999/04/27 initial release + * + * This file is licenced under GPL + * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt () */ + +#ifndef CONFIG_USB_DEBUG + #define CONFIG_USB_DEBUG /* this is still experimental! */ +#endif + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include "../hcd.h" + +#include +#include +#include +#include + +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#include +#ifndef CONFIG_PM +# define CONFIG_PM +#endif +#endif + +/* + * TO DO: + * + * - "disabled" should be the hcd state + * - bandwidth alloc to generic code + * - lots more testing!! + */ + +#define DRIVER_VERSION "$Revision: 1.7 $" +#define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" +#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" + +/*-------------------------------------------------------------------------*/ + +#define OHCI_USE_NPS // force NoPowerSwitching mode +// #define OHCI_VERBOSE_DEBUG /* not always helpful */ + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define OHCI_UNLINK_TIMEOUT (HZ / 10) + +/*-------------------------------------------------------------------------*/ + +#include "ohci.h" + +#include "ohci-hub.c" +#include "ohci-dbg.c" +#include "ohci-mem.c" +#include "ohci-q.c" + +/*-------------------------------------------------------------------------*/ + +/* + * queue up an urb for anything except the root hub + */ +static int ohci_urb_enqueue ( + struct usb_hcd *hcd, + struct urb *urb, + int mem_flags +) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct ed *ed; + urb_priv_t *urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + unsigned long flags; + int bustime = 0; + +#ifdef OHCI_VERBOSE_DEBUG + urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + /* every endpoint has a ed, locate and fill it */ + if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_CONTROL: + /* 1 TD for setup, 1 for ACK, plus ... */ + size = 2; + /* FALLTHROUGH */ + case PIPE_BULK: + /* one TD for every 4096 Bytes (can be upto 8K) */ + size += urb->transfer_buffer_length / 4096; + /* ... and for any remaining bytes ... */ + if ((urb->transfer_buffer_length % 4096) != 0) + size++; + /* ... and maybe a zero length packet to wrap it up */ + if (size == 0) + size++; + else if ((urb->transfer_flags & USB_ZERO_PACKET) != 0 + && (urb->transfer_buffer_length + % usb_maxpacket (urb->dev, pipe, + usb_pipeout (pipe))) != 0) + size++; + break; + case PIPE_ISOCHRONOUS: /* number of packets from URB */ + size = urb->number_of_packets; + if (size <= 0) { + usb_dec_dev_use (urb->dev); + return -EINVAL; + } + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc [i].actual_length = 0; + urb->iso_frame_desc [i].status = -EXDEV; + } + break; + case PIPE_INTERRUPT: /* one TD */ + size = 1; + break; + } + + /* allocate the private part of the URB */ + urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), + mem_flags); + if (!urb_priv) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); + + /* fill the private part of the URB */ + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ + spin_lock_irqsave (&ohci->lock, flags); + for (i = 0; i < size; i++) { + urb_priv->td [i] = td_alloc (ohci, SLAB_ATOMIC); + if (!urb_priv->td [i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + spin_unlock_irqrestore (&ohci->lock, flags); + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + } + +// FIXME: much of this switch should be generic, move to hcd code ... + + /* allocate and claim bandwidth if needed; ISO + * needs start frame index if it was't provided. + */ + switch (usb_pipetype (pipe)) { + case PIPE_ISOCHRONOUS: + if (urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ( (ed->state == ED_OPER) + ? (ed->last_iso + 1) + : (le16_to_cpu (ohci->hcca->frame_no) + + 10)) & 0xffff; + } + /* FALLTHROUGH */ + case PIPE_INTERRUPT: + if (urb->bandwidth == 0) { + bustime = usb_check_bandwidth (urb->dev, urb); + } + if (bustime < 0) { + urb_free_priv (ohci, urb_priv); + spin_unlock_irqrestore (&ohci->lock, flags); + usb_dec_dev_use (urb->dev); + return bustime; + } + usb_claim_bandwidth (urb->dev, urb, + bustime, usb_pipeisoc (urb->pipe)); + } + + urb->hcpriv = urb_priv; + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_urb (urb); + + spin_unlock_irqrestore (&ohci->lock, flags); + + return 0; +} + +/* + * decouple the URB from the HC queues (TDs, urb_priv); it's + * already marked for deletion. reporting is always done + * asynchronously, and we might be dealing with an urb that's + * almost completed anyway... + */ +static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + unsigned long flags; + +#ifdef DEBUG + urb_print (urb, "UNLINK", 1); +#endif + + if (!ohci->disabled) { + urb_priv_t *urb_priv; + + /* flag the urb's data for deletion in some upcoming + * SF interrupt's delete list processing + */ + spin_lock_irqsave (&ohci->lock, flags); + urb_priv = urb->hcpriv; + + if (!urb_priv || (urb_priv->state == URB_DEL)) { + spin_unlock_irqrestore (&ohci->lock, flags); + return 0; + } + + urb_priv->state = URB_DEL; + ed_unlink (urb->dev, urb_priv->ed); + spin_unlock_irqrestore (&ohci->lock, flags); + } else { + /* + * with HC dead, we won't respect hc queue pointers + * any more ... just clean up every urb's memory. + */ + finish_urb (ohci, urb); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static void +ohci_free_config (struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv; + int i; + unsigned long flags; + + /* free any eds, and dummy tds, still hanging around */ + spin_lock_irqsave (&ohci->lock, flags); + for (i = 0; i < 32; i++) { + struct ed *ed = dev->ep [i]; + struct td *tdTailP; + + if (!ed) + continue; + + ed->state &= ~ED_URB_DEL; + if (ohci->disabled && ed->state == ED_OPER) + ed->state = ED_UNLINK; + switch (ed->state) { + case ED_NEW: + break; + case ED_UNLINK: + tdTailP = dma_to_td (ohci, + le32_to_cpup (&ed->hwTailP) & 0xfffffff0); + td_free (ohci, tdTailP); /* free dummy td */ + hash_free_ed (ohci, ed); + break; + + case ED_OPER: + default: + err ("illegal ED %d state in free_config, %d", + i, ed->state); +#ifdef DEBUG + BUG (); +#endif + } + ed_free (ohci, ed); + } + spin_unlock_irqrestore (&ohci->lock, flags); +} + +static int ohci_get_frame (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + dbg ("%s: ohci_get_frame", hcd->bus_name); + return le16_to_cpu (ohci->hcca->frame_no); +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (struct ohci_hcd *ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_INTR_OC, &ohci->regs->intrenable); + writel (OHCI_OCR, &ohci->regs->cmdstatus); + dbg ("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err ("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg ("USB HC reset_hc %s: ctrl = 0x%x ;", + ohci->hcd.bus_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci->regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err ("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub + */ +static int hc_start (struct ohci_hcd *ohci) +{ + __u32 mask; + unsigned int fminterval; + struct usb_device *udev; + + spin_lock_init (&ohci->lock); + ohci->disabled = 1; + ohci->sleeping = 0; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + /* a reset clears this */ + writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + ohci->disabled = 0; + writel (ohci->hc_control, &ohci->regs->control); + + /* Choose the interrupts we care about now, others later on demand */ + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrstatus); + writel (mask, &ohci->regs->intrenable); + +#ifdef OHCI_USE_NPS + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + + // POTPGT delay is bits 24-31, in 2 ms units. + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + + /* connect the virtual root hub */ + ohci->hcd.bus->root_hub = udev = usb_alloc_dev (NULL, ohci->hcd.bus); + ohci->hcd.state = USB_STATE_READY; + if (!udev) { + ohci->disabled = 1; +// FIXME cleanup + return -ENOMEM; + } + + usb_connect (udev); + udev->speed = USB_SPEED_FULL; + if (usb_new_device (udev) != 0) { + usb_free_dev (udev); + ohci->disabled = 1; +// FIXME cleanup + return -ENODEV; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static void ohci_irq (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct ohci_regs *regs = ohci->regs; + int ints; + + if ((ohci->hcca->done_head != 0) + && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else if ((ints = (readl (®s->intrstatus) + & readl (®s->intrenable))) == 0) { + return; + } + + // dbg ("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, %s disabled", hcd->bus_name); + // e.g. due to PCI Master/Target Abort + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + hc_reset (ohci); + } + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg ("USB Schedule overrun"); + writel (OHCI_INTR_SO, ®s->intrenable); + } + + // FIXME: this assumes SOF (1/ms) interrupts don't get lost... + if (ints & OHCI_INTR_SF) { + unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1; + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list [!frame] != NULL) { + dl_del_list (ohci, !frame); + } + if (ohci->ed_rm_list [frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + } + + writel (ints, ®s->intrstatus); + writel (OHCI_INTR_MIE, ®s->intrenable); +} + +/*-------------------------------------------------------------------------*/ + +static void ohci_stop (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + dbg ("%s: stop %s controller%s", + hcd->bus_name, + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), + ohci->disabled ? " (disabled)" : "" + ); +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + + if (!ohci->disabled) + hc_reset (ohci); + + ohci_mem_cleanup (ohci); + +#ifdef CONFIG_PCI + pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca, + ohci->hcca, ohci->hcca_dma); +#endif +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + +#ifdef CONFIG_PCI + if (hcd->pdev) { + ohci->hcca = pci_alloc_consistent (hcd->pdev, + sizeof *ohci->hcca, &ohci->hcca_dma); + if (!ohci->hcca) + return -ENOMEM; + + /* AMD 756, for most chips (early revs), corrupts register + * values on read ... so enable the vendor workaround. + */ + if (hcd->pdev->vendor == 0x1022 + && hcd->pdev->device == 0x740c) { + ohci->flags = OHCI_QUIRK_AMD756; + info ("%s: AMD756 erratum 4 workaround", + hcd->bus_name); + } + + /* Apple's OHCI driver has a lot of bizarre workarounds + * for this chip. Evidently control and bulk lists + * can get confused. (B&W G3 models, and ...) + */ + else if (hcd->pdev->vendor == 0x1045 + && hcd->pdev->device == 0xc861) { + info ("%s: WARNING: OPTi workarounds unavailable", + hcd->bus_name); + } + } +#else +# error "where's hcca coming from?" +#endif /* CONFIG_PCI */ + + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + if ((ret = ohci_mem_init (ohci)) < 0) { + ohci_stop (hcd); + return ret; + } + ohci->regs = hcd->regs; + + if (hc_reset (ohci) < 0) { + ohci_stop (hcd); + return -ENODEV; + } + + if (hc_start (ohci) < 0) { + err ("can't start %s", ohci->hcd.bus_name); + ohci_stop (hcd); + return -EBUSY; + } + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +static int ohci_suspend (struct usb_hcd *hcd, u32 state) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + unsigned long flags; + u16 cmd; + + if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { + dbg ("can't suspend %s (state is %s)", hcd->bus_name, + hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); + return -EIO; + } + + /* act as if usb suspend can always be used */ + dbg ("%s: suspend to %d", hcd->bus_name, state); + ohci->sleeping = 1; + + /* First stop processing */ + spin_lock_irqsave (&ohci->lock, flags); + ohci->hc_control &= + ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ohci->hc_control, &ohci->regs->control); + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + (void) readl (&ohci->regs->intrstatus); + spin_unlock_irqrestore (&ohci->lock, flags); + + /* Wait a frame or two */ + mdelay (1); + if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) + mdelay (1); + + #ifdef CONFIG_PMAC_PBOOK + if (_machine == _MACH_Pmac) + disable_irq (ohci->irq); + /* else, 2.4 assumes shared irqs -- don't disable */ + #endif + + /* Enable remote wakeup */ + writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD, + &ohci->regs->intrenable); + + /* Suspend chip and let things settle down a bit */ + ohci->hc_control = OHCI_USB_SUSPEND; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (500); /* No schedule here ! */ + + switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { + case OHCI_USB_RESET: + dbg ("%s suspend->reset ?", hcd->bus_name); + break; + case OHCI_USB_RESUME: + dbg ("%s suspend->resume ?", hcd->bus_name); + break; + case OHCI_USB_OPER: + dbg ("%s suspend->operational ?", hcd->bus_name); + break; + case OHCI_USB_SUSPEND: + dbg ("%s suspended", hcd->bus_name); + break; + } + + /* In some rare situations, Apple's OHCI have happily trashed + * memory during sleep. We disable its bus master bit during + * suspend + */ + pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_MASTER; + pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd); +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Disable USB PAD & cell clock */ + of_node = pci_device_to_OF_node (hcd->pdev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); + } +#endif + return 0; +} + + +// FIXME: this restart logic should be generic, +// and handle full hcd state cleanup + +/* controller died; cleanup debris, then restart */ +/* must not be called from interrupt context */ + +static int hc_restart (struct ohci_hcd *ohci) +{ + int temp; + int i; + + ohci->disabled = 1; + ohci->sleeping = 0; + if (ohci->hcd.bus->root_hub) + usb_disconnect (&ohci->hcd.bus->root_hub); + + /* empty the interrupt branches */ + for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0; + for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; + + /* no EDs to remove */ + ohci->ed_rm_list [0] = NULL; + ohci->ed_rm_list [1] = NULL; + + /* empty control and bulk lists */ + ohci->ed_isotail = NULL; + ohci->ed_controltail = NULL; + ohci->ed_bulktail = NULL; + + if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { + err ("can't restart %s, %d", ohci->hcd.bus_name, temp); + return temp; + } else + dbg ("restart %s completed", ohci->hcd.bus_name); + return 0; +} + +static int ohci_resume (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int temp; + int retval = 0; + unsigned long flags; + +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Re-enable USB PAD & cell clock */ + of_node = pci_device_to_OF_node (hcd->pdev); + if (of_node) + pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); + } +#endif + /* did we suspend, or were we powered off? */ + ohci->hc_control = readl (&ohci->regs->control); + temp = ohci->hc_control & OHCI_CTRL_HCFS; + +#ifdef DEBUG + /* the registers may look crazy here */ + ohci_dump_status (ohci); +#endif + + /* Re-enable bus mastering */ + pci_set_master (ohci->hcd.pdev); + + switch (temp) { + + case OHCI_USB_RESET: // lost power + info ("USB restart: %s", hcd->bus_name); + retval = hc_restart (ohci); + break; + + case OHCI_USB_SUSPEND: // host wakeup + case OHCI_USB_RESUME: // remote wakeup + info ("USB continue: %s from %s wakeup", hcd->bus_name, + (temp == OHCI_USB_SUSPEND) + ? "host" : "remote"); + ohci->hc_control = OHCI_USB_RESUME; + writel (ohci->hc_control, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (20); /* no schedule here ! */ + /* Some controllers (lucent) need a longer delay here */ + mdelay (15); + + temp = readl (&ohci->regs->control); + temp = ohci->hc_control & OHCI_CTRL_HCFS; + if (temp != OHCI_USB_RESUME) { + err ("controller %s won't resume", hcd->bus_name); + ohci->disabled = 1; + retval = -EIO; + break; + } + + /* Some chips likes being resumed first */ + writel (OHCI_USB_OPER, &ohci->regs->control); + (void) readl (&ohci->regs->control); + mdelay (3); + + /* Then re-enable operations */ + spin_lock_irqsave (&ohci->lock, flags); + ohci->disabled = 0; + ohci->sleeping = 0; + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + if (!ohci->ed_rm_list [0] && !ohci->ed_rm_list [1]) { + if (ohci->ed_controltail) + ohci->hc_control |= OHCI_CTRL_CLE; + if (ohci->ed_bulktail) + ohci->hc_control |= OHCI_CTRL_BLE; + } + hcd->state = USB_STATE_READY; + writel (ohci->hc_control, &ohci->regs->control); + + /* trigger a start-frame interrupt (why?) */ + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + + /* Check for a pending done list */ + writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); + (void) readl (&ohci->regs->intrdisable); + spin_unlock_irqrestore (&ohci->lock, flags); + + #ifdef CONFIG_PMAC_PBOOK + if (_machine == _MACH_Pmac) + enable_irq (ohci->irq); + #endif + if (ohci->hcca->done_head) + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, &ohci->regs->intrenable); + +// writel (OHCI_BLF, &ohci->regs->cmdstatus); +// writel (OHCI_CLF, &ohci->regs->cmdstatus); +ohci_dump_status (ohci); +dbg ("sleeping = %d, disabled = %d", ohci->sleeping, ohci->disabled); + break; + + default: + warn ("odd PCI resume for %s", hcd->bus_name); + } + return retval; +} + +#endif /* CONFIG_PM */ + + +/*-------------------------------------------------------------------------*/ + +static const char hcd_name [] = "ohci-hcd"; + +static const struct hc_driver ohci_driver = { + description: hcd_name, + + /* + * generic hardware linkage + */ + irq: ohci_irq, + flags: HCD_MEMORY | HCD_USB11, + + /* + * basic lifecycle operations + */ + start: ohci_start, +#ifdef CONFIG_PM + suspend: ohci_suspend, + resume: ohci_resume, +#endif + stop: ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + hcd_alloc: ohci_hcd_alloc, + hcd_free: ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + urb_enqueue: ohci_urb_enqueue, + urb_dequeue: ohci_urb_dequeue, + free_config: ohci_free_config, + + /* + * scheduling support + */ + get_frame_number: ohci_get_frame, + + /* + * root hub support + */ + hub_status_data: ohci_hub_status_data, + hub_control: ohci_hub_control, +}; + +#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC + +EXPORT_NO_SYMBOLS; +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_INFO); +MODULE_LICENSE ("GPL"); + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PCI + +/* There do exist non-PCI implementations of OHCI ... + * Examples include the SA-1111 (ARM) and some MIPS + * and related hardware. + */ + +static const struct pci_device_id __devinitdata pci_ids [] = { { + + /* handle any USB OHCI controller */ + class: (PCI_CLASS_SERIAL_USB << 8) | 0x10, + class_mask: ~0, + driver_data: (unsigned long) &ohci_driver, + + /* no matter who makes it */ + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + + }, { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE (pci, pci_ids); + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct pci_driver ohci_pci_driver = { + name: (char *) hcd_name, + id_table: pci_ids, + + probe: usb_hcd_pci_probe, + remove: usb_hcd_pci_remove, + +#ifdef CONFIG_PM + suspend: usb_hcd_pci_suspend, + resume: usb_hcd_pci_resume, +#endif +}; + + +static int __init ohci_hcd_init (void) +{ + dbg (DRIVER_INFO); + dbg ("block sizes: ed %d td %d", + sizeof (struct ed), sizeof (struct td)); + return pci_module_init (&ohci_pci_driver); +} +module_init (ohci_hcd_init); + +/*-------------------------------------------------------------------------*/ + +static void __exit ohci_hcd_cleanup (void) +{ + pci_unregister_driver (&ohci_pci_driver); +} +module_exit (ohci_hcd_cleanup); + +#endif /* CONFIG_PCI */ + diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-hub.c linux/drivers/usb/hcd/ohci-hub.c --- linux-2.5.3-pre2/drivers/usb/hcd/ohci-hub.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci-hub.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,267 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * This file is licenced under GPL + * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $ + */ + +/*-------------------------------------------------------------------------*/ + +/* + * OHCI Root Hub ... the nonsharable stuff + * + * Registers don't need cpu_to_le32, that happens transparently + */ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect. AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define read_roothub(hc, register, mask) ({ \ + u32 temp = readl (&hc->regs->roothub.register); \ + if (hc->flags & OHCI_QUIRK_AMD756) \ + while (temp & mask) \ + temp = readl (&hc->regs->roothub.register); \ + temp; }) + +static u32 roothub_a (struct ohci_hcd *hc) + { return read_roothub (hc, a, 0xfc0fe000); } +static inline u32 roothub_b (struct ohci_hcd *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci_hcd *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci_hcd *hc, int i) + { return read_roothub (hc, portstatus [i], 0xffe0fce0); } + +/*-------------------------------------------------------------------------*/ + +#define dbg_port(hc,label,num,value) \ + dbg ("%s: %s roothub.portstatus [%d] " \ + "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", \ + hc->hcd.bus_name, label, num, temp, \ + (temp & RH_PS_PRSC) ? " PRSC" : "", \ + (temp & RH_PS_OCIC) ? " OCIC" : "", \ + (temp & RH_PS_PSSC) ? " PSSC" : "", \ + (temp & RH_PS_PESC) ? " PESC" : "", \ + (temp & RH_PS_CSC) ? " CSC" : "", \ + \ + (temp & RH_PS_LSDA) ? " LSDA" : "", \ + (temp & RH_PS_PPS) ? " PPS" : "", \ + (temp & RH_PS_PRS) ? " PRS" : "", \ + (temp & RH_PS_POCI) ? " POCI" : "", \ + (temp & RH_PS_PSS) ? " PSS" : "", \ + \ + (temp & RH_PS_PES) ? " PES" : "", \ + (temp & RH_PS_CCS) ? " CCS" : "" \ + ); + + +/*-------------------------------------------------------------------------*/ + +/* build "status change" packet (one or two bytes) from HC registers */ + +static int +ohci_hub_status_data (struct usb_hcd *hcd, char *buf) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ports, i, changed = 0, length = 1; + + ports = roothub_a (ohci) & RH_A_NDP; + if (ports > MAX_ROOT_PORTS) { + err ("%s: bogus NDP=%d", hcd->bus_name, ports); + err ("rereads as NDP=%d", + readl (&ohci->regs->roothub.a) & RH_A_NDP); + /* retry later; "should not happen" */ + return 0; + } + + /* init status */ + if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC)) + buf [0] = changed = 1; + else + buf [0] = 0; + if (ports > 7) { + buf [1] = 0; + length++; + } + + /* look at each port */ + for (i = 0; i < ports; i++) { + u32 status = roothub_portstatus (ohci, i); + + status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC + | RH_PS_OCIC | RH_PS_PRSC; + if (status) { + changed = 1; + set_bit (i + 1, buf); + } + } + return changed ? length : 0; +} + +/*-------------------------------------------------------------------------*/ + +static void +ohci_hub_descriptor ( + struct ohci_hcd *ohci, + struct usb_hub_descriptor *desc +) { + u32 rh = roothub_a (ohci); + int ports = rh & RH_A_NDP; + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24; + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + temp = 0; + if (rh & RH_A_PSM) /* per-port power switching? */ + temp |= 0x0001; + if (rh & RH_A_NOCP) /* no overcurrent reporting? */ + temp |= 0x0010; + else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */ + temp |= 0x0008; + desc->wHubCharacteristics = cpu_to_le16 (temp); + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + rh = roothub_b (ohci); + desc->bitmap [0] = rh & RH_B_DR; + if (ports > 7) { + desc->bitmap [1] = (rh & RH_B_DR) >> 8; + desc->bitmap [2] = desc->bitmap [3] = 0xff; + } else + desc->bitmap [1] = 0xff; +} + +/*-------------------------------------------------------------------------*/ + +static int ohci_hub_control ( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ports; + u32 temp; + int retval = 0; + + // if (port request) + ports = roothub_a (ohci) & RH_A_NDP; + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + writel (RH_HS_OCIC, &ohci->regs->roothub.status); + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + temp = RH_PS_CCS; + break; + case USB_PORT_FEAT_C_ENABLE: + temp = RH_PS_PESC; + break; + case USB_PORT_FEAT_SUSPEND: + temp = RH_PS_POCI; + break; + case USB_PORT_FEAT_C_SUSPEND: + temp = RH_PS_PSSC; + break; + case USB_PORT_FEAT_POWER: + temp = RH_PS_LSDA; + break; + case USB_PORT_FEAT_C_CONNECTION: + temp = RH_PS_CSC; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + temp = RH_PS_OCIC; + break; + case USB_PORT_FEAT_C_RESET: + temp = RH_PS_PRSC; + break; + default: + goto error; + } + writel (temp, &ohci->regs->roothub.portstatus [wIndex]); + // readl (&ohci->regs->roothub.portstatus [wIndex]); + break; + case GetHubDescriptor: + ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE); + *(u32 *) buf = cpu_to_le32 (temp); + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = roothub_portstatus (ohci, wIndex); + *(u32 *) buf = cpu_to_le32 (temp); + +#ifndef OHCI_VERBOSE_DEBUG + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ +#endif + dbg_port (ohci, "GetStatus", wIndex + 1, temp); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + // FIXME: this can be cleared, yes? + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case SetPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + writel (RH_PS_PSS, + &ohci->regs->roothub.portstatus [wIndex]); + break; + case USB_PORT_FEAT_POWER: + writel (RH_PS_PPS, + &ohci->regs->roothub.portstatus [wIndex]); + break; + case USB_PORT_FEAT_RESET: + temp = readl (&ohci->regs->roothub.portstatus [wIndex]); + if (temp & RH_PS_CCS) + writel (RH_PS_PRS, + &ohci->regs->roothub.portstatus [wIndex]); + break; + default: + goto error; + } + break; + + default: +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + return retval; +} + diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-mem.c linux/drivers/usb/hcd/ohci-mem.c --- linux-2.5.3-pre2/drivers/usb/hcd/ohci-mem.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci-mem.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,251 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * This file is licenced under GPL + * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $ + */ + +/*-------------------------------------------------------------------------*/ + +/* + * There's basically three types of memory: + * - data used only by the HCD ... kmalloc is fine + * - async and periodic schedules, shared by HC and HCD ... these + * need to use pci_pool or pci_alloc_consistent + * - driver buffers, read/written by HC ... single shot DMA mapped + * + * There's also PCI "register" data, which is memory mapped. + * No memory seen by this driver is pagable. + */ + +/*-------------------------------------------------------------------------*/ + +static struct usb_hcd *ohci_hcd_alloc (void) +{ + struct ohci_hcd *ohci; + + ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL); + if (ohci != 0) { + memset (ohci, 0, sizeof (struct ohci_hcd)); + return &ohci->hcd; + } + return 0; +} + +static void ohci_hcd_free (struct usb_hcd *hcd) +{ + kfree (hcd_to_ohci (hcd)); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +# define OHCI_MEM_FLAGS SLAB_POISON +#else +# define OHCI_MEM_FLAGS 0 +#endif + +#ifndef CONFIG_PCI +# error "usb-ohci currently requires PCI-based controllers" + /* to support non-PCI OHCIs, you need custom bus/mem/... glue */ +#endif + + +/* Recover a TD/ED using its collision chain */ +static inline void * +dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma) +{ + struct hash_t * scan = entry->head; + while (scan && scan->dma != dma) + scan = scan->next; + return scan->virt; +} + +static inline struct ed * +dma_to_ed (struct ohci_hcd *hc, dma_addr_t ed_dma) +{ + return (struct ed *) dma_to_ed_td(&(hc->ed_hash [ED_HASH_FUNC(ed_dma)]), + ed_dma); +} + +static inline struct td * +dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) +{ + return (struct td *) dma_to_ed_td(&(hc->td_hash [TD_HASH_FUNC(td_dma)]), + td_dma); +} + +// FIXME: when updating the hashtables this way, mem_flags is unusable... + +/* Add a hash entry for a TD/ED; return true on success */ +static int +hash_add_ed_td ( + struct hash_list_t *entry, + void *virt, + dma_addr_t dma, + int mem_flags +) +{ + struct hash_t * scan; + + scan = (struct hash_t *) kmalloc (sizeof *scan, mem_flags); + if (!scan) + return 0; + + if (!entry->tail) { + entry->head = entry->tail = scan; + } else { + entry->tail->next = scan; + entry->tail = scan; + } + + scan->virt = virt; + scan->dma = dma; + scan->next = NULL; + return 1; +} + +static inline int +hash_add_ed (struct ohci_hcd *hc, struct ed *ed, int mem_flags) +{ + return hash_add_ed_td (&(hc->ed_hash [ED_HASH_FUNC (ed->dma)]), + ed, ed->dma, mem_flags); +} + +static inline int +hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags) +{ + return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]), + td, td->td_dma, mem_flags); +} + + +static void +hash_free_ed_td (struct hash_list_t *entry, void *virt) +{ + struct hash_t *scan, *prev; + scan = prev = entry->head; + + // Find and unlink hash entry + while (scan && scan->virt != virt) { + prev = scan; + scan = scan->next; + } + if (scan) { + if (scan == entry->head) { + if (entry->head == entry->tail) + entry->head = entry->tail = NULL; + else + entry->head = scan->next; + } else if (scan == entry->tail) { + entry->tail = prev; + prev->next = NULL; + } else + prev->next = scan->next; + kfree(scan); + } +} + +static inline void +hash_free_ed (struct ohci_hcd *hc, struct ed * ed) +{ + hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed); +} + +static inline void +hash_free_td (struct ohci_hcd *hc, struct td * td) +{ + hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td); +} + + +static int ohci_mem_init (struct ohci_hcd *ohci) +{ + ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev, + sizeof (struct td), + 32 /* byte alignment */, + 0 /* no page-crossing issues */, + GFP_KERNEL | OHCI_MEM_FLAGS); + if (!ohci->td_cache) + return -ENOMEM; + ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev, + sizeof (struct ed), + 16 /* byte alignment */, + 0 /* no page-crossing issues */, + GFP_KERNEL | OHCI_MEM_FLAGS); + if (!ohci->ed_cache) { + pci_pool_destroy (ohci->td_cache); + return -ENOMEM; + } + return 0; +} + +static void ohci_mem_cleanup (struct ohci_hcd *ohci) +{ + if (ohci->td_cache) { + pci_pool_destroy (ohci->td_cache); + ohci->td_cache = 0; + } + if (ohci->ed_cache) { + pci_pool_destroy (ohci->ed_cache); + ohci->ed_cache = 0; + } +} + +/* TDs ... */ +static struct td * +td_alloc (struct ohci_hcd *hc, int mem_flags) +{ + dma_addr_t dma; + struct td *td; + + td = pci_pool_alloc (hc->td_cache, mem_flags, &dma); + if (td) { + td->td_dma = dma; + /* hash it for later reverse mapping */ + if (!hash_add_td (hc, td, mem_flags)) { + pci_pool_free (hc->td_cache, td, dma); + return NULL; + } + } + return td; +} + +static inline void +td_free (struct ohci_hcd *hc, struct td *td) +{ + hash_free_td (hc, td); + pci_pool_free (hc->td_cache, td, td->td_dma); +} + + +/* EDs ... */ +static struct ed * +ed_alloc (struct ohci_hcd *hc, int mem_flags) +{ + dma_addr_t dma; + struct ed *ed; + + ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma); + if (ed) { + memset (ed, 0, sizeof (*ed)); + ed->dma = dma; + /* hash it for later reverse mapping */ + if (!hash_add_ed (hc, ed, mem_flags)) { + pci_pool_free (hc->ed_cache, ed, dma); + return NULL; + } + } + return ed; +} + +static inline void +ed_free (struct ohci_hcd *hc, struct ed *ed) +{ + hash_free_ed (hc, ed); + pci_pool_free (hc->ed_cache, ed, ed->dma); +} + diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci-q.c linux/drivers/usb/hcd/ohci-q.c --- linux-2.5.3-pre2/drivers/usb/hcd/ohci-q.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci-q.c Mon Jan 21 17:23:23 2002 @@ -0,0 +1,1000 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * This file is licenced under GPL + * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $ + */ + +static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv) +{ + int last = urb_priv->length - 1; + + if (last >= 0) { + int i; + struct td *td = urb_priv->td [0]; +#ifdef CONFIG_PCI + int len = td->urb->transfer_buffer_length; + int dir = usb_pipeout (td->urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE; + + /* unmap CTRL URB setup buffer (always td 0) */ + if (usb_pipecontrol (td->urb->pipe)) { + pci_unmap_single (hc->hcd.pdev, + td->data_dma, 8, PCI_DMA_TODEVICE); + + /* CTRL data buffer starts at td 1 if len > 0 */ + if (len && last > 0) + td = urb_priv->td [1]; + } + /* else: ISOC, BULK, INTR data buffer starts at td 0 */ + + /* unmap data buffer */ + if (len && td->data_dma) + pci_unmap_single (hc->hcd.pdev, + td->data_dma, len, dir); +#else +# warning "assuming no buffer unmapping is needed" +#endif + + for (i = 0; i <= last; i++) { + td = urb_priv->td [i]; + if (td) + td_free (hc, td); + } + } + + kfree (urb_priv); +} + +/*-------------------------------------------------------------------------*/ + +/* + * URB goes back to driver, and isn't reissued. + * It's completely gone from HC data structures, so no locking + * is needed ... or desired! (Giveback can call back to hcd.) + */ +static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb) +{ + if (urb->hcpriv) { + urb_free_priv (ohci, urb->hcpriv); + urb->hcpriv = NULL; + } + usb_hcd_giveback_urb (&ohci->hcd, urb); +} + +static void td_submit_urb (struct urb *urb); + +/* + * URB is reported to driver, is reissued if it's periodic. + */ +static int return_urb (struct ohci_hcd *hc, struct urb *urb) +{ + urb_priv_t *urb_priv = urb->hcpriv; + struct urb *urbt; + unsigned long flags; + int i; + +#ifdef DEBUG + if (!urb_priv) { + err ("already unlinked!"); + BUG (); + } + + /* just to be sure */ + if (!urb->complete) { + err ("no completion!"); + BUG (); + } +#endif + +#ifdef OHCI_VERBOSE_DEBUG + urb_print (urb, "RET", usb_pipeout (urb->pipe)); +#endif + +// FIXME: but if urb->status says it was was unlinked ... + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: +#ifdef CONFIG_PCI + pci_unmap_single (hc->hcd.pdev, + urb_priv->td [0]->data_dma, + urb->transfer_buffer_length, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); +#endif + urb->complete (urb); + + /* implicitly requeued */ + urb->actual_length = 0; + urb->status = -EINPROGRESS; + if (urb_priv->state != URB_DEL) { + spin_lock_irqsave (&hc->lock, flags); + td_submit_urb (urb); + spin_unlock_irqrestore (&hc->lock, flags); + } + break; + + case PIPE_ISOCHRONOUS: + for (urbt = urb->next; + urbt && (urbt != urb); + urbt = urbt->next) + continue; + if (urbt) { /* send the reply and requeue URB */ +#ifdef CONFIG_PCI +// FIXME this style unmap is only done on this route ... + pci_unmap_single (hc->hcd.pdev, + urb_priv->td [0]->data_dma, + urb->transfer_buffer_length, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); +#endif + urb->complete (urb); + spin_lock_irqsave (&hc->lock, flags); + urb->actual_length = 0; + urb->status = -EINPROGRESS; + urb->start_frame = urb_priv->ed->last_iso + 1; + if (urb_priv->state != URB_DEL) { + for (i = 0; i < urb->number_of_packets; + i++) { + urb->iso_frame_desc [i] + .actual_length = 0; + urb->iso_frame_desc [i] + .status = -EXDEV; + } + td_submit_urb (urb); + } +// FIXME if not deleted, should have been "finished" + spin_unlock_irqrestore (&hc->lock, flags); + + } else { /* not reissued */ + finish_urb (hc, urb); + } + break; + + /* + * C/B requests that get here are never reissued. + */ + case PIPE_BULK: + case PIPE_CONTROL: + finish_urb (hc, urb); + break; + } + return 0; +} + + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load balancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) + */ +static int ep_int_balance (struct ohci_hcd *ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint branch */ + for (i = 0; i < NUM_INTS ; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) + branch = i; + + branch = branch % interval; + for (i = branch; i < NUM_INTS; i += interval) + ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int ( ld (inter)) */ + +static int ep_2_n_interval (int inter) +{ + int i; + + for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++) + continue; + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have + * to be mapped the mapping reverses the bits of a word of num_bits length + */ +static int ep_rev (int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) + wout |= (( (word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (struct ohci_hcd *ohci, struct ed *edi) +{ + int int_branch, i; + int inter, interval, load; + __u32 *ed_p; + volatile struct ed *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed->dma, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail + && !ohci->ed_rm_list [0] + && !ohci->ed_rm_list [1] + && !ohci->sleeping + ) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed->dma, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail + && !ohci->ed_rm_list [0] + && !ohci->ed_rm_list [1] + && !ohci->sleeping + ) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + + case PIPE_INTERRUPT: + load = ed->int_load; + interval = ep_2_n_interval (ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_balance (ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + inter = 1; + for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]); + (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); + ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) + inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); + ed->hwNextED = *ed_p; + *ed_p = cpu_to_le32 (ed->dma); + } +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_INT"); +#endif + break; + + case PIPE_ISOCHRONOUS: + ed->hwNextED = 0; + ed->int_interval = 1; + if (ohci->ed_isotail != NULL) { + ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma); + ed->ed_prev = ohci->ed_isotail; + } else { + for ( i = 0; i < NUM_INTS; i += inter) { + inter = 1; + for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); + *ed_p != 0; + ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) + inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); + *ed_p = cpu_to_le32 (ed->dma); + } + ed->ed_prev = NULL; + } + ohci->ed_isotail = edi; +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_ISO"); +#endif + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed + */ +static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) +{ + int int_branch; + int i; + int inter; + int interval; + __u32 *ed_p; + + ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (le32_to_cpup (&ed->hwNextED), + &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) + ->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (le32_to_cpup (&ed->hwNextED), + &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) + ->ed_prev = ed->ed_prev; + } + break; + + case PIPE_INTERRUPT: + int_branch = ed->int_branch; + interval = ed->int_interval; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]), inter = 1; + (*ed_p != 0) && (*ed_p != ed->hwNextED); + ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), + inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) { + if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + } + } + for (i = int_branch; i < NUM_INTS; i += interval) + ohci->ohci_int_load [i] -= ed->int_load; +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_INT"); +#endif + break; + + case PIPE_ISOCHRONOUS: + if (ohci->ed_isotail == ed) + ohci->ed_isotail = ed->ed_prev; + if (ed->hwNextED != 0) + (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) + ->ed_prev = ed->ed_prev; + + if (ed->ed_prev != NULL) { + ed->ed_prev->hwNextED = ed->hwNextED; + } else { + for (i = 0; i < NUM_INTS; i++) { + for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); + *ed_p != 0; + ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) { + // inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); + if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + } + } + } +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_ISO"); +#endif + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* (re)init an endpoint; this _should_ be done once at the + * usb_set_configuration command, but the USB stack is a bit stateless + * so we do it at every transaction. + * if the state of the ed is ED_NEW then a dummy td is added and the + * state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are set even though most of them should + * not change + */ +static struct ed *ep_add_ed ( + struct usb_device *udev, + unsigned int pipe, + int interval, + int load, + int mem_flags +) { + struct ohci_hcd *ohci = hcd_to_ohci (udev->bus->hcpriv); + struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv; + struct td *td; + struct ed *ed; + unsigned ep; + unsigned long flags; + + spin_lock_irqsave (&ohci->lock, flags); + + ep = usb_pipeendpoint (pipe) << 1; + if (!usb_pipecontrol (pipe) && usb_pipeout (pipe)) + ep |= 1; + if (!(ed = dev->ep [ep])) { + ed = ed_alloc (ohci, SLAB_ATOMIC); + if (!ed) { + /* out of memory */ + spin_unlock_irqrestore (&ohci->lock, flags); + return NULL; + } + dev->ep [ep] = ed; + } + + if (ed->state & ED_URB_DEL) { + /* pending unlink request */ + spin_unlock_irqrestore (&ohci->lock, flags); + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP); + /* dummy td; end of td list for ed */ + td = td_alloc (ohci, SLAB_ATOMIC); + if (!td) { + /* out of memory */ + spin_unlock_irqrestore (&ohci->lock, flags); + return NULL; + } + ed->hwTailP = cpu_to_le32 (td->td_dma); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + } + + ohci->dev [usb_pipedevice (pipe)] = udev; + +// FIXME: don't do this if it's linked to the HC, +// we might clobber data toggle or other state ... + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe) + ? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | (udev->speed == USB_SPEED_LOW) << 13 + | usb_maxpacket (udev, pipe, usb_pipeout (pipe)) + << 16); + + if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + + spin_unlock_irqrestore (&ohci->lock, flags); + return ed; +} + +/*-------------------------------------------------------------------------*/ + +/* request unlinking of an endpoint from an operational HC. + * put the ep on the rm_list and stop the bulk or ctrl list + * real work is done at the next start frame (SF) hardware interrupt + */ +static void ed_unlink (struct usb_device *usb_dev, struct ed *ed) +{ + unsigned int frame; + struct ohci_hcd *ohci = hcd_to_ohci (usb_dev->bus->hcpriv); + + /* already pending? */ + if (ed->state & ED_URB_DEL) + return; + ed->state |= ED_URB_DEL; + + ed->hwINFO |= __constant_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: /* stop control list */ + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, + &ohci->regs->control); + break; + case PIPE_BULK: /* stop bulk list */ + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, + &ohci->regs->control); + break; + } + + frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1; + ed->ed_rm_list = ohci->ed_rm_list [frame]; + ohci->ed_rm_list [frame] = ed; + + /* enable SOF interrupt */ + if (!ohci->sleeping) { + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + } +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void +td_fill (struct ohci_hcd *ohci, unsigned int info, + dma_addr_t data, int len, + struct urb *urb, int index) +{ + volatile struct td *td, *td_pt; + urb_priv_t *urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + err ("internal OHCI error: TD index > length"); + return; + } + + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = dma_to_td (ohci, + le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->urb = urb; + td->data_dma = data; + if (!len) + data = 0; + + td->hwINFO = cpu_to_le32 (info); + if ((td->ed->type) == PIPE_ISOCHRONOUS) { + td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); + td->ed->last_iso = info & 0xffff; + } else { + td->hwCBP = cpu_to_le32 (data); + } + if (data) + td->hwBE = cpu_to_le32 (data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = cpu_to_le32 (td_pt->td_dma); + td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_urb (struct urb *urb) +{ + urb_priv_t *urb_priv = urb->hcpriv; + struct ohci_hcd *ohci = hcd_to_ohci (urb->dev->bus->hcpriv); + dma_addr_t data; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the + * USB-toggle bits for resetting + */ + if (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + if (data_len) { +#ifdef CONFIG_PCI + data = pci_map_single (ohci->hcd.pdev, + urb->transfer_buffer, data_len, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE + ); +#else +# error "what dma addr to use" +#endif + } else + data = 0; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe) + ? TD_CC | TD_DP_OUT + : TD_CC | TD_DP_IN ; + while (data_len > 4096) { + td_fill (ohci, + info | (cnt? TD_T_TOGGLE:toggle), + data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), + data, data_len, urb, cnt); + cnt++; + if ((urb->transfer_flags & USB_ZERO_PACKET) + && cnt < urb_priv->length) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), + 0, 0, urb, cnt); + cnt++; + } + /* start bulk list */ + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); + break; + + case PIPE_INTERRUPT: + info = TD_CC | toggle; + info |= usb_pipeout (urb->pipe) + ? TD_DP_OUT + : TD_R | TD_DP_IN; + td_fill (ohci, info, data, data_len, urb, cnt++); + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, +#ifdef CONFIG_PCI + pci_map_single (ohci->hcd.pdev, + urb->setup_packet, 8, + PCI_DMA_TODEVICE), +#else +# error "what dma addr to use" +#endif + 8, urb, cnt++); + if (data_len > 0) { + info = TD_CC | TD_R | TD_T_DATA1; + info |= usb_pipeout (urb->pipe) + ? TD_DP_OUT + : TD_DP_IN; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, + urb, cnt++); + } + info = usb_pipeout (urb->pipe) + ? TD_CC | TD_DP_IN | TD_T_DATA1 + : TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, urb, cnt++); + /* start control list */ + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); + break; + + case PIPE_ISOCHRONOUS: + for (cnt = 0; cnt < urb->number_of_packets; cnt++) { + td_fill (ohci, TD_CC | TD_ISO + | ((urb->start_frame + cnt) & 0xffff), + data + urb->iso_frame_desc [cnt].offset, + urb->iso_frame_desc [cnt].length, urb, cnt); + } + break; + } + if (urb_priv->length != cnt) + dbg ("TD LENGTH %d != CNT %d", urb_priv->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length (struct td *td) +{ + __u32 tdINFO, tdBE, tdCBP; + __u16 tdPSW; + struct urb *urb = td->urb; + urb_priv_t *urb_priv = urb->hcpriv; + int dlen = 0; + int cc = 0; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (tdINFO & TD_ISO) { + tdPSW = le16_to_cpu (td->hwPSW [0]); + cc = (tdPSW >> 12) & 0xF; + if (cc < 0xE) { + if (usb_pipeout (urb->pipe)) { + dlen = urb->iso_frame_desc [td->index].length; + } else { + dlen = tdPSW & 0x3ff; + } + urb->actual_length += dlen; + urb->iso_frame_desc [td->index].actual_length = dlen; + if (! (urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + urb->iso_frame_desc [td->index].status + = cc_to_error [cc]; + } + } else { /* BULK, INT, CONTROL DATA */ + if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) + || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + urb->actual_length += (td->hwCBP == 0) + ? (tdBE - td->data_dma + 1) + : (tdCBP - td->data_dma); + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we unreverse the hc-reversed done-list + */ +static struct td *dl_reverse_done_list (struct ohci_hcd *ohci) +{ + __u32 td_list_hc; + struct td *td_rev = NULL; + struct td *td_list = NULL; + urb_priv_t *urb_priv = NULL; + unsigned long flags; + + spin_lock_irqsave (&ohci->lock, flags); + + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = dma_to_td (ohci, td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + dbg (" USB-error/status: %x : %p", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), + td_list); + if (td_list->ed->hwHeadP + & __constant_cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) + < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td [urb_priv->length - 1]->hwNextTD + & __constant_cpu_to_le32 (0xfffffff0)) + | (td_list->ed->hwHeadP + & __constant_cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length + - td_list->index - 1; + } else + td_list->ed->hwHeadP &= + __constant_cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + spin_unlock_irqrestore (&ohci->lock, flags); + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* there are some pending requests to unlink + * - some URBs/TDs if urb_priv->state == URB_DEL + */ +static void dl_del_list (struct ohci_hcd *ohci, unsigned int frame) +{ + unsigned long flags; + struct ed *ed; + __u32 edINFO; + __u32 tdINFO; + struct td *td = NULL, *td_next = NULL, + *tdHeadP = NULL, *tdTailP; + __u32 *td_p; + int ctrl = 0, bulk = 0; + + spin_lock_irqsave (&ohci->lock, flags); + + for (ed = ohci->ed_rm_list [frame]; ed != NULL; ed = ed->ed_rm_list) { + + tdTailP = dma_to_td (ohci, + le32_to_cpup (&ed->hwTailP) & 0xfffffff0); + tdHeadP = dma_to_td (ohci, + le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); + edINFO = le32_to_cpup (&ed->hwINFO); + td_p = &ed->hwHeadP; + + for (td = tdHeadP; td != tdTailP; td = td_next) { + struct urb *urb = td->urb; + urb_priv_t *urb_priv = td->urb->hcpriv; + + td_next = dma_to_td (ohci, + le32_to_cpup (&td->hwNextTD) & 0xfffffff0); + if ((urb_priv->state == URB_DEL)) { + tdINFO = le32_to_cpup (&td->hwINFO); + if (TD_CC_GET (tdINFO) < 0xE) + dl_transfer_length (td); + *td_p = td->hwNextTD | (*td_p + & __constant_cpu_to_le32 (0x3)); + + /* URB is done; clean up */ + if (++ (urb_priv->td_cnt) == urb_priv->length) +// FIXME: we shouldn't hold ohci->lock here, else the +// completion function can't talk to this hcd ... + finish_urb (ohci, urb); + } else { + td_p = &td->hwNextTD; + } + } + + ed->state &= ~ED_URB_DEL; + tdHeadP = dma_to_td (ohci, + le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); + + if (tdHeadP == tdTailP) { + if (ed->state == ED_OPER) + ep_unlink (ohci, ed); + td_free (ohci, tdTailP); + ed->hwINFO = __constant_cpu_to_le32 (OHCI_ED_SKIP); + ed->state = ED_NEW; + } else + ed->hwINFO &= ~__constant_cpu_to_le32 (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + ctrl = 1; + break; + case PIPE_BULK: + bulk = 1; + break; + } + } + + /* maybe reenable control and bulk lists */ + if (!ohci->disabled) { + if (ctrl) /* reset control list */ + writel (0, &ohci->regs->ed_controlcurrent); + if (bulk) /* reset bulk list */ + writel (0, &ohci->regs->ed_bulkcurrent); + if (!ohci->ed_rm_list [!frame]) { + if (ohci->ed_controltail) + ohci->hc_control |= OHCI_CTRL_CLE; + if (ohci->ed_bulktail) + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + } + + ohci->ed_rm_list [frame] = NULL; + spin_unlock_irqrestore (&ohci->lock, flags); +} + + + +/*-------------------------------------------------------------------------*/ + +/* + * process normal completions (error or success) and some unlinked eds + * this is the main path for handing urbs back to drivers + */ +static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list) +{ + struct td *td_list_next = NULL; + struct ed *ed; + int cc = 0; + struct urb *urb; + urb_priv_t *urb_priv; + __u32 tdINFO, edHeadP, edTailP; + + unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length (td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc == TD_CC_STALL) + usb_endpoint_halt (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)); + + if (! (urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + if (++ (urb_priv->td_cnt) == urb_priv->length) { + /* + * Except for periodic transfers, both branches do + * the same thing. Periodic urbs get reissued until + * they're "deleted" with usb_unlink_urb. + */ + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (urb_priv->state != URB_DEL)) { + spin_lock (&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = cc_to_error [cc]; + spin_unlock (&urb->lock); + return_urb (ohci, urb); + } else + finish_urb (ohci, urb); + } + + spin_lock_irqsave (&ohci->lock, flags); + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + +// FIXME: ED_UNLINK is very fuzzy w.r.t. what the hc knows... + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + spin_unlock_irqrestore (&ohci->lock, flags); + + td_list = td_list_next; + } +} + diff -urN linux-2.5.3-pre2/drivers/usb/hcd/ohci.h linux/drivers/usb/hcd/ohci.h --- linux-2.5.3-pre2/drivers/usb/hcd/ohci.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/usb/hcd/ohci.h Mon Jan 21 17:23:23 2002 @@ -0,0 +1,360 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * This file is licenced under GPL + * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $ + */ + +static const int cc_to_error [16] = { + +/* map OHCI status to errno values */ + /* No Error */ 0, + /* CRC Error */ -EILSEQ, + /* Bit Stuff */ -EPROTO, + /* Data Togg */ -EILSEQ, + /* Stall */ -EPIPE, + /* DevNotResp */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* (for hw) */ -EIO, + /* (for hw) */ -EIO, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ENOSR, + /* (for HCD) */ -EALREADY, + /* (for HCD) */ -EALREADY +}; + + +/* ED States */ + +#define ED_NEW 0x00 /* unused, no dummy td */ +#define ED_UNLINK 0x01 /* dummy td, maybe linked to hc */ +#define ED_OPER 0x02 /* dummy td, _is_ linked to hc */ + +#define ED_URB_DEL 0x08 /* masked in */ + +/* usb_ohci_ed */ +struct ed { + /* first fields are hardware-specified */ + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed * ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; // ED_{NEW,UNLINK,OPER} + __u8 type; + __u16 last_iso; + struct ed * ed_rm_list; + + dma_addr_t dma; + __u32 unused [3]; +} __attribute((aligned(16))); + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 + /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D + /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct td { + /* first hardware fields are in all tds */ + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + + __u16 hwPSW [MAXPSW]; /* PSW is only for ISO */ + + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct urb *urb; + + dma_addr_t td_dma; + dma_addr_t data_dma; + __u32 unused2 [2]; +} __attribute((aligned(32))); /* iso needs 32 */ + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. The host controller is + * told the base address of it. It must be 256-byte aligned. + */ +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table [NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc [116]; +} __attribute((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus [MAX_ROOT_PORTS]; + } roothub; + + /* and some optional registers for legacy compatibility */ +} __attribute((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + + +/* urb */ +typedef struct urb_priv +{ + struct ed *ed; + __u16 length; // # tds in this request + __u16 td_cnt; // tds already serviced + int state; + struct td *td [0]; // all TDs in this request + +} urb_priv_t; + +#define URB_DEL 1 + + +/* Hash struct used for TD/ED hashing */ +struct hash_t { + void *virt; + dma_addr_t dma; + struct hash_t *next; // chaining for collision cases +}; + +/* List of TD/ED hash entries */ +struct hash_list_t { + struct hash_t *head; + struct hash_t *tail; +}; + +#define TD_HASH_SIZE 64 /* power'o'two */ +#define ED_HASH_SIZE 64 /* power'o'two */ + +#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE) +#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE) + + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + +struct ohci_hcd { + spinlock_t lock; + + /* + * I/O memory used to communicate with the HC (uncached); + */ + struct ohci_regs *regs; + + /* + * main memory used to communicate with the HC (uncached) + */ + struct ohci_hcca *hcca; + dma_addr_t hcca_dma; + + struct ed *ed_rm_list [2]; /* to be removed */ + + struct ed *ed_bulktail; /* last in bulk list */ + struct ed *ed_controltail; /* last in ctrl list */ + struct ed *ed_isotail; /* last in iso list */ + +#ifdef CONFIG_PCI + struct pci_pool *td_cache; + struct pci_pool *ed_cache; + struct hash_list_t td_hash [TD_HASH_SIZE]; + struct hash_list_t ed_hash [ED_HASH_SIZE]; +#endif + + /* + * driver state + */ + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + int ohci_int_load [NUM_INTS]; + u32 hc_control; /* copy of hc control reg */ + struct usb_device *dev [128]; + + unsigned long flags; /* for HC bugs */ +#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ + + /* + * framework state + */ + struct usb_hcd hcd; +}; + +#define hcd_to_ohci(hcd_ptr) list_entry(hcd_ptr, struct ohci_hcd, hcd) + diff -urN linux-2.5.3-pre2/drivers/usb/hcd.c linux/drivers/usb/hcd.c --- linux-2.5.3-pre2/drivers/usb/hcd.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/hcd.c Mon Jan 21 17:23:23 2002 @@ -606,7 +606,7 @@ return retval; } } - dev->driver_data = hcd; + pci_set_drvdata(dev, hcd); hcd->driver = driver; hcd->description = driver->description; hcd->pdev = dev; @@ -689,7 +689,7 @@ struct usb_hcd *hcd; struct usb_device *hub; - hcd = (struct usb_hcd *) dev->driver_data; + hcd = pci_get_drvdata(dev); if (!hcd) return; info ("remove: %s, state %x", hcd->bus_name, hcd->state); @@ -769,7 +769,7 @@ struct usb_hcd *hcd; int retval; - hcd = (struct usb_hcd *) dev->driver_data; + hcd = pci_get_drvdata(dev); info ("suspend %s to state %d", hcd->bus_name, state); pci_save_state (dev, hcd->pci_state); @@ -798,7 +798,7 @@ struct usb_hcd *hcd; int retval; - hcd = (struct usb_hcd *) dev->driver_data; + hcd = pci_get_drvdata(dev); info ("resume %s", hcd->bus_name); /* guard against multiple resumes (APM bug?) */ diff -urN linux-2.5.3-pre2/drivers/usb/ibmcam.h linux/drivers/usb/ibmcam.h --- linux-2.5.3-pre2/drivers/usb/ibmcam.h Mon Dec 11 13:17:55 2000 +++ linux/drivers/usb/ibmcam.h Mon Jan 21 17:23:23 2002 @@ -147,7 +147,7 @@ struct ibmcam_sbuf { char *data; - urb_t *urb; + struct urb *urb; }; struct ibmcam_frame { diff -urN linux-2.5.3-pre2/drivers/usb/kaweth.c linux/drivers/usb/kaweth.c --- linux-2.5.3-pre2/drivers/usb/kaweth.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/kaweth.c Mon Jan 21 17:23:23 2002 @@ -953,7 +953,7 @@ /*-------------------------------------------------------------------* * completion handler for compatibility wrappers (sync control/bulk) * *-------------------------------------------------------------------*/ -static void usb_api_blocking_completion(urb_t *urb) +static void usb_api_blocking_completion(struct urb *urb) { struct usb_api_data *awd = (struct usb_api_data *)urb->context; @@ -966,7 +966,7 @@ *-------------------------------------------------------------------*/ // Starts urb and waits for completion or timeout -static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) { DECLARE_WAITQUEUE(wait, current); struct usb_api_data awd; @@ -1017,7 +1017,7 @@ struct usb_ctrlrequest *cmd, void *data, int len, int timeout) { - urb_t *urb; + struct urb *urb; int retv; int length; diff -urN linux-2.5.3-pre2/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.5.3-pre2/drivers/usb/ov511.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/ov511.c Mon Jan 21 17:23:23 2002 @@ -3875,7 +3875,7 @@ **********************************************************************/ static int -ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) +ov511_move_data(struct usb_ov511 *ov511, struct urb *urb) { unsigned char *cdata; int data_size, num, offset, i, totlen = 0; @@ -4108,7 +4108,7 @@ } static int -ov518_move_data(struct usb_ov511 *ov511, urb_t *urb) +ov518_move_data(struct usb_ov511 *ov511, struct urb *urb) { unsigned char *cdata; int i, data_size, totlen = 0; @@ -4369,7 +4369,7 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) { - urb_t *urb; + struct urb *urb; int fx, err, n, size; PDEBUG(3, "*** Initializing capture ***"); diff -urN linux-2.5.3-pre2/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.5.3-pre2/drivers/usb/ov511.h Thu Jan 3 18:52:28 2002 +++ linux/drivers/usb/ov511.h Mon Jan 21 17:23:23 2002 @@ -366,7 +366,7 @@ struct ov511_sbuf { char *data; - urb_t *urb; + struct urb *urb; }; enum { diff -urN linux-2.5.3-pre2/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c --- linux-2.5.3-pre2/drivers/usb/pegasus.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/pegasus.c Mon Jan 21 17:23:23 2002 @@ -94,7 +94,7 @@ static int update_eth_regs_async( pegasus_t * ); /* Aargh!!! I _really_ hate such tweaks */ -static void ctrl_callback( urb_t *urb ) +static void ctrl_callback( struct urb *urb ) { pegasus_t *pegasus = urb->context; diff -urN linux-2.5.3-pre2/drivers/usb/se401.c linux/drivers/usb/se401.c --- linux-2.5.3-pre2/drivers/usb/se401.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/se401.c Mon Jan 21 17:23:23 2002 @@ -610,7 +610,7 @@ */ static int se401_start_stream(struct usb_se401 *se401) { - urb_t *urb; + struct urb *urb; int err=0, i; se401->streaming=1; diff -urN linux-2.5.3-pre2/drivers/usb/se401.h linux/drivers/usb/se401.h --- linux-2.5.3-pre2/drivers/usb/se401.h Fri Sep 7 10:59:04 2001 +++ linux/drivers/usb/se401.h Mon Jan 21 17:23:23 2002 @@ -197,8 +197,8 @@ char *fbuf; /* Videodev buffer area */ - urb_t *urb[SE401_NUMSBUF]; - urb_t *inturb; + struct urb *urb[SE401_NUMSBUF]; + struct urb *inturb; int button; int buttonpressed; diff -urN linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.c linux/drivers/usb/serial/ftdi_sio.c --- linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/serial/ftdi_sio.c Mon Jan 21 17:23:23 2002 @@ -138,6 +138,7 @@ static __devinitdata struct usb_device_id id_table_8U232AM [] = { { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, + { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { } /* Terminating entry */ }; @@ -145,6 +146,7 @@ static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, + { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { } /* Terminating entry */ }; diff -urN linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.h linux/drivers/usb/serial/ftdi_sio.h --- linux-2.5.3-pre2/drivers/usb/serial/ftdi_sio.h Thu May 24 14:55:51 2001 +++ linux/drivers/usb/serial/ftdi_sio.h Mon Jan 21 17:23:23 2002 @@ -22,6 +22,8 @@ #define FTDI_VID 0x0403 /* Vendor Id */ #define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ +#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ +#define FTDI_NF_RIC_PID 0x0001 /* Product Id */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff -urN linux-2.5.3-pre2/drivers/usb/serial/io_edgeport.c linux/drivers/usb/serial/io_edgeport.c --- linux-2.5.3-pre2/drivers/usb/serial/io_edgeport.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/serial/io_edgeport.c Mon Jan 21 17:23:23 2002 @@ -2456,7 +2456,7 @@ { struct edgeport_serial *edge_serial = (struct edgeport_serial *)edge_port->port->serial->private; int status = 0; - urb_t *urb; + struct urb *urb; int timeout; usb_serial_debug_data (__FILE__, __FUNCTION__, length, buffer); diff -urN linux-2.5.3-pre2/drivers/usb/serial/keyspan.c linux/drivers/usb/serial/keyspan.c --- linux-2.5.3-pre2/drivers/usb/serial/keyspan.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/serial/keyspan.c Mon Jan 21 17:23:23 2002 @@ -109,11 +109,11 @@ const keyspan_device_details *device_details; - urb_t *instat_urb; + struct urb *instat_urb; char instat_buf[INSTAT_BUFLEN]; /* XXX this one probably will need a lock */ - urb_t *glocont_urb; + struct urb *glocont_urb; char glocont_buf[GLOCONT_BUFLEN]; }; @@ -128,18 +128,18 @@ const keyspan_device_details *device_details; /* Input endpoints and buffer for this port */ - urb_t *in_urbs[2]; + struct urb *in_urbs[2]; char in_buffer[2][64]; /* Output endpoints and buffer for this port */ - urb_t *out_urbs[2]; + struct urb *out_urbs[2]; char out_buffer[2][64]; /* Input ack endpoint */ - urb_t *inack_urb; + struct urb *inack_urb; char inack_buffer[1]; /* Output control endpoint */ - urb_t *outcont_urb; + struct urb *outcont_urb; char outcont_buffer[64]; /* Settings for the port */ @@ -324,7 +324,7 @@ const keyspan_device_details *d_details; int flip; int left, todo; - urb_t *this_urb; + struct urb *this_urb; int err; p_priv = (struct keyspan_port_private *)(port->private); @@ -853,7 +853,7 @@ struct usb_serial *serial = port->serial; const keyspan_device_details *d_details; int i, already_active, err; - urb_t *urb; + struct urb *urb; s_priv = (struct keyspan_serial_private *)(serial->private); p_priv = (struct keyspan_port_private *)(port->private); @@ -890,7 +890,7 @@ return (0); } -static inline void stop_urb(urb_t *urb) +static inline void stop_urb(struct urb *urb) { if (urb && urb->status == -EINPROGRESS) { urb->transfer_flags &= ~USB_ASYNC_UNLINK; @@ -1041,11 +1041,11 @@ } /* Helper functions used by keyspan_setup_urbs */ -static urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint, - int dir, void *ctx, char *buf, int len, - void (*callback)(urb_t *)) +static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint, + int dir, void *ctx, char *buf, int len, + void (*callback)(struct urb *)) { - urb_t *urb; + struct urb *urb; if (endpoint == -1) return NULL; /* endpoint not needed */ @@ -1066,12 +1066,12 @@ } static struct callbacks { - void (*instat_callback)(urb_t *); - void (*glocont_callback)(urb_t *); - void (*indat_callback)(urb_t *); - void (*outdat_callback)(urb_t *); - void (*inack_callback)(urb_t *); - void (*outcont_callback)(urb_t *); + void (*instat_callback)(struct urb *); + void (*glocont_callback)(struct urb *); + void (*indat_callback)(struct urb *); + void (*outdat_callback)(struct urb *); + void (*inack_callback)(struct urb *); + void (*outcont_callback)(struct urb *); } keyspan_callbacks[] = { { /* msg_usa26 callbacks */ @@ -1295,7 +1295,7 @@ struct keyspan_port_private *p_priv; const keyspan_device_details *d_details; int outcont_urb; - urb_t *this_urb; + struct urb *this_urb; int err; dbg ("%s reset=%d\n", __FUNCTION__, reset_port); @@ -1430,7 +1430,7 @@ struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const keyspan_device_details *d_details; - urb_t *this_urb; + struct urb *this_urb; int err; s_priv = (struct keyspan_serial_private *)(serial->private); @@ -1516,7 +1516,7 @@ struct keyspan_port_private *p_priv; const keyspan_device_details *d_details; int glocont_urb; - urb_t *this_urb; + struct urb *this_urb; int err; int device_port; diff -urN linux-2.5.3-pre2/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c --- linux-2.5.3-pre2/drivers/usb/storage/transport.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/storage/transport.c Mon Jan 21 17:23:23 2002 @@ -369,7 +369,7 @@ /* This is the completion handler which will wake us up when an URB * completes. */ -static void usb_stor_blocking_completion(urb_t *urb) +static void usb_stor_blocking_completion(struct urb *urb) { struct completion *urb_done_ptr = (struct completion *)urb->context; diff -urN linux-2.5.3-pre2/drivers/usb/stv680.c linux/drivers/usb/stv680.c --- linux-2.5.3-pre2/drivers/usb/stv680.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/stv680.c Mon Jan 21 17:23:23 2002 @@ -766,7 +766,7 @@ static int stv680_start_stream (struct usb_stv *stv680) { - urb_t *urb; + struct urb *urb; int err = 0, i; stv680->streaming = 1; diff -urN linux-2.5.3-pre2/drivers/usb/stv680.h linux/drivers/usb/stv680.h --- linux-2.5.3-pre2/drivers/usb/stv680.h Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/stv680.h Mon Jan 21 17:23:23 2002 @@ -118,7 +118,7 @@ int removed; /* device disconnected */ int streaming; /* Are we streaming video? */ char *fbuf; /* Videodev buffer area */ - urb_t *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */ + struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */ int curframe; /* Current receiving frame */ struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */ int readcount; diff -urN linux-2.5.3-pre2/drivers/usb/uhci.c linux/drivers/usb/uhci.c --- linux-2.5.3-pre2/drivers/usb/uhci.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/uhci.c Mon Jan 21 17:23:23 2002 @@ -520,7 +520,8 @@ lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), + uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1)); /* All qh's in the queue need to link to the next queue */ urbp->qh->link = eurbp->qh->link; @@ -556,6 +557,7 @@ /* Fix up the toggle for the next URB's */ if (!urbp->queued) + /* We set the toggle when we unlink */ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); else { /* If we're in the middle of the queue, grab the toggle */ @@ -1683,8 +1685,8 @@ /* Control and Isochronous ignore the toggle, so this */ /* is safe for all types */ - if (!(td->status & TD_CTRL_ACTIVE) && - (uhci_actual_length(td->status) < uhci_expected_length(td->info) || + if ((!(td->status & TD_CTRL_ACTIVE) && + (uhci_actual_length(td->status) < uhci_expected_length(td->info)) || tmp == head)) { usb_settoggle(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info), diff -urN linux-2.5.3-pre2/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c --- linux-2.5.3-pre2/drivers/usb/usb-ohci.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/usb-ohci.c Mon Jan 21 17:23:23 2002 @@ -173,7 +173,7 @@ kfree (urb_priv); } -static void urb_rm_priv_locked (urb_t * urb) +static void urb_rm_priv_locked (struct urb * urb) { urb_priv_t * urb_priv = urb->hcpriv; @@ -207,7 +207,7 @@ } } -static void urb_rm_priv (urb_t * urb) +static void urb_rm_priv (struct urb * urb) { unsigned long flags; @@ -224,7 +224,7 @@ /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void urb_print (urb_t * urb, char * str, int small) +static void urb_print (struct urb * urb, char * str, int small) { unsigned int pipe= urb->pipe; @@ -453,10 +453,10 @@ /* return a request to the completion handler */ -static int sohci_return_urb (struct ohci *hc, urb_t * urb) +static int sohci_return_urb (struct ohci *hc, struct urb * urb) { urb_priv_t * urb_priv = urb->hcpriv; - urb_t * urbt; + struct urb * urbt; unsigned long flags; int i; @@ -532,7 +532,7 @@ /* get a transfer request */ -static int sohci_submit_urb (urb_t * urb) +static int sohci_submit_urb (struct urb * urb) { ohci_t * ohci; ed_t * ed; @@ -716,7 +716,7 @@ /* deactivate all TDs and remove the private part of the URB */ /* interrupt callers must use async unlink mode */ -static int sohci_unlink_urb (urb_t * urb) +static int sohci_unlink_urb (struct urb * urb) { unsigned long flags; ohci_t * ohci; @@ -1295,7 +1295,7 @@ static void td_fill (ohci_t * ohci, unsigned int info, dma_addr_t data, int len, - urb_t * urb, int index) + struct urb * urb, int index) { volatile td_t * td, * td_pt; urb_priv_t * urb_priv = urb->hcpriv; @@ -1343,7 +1343,7 @@ /* prepare all TDs of a transfer */ -static void td_submit_urb (urb_t * urb) +static void td_submit_urb (struct urb * urb) { urb_priv_t * urb_priv = urb->hcpriv; ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; @@ -1452,7 +1452,7 @@ { __u32 tdINFO, tdBE, tdCBP; __u16 tdPSW; - urb_t * urb = td->urb; + struct urb * urb = td->urb; urb_priv_t * urb_priv = urb->hcpriv; int dlen = 0; int cc = 0; @@ -1493,7 +1493,7 @@ /* handle an urb that is being unlinked */ -static void dl_del_urb (urb_t * urb) +static void dl_del_urb (struct urb * urb) { wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; @@ -1582,7 +1582,7 @@ td_p = &ed->hwHeadP; for (td = tdHeadP; td != tdTailP; td = td_next) { - urb_t * urb = td->urb; + struct urb * urb = td->urb; urb_priv_t * urb_priv = td->urb->hcpriv; td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0); @@ -1670,7 +1670,7 @@ td_t * td_list_next = NULL; ed_t * ed; int cc = 0; - urb_t * urb; + struct urb * urb; urb_priv_t * urb_priv; __u32 tdINFO, edHeadP, edTailP; @@ -1846,7 +1846,7 @@ { int len; - urb_t * urb = (urb_t *) ptr; + struct urb * urb = (struct urb *) ptr; ohci_t * ohci = urb->dev->bus->hcpriv; if (ohci->disabled) @@ -1875,7 +1875,7 @@ /* Root Hub INTs are polled by this timer */ -static int rh_init_int_timer (urb_t * urb) +static int rh_init_int_timer (struct urb * urb) { ohci_t * ohci = urb->dev->bus->hcpriv; @@ -1900,7 +1900,7 @@ /* request to virtual root hub */ -static int rh_submit_urb (urb_t * urb) +static int rh_submit_urb (struct urb * urb) { struct usb_device * usb_dev = urb->dev; ohci_t * ohci = usb_dev->bus->hcpriv; @@ -2106,7 +2106,7 @@ /*-------------------------------------------------------------------------*/ -static int rh_unlink_urb (urb_t * urb) +static int rh_unlink_urb (struct urb * urb) { ohci_t * ohci = urb->dev->bus->hcpriv; diff -urN linux-2.5.3-pre2/drivers/usb/usb-ohci.h linux/drivers/usb/usb-ohci.h --- linux-2.5.3-pre2/drivers/usb/usb-ohci.h Sat Dec 8 20:28:45 2001 +++ linux/drivers/usb/usb-ohci.h Mon Jan 21 17:23:23 2002 @@ -111,7 +111,7 @@ __u8 index; struct ed * ed; struct td * next_dl_td; - urb_t * urb; + struct urb * urb; dma_addr_t td_dma; dma_addr_t data_dma; @@ -430,12 +430,12 @@ static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags); static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); /* td */ -static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, urb_t * urb, int index); -static void td_submit_urb(urb_t * urb); +static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index); +static void td_submit_urb(struct urb * urb); /* root hub */ -static int rh_submit_urb(urb_t * urb); -static int rh_unlink_urb(urb_t * urb); -static int rh_init_int_timer(urb_t * urb); +static int rh_submit_urb(struct urb * urb); +static int rh_unlink_urb(struct urb * urb); +static int rh_init_int_timer(struct urb * urb); /*-------------------------------------------------------------------------*/ diff -urN linux-2.5.3-pre2/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c --- linux-2.5.3-pre2/drivers/usb/usb-uhci.c Sun Jan 6 18:49:21 2002 +++ linux/drivers/usb/usb-uhci.c Mon Jan 21 17:23:23 2002 @@ -113,12 +113,12 @@ // Suppress HC interrupt error messages for 5s #define ERROR_SUPPRESSION_TIME (HZ*5) -_static int rh_submit_urb (urb_t *urb); -_static int rh_unlink_urb (urb_t *urb); +_static int rh_submit_urb (struct urb *urb); +_static int rh_unlink_urb (struct urb *urb); _static int delete_qh (uhci_t *s, uhci_desc_t *qh); -_static int process_transfer (uhci_t *s, urb_t *urb, int mode); -_static int process_interrupt (uhci_t *s, urb_t *urb); -_static int process_iso (uhci_t *s, urb_t *urb, int force); +_static int process_transfer (uhci_t *s, struct urb *urb, int mode); +_static int process_interrupt (uhci_t *s, struct urb *urb); +_static int process_iso (uhci_t *s, struct urb *urb, int force); // How much URBs with ->next are walked #define MAX_NEXT_COUNT 2048 @@ -164,7 +164,7 @@ } /*-------------------------------------------------------------------*/ #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH -_static void enable_desc_loop(uhci_t *s, urb_t *urb) +_static void enable_desc_loop(uhci_t *s, struct urb *urb) { int flags; @@ -179,7 +179,7 @@ spin_unlock_irqrestore (&s->qh_lock, flags); } /*-------------------------------------------------------------------*/ -_static void disable_desc_loop(uhci_t *s, urb_t *urb) +_static void disable_desc_loop(uhci_t *s, struct urb *urb) { int flags; @@ -200,7 +200,7 @@ } #endif /*-------------------------------------------------------------------*/ -_static void queue_urb_unlocked (uhci_t *s, urb_t *urb) +_static void queue_urb_unlocked (uhci_t *s, struct urb *urb) { struct list_head *p=&urb->urb_list; #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH @@ -220,7 +220,7 @@ uhci_switch_timer_int(s); } /*-------------------------------------------------------------------*/ -_static void queue_urb (uhci_t *s, urb_t *urb) +_static void queue_urb (uhci_t *s, struct urb *urb) { unsigned long flags=0; @@ -229,7 +229,7 @@ spin_unlock_irqrestore (&s->urb_list_lock, flags); } /*-------------------------------------------------------------------*/ -_static void dequeue_urb (uhci_t *s, urb_t *urb) +_static void dequeue_urb (uhci_t *s, struct urb *urb) { #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH int type; @@ -694,7 +694,7 @@ // LOW LEVEL STUFF // assembles QHs und TDs for control, bulk and iso /*-------------------------------------------------------------------*/ -_static int uhci_submit_control_urb (urb_t *urb) +_static int uhci_submit_control_urb (struct urb *urb) { uhci_desc_t *qh, *td; uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; @@ -811,7 +811,7 @@ // For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh) // Due to the linking with other bulk urbs, it has to be locked with urb_list_lock! -_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb) +_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL; @@ -973,7 +973,7 @@ looks a bit complicated because of all the bulk queueing goodies */ -_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode) +_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode) { uhci_desc_t *bqh, *nqh, *prevqh, *prevtd; int now; @@ -1027,7 +1027,7 @@ urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh); if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion - urb_t *nurb; + struct urb *nurb; unsigned long flags; nurb = priv->next_queued_urb; @@ -1065,7 +1065,7 @@ } /*-------------------------------------------------------------------*/ // Release bandwidth for Interrupt or Isoc. transfers -_static void uhci_release_bandwidth(urb_t *urb) +_static void uhci_release_bandwidth(struct urb *urb) { if (urb->bandwidth) { switch (usb_pipetype(urb->pipe)) { @@ -1081,7 +1081,7 @@ } } -_static void uhci_urb_dma_sync(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv) +_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) { if (urb_priv->setup_packet_dma) pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma, @@ -1095,7 +1095,7 @@ PCI_DMA_TODEVICE); } -_static void uhci_urb_dma_unmap(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv) +_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv) { if (urb_priv->setup_packet_dma) { pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma, @@ -1116,7 +1116,7 @@ mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list */ -_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode) +_static int uhci_unlink_urb_async (uhci_t *s, struct urb *urb, int mode) { uhci_desc_t *qh; urb_priv_t *urb_priv; @@ -1161,7 +1161,7 @@ } /*-------------------------------------------------------------------*/ // kills an urb by unlinking descriptors and waiting for at least one frame -_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb) +_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb) { uhci_desc_t *qh; urb_priv_t *urb_priv; @@ -1224,7 +1224,7 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) { struct list_head *q; - urb_t *urb; + struct urb *urb; struct usb_device *dev; int now, type; urb_priv_t *urb_priv; @@ -1234,7 +1234,7 @@ while (q != &s->urb_unlinked) { - urb = list_entry (q, urb_t, urb_list); + urb = list_entry (q, struct urb, urb_list); urb_priv = (urb_priv_t*)urb->hcpriv; q = urb->urb_list.next; @@ -1303,7 +1303,7 @@ } /*-------------------------------------------------------------------*/ -_static int uhci_unlink_urb (urb_t *urb) +_static int uhci_unlink_urb (struct urb *urb) { uhci_t *s; unsigned long flags=0; @@ -1336,9 +1336,9 @@ // In case of ASAP iso transfer, search the URB-list for already queued URBs // for this EP and calculate the earliest start frame for the new // URB (easy seamless URB continuation!) -_static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end) +_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end) { - urb_t *u, *last_urb = NULL; + struct urb *u, *last_urb = NULL; uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; struct list_head *p; int ret=-1; @@ -1348,7 +1348,7 @@ p=s->urb_list.prev; for (; p != &s->urb_list; p = p->prev) { - u = list_entry (p, urb_t, urb_list); + u = list_entry (p, struct urb, urb_list); // look for pending URBs with identical pipe handle // works only because iso doesn't toggle the data bit! if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { @@ -1370,7 +1370,7 @@ /*-------------------------------------------------------------------*/ // adjust start_frame according to scheduling constraints (ASAP etc) -_static int iso_find_start (urb_t *urb) +_static int iso_find_start (struct urb *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; unsigned int now; @@ -1428,7 +1428,7 @@ // ASAP-flag set implicitely // if period==0, the transfer is only done once -_static int uhci_submit_int_urb (urb_t *urb) +_static int uhci_submit_int_urb (struct urb *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; @@ -1488,7 +1488,7 @@ return 0; } /*-------------------------------------------------------------------*/ -_static int uhci_submit_iso_urb (urb_t *urb) +_static int uhci_submit_iso_urb (struct urb *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; @@ -1581,10 +1581,10 @@ /*-------------------------------------------------------------------*/ // returns: 0 (no transfer queued), urb* (this urb already queued) -_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb) +_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb) { struct list_head *p; - urb_t *tmp; + struct urb *tmp; unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); dbg("search_dev_ep:"); @@ -1592,7 +1592,7 @@ p=s->urb_list.next; for (; p != &s->urb_list; p = p->next) { - tmp = list_entry (p, urb_t, urb_list); + tmp = list_entry (p, struct urb, urb_list); dbg("urb: %p", tmp); // we can accept this urb if it is not queued at this time // or if non-iso transfer requests should be scheduled for the same device and pipe @@ -1605,13 +1605,13 @@ return 0; } /*-------------------------------------------------------------------*/ -_static int uhci_submit_urb (urb_t *urb) +_static int uhci_submit_urb (struct urb *urb) { uhci_t *s; urb_priv_t *urb_priv; int ret = 0, type; unsigned long flags; - urb_t *queued_urb=NULL; + struct urb *queued_urb=NULL; int bustime; if (!urb->dev || !urb->dev->bus) @@ -1765,7 +1765,7 @@ _static void uhci_check_timeouts(uhci_t *s) { struct list_head *p,*p2; - urb_t *urb; + struct urb *urb; int type; p = s->urb_list.prev; @@ -1775,7 +1775,7 @@ p2 = p; p = p->prev; - urb = list_entry (p2, urb_t, urb_list); + urb = list_entry (p2, struct urb, urb_list); type = usb_pipetype (urb->pipe); hcpriv = (urb_priv_t*)urb->hcpriv; @@ -1875,7 +1875,7 @@ /*-------------------------------------------------------------------------*/ /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ -_static int rh_send_irq (urb_t *urb) +_static int rh_send_irq (struct urb *urb) { int len = 1; int i; @@ -1902,12 +1902,12 @@ /*-------------------------------------------------------------------------*/ /* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ -_static int rh_init_int_timer (urb_t *urb); +_static int rh_init_int_timer (struct urb *urb); _static void rh_int_timer_do (unsigned long ptr) { int len; - urb_t *urb = (urb_t*) ptr; + struct urb *urb = (struct urb *) ptr; uhci_t *uhci = urb->dev->bus->hcpriv; if (uhci->rh.send) { @@ -1924,7 +1924,7 @@ /*-------------------------------------------------------------------------*/ /* Root Hub INTs are polled by this timer, polling interval 20ms */ -_static int rh_init_int_timer (urb_t *urb) +_static int rh_init_int_timer (struct urb *urb) { uhci_t *uhci = urb->dev->bus->hcpriv; @@ -1958,7 +1958,7 @@ *************************/ -_static int rh_submit_urb (urb_t *urb) +_static int rh_submit_urb (struct urb *urb) { struct usb_device *usb_dev = urb->dev; uhci_t *uhci = usb_dev->bus->hcpriv; @@ -2159,7 +2159,7 @@ } /*-------------------------------------------------------------------------*/ -_static int rh_unlink_urb (urb_t *urb) +_static int rh_unlink_urb (struct urb *urb) { uhci_t *uhci = urb->dev->bus->hcpriv; @@ -2217,14 +2217,14 @@ unsigned long flags; struct list_head *p; struct list_head *p2; - urb_t *urb; + struct urb *urb; spin_lock_irqsave (&s->urb_list_lock, flags); p = s->urb_list.prev; while (p != &s->urb_list) { p2 = p; p = p->prev ; - urb = list_entry (p2, urb_t, urb_list); + urb = list_entry (p2, struct urb, urb_list); dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); //urb->transfer_flags |=USB_ASYNC_UNLINK; @@ -2274,7 +2274,7 @@ uhci_unlink_urb }; -_static void correct_data_toggles(urb_t *urb) +_static void correct_data_toggles(struct urb *urb) { usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))); @@ -2304,7 +2304,7 @@ * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb) */ -_static int process_transfer (uhci_t *s, urb_t *urb, int mode) +_static int process_transfer (uhci_t *s, struct urb *urb, int mode) { int ret = 0; urb_priv_t *urb_priv = urb->hcpriv; @@ -2392,7 +2392,7 @@ if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; - urb_t *next_queued_urb=priv->next_queued_urb; + struct urb *next_queued_urb=priv->next_queued_urb; if (next_queued_urb) { urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv; @@ -2423,7 +2423,7 @@ return ret; } -_static int process_interrupt (uhci_t *s, urb_t *urb) +_static int process_interrupt (uhci_t *s, struct urb *urb) { int i, ret = -EINPROGRESS; urb_priv_t *urb_priv = urb->hcpriv; @@ -2520,7 +2520,7 @@ // mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs // mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked) -_static int process_iso (uhci_t *s, urb_t *urb, int mode) +_static int process_iso (uhci_t *s, struct urb *urb, int mode) { int i; int ret = 0; @@ -2589,9 +2589,9 @@ _static int process_urb (uhci_t *s, struct list_head *p) { int ret = 0; - urb_t *urb; + struct urb *urb; - urb=list_entry (p, urb_t, urb_list); + urb=list_entry (p, struct urb, urb_list); //dbg("process_urb: found queued urb: %p", urb); switch (usb_pipetype (urb->pipe)) { @@ -2640,7 +2640,7 @@ #endif if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { // process_interrupt does completion on its own - urb_t *next_urb = urb->next; + struct urb *next_urb = urb->next; int is_ring = 0; int contains_killed = 0; int loop_count=0; diff -urN linux-2.5.3-pre2/drivers/usb/usb-uhci.h linux/drivers/usb/usb-uhci.h --- linux-2.5.3-pre2/drivers/usb/usb-uhci.h Tue Aug 28 11:21:02 2001 +++ linux/drivers/usb/usb-uhci.h Mon Jan 21 17:23:23 2002 @@ -158,8 +158,8 @@ dma_addr_t setup_packet_dma; dma_addr_t transfer_buffer_dma; unsigned long started; - urb_t *next_queued_urb; // next queued urb for this EP - urb_t *prev_queued_urb; + struct urb *next_queued_urb; // next queued urb for this EP + struct urb *prev_queued_urb; uhci_desc_t *bottom_qh; uhci_desc_t *next_qh; // next helper QH char use_loop; diff -urN linux-2.5.3-pre2/drivers/usb/usb.c linux/drivers/usb/usb.c --- linux-2.5.3-pre2/drivers/usb/usb.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/usb.c Mon Jan 21 17:23:23 2002 @@ -1084,12 +1084,13 @@ * * The driver should call usb_free_urb() when it is finished with the urb. */ -urb_t *usb_alloc_urb(int iso_packets) +struct urb *usb_alloc_urb(int iso_packets) { - urb_t *urb; + struct urb *urb; - urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + urb = (struct urb *)kmalloc(sizeof(struct urb) + + iso_packets * sizeof(struct usb_iso_packet_descriptor), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (!urb) { err("alloc_urb: kmalloc failed"); return NULL; @@ -1110,7 +1111,7 @@ * cleaned up with a call to usb_free_urb() when the driver is finished * with it. */ -void usb_free_urb(urb_t* urb) +void usb_free_urb(struct urb *urb) { if (urb) kfree(urb); @@ -1171,7 +1172,7 @@ * the periodic request, and bandwidth reservation is being done for * this controller, submitting such a periodic request will fail. */ -int usb_submit_urb(urb_t *urb) +int usb_submit_urb(struct urb *urb) { if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) return urb->dev->bus->op->submit_urb(urb); @@ -1205,7 +1206,7 @@ * and the completion function will see status -ECONNRESET. Failure is * indicated by any other return value. */ -int usb_unlink_urb(urb_t *urb) +int usb_unlink_urb(struct urb *urb) { if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) return urb->dev->bus->op->unlink_urb(urb); @@ -1221,7 +1222,7 @@ int done; }; -static void usb_api_blocking_completion(urb_t *urb) +static void usb_api_blocking_completion(struct urb *urb) { struct usb_api_data *awd = (struct usb_api_data *)urb->context; @@ -1231,7 +1232,7 @@ } // Starts urb and waits for completion or timeout -static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) { DECLARE_WAITQUEUE(wait, current); struct usb_api_data awd; @@ -1289,7 +1290,7 @@ int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, struct usb_ctrlrequest *cmd, void *data, int len, int timeout) { - urb_t *urb; + struct urb *urb; int retv; int length; @@ -1376,7 +1377,7 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { - urb_t *urb; + struct urb *urb; if (len < 0) return -EINVAL; diff -urN linux-2.5.3-pre2/drivers/usb/usbvideo.c linux/drivers/usb/usbvideo.c --- linux-2.5.3-pre2/drivers/usb/usbvideo.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/usbvideo.c Mon Jan 21 17:23:23 2002 @@ -1776,7 +1776,7 @@ /* * Make all of the blocks of data contiguous */ -static int usbvideo_CompressIsochronous(uvd_t *uvd, urb_t *urb) +static int usbvideo_CompressIsochronous(uvd_t *uvd, struct urb *urb) { char *cdata; int i, totlen = 0; @@ -1891,7 +1891,7 @@ /* We double buffer the Iso lists */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { int j, k; - urb_t *urb = uvd->sbuf[i].urb; + struct urb *urb = uvd->sbuf[i].urb; urb->dev = dev; urb->context = uvd; urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); diff -urN linux-2.5.3-pre2/drivers/usb/usbvideo.h linux/drivers/usb/usbvideo.h --- linux-2.5.3-pre2/drivers/usb/usbvideo.h Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/usbvideo.h Mon Jan 21 17:23:23 2002 @@ -165,7 +165,7 @@ /* This structure represents one Isoc request - URB and buffer */ typedef struct { char *data; - urb_t *urb; + struct urb *urb; } usbvideo_sbuf_t; typedef struct { diff -urN linux-2.5.3-pre2/drivers/usb/vicam.c linux/drivers/usb/vicam.c --- linux-2.5.3-pre2/drivers/usb/vicam.c Mon Jan 21 17:22:37 2002 +++ linux/drivers/usb/vicam.c Mon Jan 21 17:23:23 2002 @@ -468,29 +468,23 @@ int err = 0; dbg("vicam_v4l_open"); - - MOD_INC_USE_COUNT; + down(&vicam->sem); - if (vicam->open_count) /* Maybe not needed? */ - err = -EBUSY; + vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES); + if (!vicam->fbuf) + err=-ENOMEM; else { - vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES); - if (!vicam->fbuf) - err=-ENOMEM; - else { - vicam->open_count = 1; - } + vicam->open_count = 1; + } #ifdef BLINKING - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); - info ("led on"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); + vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); + info ("led on"); + vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); #endif - } up(&vicam->sem); - if (err) - MOD_DEC_USE_COUNT; + return err; } @@ -515,7 +509,6 @@ up(&vicam->sem); /* Why does se401.c have a usbdevice check here? */ /* If device is unplugged while open, I guess we only may unregister now */ - MOD_DEC_USE_COUNT; } static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock) @@ -717,6 +710,7 @@ /* FIXME - vicam_template - important */ static struct video_device vicam_template = { + owner: THIS_MODULE, name: "vicam USB camera", type: VID_TYPE_CAPTURE, hardware: VID_HARDWARE_SE401, /* need to ask for own id */ @@ -938,6 +932,7 @@ /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver vicam_driver = { + owner: THIS_MODULE, name: "vicam", probe: vicam_probe, disconnect: vicam_disconnect, @@ -984,3 +979,4 @@ module_init(usb_vicam_init); module_exit(usb_vicam_exit); + diff -urN linux-2.5.3-pre2/drivers/usb/vicam.h linux/drivers/usb/vicam.h --- linux-2.5.3-pre2/drivers/usb/vicam.h Thu Jan 3 13:21:28 2002 +++ linux/drivers/usb/vicam.h Mon Jan 21 17:23:23 2002 @@ -68,7 +68,7 @@ /* v4l stuff */ char *camera_name; char *fbuf; - urb_t *urb[VICAM_NUMSBUF]; + struct urb *urb[VICAM_NUMSBUF]; int sizes; int *width; int *height; diff -urN linux-2.5.3-pre2/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c --- linux-2.5.3-pre2/fs/affs/amigaffs.c Fri Jan 4 09:42:12 2002 +++ linux/fs/affs/amigaffs.c Mon Jan 21 17:23:23 2002 @@ -419,7 +419,7 @@ void mode_to_prot(struct inode *inode) { - u32 prot = AFFS_INODE->i_protect; + u32 prot = AFFS_I(inode)->i_protect; mode_t mode = inode->i_mode; if (!(mode & S_IXUSR)) @@ -441,7 +441,7 @@ if (mode & S_IWOTH) prot |= FIBF_OTR_WRITE; - AFFS_INODE->i_protect = prot; + AFFS_I(inode)->i_protect = prot; } void diff -urN linux-2.5.3-pre2/fs/affs/bitmap.c linux/fs/affs/bitmap.c --- linux-2.5.3-pre2/fs/affs/bitmap.c Fri Jan 4 09:42:12 2002 +++ linux/fs/affs/bitmap.c Mon Jan 21 17:23:23 2002 @@ -155,16 +155,16 @@ pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal); - if (inode->u.affs_i.i_pa_cnt) { - pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1); - inode->u.affs_i.i_pa_cnt--; - return ++inode->u.affs_i.i_lastalloc; + if (AFFS_I(inode)->i_pa_cnt) { + pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1); + AFFS_I(inode)->i_pa_cnt--; + return ++AFFS_I(inode)->i_lastalloc; } if (!goal || goal > AFFS_SB->s_partition_size) { if (goal) affs_warning(sb, "affs_balloc", "invalid goal %d", goal); - //if (!inode->u.affs_i.i_last_block) + //if (!AFFS_I(inode)->i_last_block) // affs_warning(sb, "affs_balloc", "no last alloc block"); goal = AFFS_SB->s_reserved; } @@ -233,16 +233,16 @@ bit = ffs(tmp) - 1; blk += bit + AFFS_SB->s_reserved; mask2 = mask = 1 << (bit & 31); - inode->u.affs_i.i_lastalloc = blk; + AFFS_I(inode)->i_lastalloc = blk; /* prealloc as much as possible within this word */ while ((mask2 <<= 1)) { if (!(tmp & mask2)) break; - inode->u.affs_i.i_pa_cnt++; + AFFS_I(inode)->i_pa_cnt++; mask |= mask2; } - bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1; + bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1; *data = cpu_to_be32(tmp & ~mask); diff -urN linux-2.5.3-pre2/fs/affs/file.c linux/fs/affs/file.c --- linux-2.5.3-pre2/fs/affs/file.c Mon Jan 14 09:38:37 2002 +++ linux/fs/affs/file.c Mon Jan 21 17:23:23 2002 @@ -62,8 +62,8 @@ { if (atomic_read(&filp->f_count) != 1) return 0; - pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt); - AFFS_INODE->i_opencnt++; + pr_debug("AFFS: open(%d)\n", AFFS_I(inode)->i_opencnt); + AFFS_I(inode)->i_opencnt++; return 0; } @@ -72,9 +72,9 @@ { if (atomic_read(&filp->f_count) != 0) return 0; - pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt); - AFFS_INODE->i_opencnt--; - if (!AFFS_INODE->i_opencnt) + pr_debug("AFFS: release(%d)\n", AFFS_I(inode)->i_opencnt); + AFFS_I(inode)->i_opencnt--; + if (!AFFS_I(inode)->i_opencnt) affs_free_prealloc(inode); return 0; @@ -88,49 +88,49 @@ u32 lc_max; int i, j, key; - if (!AFFS_INODE->i_lc) { + if (!AFFS_I(inode)->i_lc) { char *ptr = (char *)get_zeroed_page(GFP_NOFS); if (!ptr) return -ENOMEM; - AFFS_INODE->i_lc = (u32 *)ptr; - AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2); + AFFS_I(inode)->i_lc = (u32 *)ptr; + AFFS_I(inode)->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2); } - lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift; + lc_max = AFFS_LC_SIZE << AFFS_I(inode)->i_lc_shift; - if (AFFS_INODE->i_extcnt > lc_max) { + if (AFFS_I(inode)->i_extcnt > lc_max) { u32 lc_shift, lc_mask, tmp, off; /* need to recalculate linear cache, start from old size */ - lc_shift = AFFS_INODE->i_lc_shift; - tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift; + lc_shift = AFFS_I(inode)->i_lc_shift; + tmp = (AFFS_I(inode)->i_extcnt / AFFS_LC_SIZE) >> lc_shift; for (; tmp; tmp >>= 1) lc_shift++; lc_mask = (1 << lc_shift) - 1; /* fix idx and old size to new shift */ - lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift); - AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift); + lc_idx >>= (lc_shift - AFFS_I(inode)->i_lc_shift); + AFFS_I(inode)->i_lc_size >>= (lc_shift - AFFS_I(inode)->i_lc_shift); /* first shrink old cache to make more space */ - off = 1 << (lc_shift - AFFS_INODE->i_lc_shift); + off = 1 << (lc_shift - AFFS_I(inode)->i_lc_shift); for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off) - AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j]; + AFFS_I(inode)->i_ac[i] = AFFS_I(inode)->i_ac[j]; - AFFS_INODE->i_lc_shift = lc_shift; - AFFS_INODE->i_lc_mask = lc_mask; + AFFS_I(inode)->i_lc_shift = lc_shift; + AFFS_I(inode)->i_lc_mask = lc_mask; } /* fill cache to the needed index */ - i = AFFS_INODE->i_lc_size; - AFFS_INODE->i_lc_size = lc_idx + 1; + i = AFFS_I(inode)->i_lc_size; + AFFS_I(inode)->i_lc_size = lc_idx + 1; for (; i <= lc_idx; i++) { if (!i) { - AFFS_INODE->i_lc[0] = inode->i_ino; + AFFS_I(inode)->i_lc[0] = inode->i_ino; continue; } - key = AFFS_INODE->i_lc[i - 1]; - j = AFFS_INODE->i_lc_mask + 1; + key = AFFS_I(inode)->i_lc[i - 1]; + j = AFFS_I(inode)->i_lc_mask + 1; // unlock cache for (; j > 0; j--) { bh = affs_bread(sb, key); @@ -140,7 +140,7 @@ affs_brelse(bh); } // lock cache - AFFS_INODE->i_lc[i] = key; + AFFS_I(inode)->i_lc[i] = key; } return 0; @@ -182,7 +182,7 @@ affs_adjust_checksum(bh, blocknr - tmp); mark_buffer_dirty_inode(bh, inode); - AFFS_INODE->i_extcnt++; + AFFS_I(inode)->i_extcnt++; mark_inode_dirty(inode); return new_bh; @@ -192,8 +192,8 @@ affs_get_extblock(struct inode *inode, u32 ext) { /* inline the simplest case: same extended block as last time */ - struct buffer_head *bh = AFFS_INODE->i_ext_bh; - if (ext == AFFS_INODE->i_ext_last) + struct buffer_head *bh = AFFS_I(inode)->i_ext_bh; + if (ext == AFFS_I(inode)->i_ext_last) atomic_inc(&bh->b_count); else /* we have to do more (not inlined) */ @@ -211,13 +211,13 @@ u32 lc_idx, lc_off, ac_idx; u32 tmp, idx; - if (ext == AFFS_INODE->i_ext_last + 1) { + if (ext == AFFS_I(inode)->i_ext_last + 1) { /* read the next extended block from the current one */ - bh = AFFS_INODE->i_ext_bh; + bh = AFFS_I(inode)->i_ext_bh; ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); - if (ext < AFFS_INODE->i_extcnt) + if (ext < AFFS_I(inode)->i_extcnt) goto read_ext; - if (ext > AFFS_INODE->i_extcnt) + if (ext > AFFS_I(inode)->i_extcnt) BUG(); bh = affs_alloc_extblock(inode, bh, ext); if (IS_ERR(bh)) @@ -231,11 +231,11 @@ goto read_ext; } - if (ext >= AFFS_INODE->i_extcnt) { + if (ext >= AFFS_I(inode)->i_extcnt) { struct buffer_head *prev_bh; /* allocate a new extended block */ - if (ext > AFFS_INODE->i_extcnt) + if (ext > AFFS_I(inode)->i_extcnt) BUG(); /* get previous extended block */ @@ -251,10 +251,10 @@ again: /* check if there is an extended cache and whether it's large enough */ - lc_idx = ext >> AFFS_INODE->i_lc_shift; - lc_off = ext & AFFS_INODE->i_lc_mask; + lc_idx = ext >> AFFS_I(inode)->i_lc_shift; + lc_off = ext & AFFS_I(inode)->i_lc_mask; - if (lc_idx >= AFFS_INODE->i_lc_size) { + if (lc_idx >= AFFS_I(inode)->i_lc_size) { int err; err = affs_grow_extcache(inode, lc_idx); @@ -265,14 +265,14 @@ /* every n'th key we find in the linear cache */ if (!lc_off) { - ext_key = AFFS_INODE->i_lc[lc_idx]; + ext_key = AFFS_I(inode)->i_lc[lc_idx]; goto read_ext; } /* maybe it's still in the associative cache */ ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK; - if (AFFS_INODE->i_ac[ac_idx].ext == ext) { - ext_key = AFFS_INODE->i_ac[ac_idx].key; + if (AFFS_I(inode)->i_ac[ac_idx].ext == ext) { + ext_key = AFFS_I(inode)->i_ac[ac_idx].key; goto read_ext; } @@ -281,14 +281,14 @@ idx = ac_idx; while (--tmp, --lc_off > 0) { idx = (idx - 1) & AFFS_AC_MASK; - if (AFFS_INODE->i_ac[idx].ext == tmp) { - ext_key = AFFS_INODE->i_ac[idx].key; + if (AFFS_I(inode)->i_ac[idx].ext == tmp) { + ext_key = AFFS_I(inode)->i_ac[idx].key; goto find_ext; } } /* fall back to the linear cache */ - ext_key = AFFS_INODE->i_lc[lc_idx]; + ext_key = AFFS_I(inode)->i_lc[lc_idx]; find_ext: /* read all extended blocks until we find the one we need */ //unlock cache @@ -304,8 +304,8 @@ /* store it in the associative cache */ // recalculate ac_idx? - AFFS_INODE->i_ac[ac_idx].ext = ext; - AFFS_INODE->i_ac[ac_idx].key = ext_key; + AFFS_I(inode)->i_ac[ac_idx].ext = ext; + AFFS_I(inode)->i_ac[ac_idx].key = ext_key; read_ext: /* finally read the right extended block */ @@ -317,9 +317,9 @@ store_ext: /* release old cached extended block and store the new one */ - affs_brelse(AFFS_INODE->i_ext_bh); - AFFS_INODE->i_ext_last = ext; - AFFS_INODE->i_ext_bh = bh; + affs_brelse(AFFS_I(inode)->i_ext_bh); + AFFS_I(inode)->i_ext_last = ext; + AFFS_I(inode)->i_ext_bh = bh; atomic_inc(&bh->b_count); return bh; @@ -341,8 +341,8 @@ if (block < 0) goto err_small; - if (block >= AFFS_INODE->i_blkcnt) { - if (block > AFFS_INODE->i_blkcnt || !create) + if (block >= AFFS_I(inode)->i_blkcnt) { + if (block > AFFS_I(inode)->i_blkcnt || !create) goto err_big; } else create = 0; @@ -362,8 +362,8 @@ if (!blocknr) goto err_alloc; bh_result->b_state |= (1UL << BH_New); - AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize; - AFFS_INODE->i_blkcnt++; + AFFS_I(inode)->mmu_private += AFFS_SB->s_data_blksize; + AFFS_I(inode)->i_blkcnt++; /* store new block */ if (bh_result->b_blocknr) @@ -418,7 +418,7 @@ static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { return cont_prepare_write(page, from, to, affs_get_block, - &page->mapping->host->u.affs_i.mmu_private); + &AFFS_I(page->mapping->host)->mmu_private); } static int _affs_bmap(struct address_space *mapping, long block) { @@ -593,11 +593,11 @@ bidx++; } affs_brelse(bh); - inode->i_size = AFFS_INODE->mmu_private = size; + inode->i_size = AFFS_I(inode)->mmu_private = size; return 0; out: - inode->i_size = AFFS_INODE->mmu_private = size; + inode->i_size = AFFS_I(inode)->mmu_private = size; return PTR_ERR(bh); } @@ -759,7 +759,7 @@ affs_brelse(bh); tmp = (page->index << PAGE_CACHE_SHIFT) + from; if (tmp > inode->i_size) - inode->i_size = AFFS_INODE->mmu_private = tmp; + inode->i_size = AFFS_I(inode)->mmu_private = tmp; return written; @@ -787,9 +787,9 @@ pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino); - while (inode->u.affs_i.i_pa_cnt) { - inode->u.affs_i.i_pa_cnt--; - affs_free_block(sb, ++inode->u.affs_i.i_lastalloc); + while (AFFS_I(inode)->i_pa_cnt) { + AFFS_I(inode)->i_pa_cnt--; + affs_free_block(sb, ++AFFS_I(inode)->i_lastalloc); } } @@ -814,7 +814,7 @@ ext = last_blk / AFFS_SB->s_hashsize; } - if (inode->i_size > AFFS_INODE->mmu_private) { + if (inode->i_size > AFFS_I(inode)->mmu_private) { struct address_space *mapping = inode->i_mapping; struct page *page; u32 size = inode->i_size - 1; @@ -831,23 +831,23 @@ page_cache_release(page); mark_inode_dirty(inode); return; - } else if (inode->i_size == AFFS_INODE->mmu_private) + } else if (inode->i_size == AFFS_I(inode)->mmu_private) return; // lock cache ext_bh = affs_get_extblock(inode, ext); - if (AFFS_INODE->i_lc) { + if (AFFS_I(inode)->i_lc) { /* clear linear cache */ - for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++) - AFFS_INODE->i_lc[i] = 0; + for (i = (ext + 1) >> AFFS_I(inode)->i_lc_shift; i < AFFS_LC_SIZE; i++) + AFFS_I(inode)->i_lc[i] = 0; /* clear associative cache */ for (i = 0; i < AFFS_AC_SIZE; i++) - if (AFFS_INODE->i_ac[i].ext >= ext) - AFFS_INODE->i_ac[i].ext = 0; + if (AFFS_I(inode)->i_ac[i].ext >= ext) + AFFS_I(inode)->i_ac[i].ext = 0; } ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension); - blkcnt = AFFS_INODE->i_blkcnt; + blkcnt = AFFS_I(inode)->i_blkcnt; i = 0; blk = last_blk; if (inode->i_size) { @@ -868,13 +868,13 @@ affs_brelse(ext_bh); if (inode->i_size) { - AFFS_INODE->i_blkcnt = last_blk + 1; - AFFS_INODE->i_extcnt = ext + 1; + AFFS_I(inode)->i_blkcnt = last_blk + 1; + AFFS_I(inode)->i_extcnt = ext + 1; } else { - AFFS_INODE->i_blkcnt = 0; - AFFS_INODE->i_extcnt = 1; + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_extcnt = 1; } - AFFS_INODE->mmu_private = inode->i_size; + AFFS_I(inode)->mmu_private = inode->i_size; // unlock cache while (ext_key) { diff -urN linux-2.5.3-pre2/fs/affs/inode.c linux/fs/affs/inode.c --- linux-2.5.3-pre2/fs/affs/inode.c Sun Sep 30 12:26:08 2001 +++ linux/fs/affs/inode.c Mon Jan 21 17:23:23 2002 @@ -68,12 +68,20 @@ inode->i_size = 0; inode->i_nlink = 1; inode->i_mode = 0; - memset(AFFS_INODE, 0, sizeof(struct affs_inode_info)); - init_MUTEX(&AFFS_INODE->i_link_lock); - init_MUTEX(&AFFS_INODE->i_ext_lock); - AFFS_INODE->i_extcnt = 1; - AFFS_INODE->i_ext_last = ~1; - AFFS_INODE->i_protect = prot; + AFFS_I(inode)->i_extcnt = 1; + AFFS_I(inode)->i_ext_last = ~1; + AFFS_I(inode)->i_protect = prot; + AFFS_I(inode)->i_opencnt = 0; + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_lc_size = 0; + AFFS_I(inode)->i_lc_shift = 0; + AFFS_I(inode)->i_lc_mask = 0; + AFFS_I(inode)->i_ac = NULL; + AFFS_I(inode)->i_ext_bh = NULL; + AFFS_I(inode)->mmu_private = 0; + AFFS_I(inode)->i_lastalloc = 0; + AFFS_I(inode)->i_pa_cnt = 0; if (AFFS_SB->s_flags & SF_SETMODE) inode->i_mode = AFFS_SB->s_mode; @@ -136,11 +144,11 @@ case ST_FILE: size = be32_to_cpu(tail->size); inode->i_mode |= S_IFREG; - AFFS_INODE->mmu_private = inode->i_size = size; + AFFS_I(inode)->mmu_private = inode->i_size = size; if (inode->i_size) { - AFFS_INODE->i_blkcnt = (size - 1) / + AFFS_I(inode)->i_blkcnt = (size - 1) / AFFS_SB->s_data_blksize + 1; - AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) / + AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) / AFFS_SB->s_hashsize + 1; } if (tail->link_chain) @@ -196,7 +204,7 @@ if (tail->stype == be32_to_cpu(ST_ROOT)) { secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change); } else { - tail->protect = cpu_to_be32(AFFS_INODE->i_protect); + tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect); tail->size = cpu_to_be32(inode->i_size); secs_to_datestamp(inode->i_mtime,&tail->change); if (!(inode->i_ino == AFFS_SB->s_root_block)) { @@ -255,7 +263,7 @@ lock_kernel(); affs_free_prealloc(inode); if (atomic_read(&inode->i_count) == 1) { - if (inode->i_size != AFFS_INODE->mmu_private) + if (inode->i_size != AFFS_I(inode)->mmu_private) affs_truncate(inode); //if (inode->i_nlink) // affs_clear_inode(inode); @@ -279,18 +287,18 @@ void affs_clear_inode(struct inode *inode) { - unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc; + unsigned long cache_page = (unsigned long) AFFS_I(inode)->i_lc; pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); if (cache_page) { pr_debug("AFFS: freeing ext cache\n"); - inode->u.affs_i.i_lc = NULL; - inode->u.affs_i.i_ac = NULL; + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_ac = NULL; free_page(cache_page); } - affs_brelse(AFFS_INODE->i_ext_bh); - AFFS_INODE->i_ext_last = ~1; - AFFS_INODE->i_ext_bh = NULL; + affs_brelse(AFFS_I(inode)->i_ext_bh); + AFFS_I(inode)->i_ext_last = ~1; + AFFS_I(inode)->i_ext_bh = NULL; } struct inode * @@ -318,11 +326,20 @@ inode->i_ino = block; inode->i_nlink = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - memset(AFFS_INODE, 0, sizeof(struct affs_inode_info)); - AFFS_INODE->i_extcnt = 1; - AFFS_INODE->i_ext_last = ~1; - init_MUTEX(&AFFS_INODE->i_link_lock); - init_MUTEX(&AFFS_INODE->i_ext_lock); + AFFS_I(inode)->i_opencnt = 0; + AFFS_I(inode)->i_blkcnt = 0; + AFFS_I(inode)->i_lc = NULL; + AFFS_I(inode)->i_lc_size = 0; + AFFS_I(inode)->i_lc_shift = 0; + AFFS_I(inode)->i_lc_mask = 0; + AFFS_I(inode)->i_ac = NULL; + AFFS_I(inode)->i_ext_bh = NULL; + AFFS_I(inode)->mmu_private = 0; + AFFS_I(inode)->i_protect = 0; + AFFS_I(inode)->i_lastalloc = 0; + AFFS_I(inode)->i_pa_cnt = 0; + AFFS_I(inode)->i_extcnt = 1; + AFFS_I(inode)->i_ext_last = ~1; insert_inode_hash(inode); diff -urN linux-2.5.3-pre2/fs/affs/super.c linux/fs/affs/super.c --- linux-2.5.3-pre2/fs/affs/super.c Fri Jan 4 09:42:12 2002 +++ linux/fs/affs/super.c Mon Jan 21 17:23:23 2002 @@ -78,7 +78,54 @@ pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean); } +static kmem_cache_t * affs_inode_cachep; + +static struct inode *affs_alloc_inode(struct super_block *sb) +{ + struct affs_inode_info *ei; + ei = (struct affs_inode_info *)kmem_cache_alloc(affs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void affs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct affs_inode_info *ei = (struct affs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + init_MUTEX(&ei->i_link_lock); + init_MUTEX(&ei->i_ext_lock); + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + affs_inode_cachep = kmem_cache_create("affs_inode_cache", + sizeof(struct affs_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (affs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(affs_inode_cachep)) + printk(KERN_INFO "affs_inode_cache: not all structures were freed\n"); +} + static struct super_operations affs_sops = { + alloc_inode: affs_alloc_inode, + destroy_inode: affs_destroy_inode, read_inode: affs_read_inode, write_inode: affs_write_inode, put_inode: affs_put_inode, @@ -490,12 +537,23 @@ static int __init init_affs_fs(void) { - return register_filesystem(&affs_fs_type); + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&affs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_affs_fs(void) { unregister_filesystem(&affs_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/coda/inode.c linux/fs/coda/inode.c --- linux-2.5.3-pre2/fs/coda/inode.c Mon Jan 14 10:10:43 2002 +++ linux/fs/coda/inode.c Mon Jan 21 17:23:23 2002 @@ -39,9 +39,60 @@ static void coda_put_super(struct super_block *); static int coda_statfs(struct super_block *sb, struct statfs *buf); +static kmem_cache_t * coda_inode_cachep; + +static struct inode *coda_alloc_inode(struct super_block *sb) +{ + struct coda_inode_info *ei; + ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, SLAB_KERNEL); + memset(&ei->c_fid, 0, sizeof(struct ViceFid)); + ei->c_flags = 0; + INIT_LIST_HEAD(&ei->c_cilist); + ei->c_container = NULL; + ei->c_contcount = 0; + memset(&ei->c_cached_cred, 0, sizeof(struct coda_cred)); + ei->c_cached_perm = 0; + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void coda_destroy_inode(struct inode *inode) +{ + kmem_cache_free(coda_inode_cachep, ITOC(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct coda_inode_info *ei = (struct coda_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +int coda_init_inodecache(void) +{ + coda_inode_cachep = kmem_cache_create("coda_inode_cache", + sizeof(struct coda_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (coda_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +void coda_destroy_inodecache(void) +{ + if (kmem_cache_destroy(coda_inode_cachep)) + printk(KERN_INFO "coda_inode_cache: not all structures were freed\n"); +} + /* exported operations */ struct super_operations coda_super_operations = { + alloc_inode: coda_alloc_inode, + destroy_inode: coda_destroy_inode, read_inode: coda_read_inode, clear_inode: coda_clear_inode, put_super: coda_put_super, @@ -188,28 +239,7 @@ if (!sbi) BUG(); -#if 0 - /* check if the inode is already initialized */ - if (inode->u.generic_ip) { - printk("coda_read_inode: initialized inode"); - return; - } - - inode->u.generic_ip = cii_alloc(); - if (!inode->u.generic_ip) { - CDEBUG(D_CNODE, "coda_read_inode: failed to allocate inode info\n"); - make_bad_inode(inode); - return; - } - memset(inode->u.generic_ip, 0, sizeof(struct coda_inode_info)); -#endif - cii = ITOC(inode); - if (!coda_isnullfid(&cii->c_fid)) { - printk("coda_read_inode: initialized inode"); - return; - } - list_add(&cii->c_cilist, &sbi->sbi_cihead); } @@ -226,11 +256,6 @@ list_del_init(&cii->c_cilist); inode->i_mapping = &inode->i_data; coda_cache_clear_inode(inode); - -#if 0 - cii_free(inode->u.generic_ip); - inode->u.generic_ip = NULL; -#endif } int coda_notify_change(struct dentry *de, struct iattr *iattr) diff -urN linux-2.5.3-pre2/fs/coda/psdev.c linux/fs/coda/psdev.c --- linux-2.5.3-pre2/fs/coda/psdev.c Fri Jan 4 09:37:49 2002 +++ linux/fs/coda/psdev.c Mon Jan 21 17:23:23 2002 @@ -407,24 +407,35 @@ MODULE_AUTHOR("Peter J. Braam "); MODULE_LICENSE("GPL"); +extern int coda_init_inodecache(void); +extern void coda_destroy_inodecache(void); static int __init init_coda(void) { int status; printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.15, coda@cs.cmu.edu\n"); + status = coda_init_inodecache(); + if (status) + goto out2; status = init_coda_psdev(); if ( status ) { printk("Problem (%d) in init_coda_psdev\n", status); - return status; + goto out1; } status = register_filesystem(&coda_fs_type); if (status) { printk("coda: failed to register filesystem!\n"); - devfs_unregister(devfs_handle); - devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); - coda_sysctl_clean(); + goto out; } + return 0; +out: + devfs_unregister(devfs_handle); + devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + coda_sysctl_clean(); +out1: + coda_destroy_inodecache(); +out2: return status; } @@ -439,6 +450,7 @@ devfs_unregister(devfs_handle); devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev"); coda_sysctl_clean(); + coda_destroy_inodecache(); } module_init(init_coda); diff -urN linux-2.5.3-pre2/fs/devfs/base.c linux/fs/devfs/base.c --- linux-2.5.3-pre2/fs/devfs/base.c Mon Jan 14 09:40:29 2002 +++ linux/fs/devfs/base.c Mon Jan 21 17:23:24 2002 @@ -1,6 +1,6 @@ /* devfs (Device FileSystem) driver. - Copyright (C) 1998-2001 Richard Gooch + Copyright (C) 1998-2002 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -604,6 +604,10 @@ 20020113 Richard Gooch Fixed (rare, old) race in . v1.9 + 20020120 Richard Gooch + Fixed deadlock bug in . + Tag VFS deletable in if handle ignored. + v1.10 */ #include #include @@ -636,7 +640,7 @@ #include #include -#define DEVFS_VERSION "1.9 (20020113)" +#define DEVFS_VERSION "1.10 (20020120)" #define DEVFS_NAME "devfs" @@ -781,7 +785,7 @@ umode_t mode; unsigned short namelen; /* I think 64k+ filenames are a way off... */ unsigned char hide:1; - unsigned char vfs_created:1; /* Whether created by driver or VFS */ + unsigned char vfs_deletable:1;/* Whether the VFS may delete the entry */ char name[1]; /* This is just a dummy: the allocated array is bigger. This is NULL-terminated */ }; @@ -1774,7 +1778,8 @@ DPRINTK (DEBUG_REGISTER, "(%s)\n", name); err = devfs_do_symlink (dir, name, flags, link, &de, info); if (err) return err; - if (handle != NULL) *handle = de; + if (handle == NULL) de->vfs_deletable = TRUE; + else *handle = de; devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT); return 0; } /* End Function devfs_mk_symlink */ @@ -1817,7 +1822,7 @@ { PRINTK ("(%s): using old entry in dir: %p \"%s\"\n", name, dir, dir->name); - old->vfs_created = FALSE; + old->vfs_deletable = FALSE; devfs_put (dir); return old; } @@ -2878,13 +2883,16 @@ struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; DECLARE_WAITQUEUE (wait, current); - if ( !dentry->d_inode && is_devfsd_or_child (fs_info) ) + if ( is_devfsd_or_child (fs_info) ) { devfs_handle_t de = lookup_info->de; struct inode *inode; - DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p de: %p by: \"%s\"\n", - dentry->d_name.name, dentry, de, current->comm); + DPRINTK (DEBUG_I_LOOKUP, + "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n", + dentry->d_name.name, dentry, dentry->d_inode, de, + current->comm); + if (dentry->d_inode) return 1; if (de == NULL) { read_lock (&parent->u.dir.lock); @@ -3015,7 +3023,7 @@ de = get_devfs_entry_from_vfs_inode (inode); DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de); if (de == NULL) return -ENOENT; - if (!de->vfs_created) return -EPERM; + if (!de->vfs_deletable) return -EPERM; write_lock (&de->parent->u.dir.lock); unhooked = _devfs_unhook (de); write_unlock (&de->parent->u.dir.lock); @@ -3044,7 +3052,7 @@ DPRINTK (DEBUG_DISABLED, "(%s): errcode from : %d\n", dentry->d_name.name, err); if (err < 0) return err; - de->vfs_created = TRUE; + de->vfs_deletable = TRUE; de->inode.uid = current->euid; de->inode.gid = current->egid; de->inode.atime = CURRENT_TIME; @@ -3073,7 +3081,7 @@ if (parent == NULL) return -ENOENT; de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); if (!de) return -ENOMEM; - de->vfs_created = TRUE; + de->vfs_deletable = TRUE; if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 ) return err; de->inode.uid = current->euid; @@ -3103,7 +3111,7 @@ de = get_devfs_entry_from_vfs_inode (inode); if (de == NULL) return -ENOENT; if ( !S_ISDIR (de->mode) ) return -ENOTDIR; - if (!de->vfs_created) return -EPERM; + if (!de->vfs_deletable) return -EPERM; /* First ensure the directory is empty and will stay thay way */ write_lock (&de->u.dir.lock); de->u.dir.no_more_additions = TRUE; @@ -3137,7 +3145,7 @@ if (parent == NULL) return -ENOENT; de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode); if (!de) return -ENOMEM; - de->vfs_created = TRUE; + de->vfs_deletable = TRUE; if ( S_ISBLK (mode) || S_ISCHR (mode) ) { de->u.fcb.u.device.major = MAJOR (rdev); diff -urN linux-2.5.3-pre2/fs/efs/super.c linux/fs/efs/super.c --- linux-2.5.3-pre2/fs/efs/super.c Tue Dec 25 15:39:20 2001 +++ linux/fs/efs/super.c Mon Jan 21 17:23:24 2002 @@ -12,21 +12,78 @@ #include #include #include +#include static DECLARE_FSTYPE_DEV(efs_fs_type, "efs", efs_read_super); +static kmem_cache_t * efs_inode_cachep; + +static struct inode *efs_alloc_inode(struct super_block *sb) +{ + struct efs_inode_info *ei; + ei = (struct efs_inode_info *)kmem_cache_alloc(efs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void efs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(efs_inode_cachep, INODE_INFO(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct efs_inode_info *ei = (struct efs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + efs_inode_cachep = kmem_cache_create("efs_inode_cache", + sizeof(struct efs_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (efs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(efs_inode_cachep)) + printk(KERN_INFO "efs_inode_cache: not all structures were freed\n"); +} + static struct super_operations efs_superblock_operations = { + alloc_inode: efs_alloc_inode, + destroy_inode: efs_destroy_inode, read_inode: efs_read_inode, statfs: efs_statfs, }; static int __init init_efs_fs(void) { + int err; printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n"); - return register_filesystem(&efs_fs_type); + err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&efs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_efs_fs(void) { unregister_filesystem(&efs_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- linux-2.5.3-pre2/fs/ext2/balloc.c Mon Jan 21 17:22:37 2002 +++ linux/fs/ext2/balloc.c Mon Jan 21 17:23:24 2002 @@ -12,8 +12,7 @@ */ #include -#include -#include +#include "ext2.h" #include #include diff -urN linux-2.5.3-pre2/fs/ext2/bitmap.c linux/fs/ext2/bitmap.c --- linux-2.5.3-pre2/fs/ext2/bitmap.c Wed Sep 27 13:41:33 2000 +++ linux/fs/ext2/bitmap.c Mon Jan 21 17:23:24 2002 @@ -7,9 +7,7 @@ * Universite Pierre et Marie Curie (Paris VI) */ -#include -#include - +#include "ext2.h" static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; diff -urN linux-2.5.3-pre2/fs/ext2/dir.c linux/fs/ext2/dir.c --- linux-2.5.3-pre2/fs/ext2/dir.c Mon Sep 17 13:16:30 2001 +++ linux/fs/ext2/dir.c Mon Jan 21 17:23:24 2002 @@ -21,8 +21,7 @@ * and moved here. AV */ -#include -#include +#include "ext2.h" #include typedef struct ext2_dir_entry_2 ext2_dirent; @@ -306,12 +305,13 @@ unsigned long start, n; unsigned long npages = dir_pages(dir); struct page *page = NULL; + struct ext2_inode_info *ei = EXT2_I(dir); ext2_dirent * de; /* OFFSET_CACHE */ *res_page = NULL; - start = dir->u.ext2_i.i_dir_start_lookup; + start = ei->i_dir_start_lookup; if (start >= npages) start = 0; n = start; @@ -336,7 +336,7 @@ found: *res_page = page; - dir->u.ext2_i.i_dir_start_lookup = n; + ei->i_dir_start_lookup = n; return de; } diff -urN linux-2.5.3-pre2/fs/ext2/ext2.h linux/fs/ext2/ext2.h --- linux-2.5.3-pre2/fs/ext2/ext2.h Wed Dec 31 16:00:00 1969 +++ linux/fs/ext2/ext2.h Mon Jan 21 17:23:24 2002 @@ -0,0 +1,120 @@ +#include +#include + +/* + * second extended file system inode data in memory + */ +struct ext2_inode_info { + __u32 i_data[15]; + __u32 i_flags; + __u32 i_faddr; + __u8 i_frag_no; + __u8 i_frag_size; + __u16 i_osync; + __u32 i_file_acl; + __u32 i_dir_acl; + __u32 i_dtime; + __u32 i_block_group; + __u32 i_next_alloc_block; + __u32 i_next_alloc_goal; + __u32 i_prealloc_block; + __u32 i_prealloc_count; + __u32 i_dir_start_lookup; + struct inode vfs_inode; +}; + +/* + * Function prototypes + */ + +/* + * Ok, these declarations are also in but none of the + * ext2 source programs needs to include it so they are duplicated here. + */ + +static inline struct ext2_inode_info *EXT2_I(struct inode *inode) +{ + return list_entry(inode, struct ext2_inode_info, vfs_inode); +} + +/* balloc.c */ +extern int ext2_bg_has_super(struct super_block *sb, int group); +extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); +extern int ext2_new_block (struct inode *, unsigned long, + __u32 *, __u32 *, int *); +extern void ext2_free_blocks (struct inode *, unsigned long, + unsigned long); +extern unsigned long ext2_count_free_blocks (struct super_block *); +extern void ext2_check_blocks_bitmap (struct super_block *); +extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh); + +/* dir.c */ +extern int ext2_add_link (struct dentry *, struct inode *); +extern ino_t ext2_inode_by_name(struct inode *, struct dentry *); +extern int ext2_make_empty(struct inode *, struct inode *); +extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **); +extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *); +extern int ext2_empty_dir (struct inode *); +extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); +extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *); + +/* fsync.c */ +extern int ext2_sync_file (struct file *, struct dentry *, int); +extern int ext2_fsync_inode (struct inode *, int); + +/* ialloc.c */ +extern struct inode * ext2_new_inode (struct inode *, int); +extern void ext2_free_inode (struct inode *); +extern unsigned long ext2_count_free_inodes (struct super_block *); +extern void ext2_check_inodes_bitmap (struct super_block *); +extern unsigned long ext2_count_free (struct buffer_head *, unsigned); + +/* inode.c */ +extern void ext2_read_inode (struct inode *); +extern void ext2_write_inode (struct inode *, int); +extern void ext2_put_inode (struct inode *); +extern void ext2_delete_inode (struct inode *); +extern int ext2_sync_inode (struct inode *); +extern void ext2_discard_prealloc (struct inode *); +extern void ext2_truncate (struct inode *); + +/* ioctl.c */ +extern int ext2_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); + +/* super.c */ +extern void ext2_error (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern NORET_TYPE void ext2_panic (struct super_block *, const char *, + const char *, ...) + __attribute__ ((NORET_AND format (printf, 3, 4))); +extern void ext2_warning (struct super_block *, const char *, const char *, ...) + __attribute__ ((format (printf, 3, 4))); +extern void ext2_update_dynamic_rev (struct super_block *sb); +extern void ext2_put_super (struct super_block *); +extern void ext2_write_super (struct super_block *); +extern int ext2_remount (struct super_block *, int *, char *); +extern struct super_block * ext2_read_super (struct super_block *,void *,int); +extern int ext2_statfs (struct super_block *, struct statfs *); + +/* + * Inodes and files operations + */ + +/* dir.c */ +extern struct file_operations ext2_dir_operations; + +/* file.c */ +extern struct inode_operations ext2_file_inode_operations; +extern struct file_operations ext2_file_operations; + +/* inode.c */ +extern struct address_space_operations ext2_aops; + +/* namei.c */ +extern struct inode_operations ext2_dir_inode_operations; + +/* symlink.c */ +extern struct inode_operations ext2_fast_symlink_inode_operations; diff -urN linux-2.5.3-pre2/fs/ext2/file.c linux/fs/ext2/file.c --- linux-2.5.3-pre2/fs/ext2/file.c Thu Oct 11 08:05:18 2001 +++ linux/fs/ext2/file.c Mon Jan 21 17:23:24 2002 @@ -18,8 +18,7 @@ * (jj@sunsite.ms.mff.cuni.cz) */ -#include -#include +#include "ext2.h" #include /* diff -urN linux-2.5.3-pre2/fs/ext2/fsync.c linux/fs/ext2/fsync.c --- linux-2.5.3-pre2/fs/ext2/fsync.c Mon Sep 17 13:16:30 2001 +++ linux/fs/ext2/fsync.c Mon Jan 21 17:23:24 2002 @@ -22,8 +22,7 @@ * we can depend on generic_block_fdatasync() to sync the data blocks. */ -#include -#include +#include "ext2.h" #include #include diff -urN linux-2.5.3-pre2/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c --- linux-2.5.3-pre2/fs/ext2/ialloc.c Sun Dec 30 10:53:53 2001 +++ linux/fs/ext2/ialloc.c Mon Jan 21 17:23:24 2002 @@ -13,8 +13,7 @@ */ #include -#include -#include +#include "ext2.h" #include #include @@ -311,7 +310,7 @@ return group; } -struct inode * ext2_new_inode (const struct inode * dir, int mode) +struct inode * ext2_new_inode(struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -321,6 +320,7 @@ struct inode * inode; struct ext2_group_desc * desc; struct ext2_super_block * es; + struct ext2_inode_info *ei; int err; sb = dir->i_sb; @@ -328,13 +328,14 @@ if (!inode) return ERR_PTR(-ENOMEM); + ei = EXT2_I(inode); lock_super (sb); es = sb->u.ext2_sb.s_es; repeat: if (S_ISDIR(mode)) - group = find_group_dir(sb, dir->u.ext2_i.i_block_group); + group = find_group_dir(sb, EXT2_I(dir)->i_block_group); else - group = find_group_other(sb, dir->u.ext2_i.i_block_group); + group = find_group_other(sb, EXT2_I(dir)->i_block_group); err = -ENOSPC; if (group == -1) @@ -385,15 +386,26 @@ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->u.ext2_i.i_new_inode = 1; - inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags; + memset(ei->i_data, 0, sizeof(ei->i_data)); + ei->i_flags = EXT2_I(dir)->i_flags; if (S_ISLNK(mode)) - inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL); - inode->u.ext2_i.i_block_group = group; - if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) + ei->i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL); + ei->i_faddr = 0; + ei->i_frag_no = 0; + ei->i_osync = 0; + ei->i_file_acl = 0; + ei->i_dir_acl = 0; + ei->i_dtime = 0; + ei->i_block_group = group; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; + ei->i_prealloc_block = 0; + ei->i_prealloc_count = 0; + ei->i_dir_start_lookup = 0; + if (ei->i_flags & EXT2_SYNC_FL) inode->i_flags |= S_SYNC; - insert_inode_hash(inode); inode->i_generation = sb->u.ext2_sb.s_next_generation++; + insert_inode_hash(inode); mark_inode_dirty(inode); unlock_super (sb); diff -urN linux-2.5.3-pre2/fs/ext2/inode.c linux/fs/ext2/inode.c --- linux-2.5.3-pre2/fs/ext2/inode.c Fri Jan 4 09:42:12 2002 +++ linux/fs/ext2/inode.c Mon Jan 21 17:23:24 2002 @@ -22,8 +22,7 @@ * Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000 */ -#include -#include +#include "ext2.h" #include #include #include @@ -57,7 +56,7 @@ inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) goto no_delete; - inode->u.ext2_i.i_dtime = CURRENT_TIME; + EXT2_I(inode)->i_dtime = CURRENT_TIME; mark_inode_dirty(inode); ext2_update_inode(inode, IS_SYNC(inode)); inode->i_size = 0; @@ -75,13 +74,14 @@ void ext2_discard_prealloc (struct inode * inode) { #ifdef EXT2_PREALLOCATE + struct ext2_inode_info *ei = EXT2_I(inode); lock_kernel(); /* Writer: ->i_prealloc* */ - if (inode->u.ext2_i.i_prealloc_count) { - unsigned short total = inode->u.ext2_i.i_prealloc_count; - unsigned long block = inode->u.ext2_i.i_prealloc_block; - inode->u.ext2_i.i_prealloc_count = 0; - inode->u.ext2_i.i_prealloc_block = 0; + if (ei->i_prealloc_count) { + unsigned short total = ei->i_prealloc_count; + unsigned long block = ei->i_prealloc_block; + ei->i_prealloc_count = 0; + ei->i_prealloc_block = 0; /* Writer: end */ ext2_free_blocks (inode, block, total); } @@ -98,13 +98,14 @@ #ifdef EXT2_PREALLOCATE + struct ext2_inode_info *ei = EXT2_I(inode); /* Writer: ->i_prealloc* */ - if (inode->u.ext2_i.i_prealloc_count && - (goal == inode->u.ext2_i.i_prealloc_block || - goal + 1 == inode->u.ext2_i.i_prealloc_block)) + if (ei->i_prealloc_count && + (goal == ei->i_prealloc_block || + goal + 1 == ei->i_prealloc_block)) { - result = inode->u.ext2_i.i_prealloc_block++; - inode->u.ext2_i.i_prealloc_count--; + result = ei->i_prealloc_block++; + ei->i_prealloc_count--; /* Writer: end */ ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); @@ -114,8 +115,8 @@ alloc_hits, ++alloc_attempts); if (S_ISREG(inode->i_mode)) result = ext2_new_block (inode, goal, - &inode->u.ext2_i.i_prealloc_count, - &inode->u.ext2_i.i_prealloc_block, err); + &ei->i_prealloc_count, + &ei->i_prealloc_block, err); else result = ext2_new_block (inode, goal, 0, 0, err); } @@ -245,7 +246,7 @@ *err = 0; /* i_data is not going away, no lock needed */ - add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets); + add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets); if (!p->key) goto no_block; while (--depth) { @@ -287,7 +288,8 @@ static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind) { - u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext2_i.i_data; + struct ext2_inode_info *ei = EXT2_I(inode); + u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data; u32 *p; /* Try to find previous block */ @@ -303,8 +305,7 @@ * It is going to be refered from inode itself? OK, just put it into * the same cylinder group then. */ - return (inode->u.ext2_i.i_block_group * - EXT2_BLOCKS_PER_GROUP(inode->i_sb)) + + return (ei->i_block_group * EXT2_BLOCKS_PER_GROUP(inode->i_sb)) + le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block); } @@ -327,10 +328,11 @@ Indirect *partial, unsigned long *goal) { + struct ext2_inode_info *ei = EXT2_I(inode); /* Writer: ->i_next_alloc* */ - if (block == inode->u.ext2_i.i_next_alloc_block + 1) { - inode->u.ext2_i.i_next_alloc_block++; - inode->u.ext2_i.i_next_alloc_goal++; + if (block == ei->i_next_alloc_block + 1) { + ei->i_next_alloc_block++; + ei->i_next_alloc_goal++; } /* Writer: end */ /* Reader: pointers, ->i_next_alloc* */ @@ -339,8 +341,8 @@ * try the heuristic for sequential allocation, * failing that at least try to get decent locality. */ - if (block == inode->u.ext2_i.i_next_alloc_block) - *goal = inode->u.ext2_i.i_next_alloc_goal; + if (block == ei->i_next_alloc_block) + *goal = ei->i_next_alloc_goal; if (!*goal) *goal = ext2_find_near(inode, partial); return 0; @@ -407,7 +409,7 @@ mark_buffer_uptodate(bh, 1); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { + if (IS_SYNC(inode) || EXT2_I(inode)->i_osync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } @@ -447,6 +449,7 @@ Indirect *where, int num) { + struct ext2_inode_info *ei = EXT2_I(inode); int i; /* Verify that place we are splicing to is still there and vacant */ @@ -459,8 +462,8 @@ /* That's it */ *where->p = where->key; - inode->u.ext2_i.i_next_alloc_block = block; - inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key); + ei->i_next_alloc_block = block; + ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key); /* Writer: end */ @@ -471,13 +474,13 @@ /* had we spliced it onto indirect block? */ if (where->bh) { mark_buffer_dirty_inode(where->bh, inode); - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { + if (IS_SYNC(inode) || ei->i_osync) { ll_rw_block (WRITE, 1, &where->bh); wait_on_buffer(where->bh); } } - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) + if (IS_SYNC(inode) || ei->i_osync) ext2_sync_inode (inode); else mark_inode_dirty(inode); @@ -785,7 +788,7 @@ void ext2_truncate (struct inode * inode) { - u32 *i_data = inode->u.ext2_i.i_data; + u32 *i_data = EXT2_I(inode)->i_data; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int offsets[4]; Indirect chain[4]; @@ -886,6 +889,7 @@ unsigned long block; unsigned long offset; struct ext2_group_desc * gdp; + struct ext2_inode_info *ei = EXT2_I(inode); if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO && inode->i_ino != EXT2_ACL_DATA_INO && @@ -939,13 +943,13 @@ inode->i_atime = le32_to_cpu(raw_inode->i_atime); inode->i_ctime = le32_to_cpu(raw_inode->i_ctime); inode->i_mtime = le32_to_cpu(raw_inode->i_mtime); - inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime); + ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); /* We now have enough fields to check if the inode was active or not. * This is needed because nfsd might try to access dead inodes * the test is that same one that e2fsck uses * NeilBrown 1999oct15 */ - if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) { + if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) { /* this inode is deleted */ brelse (bh); goto bad_inode; @@ -953,25 +957,30 @@ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); inode->i_version = ++event; - inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags); - inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr); - inode->u.ext2_i.i_frag_no = raw_inode->i_frag; - inode->u.ext2_i.i_frag_size = raw_inode->i_fsize; - inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); + ei->i_flags = le32_to_cpu(raw_inode->i_flags); + ei->i_faddr = le32_to_cpu(raw_inode->i_faddr); + ei->i_frag_no = raw_inode->i_frag; + ei->i_frag_size = raw_inode->i_fsize; + ei->i_osync = 0; + ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl); if (S_ISREG(inode->i_mode)) inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; else - inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); + ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); + ei->i_dtime = 0; inode->i_generation = le32_to_cpu(raw_inode->i_generation); - inode->u.ext2_i.i_prealloc_count = 0; - inode->u.ext2_i.i_block_group = block_group; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; + ei->i_prealloc_count = 0; + ei->i_block_group = block_group; + ei->i_dir_start_lookup = 0; /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! */ for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + ei->i_data[block] = raw_inode->i_block[block]; if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) @@ -996,19 +1005,19 @@ le32_to_cpu(raw_inode->i_block[0])); brelse (bh); inode->i_attr_flags = 0; - if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) { + if (ei->i_flags & EXT2_SYNC_FL) { inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; inode->i_flags |= S_SYNC; } - if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) { + if (ei->i_flags & EXT2_APPEND_FL) { inode->i_attr_flags |= ATTR_FLAG_APPEND; inode->i_flags |= S_APPEND; } - if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) { + if (ei->i_flags & EXT2_IMMUTABLE_FL) { inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; inode->i_flags |= S_IMMUTABLE; } - if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) { + if (ei->i_flags & EXT2_NOATIME_FL) { inode->i_attr_flags |= ATTR_FLAG_NOATIME; inode->i_flags |= S_NOATIME; } @@ -1030,6 +1039,7 @@ unsigned long offset; int err = 0; struct ext2_group_desc * gdp; + struct ext2_inode_info *ei = EXT2_I(inode); if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) || @@ -1077,7 +1087,7 @@ * Fix up interoperability with old kernels. Otherwise, old inodes get * re-used with the upper 16 bits of the uid/gid intact */ - if(!inode->u.ext2_i.i_dtime) { + if(!ei->i_dtime) { raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid)); raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid)); } else { @@ -1096,14 +1106,14 @@ raw_inode->i_ctime = cpu_to_le32(inode->i_ctime); raw_inode->i_mtime = cpu_to_le32(inode->i_mtime); raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); - raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime); - raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags); - raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr); - raw_inode->i_frag = inode->u.ext2_i.i_frag_no; - raw_inode->i_fsize = inode->u.ext2_i.i_frag_size; - raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl); + raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); + raw_inode->i_flags = cpu_to_le32(ei->i_flags); + raw_inode->i_faddr = cpu_to_le32(ei->i_faddr); + raw_inode->i_frag = ei->i_frag_no; + raw_inode->i_fsize = ei->i_frag_size; + raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl); if (S_ISDIR(inode->i_mode)) - raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); + raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl); else { raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); if (inode->i_size > 0x7fffffffULL) { @@ -1129,7 +1139,7 @@ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; + raw_inode->i_block[block] = ei->i_data[block]; mark_buffer_dirty(bh); if (do_sync) { ll_rw_block (WRITE, 1, &bh); diff -urN linux-2.5.3-pre2/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c --- linux-2.5.3-pre2/fs/ext2/ioctl.c Wed Sep 27 13:41:33 2000 +++ linux/fs/ext2/ioctl.c Mon Jan 21 17:23:24 2002 @@ -7,8 +7,7 @@ * Universite Pierre et Marie Curie (Paris VI) */ -#include -#include +#include "ext2.h" #include #include @@ -16,13 +15,14 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { + struct ext2_inode_info *ei = EXT2_I(inode); unsigned int flags; ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT2_IOC_GETFLAGS: - flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE; + flags = ei->i_flags & EXT2_FL_USER_VISIBLE; return put_user(flags, (int *) arg); case EXT2_IOC_SETFLAGS: { unsigned int oldflags; @@ -36,7 +36,7 @@ if (get_user(flags, (int *) arg)) return -EFAULT; - oldflags = inode->u.ext2_i.i_flags; + oldflags = ei->i_flags; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by @@ -51,7 +51,7 @@ flags = flags & EXT2_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; - inode->u.ext2_i.i_flags = flags; + ei->i_flags = flags; if (flags & EXT2_SYNC_FL) inode->i_flags |= S_SYNC; diff -urN linux-2.5.3-pre2/fs/ext2/namei.c linux/fs/ext2/namei.c --- linux-2.5.3-pre2/fs/ext2/namei.c Wed Oct 3 22:57:36 2001 +++ linux/fs/ext2/namei.c Mon Jan 21 17:23:24 2002 @@ -29,8 +29,7 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include -#include +#include "ext2.h" #include /* @@ -134,7 +133,7 @@ if (IS_ERR(inode)) goto out; - if (l > sizeof (inode->u.ext2_i.i_data)) { + if (l > sizeof (EXT2_I(inode)->i_data)) { /* slow symlink */ inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &ext2_aops; @@ -144,7 +143,7 @@ } else { /* fast symlink */ inode->i_op = &ext2_fast_symlink_inode_operations; - memcpy((char*)&inode->u.ext2_i.i_data,symname,l); + memcpy((char*)(EXT2_I(inode)->i_data),symname,l); inode->i_size = l-1; } mark_inode_dirty(inode); diff -urN linux-2.5.3-pre2/fs/ext2/super.c linux/fs/ext2/super.c --- linux-2.5.3-pre2/fs/ext2/super.c Fri Jan 4 09:42:12 2002 +++ linux/fs/ext2/super.c Mon Jan 21 17:23:24 2002 @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include "ext2.h" #include #include #include @@ -148,7 +147,51 @@ return; } +static kmem_cache_t * ext2_inode_cachep; + +static struct inode *ext2_alloc_inode(struct super_block *sb) +{ + struct ext2_inode_info *ei; + ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void ext2_destroy_inode(struct inode *inode) +{ + kmem_cache_free(ext2_inode_cachep, EXT2_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + ext2_inode_cachep = kmem_cache_create("ext2_inode_cache", + sizeof(struct ext2_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (ext2_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(ext2_inode_cachep)) + printk(KERN_INFO "ext2_inode_cache: not all structures were freed\n"); +} + static struct super_operations ext2_sops = { + alloc_inode: ext2_alloc_inode, + destroy_inode: ext2_destroy_inode, read_inode: ext2_read_inode, write_inode: ext2_write_inode, put_inode: ext2_put_inode, @@ -804,12 +847,23 @@ static int __init init_ext2_fs(void) { - return register_filesystem(&ext2_fs_type); + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&ext2_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_ext2_fs(void) { unregister_filesystem(&ext2_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/ext2/symlink.c linux/fs/ext2/symlink.c --- linux-2.5.3-pre2/fs/ext2/symlink.c Wed Sep 27 13:41:33 2000 +++ linux/fs/ext2/symlink.c Mon Jan 21 17:23:24 2002 @@ -17,19 +17,18 @@ * ext2 symlink handling code */ -#include -#include +#include "ext2.h" static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen) { - char *s = (char *)dentry->d_inode->u.ext2_i.i_data; - return vfs_readlink(dentry, buffer, buflen, s); + struct ext2_inode_info *ei = EXT2_I(dentry->d_inode); + return vfs_readlink(dentry, buffer, buflen, (char *)ei->i_data); } static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd) { - char *s = (char *)dentry->d_inode->u.ext2_i.i_data; - return vfs_follow_link(nd, s); + struct ext2_inode_info *ei = EXT2_I(dentry->d_inode); + return vfs_follow_link(nd, (char *)ei->i_data); } struct inode_operations ext2_fast_symlink_inode_operations = { diff -urN linux-2.5.3-pre2/fs/ext3/ialloc.c linux/fs/ext3/ialloc.c --- linux-2.5.3-pre2/fs/ext3/ialloc.c Fri Jan 4 09:37:49 2002 +++ linux/fs/ext3/ialloc.c Mon Jan 21 17:23:24 2002 @@ -293,7 +293,7 @@ * group to find a free inode. */ struct inode * ext3_new_inode (handle_t *handle, - const struct inode * dir, int mode) + struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; @@ -304,6 +304,7 @@ struct ext3_group_desc * gdp; struct ext3_group_desc * tmp; struct ext3_super_block * es; + struct ext3_inode_info *ei; int err = 0; /* Cannot create files in a deleted directory */ @@ -314,7 +315,7 @@ inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); - init_rwsem(&inode->u.ext3_i.truncate_sem); + ei = EXT3_I(inode); lock_super (sb); es = sb->u.ext3_sb.s_es; @@ -346,7 +347,7 @@ /* * Try to place the inode in its parent directory */ - i = dir->u.ext3_i.i_block_group; + i = EXT3_I(dir)->i_block_group; tmp = ext3_get_group_desc (sb, i, &bh2); if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) gdp = tmp; @@ -372,7 +373,7 @@ /* * That failed: try linear search for a free inode */ - i = dir->u.ext3_i.i_block_group + 1; + i = EXT3_I(dir)->i_block_group + 1; for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) { if (++i >= sb->u.ext3_sb.s_groups_count) i = 0; @@ -479,31 +480,37 @@ inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL; + + memset(ei->i_data, 0, sizeof(ei->i_data)); + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; + ei->i_dir_start_lookup = 0; + ei->i_disksize = 0; + + ei->i_flags = EXT3_I(dir)->i_flags & ~EXT3_INDEX_FL; if (S_ISLNK(mode)) - inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL); + ei->i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL); #ifdef EXT3_FRAGMENTS - inode->u.ext3_i.i_faddr = 0; - inode->u.ext3_i.i_frag_no = 0; - inode->u.ext3_i.i_frag_size = 0; + ei->i_faddr = 0; + ei->i_frag_no = 0; + ei->i_frag_size = 0; #endif - inode->u.ext3_i.i_file_acl = 0; - inode->u.ext3_i.i_dir_acl = 0; - inode->u.ext3_i.i_dtime = 0; - INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); + ei->i_file_acl = 0; + ei->i_dir_acl = 0; + ei->i_dtime = 0; #ifdef EXT3_PREALLOCATE - inode->u.ext3_i.i_prealloc_count = 0; + ei->i_prealloc_count = 0; #endif - inode->u.ext3_i.i_block_group = i; + ei->i_block_group = i; - if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) + if (ei->i_flags & EXT3_SYNC_FL) inode->i_flags |= S_SYNC; if (IS_SYNC(inode)) handle->h_sync = 1; insert_inode_hash(inode); inode->i_generation = event++; - inode->u.ext3_i.i_state = EXT3_STATE_NEW; + ei->i_state = EXT3_STATE_NEW; err = ext3_mark_inode_dirty(handle, inode); if (err) goto fail; diff -urN linux-2.5.3-pre2/fs/ext3/inode.c linux/fs/ext3/inode.c --- linux-2.5.3-pre2/fs/ext3/inode.c Sun Dec 30 16:58:52 2001 +++ linux/fs/ext3/inode.c Mon Jan 21 17:23:24 2002 @@ -196,7 +196,7 @@ * (Well, we could do this if we need to, but heck - it works) */ ext3_orphan_del(handle, inode); - inode->u.ext3_i.i_dtime = CURRENT_TIME; + EXT3_I(inode)->i_dtime = CURRENT_TIME; /* * One subtle ordering requirement: if anything has gone wrong @@ -220,13 +220,14 @@ void ext3_discard_prealloc (struct inode * inode) { #ifdef EXT3_PREALLOCATE + struct ext3_inode_info *ei = EXT3_I(inode); lock_kernel(); /* Writer: ->i_prealloc* */ - if (inode->u.ext3_i.i_prealloc_count) { - unsigned short total = inode->u.ext3_i.i_prealloc_count; - unsigned long block = inode->u.ext3_i.i_prealloc_block; - inode->u.ext3_i.i_prealloc_count = 0; - inode->u.ext3_i.i_prealloc_block = 0; + if (ei->i_prealloc_count) { + unsigned short total = ei->i_prealloc_count; + unsigned long block = ei->i_prealloc_block; + ei->i_prealloc_count = 0; + ei->i_prealloc_block = 0; /* Writer: end */ ext3_free_blocks (inode, block, total); } @@ -243,13 +244,14 @@ unsigned long result; #ifdef EXT3_PREALLOCATE + struct ext3_inode_info *ei = EXT3_I(inode); /* Writer: ->i_prealloc* */ - if (inode->u.ext3_i.i_prealloc_count && - (goal == inode->u.ext3_i.i_prealloc_block || - goal + 1 == inode->u.ext3_i.i_prealloc_block)) + if (ei->i_prealloc_count && + (goal == ei->i_prealloc_block || + goal + 1 == ei->i_prealloc_block)) { - result = inode->u.ext3_i.i_prealloc_block++; - inode->u.ext3_i.i_prealloc_count--; + result = ei->i_prealloc_block++; + ei->i_prealloc_count--; /* Writer: end */ ext3_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); @@ -259,8 +261,8 @@ alloc_hits, ++alloc_attempts); if (S_ISREG(inode->i_mode)) result = ext3_new_block (inode, goal, - &inode->u.ext3_i.i_prealloc_count, - &inode->u.ext3_i.i_prealloc_block, err); + &ei->i_prealloc_count, + &ei->i_prealloc_block, err); else result = ext3_new_block (inode, goal, 0, 0, err); /* @@ -394,7 +396,7 @@ *err = 0; /* i_data is not going away, no lock needed */ - add_chain (chain, NULL, inode->u.ext3_i.i_data + *offsets); + add_chain (chain, NULL, EXT3_I(inode)->i_data + *offsets); if (!p->key) goto no_block; while (--depth) { @@ -437,7 +439,8 @@ static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind) { - u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext3_i.i_data; + struct ext3_inode_info *ei = EXT3_I(inode); + u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data; u32 *p; /* Try to find previous block */ @@ -453,8 +456,7 @@ * It is going to be refered from inode itself? OK, just put it into * the same cylinder group then. */ - return (inode->u.ext3_i.i_block_group * - EXT3_BLOCKS_PER_GROUP(inode->i_sb)) + + return (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) + le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block); } @@ -474,14 +476,15 @@ static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4], Indirect *partial, unsigned long *goal) { + struct ext3_inode_info *ei = EXT3_I(inode); /* Writer: ->i_next_alloc* */ - if (block == inode->u.ext3_i.i_next_alloc_block + 1) { - inode->u.ext3_i.i_next_alloc_block++; - inode->u.ext3_i.i_next_alloc_goal++; + if (block == ei->i_next_alloc_block + 1) { + ei->i_next_alloc_block++; + ei->i_next_alloc_goal++; } #ifdef SEARCH_FROM_ZERO - inode->u.ext3_i.i_next_alloc_block = 0; - inode->u.ext3_i.i_next_alloc_goal = 0; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; #endif /* Writer: end */ /* Reader: pointers, ->i_next_alloc* */ @@ -490,8 +493,8 @@ * try the heuristic for sequential allocation, * failing that at least try to get decent locality. */ - if (block == inode->u.ext3_i.i_next_alloc_block) - *goal = inode->u.ext3_i.i_next_alloc_goal; + if (block == ei->i_next_alloc_block) + *goal = ei->i_next_alloc_goal; if (!*goal) *goal = ext3_find_near(inode, partial); #ifdef SEARCH_FROM_ZERO @@ -619,6 +622,7 @@ { int i; int err = 0; + struct ext3_inode_info *ei = EXT3_I(inode); /* * If we're splicing into a [td]indirect block (as opposed to the @@ -641,11 +645,11 @@ /* That's it */ *where->p = where->key; - inode->u.ext3_i.i_next_alloc_block = block; - inode->u.ext3_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key); + ei->i_next_alloc_block = block; + ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key); #ifdef SEARCH_FROM_ZERO - inode->u.ext3_i.i_next_alloc_block = 0; - inode->u.ext3_i.i_next_alloc_goal = 0; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; #endif /* Writer: end */ @@ -729,6 +733,7 @@ unsigned long goal; int left; int depth = ext3_block_to_path(inode, iblock, offsets); + struct ext3_inode_info *ei = EXT3_I(inode); loff_t new_size; J_ASSERT(handle != NULL || create == 0); @@ -780,7 +785,7 @@ /* * Block out ext3_truncate while we alter the tree */ - down_read(&inode->u.ext3_i.truncate_sem); + down_read(&ei->truncate_sem); err = ext3_alloc_branch(handle, inode, left, goal, offsets+(partial-chain), partial); @@ -792,7 +797,7 @@ if (!err) err = ext3_splice_branch(handle, inode, iblock, chain, partial, left); - up_read(&inode->u.ext3_i.truncate_sem); + up_read(&ei->truncate_sem); if (err == -EAGAIN) goto changed; if (err) @@ -805,8 +810,8 @@ * truncate is in progress. It is racy between multiple parallel * instances of get_block, but we have the BKL. */ - if (new_size > inode->u.ext3_i.i_disksize) - inode->u.ext3_i.i_disksize = new_size; + if (new_size > ei->i_disksize) + ei->i_disksize = new_size; bh_result->b_state |= (1UL << BH_New); goto got_it; @@ -916,7 +921,7 @@ struct buffer_head *tmp_bh; for (i = 1; - inode->u.ext3_i.i_prealloc_count && + EXT3_I(inode)->i_prealloc_count && i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks; i++) { /* @@ -1126,8 +1131,8 @@ kunmap(page); } } - if (inode->i_size > inode->u.ext3_i.i_disksize) { - inode->u.ext3_i.i_disksize = inode->i_size; + if (inode->i_size > EXT3_I(inode)->i_disksize) { + EXT3_I(inode)->i_disksize = inode->i_size; ret2 = ext3_mark_inode_dirty(handle, inode); if (!ret) ret = ret2; @@ -1826,7 +1831,8 @@ void ext3_truncate(struct inode * inode) { handle_t *handle; - u32 *i_data = inode->u.ext3_i.i_data; + struct ext3_inode_info *ei = EXT3_I(inode); + u32 *i_data = ei->i_data; int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb); int offsets[4]; Indirect chain[4]; @@ -1878,13 +1884,13 @@ * on-disk inode. We do this via i_disksize, which is the value which * ext3 *really* writes onto the disk inode. */ - inode->u.ext3_i.i_disksize = inode->i_size; + ei->i_disksize = inode->i_size; /* * From here we block out all ext3_get_block() callers who want to * modify the block allocation tree. */ - down_write(&inode->u.ext3_i.truncate_sem); + down_write(&ei->truncate_sem); if (n == 1) { /* direct blocks */ ext3_free_data(handle, inode, NULL, i_data+offsets[0], @@ -1948,7 +1954,7 @@ case EXT3_TIND_BLOCK: ; } - up_write(&inode->u.ext3_i.truncate_sem); + up_write(&ei->truncate_sem); inode->i_mtime = inode->i_ctime = CURRENT_TIME; ext3_mark_inode_dirty(handle, inode); @@ -2041,6 +2047,7 @@ { struct ext3_iloc iloc; struct ext3_inode *raw_inode; + struct ext3_inode_info *ei = EXT3_I(inode); struct buffer_head *bh; int block; @@ -2048,7 +2055,6 @@ goto bad_inode; bh = iloc.bh; raw_inode = iloc.raw_inode; - init_rwsem(&inode->u.ext3_i.truncate_sem); inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); @@ -2061,7 +2067,12 @@ inode->i_atime = le32_to_cpu(raw_inode->i_atime); inode->i_ctime = le32_to_cpu(raw_inode->i_ctime); inode->i_mtime = le32_to_cpu(raw_inode->i_mtime); - inode->u.ext3_i.i_dtime = le32_to_cpu(raw_inode->i_dtime); + + ei->i_state = 0; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; + ei->i_dir_start_lookup = 0; + ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); /* We now have enough fields to check if the inode was active or not. * This is needed because nfsd might try to access dead inodes * the test is that same one that e2fsck uses @@ -2084,33 +2095,33 @@ * size */ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); inode->i_version = ++event; - inode->u.ext3_i.i_flags = le32_to_cpu(raw_inode->i_flags); + ei->i_flags = le32_to_cpu(raw_inode->i_flags); #ifdef EXT3_FRAGMENTS - inode->u.ext3_i.i_faddr = le32_to_cpu(raw_inode->i_faddr); - inode->u.ext3_i.i_frag_no = raw_inode->i_frag; - inode->u.ext3_i.i_frag_size = raw_inode->i_fsize; + ei->i_faddr = le32_to_cpu(raw_inode->i_faddr); + ei->i_frag_no = raw_inode->i_frag; + ei->i_frag_size = raw_inode->i_fsize; #endif - inode->u.ext3_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); + ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl); if (!S_ISREG(inode->i_mode)) { - inode->u.ext3_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); + ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); } else { inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; } - inode->u.ext3_i.i_disksize = inode->i_size; + ei->i_disksize = inode->i_size; inode->i_generation = le32_to_cpu(raw_inode->i_generation); #ifdef EXT3_PREALLOCATE - inode->u.ext3_i.i_prealloc_count = 0; + ei->i_prealloc_count = 0; #endif - inode->u.ext3_i.i_block_group = iloc.block_group; + ei->i_block_group = iloc.block_group; /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! */ for (block = 0; block < EXT3_N_BLOCKS; block++) - inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block]; - INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); + ei->i_data[block] = iloc.raw_inode->i_block[block]; + INIT_LIST_HEAD(&ei->i_orphan); brelse (iloc.bh); @@ -2135,19 +2146,19 @@ init_special_inode(inode, inode->i_mode, le32_to_cpu(iloc.raw_inode->i_block[0])); /* inode->i_attr_flags = 0; unused */ - if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) { + if (ei->i_flags & EXT3_SYNC_FL) { /* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */ inode->i_flags |= S_SYNC; } - if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) { + if (ei->i_flags & EXT3_APPEND_FL) { /* inode->i_attr_flags |= ATTR_FLAG_APPEND; unused */ inode->i_flags |= S_APPEND; } - if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) { + if (ei->i_flags & EXT3_IMMUTABLE_FL) { /* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE; unused */ inode->i_flags |= S_IMMUTABLE; } - if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) { + if (ei->i_flags & EXT3_NOATIME_FL) { /* inode->i_attr_flags |= ATTR_FLAG_NOATIME; unused */ inode->i_flags |= S_NOATIME; } @@ -2169,6 +2180,7 @@ struct ext3_iloc *iloc) { struct ext3_inode *raw_inode = iloc->raw_inode; + struct ext3_inode_info *ei = EXT3_I(inode); struct buffer_head *bh = iloc->bh; int err = 0, rc, block; @@ -2186,7 +2198,7 @@ * Fix up interoperability with old kernels. Otherwise, old inodes get * re-used with the upper 16 bits of the uid/gid intact */ - if(!inode->u.ext3_i.i_dtime) { + if(!ei->i_dtime) { raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid)); raw_inode->i_gid_high = @@ -2204,34 +2216,34 @@ raw_inode->i_gid_high = 0; } raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); - raw_inode->i_size = cpu_to_le32(inode->u.ext3_i.i_disksize); + raw_inode->i_size = cpu_to_le32(ei->i_disksize); raw_inode->i_atime = cpu_to_le32(inode->i_atime); raw_inode->i_ctime = cpu_to_le32(inode->i_ctime); raw_inode->i_mtime = cpu_to_le32(inode->i_mtime); raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); - raw_inode->i_dtime = cpu_to_le32(inode->u.ext3_i.i_dtime); - raw_inode->i_flags = cpu_to_le32(inode->u.ext3_i.i_flags); + raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); + raw_inode->i_flags = cpu_to_le32(ei->i_flags); #ifdef EXT3_FRAGMENTS - raw_inode->i_faddr = cpu_to_le32(inode->u.ext3_i.i_faddr); - raw_inode->i_frag = inode->u.ext3_i.i_frag_no; - raw_inode->i_fsize = inode->u.ext3_i.i_frag_size; + raw_inode->i_faddr = cpu_to_le32(ei->i_faddr); + raw_inode->i_frag = ei->i_frag_no; + raw_inode->i_fsize = ei->i_frag_size; #else /* If we are not tracking these fields in the in-memory inode, * then preserve them on disk, but still initialise them to zero * for new inodes. */ - if (EXT3_I(inode)->i_state & EXT3_STATE_NEW) { + if (ei->i_state & EXT3_STATE_NEW) { raw_inode->i_faddr = 0; raw_inode->i_frag = 0; raw_inode->i_fsize = 0; } #endif - raw_inode->i_file_acl = cpu_to_le32(inode->u.ext3_i.i_file_acl); + raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl); if (!S_ISREG(inode->i_mode)) { - raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext3_i.i_dir_acl); + raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl); } else { raw_inode->i_size_high = - cpu_to_le32(inode->u.ext3_i.i_disksize >> 32); - if (inode->u.ext3_i.i_disksize > 0x7fffffffULL) { + cpu_to_le32(ei->i_disksize >> 32); + if (ei->i_disksize > 0x7fffffffULL) { struct super_block *sb = inode->i_sb; if (!EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT3_FEATURE_RO_COMPAT_LARGE_FILE) || @@ -2259,13 +2271,13 @@ raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); else for (block = 0; block < EXT3_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext3_i.i_data[block]; + raw_inode->i_block[block] = ei->i_data[block]; BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); rc = ext3_journal_dirty_metadata(handle, bh); if (!err) err = rc; - EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW; + ei->i_state &= ~EXT3_STATE_NEW; out_brelse: brelse (bh); @@ -2373,7 +2385,7 @@ } error = ext3_orphan_add(handle, inode); - inode->u.ext3_i.i_disksize = attr->ia_size; + EXT3_I(inode)->i_disksize = attr->ia_size; rc = ext3_mark_inode_dirty(handle, inode); if (!error) error = rc; @@ -2616,9 +2628,9 @@ */ if (val) - inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL; + EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL; else - inode->u.ext3_i.i_flags &= ~EXT3_JOURNAL_DATA_FL; + EXT3_I(inode)->i_flags &= ~EXT3_JOURNAL_DATA_FL; journal_unlock_updates(journal); diff -urN linux-2.5.3-pre2/fs/ext3/ioctl.c linux/fs/ext3/ioctl.c --- linux-2.5.3-pre2/fs/ext3/ioctl.c Fri Nov 9 14:25:04 2001 +++ linux/fs/ext3/ioctl.c Mon Jan 21 17:23:24 2002 @@ -18,13 +18,14 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { + struct ext3_inode_info *ei = EXT3_I(inode); unsigned int flags; ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT3_IOC_GETFLAGS: - flags = inode->u.ext3_i.i_flags & EXT3_FL_USER_VISIBLE; + flags = ei->i_flags & EXT3_FL_USER_VISIBLE; return put_user(flags, (int *) arg); case EXT3_IOC_SETFLAGS: { handle_t *handle = NULL; @@ -42,7 +43,7 @@ if (get_user(flags, (int *) arg)) return -EFAULT; - oldflags = inode->u.ext3_i.i_flags; + oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ jflag = flags & EXT3_JOURNAL_DATA_FL; @@ -79,7 +80,7 @@ flags = flags & EXT3_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE; - inode->u.ext3_i.i_flags = flags; + ei->i_flags = flags; if (flags & EXT3_SYNC_FL) inode->i_flags |= S_SYNC; diff -urN linux-2.5.3-pre2/fs/ext3/namei.c linux/fs/ext3/namei.c --- linux-2.5.3-pre2/fs/ext3/namei.c Fri Nov 9 14:25:04 2001 +++ linux/fs/ext3/namei.c Mon Jan 21 17:23:24 2002 @@ -124,7 +124,7 @@ sb = dir->i_sb; nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb); - start = dir->u.ext3_i.i_dir_start_lookup; + start = EXT3_I(dir)->i_dir_start_lookup; if (start >= nblocks) start = 0; block = start; @@ -165,7 +165,7 @@ i = search_dirblock(bh, dir, dentry, block << EXT3_BLOCK_SIZE_BITS(sb), res_dir); if (i == 1) { - dir->u.ext3_i.i_dir_start_lookup = block; + EXT3_I(dir)->i_dir_start_lookup = block; ret = bh; goto cleanup_and_exit; } else { @@ -295,9 +295,9 @@ de = (struct ext3_dir_entry_2 *) bh->b_data; de->inode = 0; de->rec_len = le16_to_cpu(sb->s_blocksize); - dir->u.ext3_i.i_disksize = + EXT3_I(dir)->i_disksize = dir->i_size = offset + sb->s_blocksize; - dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, dir); } else { @@ -353,7 +353,7 @@ * and/or different from the directory change time. */ dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, dir); dir->i_version = ++event; BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); @@ -521,7 +521,7 @@ inode->i_op = &ext3_dir_inode_operations; inode->i_fop = &ext3_dir_operations; - inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize; + inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize; inode->i_blocks = 0; dir_block = ext3_bread (handle, inode, 0, 1, &err); if (!dir_block) { @@ -557,7 +557,7 @@ if (err) goto out_no_entry; dir->i_nlink++; - dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, dir); d_instantiate(dentry, inode); out_stop: @@ -655,7 +655,7 @@ int err = 0, rc; lock_super(sb); - if (!list_empty(&inode->u.ext3_i.i_orphan)) + if (!list_empty(&EXT3_I(inode)->i_orphan)) goto out_unlock; /* Orphan handling is only valid for files with data blocks @@ -696,7 +696,7 @@ * This is safe: on error we're going to ignore the orphan list * anyway on the next recovery. */ if (!err) - list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan); + list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan); jbd_debug(4, "superblock will point to %ld\n", inode->i_ino); jbd_debug(4, "orphan inode %ld will point to %d\n", @@ -714,25 +714,25 @@ int ext3_orphan_del(handle_t *handle, struct inode *inode) { struct list_head *prev; + struct ext3_inode_info *ei = EXT3_I(inode); struct ext3_sb_info *sbi; ino_t ino_next; struct ext3_iloc iloc; int err = 0; lock_super(inode->i_sb); - if (list_empty(&inode->u.ext3_i.i_orphan)) { + if (list_empty(&ei->i_orphan)) { unlock_super(inode->i_sb); return 0; } ino_next = NEXT_ORPHAN(inode); - prev = inode->u.ext3_i.i_orphan.prev; + prev = ei->i_orphan.prev; sbi = EXT3_SB(inode->i_sb); jbd_debug(4, "remove inode %ld from orphan list\n", inode->i_ino); - list_del(&inode->u.ext3_i.i_orphan); - INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); + list_del_init(&ei->i_orphan); /* If we're on an error path, we may not have a valid * transaction handle with which to update the orphan list on @@ -756,7 +756,7 @@ } else { struct ext3_iloc iloc2; struct inode *i_prev = - list_entry(prev, struct inode, u.ext3_i.i_orphan); + &list_entry(prev, struct ext3_inode_info, i_orphan)->vfs_inode; jbd_debug(4, "orphan inode %ld will point to %ld\n", i_prev->i_ino, ino_next); @@ -832,7 +832,7 @@ ext3_mark_inode_dirty(handle, inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, dir); end_rmdir: @@ -878,7 +878,7 @@ if (retval) goto end_unlink; dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, dir); inode->i_nlink--; if (!inode->i_nlink) @@ -916,7 +916,7 @@ if (IS_ERR(inode)) goto out_stop; - if (l > sizeof (inode->u.ext3_i.i_data)) { + if (l > sizeof (EXT3_I(inode)->i_data)) { inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &ext3_aops; /* @@ -929,10 +929,10 @@ goto out_no_entry; } else { inode->i_op = &ext3_fast_symlink_inode_operations; - memcpy((char*)&inode->u.ext3_i.i_data,symname,l); + memcpy((char*)&EXT3_I(inode)->i_data,symname,l); inode->i_size = l-1; } - inode->u.ext3_i.i_disksize = inode->i_size; + EXT3_I(inode)->i_disksize = inode->i_size; ext3_mark_inode_dirty(handle, inode); err = ext3_add_nondir(handle, dentry, inode); out_stop: @@ -1077,7 +1077,7 @@ new_inode->i_ctime = CURRENT_TIME; } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(old_dir)->i_flags &= ~EXT3_INDEX_FL; if (dir_bh) { BUFFER_TRACE(dir_bh, "get_write_access"); ext3_journal_get_write_access(handle, dir_bh); @@ -1089,7 +1089,7 @@ new_inode->i_nlink--; } else { new_dir->i_nlink++; - new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + EXT3_I(new_dir)->i_flags &= ~EXT3_INDEX_FL; ext3_mark_inode_dirty(handle, new_dir); } } diff -urN linux-2.5.3-pre2/fs/ext3/super.c linux/fs/ext3/super.c --- linux-2.5.3-pre2/fs/ext3/super.c Wed Jan 9 11:56:57 2002 +++ linux/fs/ext3/super.c Mon Jan 21 17:23:24 2002 @@ -376,7 +376,10 @@ return ret; } -#define orphan_list_entry(l) list_entry((l), struct inode, u.ext3_i.i_orphan) +static inline struct inode *orphan_list_entry(struct list_head *l) +{ + return &list_entry(l, struct ext3_inode_info, i_orphan)->vfs_inode; +} static void dump_orphan_list(struct super_block *sb, struct ext3_sb_info *sbi) { @@ -444,7 +447,54 @@ return; } +static kmem_cache_t * ext3_inode_cachep; + +static struct inode *ext3_alloc_inode(struct super_block *sb) +{ + struct ext3_inode_info *ei; + ei = (struct ext3_inode_info *)kmem_cache_alloc(ext3_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void ext3_destroy_inode(struct inode *inode) +{ + kmem_cache_free(ext3_inode_cachep, EXT3_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct ext3_inode_info *ei = (struct ext3_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + INIT_LIST_HEAD(&ei->i_orphan); + init_rwsem(&ei->truncate_sem); + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + ext3_inode_cachep = kmem_cache_create("ext3_inode_cache", + sizeof(struct ext3_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (ext3_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(ext3_inode_cachep)) + printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n"); +} + static struct super_operations ext3_sops = { + alloc_inode: ext3_alloc_inode, + destroy_inode: ext3_destroy_inode, read_inode: ext3_read_inode, /* BKL held */ write_inode: ext3_write_inode, /* BKL not held. Don't need */ dirty_inode: ext3_dirty_inode, /* BKL not held. We take it */ @@ -1723,12 +1773,23 @@ static int __init init_ext3_fs(void) { - return register_filesystem(&ext3_fs_type); + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&ext3_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_ext3_fs(void) { unregister_filesystem(&ext3_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/ext3/symlink.c linux/fs/ext3/symlink.c --- linux-2.5.3-pre2/fs/ext3/symlink.c Fri Nov 9 14:25:04 2001 +++ linux/fs/ext3/symlink.c Mon Jan 21 17:23:24 2002 @@ -23,14 +23,14 @@ static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen) { - char *s = (char *)dentry->d_inode->u.ext3_i.i_data; - return vfs_readlink(dentry, buffer, buflen, s); + struct ext3_inode_info *ei = EXT3_I(dentry->d_inode); + return vfs_readlink(dentry, buffer, buflen, (char*)ei->i_data); } static int ext3_follow_link(struct dentry *dentry, struct nameidata *nd) { - char *s = (char *)dentry->d_inode->u.ext3_i.i_data; - return vfs_follow_link(nd, s); + struct ext3_inode_info *ei = EXT3_I(dentry->d_inode); + return vfs_follow_link(nd, (char*)ei->i_data); } struct inode_operations ext3_fast_symlink_inode_operations = { diff -urN linux-2.5.3-pre2/fs/fat/dir.c linux/fs/fat/dir.c --- linux-2.5.3-pre2/fs/fat/dir.c Mon Jan 21 17:22:37 2002 +++ linux/fs/fat/dir.c Mon Jan 21 17:23:24 2002 @@ -744,8 +744,8 @@ if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) return -ENOSPC; new_bh = fat_extend_dir(dir); - if (!new_bh) - return -ENOSPC; + if (IS_ERR(new_bh)) + return PTR_ERR(new_bh); fat_brelse(sb, new_bh); do { fat_get_entry(dir, &curr, bh, de, ino); @@ -761,7 +761,10 @@ struct msdos_dir_entry *de; __u16 date, time; - if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC; + bh = fat_extend_dir(dir); + if (IS_ERR(bh)) + return PTR_ERR(bh); + /* zeroed out, so... */ fat_date_unix2dos(dir->i_mtime,&time,&date); de = (struct msdos_dir_entry*)&bh->b_data[0]; diff -urN linux-2.5.3-pre2/fs/fat/file.c linux/fs/fat/file.c --- linux-2.5.3-pre2/fs/fat/file.c Thu Dec 27 08:17:43 2001 +++ linux/fs/fat/file.c Mon Jan 21 17:23:24 2002 @@ -65,8 +65,11 @@ return -EIO; } if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) { - if (fat_add_cluster(inode) < 0) - return -ENOSPC; + int error; + + error = fat_add_cluster(inode); + if (error < 0) + return error; } MSDOS_I(inode)->mmu_private += sb->s_blocksize; phys = fat_bmap(inode, iblock); diff -urN linux-2.5.3-pre2/fs/fat/misc.c linux/fs/fat/misc.c --- linux-2.5.3-pre2/fs/fat/misc.c Mon Jan 21 17:22:37 2002 +++ linux/fs/fat/misc.c Mon Jan 21 17:23:24 2002 @@ -131,14 +131,13 @@ { struct super_block *sb = inode->i_sb; int count, nr, limit, last, curr, file_cluster; - int cluster_size = MSDOS_SB(sb)->cluster_size; - int res = -ENOSPC; + int cluster_bits = MSDOS_SB(sb)->cluster_bits; lock_fat(sb); if (MSDOS_SB(sb)->free_clusters == 0) { unlock_fat(sb); - return res; + return -ENOSPC; } limit = MSDOS_SB(sb)->clusters; nr = limit; /* to keep GCC happy */ @@ -150,7 +149,7 @@ if (count >= limit) { MSDOS_SB(sb)->free_clusters = 0; unlock_fat(sb); - return res; + return -ENOSPC; } MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit; @@ -174,13 +173,20 @@ */ last = file_cluster = 0; if ((curr = MSDOS_I(inode)->i_start) != 0) { + int max_cluster = MSDOS_I(inode)->mmu_private >> cluster_bits; + fat_cache_lookup(inode, INT_MAX, &last, &curr); file_cluster = last; - while (curr && curr != -1){ + while (curr && curr != -1) { file_cluster++; - if (!(curr = fat_access(sb, last = curr,-1))) { + if (!(curr = fat_access(sb, last = curr, -1))) { fat_fs_panic(sb, "File without EOF"); - return res; + return -EIO; + } + if (file_cluster > max_cluster) { + fat_fs_panic(sb,"inode %lu: bad cluster counts", + inode->i_ino); + return -EIO; } } } @@ -192,14 +198,12 @@ MSDOS_I(inode)->i_logstart = nr; mark_inode_dirty(inode); } - if (file_cluster - != inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) { + if (file_cluster != (inode->i_blocks >> (cluster_bits - 9))) { printk ("file_cluster badly computed!!! %d <> %ld\n", - file_cluster, - inode->i_blocks / cluster_size / (sb->s_blocksize / 512)); + file_cluster, inode->i_blocks >> (cluster_bits - 9)); fat_cache_inval_inode(inode); } - inode->i_blocks += (1 << MSDOS_SB(sb)->cluster_bits) / 512; + inode->i_blocks += (1 << cluster_bits) >> 9; return nr; } @@ -213,24 +217,23 @@ if (MSDOS_SB(sb)->fat_bits != 32) { if (inode->i_ino == MSDOS_ROOT_INO) - return res; + return ERR_PTR(-ENOSPC); } nr = fat_add_cluster(inode); if (nr < 0) - return res; + return ERR_PTR(nr); sector = MSDOS_SB(sb)->data_start + (nr - 2) * cluster_size; last_sector = sector + cluster_size; - if (MSDOS_SB(sb)->cvf_format && MSDOS_SB(sb)->cvf_format->zero_out_cluster) + if (MSDOS_SB(sb)->cvf_format + && MSDOS_SB(sb)->cvf_format->zero_out_cluster) { + res = ERR_PTR(-EIO); MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode, nr); - else { + } else { for ( ; sector < last_sector; sector++) { -#ifdef DEBUG - printk("zeroing sector %d\n", sector); -#endif if (!(bh = fat_getblk(sb, sector))) - printk("getblk failed\n"); + printk("FAT: fat_getblk() failed\n"); else { memset(bh->b_data, 0, sb->s_blocksize); fat_set_uptodate(sb, bh, 1); @@ -241,6 +244,8 @@ fat_brelse(sb, bh); } } + if (res == NULL) + res = ERR_PTR(-EIO); } if (inode->i_size & (sb->s_blocksize - 1)) { fat_fs_panic(sb, "Odd directory size"); @@ -484,12 +489,11 @@ **res_de) { int count, cluster; - unsigned long dir_size; + unsigned long dir_size = 0; #ifdef DEBUG printk("raw_scan_nonroot: start=%d\n",start); #endif - dir_size = 0; do { for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { if ((cluster = raw_scan_sector(sb,(start-2)* diff -urN linux-2.5.3-pre2/fs/hpfs/anode.c linux/fs/hpfs/anode.c --- linux-2.5.3-pre2/fs/hpfs/anode.c Mon Jun 11 19:15:27 2001 +++ linux/fs/hpfs/anode.c Mon Jan 21 17:23:24 2002 @@ -42,9 +42,10 @@ return -1; } if (inode) { - inode->i_hpfs_file_sec = btree->u.external[i].file_secno; - inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno; - inode->i_hpfs_n_secs = btree->u.external[i].length; + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + hpfs_inode->i_file_sec = btree->u.external[i].file_secno; + hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno; + hpfs_inode->i_n_secs = btree->u.external[i].length; } brelse(bh); return a; diff -urN linux-2.5.3-pre2/fs/hpfs/buffer.c linux/fs/hpfs/buffer.c --- linux-2.5.3-pre2/fs/hpfs/buffer.c Sun Dec 16 12:23:00 2001 +++ linux/fs/hpfs/buffer.c Mon Jan 21 17:23:24 2002 @@ -47,38 +47,72 @@ void hpfs_lock_inode(struct inode *i) { - if (i) down(&i->i_hpfs_sem); + if (i) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + down(&hpfs_inode->i_sem); + } } void hpfs_unlock_inode(struct inode *i) { - if (i) up(&i->i_hpfs_sem); + if (i) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + up(&hpfs_inode->i_sem); + } } void hpfs_lock_2inodes(struct inode *i1, struct inode *i2) { - if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; } - if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; } + struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL; + + if (!i1) { + if (i2) { + hpfs_i2 = hpfs_i(i2); + down(&hpfs_i2->i_sem); + } + return; + } + if (!i2) { + if (i1) { + hpfs_i1 = hpfs_i(i1); + down(&hpfs_i1->i_sem); + } + return; + } if (i1->i_ino < i2->i_ino) { - down(&i1->i_hpfs_sem); - down(&i2->i_hpfs_sem); + down(&hpfs_i1->i_sem); + down(&hpfs_i2->i_sem); } else if (i1->i_ino > i2->i_ino) { - down(&i2->i_hpfs_sem); - down(&i1->i_hpfs_sem); - } else down(&i1->i_hpfs_sem); + down(&hpfs_i2->i_sem); + down(&hpfs_i1->i_sem); + } else down(&hpfs_i1->i_sem); } void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2) { - if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; } - if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; } + struct hpfs_inode_info *hpfs_i1 = NULL, *hpfs_i2 = NULL; + + if (!i1) { + if (i2) { + hpfs_i2 = hpfs_i(i2); + up(&hpfs_i2->i_sem); + } + return; + } + if (!i2) { + if (i1) { + hpfs_i1 = hpfs_i(i1); + up(&hpfs_i1->i_sem); + } + return; + } if (i1->i_ino < i2->i_ino) { - up(&i2->i_hpfs_sem); - up(&i1->i_hpfs_sem); + up(&hpfs_i2->i_sem); + up(&hpfs_i1->i_sem); } else if (i1->i_ino > i2->i_ino) { - up(&i1->i_hpfs_sem); - up(&i2->i_hpfs_sem); - } else up(&i1->i_hpfs_sem); + up(&hpfs_i1->i_sem); + up(&hpfs_i2->i_sem); + } else up(&hpfs_i1->i_sem); } void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3) @@ -87,13 +121,16 @@ if (!i2) { hpfs_lock_2inodes(i1, i3); return; } if (!i3) { hpfs_lock_2inodes(i1, i2); return; } if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { - down(&i1->i_hpfs_sem); + struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1); + down(&hpfs_i1->i_sem); hpfs_lock_2inodes(i2, i3); } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { - down(&i2->i_hpfs_sem); + struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2); + down(&hpfs_i2->i_sem); hpfs_lock_2inodes(i1, i3); } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { - down(&i3->i_hpfs_sem); + struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3); + down(&hpfs_i3->i_sem); hpfs_lock_2inodes(i1, i2); } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2); else hpfs_lock_2inodes(i1, i3); @@ -105,14 +142,17 @@ if (!i2) { hpfs_unlock_2inodes(i1, i3); return; } if (!i3) { hpfs_unlock_2inodes(i1, i2); return; } if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { + struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1); hpfs_unlock_2inodes(i2, i3); - up(&i1->i_hpfs_sem); + up(&hpfs_i1->i_sem); } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { + struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2); hpfs_unlock_2inodes(i1, i3); - up(&i2->i_hpfs_sem); + up(&hpfs_i2->i_sem); } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { + struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3); hpfs_unlock_2inodes(i1, i2); - up(&i3->i_hpfs_sem); + up(&hpfs_i3->i_sem); } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2); else hpfs_unlock_2inodes(i1, i3); } diff -urN linux-2.5.3-pre2/fs/hpfs/dir.c linux/fs/hpfs/dir.c --- linux-2.5.3-pre2/fs/hpfs/dir.c Mon Jun 11 19:15:27 2001 +++ linux/fs/hpfs/dir.c Mon Jan 21 17:23:24 2002 @@ -27,11 +27,12 @@ loff_t pos; struct quad_buffer_head qbh; struct inode *i = filp->f_dentry->d_inode; + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct super_block *s = i->i_sb; /*printk("dir lseek\n");*/ if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok; hpfs_lock_inode(i); - pos = ((loff_t) hpfs_de_as_down_as_possible(s, i->i_hpfs_dno) << 4) + 1; + pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1; while (pos != new_off) { if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh); else goto fail; @@ -49,6 +50,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); struct quad_buffer_head qbh; struct hpfs_dirent *de; int lc; @@ -59,7 +61,7 @@ if (inode->i_sb->s_hpfs_chk) { if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) return -EFSERROR; - if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode")) + if (hpfs_chk_sectors(inode->i_sb, hpfs_inode->i_dno, 4, "dir_dnode")) return -EFSERROR; } if (inode->i_sb->s_hpfs_chk >= 2) { @@ -72,9 +74,9 @@ e = 1; hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino); } - if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) { + if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) { e = 1; - hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno); + hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno); } brelse(bh); if (e) return -EFSERROR; @@ -115,14 +117,14 @@ filp->f_pos = 11; } if (filp->f_pos == 11) { - if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir, DT_DIR) < 0) { + if (filldir(dirent, "..", 2, filp->f_pos, hpfs_inode->i_parent_dir, DT_DIR) < 0) { hpfs_unlock_inode(inode); return 0; } filp->f_pos = 1; } if (filp->f_pos == 1) { - filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1; + filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, hpfs_inode->i_dno) << 4) + 1; hpfs_add_pos(inode, &filp->f_pos); filp->f_version = inode->i_version; } @@ -180,6 +182,7 @@ ino_t ino; int err; struct inode *result = NULL; + struct hpfs_inode_info *hpfs_result; if ((err = hpfs_chk_name((char *)name, &len))) { if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG); @@ -191,7 +194,7 @@ * '.' and '..' will never be passed here. */ - de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh); + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh); /* * This is not really a bailout, just means file not found. @@ -215,7 +218,8 @@ hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode"); goto bail1; } - if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino; + hpfs_result = hpfs_i(result); + if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino; hpfs_unlock_iget(dir->i_sb); hpfs_decide_conv(result, (char *)name, len); @@ -235,14 +239,14 @@ result->i_ctime = 1; result->i_mtime = local_to_gmt(dir->i_sb, de->write_date); result->i_atime = local_to_gmt(dir->i_sb, de->read_date); - result->i_hpfs_ea_size = de->ea_size; - if (!result->i_hpfs_ea_mode && de->read_only) + hpfs_result->i_ea_size = de->ea_size; + if (!hpfs_result->i_ea_mode && de->read_only) result->i_mode &= ~0222; if (!de->directory) { if (result->i_size == -1) { result->i_size = de->file_size; result->i_data.a_ops = &hpfs_aops; - result->u.hpfs_i.mmu_private = result->i_size; + hpfs_i(result)->mmu_private = result->i_size; /* * i_blocks should count the fnode and any anodes. * We count 1 for the fnode and don't bother about diff -urN linux-2.5.3-pre2/fs/hpfs/dnode.c linux/fs/hpfs/dnode.c --- linux-2.5.3-pre2/fs/hpfs/dnode.c Tue Sep 5 14:07:29 2000 +++ linux/fs/hpfs/dnode.c Mon Jan 21 17:23:24 2002 @@ -23,39 +23,43 @@ void hpfs_add_pos(struct inode *inode, loff_t *pos) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); int i = 0; loff_t **ppos; - if (inode->i_hpfs_rddir_off) - for (; inode->i_hpfs_rddir_off[i]; i++) - if (inode->i_hpfs_rddir_off[i] == pos) return; + + if (hpfs_inode->i_rddir_off) + for (; hpfs_inode->i_rddir_off[i]; i++) + if (hpfs_inode->i_rddir_off[i] == pos) return; if (!(i&0x0f)) { if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) { printk("HPFS: out of memory for position list\n"); return; } - if (inode->i_hpfs_rddir_off) { - memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t)); - kfree(inode->i_hpfs_rddir_off); + if (hpfs_inode->i_rddir_off) { + memcpy(ppos, hpfs_inode->i_rddir_off, i * sizeof(loff_t)); + kfree(hpfs_inode->i_rddir_off); } - inode->i_hpfs_rddir_off = ppos; + hpfs_inode->i_rddir_off = ppos; } - inode->i_hpfs_rddir_off[i] = pos; - inode->i_hpfs_rddir_off[i + 1] = NULL; + hpfs_inode->i_rddir_off[i] = pos; + hpfs_inode->i_rddir_off[i + 1] = NULL; } void hpfs_del_pos(struct inode *inode, loff_t *pos) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); loff_t **i, **j; - if (!inode->i_hpfs_rddir_off) goto not_f; - for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd; + + if (!hpfs_inode->i_rddir_off) goto not_f; + for (i = hpfs_inode->i_rddir_off; *i; i++) if (*i == pos) goto fnd; goto not_f; fnd: for (j = i + 1; *j; j++) ; *i = *(j - 1); *(j - 1) = NULL; - if (j - 1 == inode->i_hpfs_rddir_off) { - kfree(inode->i_hpfs_rddir_off); - inode->i_hpfs_rddir_off = NULL; + if (j - 1 == hpfs_inode->i_rddir_off) { + kfree(hpfs_inode->i_rddir_off); + hpfs_inode->i_rddir_off = NULL; } return; not_f: @@ -66,9 +70,11 @@ static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t), loff_t p1, loff_t p2) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); loff_t **i; - if (!inode->i_hpfs_rddir_off) return; - for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2); + + if (!hpfs_inode->i_rddir_off) return; + for (i = hpfs_inode->i_rddir_off; *i; i++) (*f)(*i, p1, p2); return; } @@ -339,7 +345,7 @@ fnode->u.external[0].disk_secno = rdno; mark_buffer_dirty(bh); brelse(bh); - d->up = ad->up = i->i_hpfs_dno = rdno; + d->up = ad->up = hpfs_i(i)->i_dno = rdno; d->root_dnode = ad->root_dnode = 0; hpfs_mark_4buffers_dirty(&qbh); hpfs_brelse4(&qbh); @@ -363,13 +369,14 @@ int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen, struct hpfs_dirent *new_de, int cdepth) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct dnode *d; struct hpfs_dirent *de, *de_end; struct quad_buffer_head qbh; dnode_secno dno; int c; int c1, c2 = 0; - dno = i->i_hpfs_dno; + dno = hpfs_inode->i_dno; down: if (i->i_sb->s_hpfs_chk) if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1; @@ -494,6 +501,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct quad_buffer_head qbh; struct dnode *dnode; dnode_secno down, up, ndown; @@ -538,7 +546,7 @@ mark_buffer_dirty(bh); brelse(bh); } - i->i_hpfs_dno = down; + hpfs_inode->i_dno = down; for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12); return; } diff -urN linux-2.5.3-pre2/fs/hpfs/ea.c linux/fs/hpfs/ea.c --- linux-2.5.3-pre2/fs/hpfs/ea.c Tue Sep 5 14:07:29 2000 +++ linux/fs/hpfs/ea.c Mon Jan 21 17:23:24 2002 @@ -355,7 +355,7 @@ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail; fnode->ea_size_l = pos; ret: - inode->i_hpfs_ea_size += 5 + strlen(key) + size; + hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size; return; bail: if (fnode->ea_secno) diff -urN linux-2.5.3-pre2/fs/hpfs/file.c linux/fs/hpfs/file.c --- linux-2.5.3-pre2/fs/hpfs/file.c Thu Dec 27 08:17:43 2001 +++ linux/fs/hpfs/file.c Mon Jan 21 17:23:24 2002 @@ -45,12 +45,13 @@ secno hpfs_bmap(struct inode *inode, unsigned file_secno) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); unsigned n, disk_secno; struct fnode *fnode; struct buffer_head *bh; - if (BLOCKS(inode->u.hpfs_i.mmu_private) <= file_secno) return 0; - n = file_secno - inode->i_hpfs_file_sec; - if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n; + if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0; + n = file_secno - hpfs_inode->i_file_sec; + if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n; if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0; disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh); if (disk_secno == -1) return 0; @@ -61,9 +62,9 @@ void hpfs_truncate(struct inode *i) { if (IS_IMMUTABLE(i)) return /*-EPERM*/; - i->i_hpfs_n_secs = 0; + hpfs_i(i)->i_n_secs = 0; i->i_blocks = 1 + ((i->i_size + 511) >> 9); - i->u.hpfs_i.mmu_private = i->i_size; + hpfs_i(i)->mmu_private = i->i_size; hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); hpfs_write_inode(i); } @@ -77,7 +78,7 @@ return 0; } if (!create) return 0; - if (iblock<<9 != inode->u.hpfs_i.mmu_private) { + if (iblock<<9 != hpfs_i(inode)->mmu_private) { BUG(); return -EIO; } @@ -86,7 +87,7 @@ return -ENOSPC; } inode->i_blocks++; - inode->u.hpfs_i.mmu_private += 512; + hpfs_i(inode)->mmu_private += 512; bh_result->b_state |= 1UL << BH_New; map_bh(bh_result, inode->i_sb, s); return 0; @@ -103,7 +104,7 @@ static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { return cont_prepare_write(page,from,to,hpfs_get_block, - &page->mapping->host->u.hpfs_i.mmu_private); + &hpfs_i(page->mapping->host)->mmu_private); } static int _hpfs_bmap(struct address_space *mapping, long block) { @@ -126,7 +127,7 @@ if (retval > 0) { struct inode *inode = file->f_dentry->d_inode; inode->i_mtime = CURRENT_TIME; - inode->i_hpfs_dirty = 1; + hpfs_i(inode)->i_dirty = 1; } return retval; } diff -urN linux-2.5.3-pre2/fs/hpfs/hpfs_fn.h linux/fs/hpfs/hpfs_fn.h --- linux-2.5.3-pre2/fs/hpfs/hpfs_fn.h Sun Dec 30 10:31:51 2001 +++ linux/fs/hpfs/hpfs_fn.h Mon Jan 21 17:23:24 2002 @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -302,6 +303,11 @@ int hpfs_rmdir(struct inode *, struct dentry *); int hpfs_symlink_readpage(struct file *, struct page *); int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); + +static inline struct hpfs_inode_info *hpfs_i(struct inode *inode) +{ + return list_entry(inode, struct hpfs_inode_info, vfs_inode); +} /* super.c */ diff -urN linux-2.5.3-pre2/fs/hpfs/inode.c linux/fs/hpfs/inode.c --- linux-2.5.3-pre2/fs/hpfs/inode.c Mon Sep 10 07:31:25 2001 +++ linux/fs/hpfs/inode.c Mon Jan 21 17:23:24 2002 @@ -59,31 +59,32 @@ struct buffer_head *bh; struct fnode *fnode; struct super_block *sb = i->i_sb; + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); unsigned char *ea; int ea_size; - init_MUTEX(&i->i_hpfs_sem); + i->i_uid = sb->s_hpfs_uid; i->i_gid = sb->s_hpfs_gid; i->i_mode = sb->s_hpfs_mode; - i->i_hpfs_conv = sb->s_hpfs_conv; + hpfs_inode->i_conv = sb->s_hpfs_conv; i->i_blksize = 512; i->i_size = -1; i->i_blocks = -1; - i->i_hpfs_dno = 0; - i->i_hpfs_n_secs = 0; - i->i_hpfs_file_sec = 0; - i->i_hpfs_disk_sec = 0; - i->i_hpfs_dpos = 0; - i->i_hpfs_dsubdno = 0; - i->i_hpfs_ea_mode = 0; - i->i_hpfs_ea_uid = 0; - i->i_hpfs_ea_gid = 0; - i->i_hpfs_ea_size = 0; + hpfs_inode->i_dno = 0; + hpfs_inode->i_n_secs = 0; + hpfs_inode->i_file_sec = 0; + hpfs_inode->i_disk_sec = 0; + hpfs_inode->i_dpos = 0; + hpfs_inode->i_dsubdno = 0; + hpfs_inode->i_ea_mode = 0; + hpfs_inode->i_ea_uid = 0; + hpfs_inode->i_ea_gid = 0; + hpfs_inode->i_ea_size = 0; i->i_version = ++event; - i->i_hpfs_rddir_off = NULL; - i->i_hpfs_dirty = 0; + hpfs_inode->i_rddir_off = NULL; + hpfs_inode->i_dirty = 0; i->i_atime = 0; i->i_mtime = 0; @@ -112,14 +113,14 @@ if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) { if (ea_size == 2) { i->i_uid = ea[0] + (ea[1] << 8); - i->i_hpfs_ea_uid = 1; + hpfs_inode->i_ea_uid = 1; } kfree(ea); } if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) { if (ea_size == 2) { i->i_gid = ea[0] + (ea[1] << 8); - i->i_hpfs_ea_gid = 1; + hpfs_inode->i_ea_gid = 1; } kfree(ea); } @@ -139,7 +140,7 @@ umode_t mode = sb->s_hpfs_mode; if (ea_size == 2) { mode = ea[0] + (ea[1] << 8); - i->i_hpfs_ea_mode = 1; + hpfs_inode->i_ea_mode = 1; } kfree(ea); i->i_mode = mode; @@ -165,60 +166,61 @@ i->i_mode |= S_IFDIR; i->i_op = &hpfs_dir_iops; i->i_fop = &hpfs_dir_ops; - i->i_hpfs_parent_dir = fnode->up; - i->i_hpfs_dno = fnode->u.external[0].disk_secno; + hpfs_inode->i_parent_dir = fnode->up; + hpfs_inode->i_dno = fnode->u.external[0].disk_secno; if (sb->s_hpfs_chk >= 2) { struct buffer_head *bh0; - if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0); + if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0); } n_dnodes = 0; n_subdirs = 0; - hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL); + hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL); i->i_blocks = 4 * n_dnodes; i->i_size = 2048 * n_dnodes; i->i_nlink = 2 + n_subdirs; } else { i->i_mode |= S_IFREG; - if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111; + if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111; i->i_op = &hpfs_file_iops; i->i_fop = &hpfs_file_ops; i->i_nlink = 1; i->i_size = fnode->file_size; i->i_blocks = ((i->i_size + 511) >> 9) + 1; i->i_data.a_ops = &hpfs_aops; - i->u.hpfs_i.mmu_private = i->i_size; + hpfs_i(i)->mmu_private = i->i_size; } brelse(bh); } void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); if (fnode->acl_size_l || fnode->acl_size_s) { /* Some unknown structures like ACL may be in fnode, we'd better not overwrite them */ hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino); } else if (i->i_sb->s_hpfs_eas >= 2) { unsigned char ea[4]; - if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) { + if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) { ea[0] = i->i_uid & 0xff; ea[1] = i->i_uid >> 8; hpfs_set_ea(i, fnode, "UID", ea, 2); - i->i_hpfs_ea_uid = 1; + hpfs_inode->i_ea_uid = 1; } - if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) { + if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) { ea[0] = i->i_gid & 0xff; ea[1] = i->i_gid >> 8; hpfs_set_ea(i, fnode, "GID", ea, 2); - i->i_hpfs_ea_gid = 1; + hpfs_inode->i_ea_gid = 1; } if (!S_ISLNK(i->i_mode)) if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111)) | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG)) && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333)) - | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) { + | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) { ea[0] = i->i_mode & 0xff; ea[1] = i->i_mode >> 8; hpfs_set_ea(i, fnode, "MODE", ea, 2); - i->i_hpfs_ea_mode = 1; + hpfs_inode->i_ea_mode = 1; } if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) { int d = kdev_t_to_nr(i->i_rdev); @@ -233,17 +235,18 @@ void hpfs_write_inode(struct inode *i) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct inode *parent; if (!i->i_nlink) return; if (i->i_ino == i->i_sb->s_hpfs_root) return; - if (i->i_hpfs_rddir_off && !atomic_read(&i->i_count)) { - if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n"); - kfree(i->i_hpfs_rddir_off); - i->i_hpfs_rddir_off = NULL; + if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) { + if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n"); + kfree(hpfs_inode->i_rddir_off); + hpfs_inode->i_rddir_off = NULL; } - i->i_hpfs_dirty = 0; + hpfs_inode->i_dirty = 0; hpfs_lock_iget(i->i_sb, 1); - parent = iget(i->i_sb, i->i_hpfs_parent_dir); + parent = iget(i->i_sb, hpfs_inode->i_parent_dir); hpfs_unlock_iget(i->i_sb); hpfs_lock_inode(parent); hpfs_write_inode_nolock(i); @@ -253,6 +256,7 @@ void hpfs_write_inode_nolock(struct inode *i) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct buffer_head *bh; struct fnode *fnode; struct quad_buffer_head qbh; @@ -276,17 +280,17 @@ de->read_date = gmt_to_local(i->i_sb, i->i_atime); de->creation_date = gmt_to_local(i->i_sb, i->i_ctime); de->read_only = !(i->i_mode & 0222); - de->ea_size = i->i_hpfs_ea_size; + de->ea_size = hpfs_inode->i_ea_size; hpfs_mark_4buffers_dirty(&qbh); hpfs_brelse4(&qbh); } if (S_ISDIR(i->i_mode)) { - if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) { + if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) { de->write_date = gmt_to_local(i->i_sb, i->i_mtime); de->read_date = gmt_to_local(i->i_sb, i->i_atime); de->creation_date = gmt_to_local(i->i_sb, i->i_ctime); de->read_only = !(i->i_mode & 0222); - de->ea_size = /*i->i_hpfs_ea_size*/0; + de->ea_size = /*hpfs_inode->i_ea_size*/0; de->file_size = 0; hpfs_mark_4buffers_dirty(&qbh); hpfs_brelse4(&qbh); @@ -312,9 +316,10 @@ void hpfs_write_if_changed(struct inode *inode) { - if (inode->i_hpfs_dirty) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + + if (hpfs_inode->i_dirty) hpfs_write_inode(inode); - } } void hpfs_delete_inode(struct inode *inode) diff -urN linux-2.5.3-pre2/fs/hpfs/name.c linux/fs/hpfs/name.c --- linux-2.5.3-pre2/fs/hpfs/name.c Tue Jun 1 23:25:47 1999 +++ linux/fs/hpfs/name.c Mon Jan 21 17:23:24 2002 @@ -20,8 +20,9 @@ void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); int i; - if (inode->i_hpfs_conv != CONV_AUTO) return; + if (hpfs_inode->i_conv != CONV_AUTO) return; for (i = 0; *text_postfix[i]; i++) { int l = strlen(text_postfix[i]); if (l <= len) @@ -34,10 +35,10 @@ if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0)) goto text; } - inode->i_hpfs_conv = CONV_BINARY; + hpfs_inode->i_conv = CONV_BINARY; return; text: - inode->i_hpfs_conv = CONV_TEXT; + hpfs_inode->i_conv = CONV_TEXT; return; } diff -urN linux-2.5.3-pre2/fs/hpfs/namei.c linux/fs/hpfs/namei.c --- linux-2.5.3-pre2/fs/hpfs/namei.c Fri Dec 29 14:07:57 2000 +++ linux/fs/hpfs/namei.c Mon Jan 21 17:23:24 2002 @@ -25,7 +25,7 @@ struct hpfs_dirent dee; int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1; memset(&dee, 0, sizeof dee); dee.directory = 1; @@ -69,9 +69,9 @@ dir->i_nlink++; hpfs_lock_iget(dir->i_sb, 1); if ((result = iget(dir->i_sb, fno))) { - result->i_hpfs_parent_dir = dir->i_ino; + hpfs_i(result)->i_parent_dir = dir->i_ino; result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); - result->i_hpfs_ea_size = 0; + hpfs_i(result)->i_ea_size = 0; if (dee.read_only) result->i_mode &= ~0222; if (result->i_uid != current->fsuid || result->i_gid != current->fsgid || @@ -109,7 +109,7 @@ struct hpfs_dirent dee; int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; dee.archive = 1; @@ -133,15 +133,15 @@ hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { hpfs_decide_conv(result, (char *)name, len); - result->i_hpfs_parent_dir = dir->i_ino; + hpfs_i(result)->i_parent_dir = dir->i_ino; result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); - result->i_hpfs_ea_size = 0; + hpfs_i(result)->i_ea_size = 0; if (dee.read_only) result->i_mode &= ~0222; if (result->i_blocks == -1) result->i_blocks = 1; if (result->i_size == -1) { result->i_size = 0; result->i_data.a_ops = &hpfs_aops; - result->u.hpfs_i.mmu_private = 0; + hpfs_i(result)->mmu_private = 0; } if (result->i_uid != current->fsuid || result->i_gid != current->fsgid || @@ -177,7 +177,7 @@ int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; dee.archive = 1; @@ -199,9 +199,9 @@ mark_buffer_dirty(bh); hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { - result->i_hpfs_parent_dir = dir->i_ino; + hpfs_i(result)->i_parent_dir = dir->i_ino; result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); - result->i_hpfs_ea_size = 0; + hpfs_i(result)->i_ea_size = 0; /*if (result->i_blocks == -1) result->i_blocks = 1; if (result->i_size == -1) result->i_size = 0;*/ result->i_uid = current->fsuid; @@ -240,7 +240,7 @@ int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); dee.archive = 1; dee.hidden = name[0] == '.'; @@ -262,9 +262,9 @@ brelse(bh); hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { - result->i_hpfs_parent_dir = dir->i_ino; + hpfs_i(result)->i_parent_dir = dir->i_ino; result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); - result->i_hpfs_ea_size = 0; + hpfs_i(result)->i_ea_size = 0; /*if (result->i_blocks == -1) result->i_blocks = 1; if (result->i_size == -1) result->i_size = 0;*/ result->i_mode = S_IFLNK | 0777; @@ -307,7 +307,7 @@ hpfs_adjust_length((char *)name, &len); again: hpfs_lock_2inodes(dir, inode); - if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) { + if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { hpfs_unlock_2inodes(dir, inode); return -ENOENT; } @@ -368,7 +368,7 @@ int r; hpfs_adjust_length((char *)name, &len); hpfs_lock_2inodes(dir, inode); - if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) { + if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { hpfs_unlock_2inodes(dir, inode); return -ENOENT; } @@ -382,7 +382,7 @@ hpfs_unlock_2inodes(dir, inode); return -ENOTDIR; } - hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items); + hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items); if (n_items) { hpfs_brelse4(&qbh); hpfs_unlock_2inodes(dir, inode); @@ -458,7 +458,7 @@ goto end1; } - if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) { + if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) { hpfs_error(i->i_sb, "lookup succeeded but map dirent failed"); err = -ENOENT; goto end1; @@ -469,7 +469,7 @@ if (new_inode) { int r; if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) { - if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) { + if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) { new_inode->i_nlink = 0; copy_de(nde, &de); memcpy(nde->name, new_name, new_len); @@ -497,7 +497,7 @@ } if (new_dir == old_dir) - if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) { + if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) { hpfs_unlock_creation(i->i_sb); hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2"); err = -ENOENT; @@ -513,7 +513,7 @@ hpfs_unlock_creation(i->i_sb); end: - i->i_hpfs_parent_dir = new_dir->i_ino; + hpfs_i(i)->i_parent_dir = new_dir->i_ino; if (S_ISDIR(i->i_mode)) { new_dir->i_nlink++; old_dir->i_nlink--; @@ -526,7 +526,7 @@ mark_buffer_dirty(bh); brelse(bh); } - i->i_hpfs_conv = i->i_sb->s_hpfs_conv; + hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv; hpfs_decide_conv(i, (char *)new_name, new_len); end1: hpfs_unlock_3inodes(old_dir, new_dir, i); diff -urN linux-2.5.3-pre2/fs/hpfs/super.c linux/fs/hpfs/super.c --- linux-2.5.3-pre2/fs/hpfs/super.c Fri Jan 4 09:42:12 2002 +++ linux/fs/hpfs/super.c Mon Jan 21 17:23:24 2002 @@ -148,10 +148,56 @@ return 0; } +static kmem_cache_t * hpfs_inode_cachep; + +static struct inode *hpfs_alloc_inode(struct super_block *sb) +{ + struct hpfs_inode_info *ei; + ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void hpfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + init_MUTEX(&ei->i_sem); + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache", + sizeof(struct hpfs_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (hpfs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(hpfs_inode_cachep)) + printk(KERN_INFO "hpfs_inode_cache: not all structures were freed\n"); +} + /* Super operations */ static struct super_operations hpfs_sops = { + alloc_inode: hpfs_alloc_inode, + destroy_inode: hpfs_destroy_inode, read_inode: hpfs_read_inode, delete_inode: hpfs_delete_inode, put_super: hpfs_put_super, @@ -545,8 +591,8 @@ s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date); s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date); s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date); - s->s_root->d_inode->i_hpfs_ea_size = de->ea_size; - s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino; + hpfs_i(s->s_root->d_inode)->i_ea_size = de->ea_size; + hpfs_i(s->s_root->d_inode)->i_parent_dir = s->s_root->d_inode->i_ino; if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048; if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5; } @@ -568,12 +614,23 @@ static int __init init_hpfs_fs(void) { - return register_filesystem(&hpfs_fs_type); + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&hpfs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_hpfs_fs(void) { unregister_filesystem(&hpfs_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_ext3.c linux/fs/intermezzo/journal_ext3.c --- linux-2.5.3-pre2/fs/intermezzo/journal_ext3.c Sun Dec 30 10:31:51 2001 +++ linux/fs/intermezzo/journal_ext3.c Mon Jan 21 17:23:24 2002 @@ -188,7 +188,7 @@ void presto_e3_journal_file_data(struct inode *inode) { #ifdef EXT3_JOURNAL_DATA_FL - inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL; + EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL; #else #warning You must have a facility to enable journaled writes for recovery! #endif diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_obdfs.c linux/fs/intermezzo/journal_obdfs.c --- linux-2.5.3-pre2/fs/intermezzo/journal_obdfs.c Sun Dec 30 10:31:51 2001 +++ linux/fs/intermezzo/journal_obdfs.c Mon Jan 21 17:23:24 2002 @@ -163,7 +163,7 @@ void presto_obdfs_journal_file_data(struct inode *inode) { #ifdef EXT3_JOURNAL_DATA_FL - inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL; + EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL; #else #warning You must have a facility to enable journaled writes for recovery! #endif diff -urN linux-2.5.3-pre2/fs/intermezzo/journal_reiserfs.c linux/fs/intermezzo/journal_reiserfs.c --- linux-2.5.3-pre2/fs/intermezzo/journal_reiserfs.c Sun Dec 30 10:31:51 2001 +++ linux/fs/intermezzo/journal_reiserfs.c Mon Jan 21 17:23:24 2002 @@ -101,7 +101,7 @@ void presto_reiserfs_journal_file_data(struct inode *inode) { #ifdef EXT3_JOURNAL_DATA_FL - inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL; + EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL; #else #warning You must have a facility to enable journaled writes for recovery! #endif diff -urN linux-2.5.3-pre2/fs/nfs/inode.c linux/fs/nfs/inode.c --- linux-2.5.3-pre2/fs/nfs/inode.c Mon Jan 7 09:59:01 2002 +++ linux/fs/nfs/inode.c Mon Jan 21 17:23:24 2002 @@ -45,6 +45,8 @@ void nfs_zap_caches(struct inode *); static void nfs_invalidate_inode(struct inode *); +static struct inode *nfs_alloc_inode(struct super_block *sb); +static void nfs_destroy_inode(struct inode *); static void nfs_read_inode(struct inode *); static void nfs_write_inode(struct inode *,int); static void nfs_delete_inode(struct inode *); @@ -55,6 +57,8 @@ static int nfs_show_options(struct seq_file *, struct vfsmount *); static struct super_operations nfs_sops = { + alloc_inode: nfs_alloc_inode, + destroy_inode: nfs_destroy_inode, read_inode: nfs_read_inode, write_inode: nfs_write_inode, delete_inode: nfs_delete_inode, @@ -94,26 +98,11 @@ /* * The "read_inode" function doesn't actually do anything: - * the real data is filled in later in nfs_fhget. Here we - * just mark the cache times invalid, and zero out i_mode - * (the latter makes "nfs_refresh_inode" do the right thing - * wrt pipe inodes) + * the real data is filled in later in nfs_fhget. */ static void nfs_read_inode(struct inode * inode) { - inode->i_blksize = inode->i_sb->s_blocksize; - inode->i_mode = 0; - inode->i_rdev = NODEV; - /* We can't support UPDATE_ATIME(), since the server will reset it */ - inode->i_flags |= S_NOATIME; - INIT_LIST_HEAD(&inode->u.nfs_i.read); - INIT_LIST_HEAD(&inode->u.nfs_i.dirty); - INIT_LIST_HEAD(&inode->u.nfs_i.commit); - INIT_LIST_HEAD(&inode->u.nfs_i.writeback); - NFS_CACHEINV(inode); - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - NFS_ATTRTIMEO_UPDATE(inode) = jiffies; } static void @@ -623,39 +612,6 @@ nfs_zap_caches(inode); } -/* - * Fill in inode information from the fattr. - */ -static void -nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) -{ - /* - * Check whether the mode has been set, as we only want to - * do this once. (We don't allow inodes to change types.) - */ - if (inode->i_mode == 0) { - NFS_FILEID(inode) = fattr->fileid; - NFS_FSID(inode) = fattr->fsid; - inode->i_mode = fattr->mode; - /* Why so? Because we want revalidate for devices/FIFOs, and - * that's precisely what we have in nfs_file_inode_operations. - */ - inode->i_op = &nfs_file_inode_operations; - if (S_ISREG(inode->i_mode)) { - inode->i_fop = &nfs_file_operations; - inode->i_data.a_ops = &nfs_file_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &nfs_dir_inode_operations; - inode->i_fop = &nfs_dir_operations; - } else if (S_ISLNK(inode->i_mode)) - inode->i_op = &nfs_symlink_inode_operations; - else - init_special_inode(inode, inode->i_mode, fattr->rdev); - memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)); - } - nfs_refresh_inode(inode, fattr); -} - struct nfs_find_desc { struct nfs_fh *fh; struct nfs_fattr *fattr; @@ -678,7 +634,7 @@ return 0; if (NFS_FILEID(inode) != fattr->fileid) return 0; - if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0) + if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0) return 0; /* Force an attribute cache update if inode->i_count == 0 */ if (!atomic_read(&inode->i_count)) @@ -700,7 +656,7 @@ return 1; /* Has the filehandle changed? If so is the old one stale? */ - if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 && + if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0 && __nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE) return 1; @@ -749,7 +705,65 @@ if (!(inode = iget4(sb, ino, nfs_find_actor, &desc))) goto out_no_inode; - nfs_fill_inode(inode, fh, fattr); + if (NFS_NEW(inode)) { + __u64 new_size, new_mtime; + loff_t new_isize; + time_t new_atime; + + /* We can't support UPDATE_ATIME(), since the server will reset it */ + NFS_FLAGS(inode) &= ~NFS_INO_NEW; + NFS_FILEID(inode) = fattr->fileid; + NFS_FSID(inode) = fattr->fsid; + memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh)); + inode->i_flags |= S_NOATIME; + inode->i_mode = fattr->mode; + /* Why so? Because we want revalidate for devices/FIFOs, and + * that's precisely what we have in nfs_file_inode_operations. + */ + inode->i_op = &nfs_file_inode_operations; + if (S_ISREG(inode->i_mode)) { + inode->i_fop = &nfs_file_operations; + inode->i_data.a_ops = &nfs_file_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &nfs_dir_inode_operations; + inode->i_fop = &nfs_dir_operations; + } else if (S_ISLNK(inode->i_mode)) + inode->i_op = &nfs_symlink_inode_operations; + else + init_special_inode(inode, inode->i_mode, fattr->rdev); + + new_mtime = fattr->mtime; + new_size = fattr->size; + new_isize = nfs_size_to_loff_t(fattr->size); + new_atime = nfs_time_to_secs(fattr->atime); + + NFS_READTIME(inode) = jiffies; + NFS_CACHE_CTIME(inode) = fattr->ctime; + inode->i_ctime = nfs_time_to_secs(fattr->ctime); + inode->i_atime = new_atime; + NFS_CACHE_MTIME(inode) = new_mtime; + inode->i_mtime = nfs_time_to_secs(new_mtime); + NFS_CACHE_ISIZE(inode) = new_size; + inode->i_size = new_isize; + inode->i_mode = fattr->mode; + inode->i_nlink = fattr->nlink; + inode->i_uid = fattr->uid; + inode->i_gid = fattr->gid; + if (fattr->valid & NFS_ATTR_FATTR_V3) { + /* + * report the blocks in 512byte units + */ + inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used); + inode->i_blksize = inode->i_sb->s_blocksize; + } else { + inode->i_blocks = fattr->du.nfs2.blocks; + inode->i_blksize = fattr->du.nfs2.blocksize; + } + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + NFS_ATTRTIMEO_UPDATE(inode) = jiffies; + memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); + } else + nfs_refresh_inode(inode, fattr); dprintk("NFS: __nfs_fhget(%s/%Ld ct=%d)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -1152,6 +1166,64 @@ extern int nfs_init_writepagecache(void); extern int nfs_destroy_writepagecache(void); +static kmem_cache_t * nfs_inode_cachep; + +static struct inode *nfs_alloc_inode(struct super_block *sb) +{ + struct nfs_inode *nfsi; + nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL); + if (!nfsi) + return NULL; + nfsi->flags = NFS_INO_NEW; + /* do we need the next 4 lines? */ + nfsi->hash_next = NULL; + nfsi->hash_prev = NULL; + nfsi->nextscan = 0; + nfsi->mm_cred = NULL; + return &nfsi->vfs_inode; +} + +static void nfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(nfs_inode_cachep, NFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct nfs_inode *nfsi = (struct nfs_inode *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + inode_init_once(&nfsi->vfs_inode); + INIT_LIST_HEAD(&nfsi->read); + INIT_LIST_HEAD(&nfsi->dirty); + INIT_LIST_HEAD(&nfsi->commit); + INIT_LIST_HEAD(&nfsi->writeback); + nfsi->nread = 0; + nfsi->ndirty = 0; + nfsi->ncommit = 0; + nfsi->npages = 0; + } +} + +int nfs_init_inodecache(void) +{ + nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", + sizeof(struct nfs_inode), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (nfs_inode_cachep == NULL) + return -ENOMEM; + + return 0; +} + +void nfs_destroy_inodecache(void) +{ + if (kmem_cache_destroy(nfs_inode_cachep)) + printk(KERN_INFO "nfs_inode_cache: not all structures were freed\n"); +} + /* * Initialize NFS */ @@ -1161,26 +1233,45 @@ err = nfs_init_nfspagecache(); if (err) - return err; + goto out4; + + err = nfs_init_inodecache(); + if (err) + goto out3; err = nfs_init_readpagecache(); if (err) - return err; + goto out2; err = nfs_init_writepagecache(); if (err) - return err; + goto out1; #ifdef CONFIG_PROC_FS rpc_proc_register(&nfs_rpcstat); #endif - return register_filesystem(&nfs_fs_type); + err = register_filesystem(&nfs_fs_type); + if (err) + goto out; + return 0; +out: + rpc_proc_unregister("nfs"); + nfs_destroy_writepagecache(); +out1: + nfs_destroy_readpagecache(); +out2: + nfs_destroy_inodecache(); +out3: + nfs_destroy_nfspagecache(); +out4: + return err; } static void __exit exit_nfs_fs(void) { nfs_destroy_writepagecache(); nfs_destroy_readpagecache(); + nfs_destroy_inodecache(); nfs_destroy_nfspagecache(); #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); diff -urN linux-2.5.3-pre2/fs/nfs/read.c linux/fs/nfs/read.c --- linux-2.5.3-pre2/fs/nfs/read.c Fri Jan 4 09:42:12 2002 +++ linux/fs/nfs/read.c Mon Jan 21 17:23:24 2002 @@ -156,10 +156,11 @@ nfs_mark_request_read(struct nfs_page *req) { struct inode *inode = req->wb_inode; + struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfs_wreq_lock); - nfs_list_add_request(req, &inode->u.nfs_i.read); - inode->u.nfs_i.nread++; + nfs_list_add_request(req, &nfsi->read); + nfsi->nread++; __nfs_add_lru(&NFS_SERVER(inode)->lru_read, req); spin_unlock(&nfs_wreq_lock); } @@ -167,6 +168,7 @@ static int nfs_readpage_async(struct file *file, struct inode *inode, struct page *page) { + struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *new; new = nfs_create_request(file, inode, page, 0, PAGE_CACHE_SIZE); @@ -174,7 +176,7 @@ return PTR_ERR(new); nfs_mark_request_read(new); - if (inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages || + if (nfsi->nread >= NFS_SERVER(inode)->rpages || page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) nfs_pagein_inode(inode, 0, 0); return 0; @@ -318,13 +320,13 @@ int nfs_scan_lru_read_timeout(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru_timeout(&server->lru_read, dst, server->rpages); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - inode->u.nfs_i.nread -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + nfsi->nread -= npages; } return npages; } @@ -341,13 +343,13 @@ int nfs_scan_lru_read(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru(&server->lru_read, dst, server->rpages); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - inode->u.nfs_i.nread -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + nfsi->nread -= npages; } return npages; } @@ -365,10 +367,11 @@ static int nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) { + struct nfs_inode *nfsi = NFS_I(inode); int res; - res = nfs_scan_list(&inode->u.nfs_i.read, dst, NULL, idx_start, npages); - inode->u.nfs_i.nread -= res; - if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read)) + res = nfs_scan_list(&nfsi->read, dst, NULL, idx_start, npages); + nfsi->nread -= res; + if ((nfsi->nread == 0) != list_empty(&nfsi->read)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n"); return res; } diff -urN linux-2.5.3-pre2/fs/nfs/write.c linux/fs/nfs/write.c --- linux-2.5.3-pre2/fs/nfs/write.c Fri Jan 4 09:42:12 2002 +++ linux/fs/nfs/write.c Mon Jan 21 17:23:24 2002 @@ -315,14 +315,15 @@ static inline void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) { + struct nfs_inode *nfsi = NFS_I(inode); if (!list_empty(&req->wb_hash)) return; if (!NFS_WBACK_BUSY(req)) printk(KERN_ERR "NFS: unlocked request attempted hashed!\n"); - if (list_empty(&inode->u.nfs_i.writeback)) + if (list_empty(&nfsi->writeback)) igrab(inode); - inode->u.nfs_i.npages++; - list_add(&req->wb_hash, &inode->u.nfs_i.writeback); + nfsi->npages++; + list_add(&req->wb_hash, &nfsi->writeback); req->wb_count++; } @@ -332,6 +333,7 @@ static inline void nfs_inode_remove_request(struct nfs_page *req) { + struct nfs_inode *nfsi; struct inode *inode; spin_lock(&nfs_wreq_lock); if (list_empty(&req->wb_hash)) { @@ -343,10 +345,11 @@ inode = req->wb_inode; list_del(&req->wb_hash); INIT_LIST_HEAD(&req->wb_hash); - inode->u.nfs_i.npages--; - if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback)) + nfsi = NFS_I(inode); + nfsi->npages--; + if ((nfsi->npages == 0) != list_empty(&nfsi->writeback)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n"); - if (list_empty(&inode->u.nfs_i.writeback)) { + if (list_empty(&nfsi->writeback)) { spin_unlock(&nfs_wreq_lock); iput(inode); } else @@ -360,9 +363,10 @@ static inline struct nfs_page * _nfs_find_request(struct inode *inode, struct page *page) { + struct nfs_inode *nfsi = NFS_I(inode); struct list_head *head, *next; - head = &inode->u.nfs_i.writeback; + head = &nfsi->writeback; next = head->next; while (next != head) { struct nfs_page *req = nfs_inode_wb_entry(next); @@ -393,10 +397,11 @@ nfs_mark_request_dirty(struct nfs_page *req) { struct inode *inode = req->wb_inode; + struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfs_wreq_lock); - nfs_list_add_request(req, &inode->u.nfs_i.dirty); - inode->u.nfs_i.ndirty++; + nfs_list_add_request(req, &nfsi->dirty); + nfsi->ndirty++; __nfs_del_lru(req); __nfs_add_lru(&NFS_SERVER(inode)->lru_dirty, req); spin_unlock(&nfs_wreq_lock); @@ -409,8 +414,8 @@ static inline int nfs_dirty_request(struct nfs_page *req) { - struct inode *inode = req->wb_inode; - return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty; + struct nfs_inode *nfsi = NFS_I(req->wb_inode); + return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty; } #ifdef CONFIG_NFS_V3 @@ -421,10 +426,11 @@ nfs_mark_request_commit(struct nfs_page *req) { struct inode *inode = req->wb_inode; + struct nfs_inode *nfsi = NFS_I(inode); spin_lock(&nfs_wreq_lock); - nfs_list_add_request(req, &inode->u.nfs_i.commit); - inode->u.nfs_i.ncommit++; + nfs_list_add_request(req, &nfsi->commit); + nfsi->ncommit++; __nfs_del_lru(req); __nfs_add_lru(&NFS_SERVER(inode)->lru_commit, req); spin_unlock(&nfs_wreq_lock); @@ -440,6 +446,7 @@ static int nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages) { + struct nfs_inode *nfsi = NFS_I(inode); struct list_head *p, *head; unsigned long idx_end; unsigned int res = 0; @@ -451,7 +458,7 @@ idx_end = idx_start + npages - 1; spin_lock(&nfs_wreq_lock); - head = &inode->u.nfs_i.writeback; + head = &nfsi->writeback; p = head->next; while (p != head) { unsigned long pg_idx; @@ -494,13 +501,13 @@ int nfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru_timeout(&server->lru_dirty, dst, server->wpages); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - inode->u.nfs_i.ndirty -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + nfsi->ndirty -= npages; } return npages; } @@ -517,13 +524,13 @@ int nfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru(&server->lru_dirty, dst, server->wpages); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - inode->u.nfs_i.ndirty -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + nfsi->ndirty -= npages; } return npages; } @@ -542,10 +549,11 @@ static int nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) { + struct nfs_inode *nfsi = NFS_I(inode); int res; - res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages); - inode->u.nfs_i.ndirty -= res; - if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty)) + res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages); + nfsi->ndirty -= res; + if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); return res; } @@ -565,14 +573,14 @@ int nfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru_timeout(&server->lru_commit, dst, 1); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0); - inode->u.nfs_i.ncommit -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0); + nfsi->ncommit -= npages; } return npages; } @@ -592,14 +600,14 @@ int nfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst) { - struct inode *inode; + struct nfs_inode *nfsi; int npages; npages = nfs_scan_lru(&server->lru_commit, dst, 1); if (npages) { - inode = nfs_list_entry(dst->next)->wb_inode; - npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, NULL, 0, 0); - inode->u.nfs_i.ncommit -= npages; + nfsi = NFS_I(nfs_list_entry(dst->next)->wb_inode); + npages += nfs_scan_list(&nfsi->commit, dst, NULL, 0, 0); + nfsi->ncommit -= npages; } return npages; } @@ -618,10 +626,11 @@ static int nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) { + struct nfs_inode *nfsi = NFS_I(inode); int res; - res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages); - inode->u.nfs_i.ncommit -= res; - if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit)) + res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages); + nfsi->ncommit -= res; + if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); return res; } @@ -738,7 +747,7 @@ { unsigned int dirty, wpages; - dirty = inode->u.nfs_i.ndirty; + dirty = NFS_I(inode)->ndirty; wpages = NFS_SERVER(inode)->wpages; #ifdef CONFIG_NFS_V3 if (NFS_PROTO(inode)->version == 2) { @@ -889,6 +898,7 @@ static int nfs_flush_one(struct list_head *head, struct inode *inode, int how) { + struct nfs_inode *nfsi = NFS_I(inode); struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_write_data *data; struct rpc_task *task; @@ -913,7 +923,7 @@ if (nfsvers < 3) data->args.stable = NFS_FILE_SYNC; else if (stable) { - if (!inode->u.nfs_i.ncommit) + if (!nfsi->ncommit) data->args.stable = NFS_FILE_SYNC; else data->args.stable = NFS_DATA_SYNC; diff -urN linux-2.5.3-pre2/fs/qnx4/bitmap.c linux/fs/qnx4/bitmap.c --- linux-2.5.3-pre2/fs/qnx4/bitmap.c Sun Dec 16 12:23:05 2001 +++ linux/fs/qnx4/bitmap.c Mon Jan 21 17:23:24 2002 @@ -142,13 +142,13 @@ static void qnx4_clear_inode(struct inode *inode) { - struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i; - - memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved); - qnx4_ino->i_size = 0; - qnx4_ino->i_num_xtnts = 0; - qnx4_ino->i_mode = 0; - qnx4_ino->i_status = 0; + struct qnx4_inode_entry *qnx4_ino = qnx4_raw_inode(inode); + /* What for? */ + memset(qnx4_ino->di_fname, 0, sizeof qnx4_ino->di_fname); + qnx4_ino->di_size = 0; + qnx4_ino->di_num_xtnts = 0; + qnx4_ino->di_mode = 0; + qnx4_ino->di_status = 0; } void qnx4_free_inode(struct inode *inode) diff -urN linux-2.5.3-pre2/fs/qnx4/fsync.c linux/fs/qnx4/fsync.c --- linux-2.5.3-pre2/fs/qnx4/fsync.c Sun Dec 30 10:31:51 2001 +++ linux/fs/qnx4/fsync.c Mon Jan 21 17:23:24 2002 @@ -90,7 +90,7 @@ for (i = 0; i < 7; i++) { rc = sync_block(inode, - (unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait); + (unsigned short *) qnx4_raw_inode(inode)->di_first_xtnt.xtnt_blk + i, wait); if (rc > 0) break; if (rc) diff -urN linux-2.5.3-pre2/fs/qnx4/inode.c linux/fs/qnx4/inode.c --- linux-2.5.3-pre2/fs/qnx4/inode.c Fri Jan 4 09:42:12 2002 +++ linux/fs/qnx4/inode.c Mon Jan 21 17:23:24 2002 @@ -121,12 +121,16 @@ static struct super_block *qnx4_read_super(struct super_block *, void *, int); static void qnx4_put_super(struct super_block *sb); +static struct inode *qnx4_alloc_inode(struct super_block *sb); +static void qnx4_destroy_inode(struct inode *inode); static void qnx4_read_inode(struct inode *); static int qnx4_remount(struct super_block *sb, int *flags, char *data); static int qnx4_statfs(struct super_block *, struct statfs *); static struct super_operations qnx4_sops = { + alloc_inode: qnx4_alloc_inode, + destroy_inode: qnx4_destroy_inode, read_inode: qnx4_read_inode, #ifdef CONFIG_QNX4FS_RW write_inode: qnx4_write_inode, @@ -227,16 +231,16 @@ unsigned long block = 0; struct buffer_head *bh = 0; struct qnx4_xblk *xblk = 0; - struct qnx4_inode_info *qnx4_inode = &inode->u.qnx4_i; - qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->i_num_xtnts); + struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); + qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts); - if ( iblock < le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size) ) { + if ( iblock < le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size) ) { // iblock is in the first extent. This is easy. - block = le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_blk) + iblock - 1; + block = le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_blk) + iblock - 1; } else { // iblock is beyond first extent. We have to follow the extent chain. - i_xblk = le32_to_cpu(qnx4_inode->i_xblk); - offset = iblock - le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size); + i_xblk = le32_to_cpu(qnx4_inode->di_xblk); + offset = iblock - le32_to_cpu(qnx4_inode->di_first_xtnt.xtnt_size); ix = 0; while ( --nxtnt > 0 ) { if ( ix == 0 ) { @@ -417,10 +421,12 @@ { return block_read_full_page(page,qnx4_get_block); } -static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +static int qnx4_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) { - return cont_prepare_write(page,from,to,qnx4_get_block, - &page->mapping->host->u.qnx4_i.mmu_private); + struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host); + return cont_prepare_write(page, from, to, qnx4_get_block, + &qnx4_inode->mmu_private); } static int qnx4_bmap(struct address_space *mapping, long block) { @@ -441,6 +447,7 @@ struct qnx4_inode_entry *raw_inode; int block, ino; struct super_block *sb = inode->i_sb; + struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); ino = inode->i_ino; inode->i_mode = 0; @@ -472,35 +479,92 @@ inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size); inode->i_blksize = QNX4_DIR_ENTRY_SIZE; - memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE); + memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE); if (S_ISREG(inode->i_mode)) { inode->i_op = &qnx4_file_inode_operations; inode->i_fop = &qnx4_file_operations; inode->i_mapping->a_ops = &qnx4_aops; - inode->u.qnx4_i.mmu_private = inode->i_size; + qnx4_i(inode)->mmu_private = inode->i_size; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &qnx4_dir_inode_operations; inode->i_fop = &qnx4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &qnx4_aops; - inode->u.qnx4_i.mmu_private = inode->i_size; + qnx4_i(inode)->mmu_private = inode->i_size; } else printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id); brelse(bh); } +static kmem_cache_t *qnx4_inode_cachep; + +static struct inode *qnx4_alloc_inode(struct super_block *sb) +{ + struct qnx4_inode_info *ei; + ei = kmem_cache_alloc(qnx4_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void qnx4_destroy_inode(struct inode *inode) +{ + kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); +} + +static void init_once(void *foo, kmem_cache_t * cachep, + unsigned long flags) +{ + struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", + sizeof(struct qnx4_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (qnx4_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(qnx4_inode_cachep)) + printk(KERN_INFO + "qnx4_inode_cache: not all structures were freed\n"); +} + static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super); static int __init init_qnx4_fs(void) { + int err; + + err = init_inodecache(); + if (err) + return err; + + err = register_filesystem(&qnx4_fs_type); + if (err) { + destroy_inodecache(); + return err; + } + printk("QNX4 filesystem 0.2.2 registered.\n"); - return register_filesystem(&qnx4_fs_type); + return 0; } static void __exit exit_qnx4_fs(void) { unregister_filesystem(&qnx4_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; diff -urN linux-2.5.3-pre2/fs/ufs/balloc.c linux/fs/ufs/balloc.c --- linux-2.5.3-pre2/fs/ufs/balloc.c Sun Dec 16 12:23:05 2001 +++ linux/fs/ufs/balloc.c Mon Jan 21 17:23:24 2002 @@ -271,7 +271,7 @@ unlock_super (sb); return (unsigned)-1; } - if (fragment < inode->u.ufs_i.i_lastfrag) { + if (fragment < UFS_I(inode)->i_lastfrag) { UFSD(("EXIT (ALREADY ALLOCATED)\n")) unlock_super (sb); return 0; @@ -310,7 +310,7 @@ *p = cpu_to_fs32(sb, result); *err = 0; inode->i_blocks += count << uspi->s_nspfshift; - inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count); + UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); NULLIFY_FRAGMENTS } unlock_super(sb); @@ -325,7 +325,7 @@ if (result) { *err = 0; inode->i_blocks += count << uspi->s_nspfshift; - inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count); + UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); NULLIFY_FRAGMENTS unlock_super(sb); UFSD(("EXIT, result %u\n", result)) @@ -378,7 +378,7 @@ *p = cpu_to_fs32(sb, result); *err = 0; inode->i_blocks += count << uspi->s_nspfshift; - inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count); + UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); NULLIFY_FRAGMENTS unlock_super(sb); if (newcount < request) diff -urN linux-2.5.3-pre2/fs/ufs/ialloc.c linux/fs/ufs/ialloc.c --- linux-2.5.3-pre2/fs/ufs/ialloc.c Mon Nov 19 14:55:46 2001 +++ linux/fs/ufs/ialloc.c Mon Jan 21 17:23:24 2002 @@ -142,7 +142,7 @@ * For other inodes, search forward from the parent directory's block * group to find a free inode. */ -struct inode * ufs_new_inode (const struct inode * dir, int mode) +struct inode * ufs_new_inode(struct inode * dir, int mode) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -151,6 +151,7 @@ struct ufs_cylinder_group * ucg; struct inode * inode; unsigned cg, bit, i, j, start; + struct ufs_inode_info *ufsi; UFSD(("ENTER\n")) @@ -161,6 +162,7 @@ inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); + ufsi = UFS_I(inode); uspi = sb->u.ufs_sb.s_uspi; usb1 = ubh_get_usb_first(USPI_UBH); @@ -261,8 +263,13 @@ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->u.ufs_i.i_flags = dir->u.ufs_i.i_flags; - inode->u.ufs_i.i_lastfrag = 0; + ufsi->i_flags = UFS_I(dir)->i_flags; + ufsi->i_lastfrag = 0; + ufsi->i_gen = 0; + ufsi->i_shadow = 0; + ufsi->i_osync = 0; + ufsi->i_oeftflag = 0; + memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1)); insert_inode_hash(inode); mark_inode_dirty(inode); diff -urN linux-2.5.3-pre2/fs/ufs/inode.c linux/fs/ufs/inode.c --- linux-2.5.3-pre2/fs/ufs/inode.c Sun Dec 30 16:59:04 2001 +++ linux/fs/ufs/inode.c Mon Jan 21 17:23:24 2002 @@ -84,6 +84,7 @@ int ufs_frag_map(struct inode *inode, int frag) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; struct ufs_sb_private_info *uspi = sb->u.ufs_sb.s_uspi; int mask = uspi->s_apbmask>>uspi->s_fpbshift; @@ -99,7 +100,7 @@ p = offsets; lock_kernel(); - block = inode->u.ufs_i.i_u1.i_data[*p++]; + block = ufsi->i_u1.i_data[*p++]; if (!block) goto out; while (--depth) { @@ -124,6 +125,7 @@ unsigned int fragment, unsigned int new_fragment, unsigned int required, int *err, int metadata, long *phys, int *new) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct buffer_head * result; @@ -138,12 +140,12 @@ uspi = sb->u.ufs_sb.s_uspi; block = ufs_fragstoblks (fragment); blockoff = ufs_fragnum (fragment); - p = inode->u.ufs_i.i_u1.i_data + block; + p = ufsi->i_u1.i_data + block; goal = 0; repeat: tmp = fs32_to_cpu(sb, *p); - lastfrag = inode->u.ufs_i.i_lastfrag; + lastfrag = ufsi->i_lastfrag; if (tmp && fragment < lastfrag) { if (metadata) { result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff); @@ -169,19 +171,19 @@ * We must reallocate last allocated block */ if (lastblockoff) { - p2 = inode->u.ufs_i.i_u1.i_data + lastblock; + p2 = ufsi->i_u1.i_data + lastblock; tmp = ufs_new_fragments (inode, p2, lastfrag, fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, err); if (!tmp) { - if (lastfrag != inode->u.ufs_i.i_lastfrag) + if (lastfrag != ufsi->i_lastfrag) goto repeat; else return NULL; } - lastfrag = inode->u.ufs_i.i_lastfrag; + lastfrag = ufsi->i_lastfrag; } - goal = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock]) + uspi->s_fpb; + goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, fragment - blockoff, goal, required + blockoff, err); } @@ -196,14 +198,14 @@ * We will allocate new block before last allocated block */ else /* (lastblock > block) */ { - if (lastblock && (tmp = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock-1]))) + if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1]))) goal = tmp + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, fragment - blockoff, goal, uspi->s_fpb, err); } if (!tmp) { if ((!blockoff && *p) || - (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag)) + (blockoff && lastfrag != ufsi->i_lastfrag)) goto repeat; *err = -ENOSPC; return NULL; @@ -470,6 +472,7 @@ void ufs_read_inode (struct inode * inode) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_inode * ufs_inode; @@ -517,24 +520,23 @@ inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks); inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */ inode->i_version = ++event; - - inode->u.ufs_i.i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags); - inode->u.ufs_i.i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen); - inode->u.ufs_i.i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow); - inode->u.ufs_i.i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag); - inode->u.ufs_i.i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift; + ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags); + ufsi->i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen); + ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow); + ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag); + ufsi->i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) ; else if (inode->i_blocks) { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) - inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i]; + ufsi->i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i]; } else { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) - inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i]; + ufsi->i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i]; } - + ufsi->i_osync = 0; if (S_ISREG(inode->i_mode)) { inode->i_op = &ufs_file_inode_operations; @@ -566,6 +568,7 @@ static int ufs_update_inode(struct inode * inode, int do_sync) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct buffer_head * bh; @@ -606,23 +609,23 @@ ufs_inode->ui_mtime.tv_sec = cpu_to_fs32(sb, inode->i_mtime); ufs_inode->ui_mtime.tv_usec = 0; ufs_inode->ui_blocks = cpu_to_fs32(sb, inode->i_blocks); - ufs_inode->ui_flags = cpu_to_fs32(sb, inode->u.ufs_i.i_flags); - ufs_inode->ui_gen = cpu_to_fs32(sb, inode->u.ufs_i.i_gen); + ufs_inode->ui_flags = cpu_to_fs32(sb, ufsi->i_flags); + ufs_inode->ui_gen = cpu_to_fs32(sb, ufsi->i_gen); if ((flags & UFS_UID_MASK) == UFS_UID_EFT) { - ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, inode->u.ufs_i.i_shadow); - ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, inode->u.ufs_i.i_oeftflag); + ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, ufsi->i_shadow); + ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, ufsi->i_oeftflag); } if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev)); else if (inode->i_blocks) { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) - ufs_inode->ui_u2.ui_addr.ui_db[i] = inode->u.ufs_i.i_u1.i_data[i]; + ufs_inode->ui_u2.ui_addr.ui_db[i] = ufsi->i_u1.i_data[i]; } else { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) - ufs_inode->ui_u2.ui_symlink[i] = inode->u.ufs_i.i_u1.i_symlink[i]; + ufs_inode->ui_u2.ui_symlink[i] = ufsi->i_u1.i_symlink[i]; } if (!inode->i_nlink) @@ -653,7 +656,7 @@ void ufs_delete_inode (struct inode * inode) { - /*inode->u.ufs_i.i_dtime = CURRENT_TIME;*/ + /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ lock_kernel(); mark_inode_dirty(inode); ufs_update_inode(inode, IS_SYNC(inode)); diff -urN linux-2.5.3-pre2/fs/ufs/namei.c linux/fs/ufs/namei.c --- linux-2.5.3-pre2/fs/ufs/namei.c Sat Oct 20 19:14:42 2001 +++ linux/fs/ufs/namei.c Mon Jan 21 17:23:24 2002 @@ -138,7 +138,7 @@ } else { /* fast symlink */ inode->i_op = &ufs_fast_symlink_inode_operations; - memcpy((char*)&inode->u.ufs_i.i_u1.i_data,symname,l); + memcpy((char*)&UFS_I(inode)->i_u1.i_data,symname,l); inode->i_size = l-1; } mark_inode_dirty(inode); diff -urN linux-2.5.3-pre2/fs/ufs/super.c linux/fs/ufs/super.c --- linux-2.5.3-pre2/fs/ufs/super.c Fri Jan 4 09:42:12 2002 +++ linux/fs/ufs/super.c Mon Jan 21 17:23:24 2002 @@ -953,7 +953,51 @@ return 0; } +static kmem_cache_t * ufs_inode_cachep; + +static struct inode *ufs_alloc_inode(struct super_block *sb) +{ + struct ufs_inode_info *ei; + ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void ufs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(ufs_inode_cachep, UFS_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct ufs_inode_info *ei = (struct ufs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&ei->vfs_inode); +} + +static int init_inodecache(void) +{ + ufs_inode_cachep = kmem_cache_create("ufs_inode_cache", + sizeof(struct ufs_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (ufs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(ufs_inode_cachep)) + printk(KERN_INFO "ufs_inode_cache: not all structures were freed\n"); +} + static struct super_operations ufs_super_ops = { + alloc_inode: ufs_alloc_inode, + destroy_inode: ufs_destroy_inode, read_inode: ufs_read_inode, write_inode: ufs_write_inode, delete_inode: ufs_delete_inode, @@ -967,15 +1011,27 @@ static int __init init_ufs_fs(void) { - return register_filesystem(&ufs_fs_type); + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&ufs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; } static void __exit exit_ufs_fs(void) { unregister_filesystem(&ufs_fs_type); + destroy_inodecache(); } EXPORT_NO_SYMBOLS; module_init(init_ufs_fs) module_exit(exit_ufs_fs) +MODULE_LICENSE("GPL"); diff -urN linux-2.5.3-pre2/fs/ufs/symlink.c linux/fs/ufs/symlink.c --- linux-2.5.3-pre2/fs/ufs/symlink.c Fri Apr 7 13:38:00 2000 +++ linux/fs/ufs/symlink.c Mon Jan 21 17:23:24 2002 @@ -26,17 +26,18 @@ */ #include +#include static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen) { - char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink; - return vfs_readlink(dentry, buffer, buflen, s); + struct ufs_inode_info *p = UFS_I(dentry->d_inode); + return vfs_readlink(dentry, buffer, buflen, (char*)p->i_u1.i_symlink); } static int ufs_follow_link(struct dentry *dentry, struct nameidata *nd) { - char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink; - return vfs_follow_link(nd, s); + struct ufs_inode_info *p = UFS_I(dentry->d_inode); + return vfs_follow_link(nd, (char*)p->i_u1.i_symlink); } struct inode_operations ufs_fast_symlink_inode_operations = { diff -urN linux-2.5.3-pre2/fs/ufs/truncate.c linux/fs/ufs/truncate.c --- linux-2.5.3-pre2/fs/ufs/truncate.c Mon Jan 7 12:55:16 2002 +++ linux/fs/ufs/truncate.c Mon Jan 21 17:23:24 2002 @@ -67,6 +67,7 @@ static int ufs_trunc_direct (struct inode * inode) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct buffer_head * bh; @@ -86,7 +87,7 @@ retry = 0; frag1 = DIRECT_FRAGMENT; - frag4 = min_t(u32, UFS_NDIR_FRAGMENT, inode->u.ufs_i.i_lastfrag); + frag4 = min_t(u32, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag); frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1); frag3 = frag4 & ~uspi->s_fpbmask; block1 = block2 = 0; @@ -107,7 +108,7 @@ /* * Free first free fragments */ - p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag1); + p = ufsi->i_u1.i_data + ufs_fragstoblks (frag1); tmp = fs32_to_cpu(sb, *p); if (!tmp ) ufs_panic (sb, "ufs_trunc_direct", "internal error"); @@ -132,7 +133,7 @@ * Free whole blocks */ for (i = block1 ; i < block2; i++) { - p = inode->u.ufs_i.i_u1.i_data + i; + p = ufsi->i_u1.i_data + i; tmp = fs32_to_cpu(sb, *p); if (!tmp) continue; @@ -170,7 +171,7 @@ /* * Free last free fragments */ - p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag3); + p = ufsi->i_u1.i_data + ufs_fragstoblks (frag3); tmp = fs32_to_cpu(sb, *p); if (!tmp ) ufs_panic(sb, "ufs_truncate_direct", "internal error"); @@ -360,6 +361,7 @@ static int ufs_trunc_tindirect (struct inode * inode) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_buffer_head * tind_bh; @@ -375,7 +377,7 @@ tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb)) ? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0; - p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK; + p = ufsi->i_u1.i_data + UFS_TIND_BLOCK; if (!(tmp = fs32_to_cpu(sb, *p))) return 0; tind_bh = ubh_bread (sb, tmp, uspi->s_bsize); @@ -422,6 +424,7 @@ void ufs_truncate (struct inode * inode) { + struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; struct buffer_head * bh; @@ -439,9 +442,9 @@ while (1) { retry = ufs_trunc_direct(inode); retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK, - (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]); + (u32 *) &ufsi->i_u1.i_data[UFS_IND_BLOCK]); retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb, - (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]); + (u32 *) &ufsi->i_u1.i_data[UFS_DIND_BLOCK]); retry |= ufs_trunc_tindirect (inode); if (!retry) break; @@ -460,7 +463,7 @@ } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->u.ufs_i.i_lastfrag = DIRECT_FRAGMENT; + ufsi->i_lastfrag = DIRECT_FRAGMENT; mark_inode_dirty(inode); UFSD(("EXIT\n")) } diff -urN linux-2.5.3-pre2/fs/ufs/util.c linux/fs/ufs/util.c --- linux-2.5.3-pre2/fs/ufs/util.c Sun Dec 16 12:23:05 2001 +++ linux/fs/ufs/util.c Mon Jan 21 17:23:24 2002 @@ -9,6 +9,7 @@ #include #include #include +#include #include "swab.h" #include "util.h" diff -urN linux-2.5.3-pre2/fs/umsdos/dir.c linux/fs/umsdos/dir.c --- linux-2.5.3-pre2/fs/umsdos/dir.c Sat Sep 1 10:59:08 2001 +++ linux/fs/umsdos/dir.c Mon Jan 21 17:23:24 2002 @@ -204,7 +204,7 @@ if (!inode) goto remove_name; #ifdef UMSDOS_DEBUG_VERBOSE -if (inode->u.umsdos_i.i_is_hlink) +if (UMSDOS_I(inode)->i_is_hlink) printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n", dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino); #endif @@ -214,7 +214,7 @@ entry.flags)); /* check whether to resolve a hard-link */ if ((entry.flags & UMSDOS_HLINK) && - !inode->u.umsdos_i.i_is_hlink) { + !UMSDOS_I(inode)->i_is_hlink) { dret = umsdos_solve_hlink (dret); ret = PTR_ERR(dret); if (IS_ERR(dret)) @@ -361,9 +361,9 @@ /* * This part of the initialization depends only on i_patched. */ - if (inode->u.umsdos_i.i_patched) + if (UMSDOS_I(inode)->i_patched) goto out; - inode->u.umsdos_i.i_patched = 1; + UMSDOS_I(inode)->i_patched = 1; if (S_ISREG (entry->mode)) entry->mtime = inode->i_mtime; inode->i_mode = entry->mode; @@ -498,7 +498,7 @@ /* Check for a hard link */ if ((info.entry.flags & UMSDOS_HLINK) && - !inode->u.umsdos_i.i_is_hlink) { + !UMSDOS_I(inode)->i_is_hlink) { dret = umsdos_solve_hlink (dret); ret = PTR_ERR(dret); if (IS_ERR(dret)) @@ -756,7 +756,7 @@ if (!IS_ERR(dentry_dst)) { struct inode *inode = dentry_dst->d_inode; if (inode) { - inode->u.umsdos_i.i_is_hlink = 1; + UMSDOS_I(inode)->i_is_hlink = 1; #ifdef UMSDOS_DEBUG_VERBOSE printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n", dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino); diff -urN linux-2.5.3-pre2/fs/umsdos/inode.c linux/fs/umsdos/inode.c --- linux-2.5.3-pre2/fs/umsdos/inode.c Mon Jan 21 17:22:37 2002 +++ linux/fs/umsdos/inode.c Mon Jan 21 17:23:24 2002 @@ -34,7 +34,7 @@ PRINTK ((KERN_DEBUG "put inode %p (%lu) pos %lu count=%d\n" ,inode, inode->i_ino - ,inode->u.umsdos_i.pos + ,UMSDOS_I(inode)->pos ,atomic_read(&inode->i_count))); if (inode == pseudo_root) { @@ -42,7 +42,7 @@ } if (atomic_read(&inode->i_count) == 1) - inode->u.umsdos_i.i_patched = 0; + UMSDOS_I(inode)->i_patched = 0; } @@ -67,15 +67,16 @@ void umsdos_setup_dir(struct dentry *dir) { struct inode *inode = dir->d_inode; + struct umsdos_inode_info *ui = UMSDOS_I(inode); if (!S_ISDIR(inode->i_mode)) printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n", dir->d_parent->d_name.name, dir->d_name.name); - init_waitqueue_head (&inode->u.umsdos_i.dir_info.p); - inode->u.umsdos_i.dir_info.looking = 0; - inode->u.umsdos_i.dir_info.creating = 0; - inode->u.umsdos_i.dir_info.pid = 0; + init_waitqueue_head (&ui->dir_info.p); + ui->dir_info.looking = 0; + ui->dir_info.creating = 0; + ui->dir_info.pid = 0; inode->i_op = &umsdos_rdir_inode_operations; inode->i_fop = &umsdos_rdir_operations; @@ -96,7 +97,7 @@ struct inode *inode = dentry->d_inode; struct dentry *demd; - inode->u.umsdos_i.pos = f_pos; + UMSDOS_I(inode)->pos = f_pos; /* now check the EMD file */ demd = umsdos_get_emd_dentry(dentry->d_parent); @@ -233,7 +234,7 @@ int offs; Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n", -dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched)); +dentry->d_parent->d_name.name, dentry->d_name.name, UMSDOS_I(inode)->i_patched)); if (inode->i_nlink == 0) goto out; @@ -265,9 +266,9 @@ /* Read only the start of the entry since we don't touch the name */ mapping = demd->d_inode->i_mapping; - offs = inode->u.umsdos_i.pos & ~PAGE_CACHE_MASK; + offs = UMSDOS_I(inode)->pos & ~PAGE_CACHE_MASK; ret = -ENOMEM; - page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT); + page=grab_cache_page(mapping,UMSDOS_I(inode)->pos>>PAGE_CACHE_SHIFT); if (!page) goto out_dput; ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE); diff -urN linux-2.5.3-pre2/fs/umsdos/namei.c linux/fs/umsdos/namei.c --- linux-2.5.3-pre2/fs/umsdos/namei.c Sat Sep 1 10:59:08 2001 +++ linux/fs/umsdos/namei.c Mon Jan 21 17:23:24 2002 @@ -28,12 +28,12 @@ static inline void u_sleep_on (struct inode *dir) { - sleep_on (&dir->u.umsdos_i.dir_info.p); + sleep_on (&UMSDOS_I(dir)->dir_info.p); } static inline void u_wake_up (struct inode *dir) { - wake_up (&dir->u.umsdos_i.dir_info.p); + wake_up (&UMSDOS_I(dir)->dir_info.p); } /* @@ -47,9 +47,9 @@ { int ret = 0; - if (dir->u.umsdos_i.dir_info.creating - && dir->u.umsdos_i.dir_info.pid != current->pid) { - PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", dir->u.umsdos_i.dir_info.pid, current->pid)); + if (UMSDOS_I(dir)->dir_info.creating + && UMSDOS_I(dir)->dir_info.pid != current->pid) { + PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", UMSDOS_I(dir)->dir_info.pid, current->pid)); u_sleep_on (dir); ret = 1; } @@ -61,7 +61,7 @@ */ static void umsdos_waitlookup (struct inode *dir) { - while (dir->u.umsdos_i.dir_info.looking) { + while (UMSDOS_I(dir)->dir_info.looking) { u_sleep_on (dir); } } @@ -104,8 +104,8 @@ * if we (the process) own the lock */ while (umsdos_waitcreate (dir) != 0); - dir->u.umsdos_i.dir_info.creating++; - dir->u.umsdos_i.dir_info.pid = current->pid; + UMSDOS_I(dir)->dir_info.creating++; + UMSDOS_I(dir)->dir_info.pid = current->pid; umsdos_waitlookup (dir); } @@ -124,10 +124,10 @@ if (umsdos_waitcreate (dir1) == 0 && umsdos_waitcreate (dir2) == 0) { /* We own both now */ - dir1->u.umsdos_i.dir_info.creating++; - dir1->u.umsdos_i.dir_info.pid = current->pid; - dir2->u.umsdos_i.dir_info.creating++; - dir2->u.umsdos_i.dir_info.pid = current->pid; + UMSDOS_I(dir1)->dir_info.creating++; + UMSDOS_I(dir1)->dir_info.pid = current->pid; + UMSDOS_I(dir2)->dir_info.creating++; + UMSDOS_I(dir2)->dir_info.pid = current->pid; break; } } @@ -141,7 +141,7 @@ void umsdos_startlookup (struct inode *dir) { while (umsdos_waitcreate (dir) != 0); - dir->u.umsdos_i.dir_info.looking++; + UMSDOS_I(dir)->dir_info.looking++; } /* @@ -149,10 +149,10 @@ */ void umsdos_unlockcreate (struct inode *dir) { - dir->u.umsdos_i.dir_info.creating--; - if (dir->u.umsdos_i.dir_info.creating < 0) { - printk ("UMSDOS: dir->u.umsdos_i.dir_info.creating < 0: %d" - ,dir->u.umsdos_i.dir_info.creating); + UMSDOS_I(dir)->dir_info.creating--; + if (UMSDOS_I(dir)->dir_info.creating < 0) { + printk ("UMSDOS: UMSDOS_I(dir)->dir_info.creating < 0: %d" + ,UMSDOS_I(dir)->dir_info.creating); } u_wake_up (dir); } @@ -162,10 +162,10 @@ */ void umsdos_endlookup (struct inode *dir) { - dir->u.umsdos_i.dir_info.looking--; - if (dir->u.umsdos_i.dir_info.looking < 0) { - printk ("UMSDOS: dir->u.umsdos_i.dir_info.looking < 0: %d" - ,dir->u.umsdos_i.dir_info.looking); + UMSDOS_I(dir)->dir_info.looking--; + if (UMSDOS_I(dir)->dir_info.looking < 0) { + printk ("UMSDOS: UMSDOS_I(dir)->dir_info.looking < 0: %d" + ,UMSDOS_I(dir)->dir_info.looking); } u_wake_up (dir); } @@ -618,7 +618,7 @@ goto cleanup; } /* mark the inode as a hardlink */ - oldinode->u.umsdos_i.i_is_hlink = 1; + UMSDOS_I(oldinode)->i_is_hlink = 1; /* * Capture the path to the hidden link. @@ -667,7 +667,7 @@ * the dentry for its real name, not the visible name. * N.B. make sure it's the hidden inode ... */ - if (!oldinode->u.umsdos_i.i_is_hlink) + if (!UMSDOS_I(oldinode)->i_is_hlink) printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n", olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino); @@ -721,7 +721,7 @@ #ifdef UMSDOS_PARANOIA -if (!oldinode->u.umsdos_i.i_is_hlink) +if (!UMSDOS_I(oldinode)->i_is_hlink) printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n", olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino); #endif diff -urN linux-2.5.3-pre2/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c --- linux-2.5.3-pre2/fs/umsdos/rdir.c Sat Sep 1 10:59:08 2001 +++ linux/fs/umsdos/rdir.c Mon Jan 21 17:23:24 2002 @@ -113,7 +113,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name)); /* only patch if needed (because we get called even for lookup (not only rlookup) stuff sometimes, like in umsdos_covered() */ - if (dentry->d_inode->u.umsdos_i.i_patched == 0) + if (UMSDOS_I(dentry->d_inode)->i_patched == 0) umsdos_patch_dentry_inode(dentry, 0); } diff -urN linux-2.5.3-pre2/include/asm-cris/bitops.h linux/include/asm-cris/bitops.h --- linux-2.5.3-pre2/include/asm-cris/bitops.h Mon Oct 8 11:43:54 2001 +++ linux/include/asm-cris/bitops.h Mon Jan 21 17:23:24 2002 @@ -22,6 +22,7 @@ /* We use generic_ffs so get it; include guards resolve the possible mutually inclusion. */ #include +#include /* * Some hacks to defeat gcc over-optimizations.. @@ -43,6 +44,8 @@ #define set_bit(nr, addr) (void)test_and_set_bit(nr, addr) +#define __set_bit(nr, addr) (void)__test_and_set_bit(nr, addr) + /* * clear_bit - Clears a bit in memory * @nr: Bit to clear @@ -56,6 +59,8 @@ #define clear_bit(nr, addr) (void)test_and_clear_bit(nr, addr) +#define __clear_bit(nr, addr) (void)__test_and_clear_bit(nr, addr) + /* * change_bit - Toggle a bit in memory * @nr: Bit to clear @@ -89,7 +94,7 @@ * It also implies a memory barrier. */ -static __inline__ int test_and_set_bit(int nr, void *addr) +static inline int test_and_set_bit(int nr, void *addr) { unsigned int mask, retval; unsigned long flags; @@ -105,6 +110,18 @@ return retval; } +static inline int __test_and_set_bit(int nr, void *addr) +{ + unsigned int mask, retval; + unsigned int *adr = (unsigned int *)addr; + + adr += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *adr) != 0; + *adr |= mask; + return retval; +} + /* * clear_bit() doesn't provide any barrier for the compiler. */ @@ -120,7 +137,7 @@ * It also implies a memory barrier. */ -static __inline__ int test_and_clear_bit(int nr, void *addr) +static inline int test_and_clear_bit(int nr, void *addr) { unsigned int mask, retval; unsigned long flags; @@ -146,7 +163,7 @@ * but actually fail. You must protect multiple accesses with a lock. */ -static __inline__ int __test_and_clear_bit(int nr, void *addr) +static inline int __test_and_clear_bit(int nr, void *addr) { unsigned int mask, retval; unsigned int *adr = (unsigned int *)addr; @@ -166,7 +183,7 @@ * It also implies a memory barrier. */ -static __inline__ int test_and_change_bit(int nr, void *addr) +static inline int test_and_change_bit(int nr, void *addr) { unsigned int mask, retval; unsigned long flags; @@ -183,7 +200,7 @@ /* WARNING: non atomic and it can be reordered! */ -static __inline__ int __test_and_change_bit(int nr, void *addr) +static inline int __test_and_change_bit(int nr, void *addr) { unsigned int mask, retval; unsigned int *adr = (unsigned int *)addr; @@ -204,7 +221,7 @@ * This routine doesn't need to be atomic. */ -static __inline__ int test_bit(int nr, const void *addr) +static inline int test_bit(int nr, const void *addr) { unsigned int mask; unsigned int *adr = (unsigned int *)addr; @@ -225,7 +242,7 @@ * number. They differ in that the first function also inverts all bits * in the input. */ -static __inline__ unsigned long cris_swapnwbrlz(unsigned long w) +static inline unsigned long cris_swapnwbrlz(unsigned long w) { /* Let's just say we return the result in the same register as the input. Saying we clobber the input but can return the result @@ -241,7 +258,7 @@ return res; } -static __inline__ unsigned long cris_swapwbrlz(unsigned long w) +static inline unsigned long cris_swapwbrlz(unsigned long w) { unsigned res; __asm__ ("swapwbr %0 \n\t" @@ -255,7 +272,7 @@ * ffz = Find First Zero in word. Undefined if no zero exists, * so code should check against ~0UL first.. */ -static __inline__ unsigned long ffz(unsigned long w) +static inline unsigned long ffz(unsigned long w) { /* The generic_ffs function is used to avoid the asm when the argument is a constant. */ @@ -268,7 +285,7 @@ * Somewhat like ffz but the equivalent of generic_ffs: in contrast to * ffz we return the first one-bit *plus one*. */ -static __inline__ unsigned long ffs(unsigned long w) +static inline unsigned long ffs(unsigned long w) { /* The generic_ffs function is used to avoid the asm when the argument is a constant. */ @@ -283,7 +300,7 @@ * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -static __inline__ int find_next_zero_bit (void * addr, int size, int offset) +static inline int find_next_zero_bit (void * addr, int size, int offset) { unsigned long *p = ((unsigned long *) addr) + (offset >> 5); unsigned long result = offset & ~31UL; @@ -331,6 +348,17 @@ #define find_first_zero_bit(addr, size) \ find_next_zero_bit((addr), (size), 0) +/* + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + #define ext2_set_bit test_and_set_bit #define ext2_clear_bit test_and_clear_bit #define ext2_test_bit test_bit @@ -343,7 +371,45 @@ #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) -#endif /* __KERNEL__ */ +#if 0 +/* TODO: see below */ +#define sched_find_first_zero_bit(addr) find_first_zero_bit(addr, 168) + +#else +/* TODO: left out pending where to put it.. (there are .h dependencies) */ + + /* + * Every architecture must define this function. It's the fastest + * way of searching a 168-bit bitmap where the first 128 bits are + * unlikely to be set. It's guaranteed that at least one of the 168 + * bits is cleared. + */ +#if 0 +#if MAX_RT_PRIO != 128 || MAX_PRIO != 168 +# error update this function. +#endif +#else +#define MAX_RT_PRIO 128 +#define MAX_PRIO 168 +#endif + +static inline int sched_find_first_zero_bit(char *bitmap) +{ + unsigned int *b = (unsigned int *)bitmap; + unsigned int rt; + + rt = b[0] & b[1] & b[2] & b[3]; + if (unlikely(rt != 0xffffffff)) + return find_first_zero_bit(bitmap, MAX_RT_PRIO); + + if (b[4] != ~0) + return ffz(b[4]) + MAX_RT_PRIO; + return ffz(b[5]) + 32 + MAX_RT_PRIO; +} +#undef MAX_PRIO +#undef MAX_RT_PRIO +#endif +#endif /* __KERNEL__ */ #endif /* _CRIS_BITOPS_H */ diff -urN linux-2.5.3-pre2/include/asm-cris/elf.h linux/include/asm-cris/elf.h --- linux-2.5.3-pre2/include/asm-cris/elf.h Tue May 1 16:05:00 2001 +++ linux/include/asm-cris/elf.h Mon Jan 21 17:23:24 2002 @@ -9,8 +9,9 @@ typedef unsigned long elf_greg_t; -/* These probably need fixing. */ -#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) +/* Note that NGREG is defined to ELF_NGREG in include/linux/elfcore.h, and is + thus exposed to user-space. */ +#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; /* A placeholder; CRIS does not have any fp regs. */ @@ -45,7 +46,52 @@ (_r)->r1 = 0; (_r)->r0 = 0; (_r)->mof = 0; (_r)->srp = 0; \ } while (0) -#undef USE_ELF_CORE_DUMP +#define USE_ELF_CORE_DUMP + +/* The additional layer below is because the stack pointer is missing in + the pt_regs struct, but needed in a core dump. pr_reg is a elf_gregset_t, + and should be filled in according to the layout of the user_regs_struct + struct; regs is a pt_regs struct. We dump all registers, though several are + obviously unnecessary. That way there's less need for intelligence at + the receiving end (i.e. gdb). */ +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + pr_reg[0] = regs->r0; \ + pr_reg[1] = regs->r1; \ + pr_reg[2] = regs->r2; \ + pr_reg[3] = regs->r3; \ + pr_reg[4] = regs->r4; \ + pr_reg[5] = regs->r5; \ + pr_reg[6] = regs->r6; \ + pr_reg[7] = regs->r7; \ + pr_reg[8] = regs->r8; \ + pr_reg[9] = regs->r9; \ + pr_reg[10] = regs->r10; \ + pr_reg[11] = regs->r11; \ + pr_reg[12] = regs->r12; \ + pr_reg[13] = regs->r13; \ + pr_reg[14] = rdusp(); /* sp */ \ + pr_reg[15] = regs->irp; /* pc */ \ + pr_reg[16] = 0; /* p0 */ \ + pr_reg[17] = rdvr(); /* vr */ \ + pr_reg[18] = 0; /* p2 */ \ + pr_reg[19] = 0; /* p3 */ \ + pr_reg[20] = 0; /* p4 */ \ + pr_reg[21] = (regs->dccr & 0xffff); /* ccr */ \ + pr_reg[22] = 0; /* p6 */ \ + pr_reg[23] = regs->mof; /* mof */ \ + pr_reg[24] = 0; /* p8 */ \ + pr_reg[25] = 0; /* ibr */ \ + pr_reg[26] = 0; /* irp */ \ + pr_reg[27] = regs->srp; /* srp */ \ + pr_reg[28] = 0; /* bar */ \ + pr_reg[29] = regs->dccr; /* dccr */ \ + pr_reg[30] = 0; /* brp */ \ + pr_reg[31] = rdusp(); /* usp */ \ + pr_reg[32] = 0; /* csrinstr */ \ + pr_reg[33] = 0; /* csraddr */ \ + pr_reg[34] = 0; /* csrdata */ + + #define ELF_EXEC_PAGESIZE 8192 /* This is the location that an ET_DYN program is loaded if exec'ed. Typical diff -urN linux-2.5.3-pre2/include/asm-cris/ethernet.h linux/include/asm-cris/ethernet.h --- linux-2.5.3-pre2/include/asm-cris/ethernet.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-cris/ethernet.h Mon Jan 21 17:23:24 2002 @@ -0,0 +1,18 @@ +/* + * ioctl defines for ethernet driver + * + * Copyright (c) 2001 Axis Communications AB + * + * Author: Mikael Starvik + * + */ + +#ifndef _CRIS_ETHERNET_H +#define _CRIS_ETHERNET_H +#define SET_ETH_SPEED_AUTO SIOCDEVPRIVATE /* Auto neg speed */ +#define SET_ETH_SPEED_10 SIOCDEVPRIVATE+1 /* 10 Mbps */ +#define SET_ETH_SPEED_100 SIOCDEVPRIVATE+2 /* 100 Mbps. */ +#define SET_ETH_DUPLEX_AUTO SIOCDEVPRIVATE+3 /* Auto neg duplex */ +#define SET_ETH_DUPLEX_HALF SIOCDEVPRIVATE+4 /* Full duplex */ +#define SET_ETH_DUPLEX_FULL SIOCDEVPRIVATE+5 /* Half duplex */ +#endif /* _CRIS_ETHERNET_H */ diff -urN linux-2.5.3-pre2/include/asm-cris/irq.h linux/include/asm-cris/irq.h --- linux-2.5.3-pre2/include/asm-cris/irq.h Mon Oct 8 11:43:54 2001 +++ linux/include/asm-cris/irq.h Mon Jan 21 17:23:24 2002 @@ -126,6 +126,9 @@ /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls * do_IRQ (with irq disabled still). after that it unblocks and jumps to * ret_from_intr (entry.S) + * + * The reason the IRQ is blocked is to allow an sti() before the handler which + * will acknowledge the interrupt is run. */ #define BUILD_IRQ(nr,mask) \ @@ -151,6 +154,41 @@ "reti\n\t" \ "nop\n"); +/* This is subtle. The timer interrupt is crucial and it should not be disabled for + * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would + * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK. + * If the softirq's take too much time to run, the timer irq won't run and the + * watchdog will kill us. + * + * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq + * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed + * it here, we would not get the multiple_irq at all. + * + * The non-blocking here is based on the knowledge that the timer interrupt is + * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not + * be an sti() before the timer irq handler is run to acknowledge the interrupt. + */ + +#define BUILD_TIMER_IRQ(nr,mask) \ +void IRQ_NAME(nr); \ +void sIRQ_NAME(nr); \ +void BAD_IRQ_NAME(nr); \ +__asm__ ( \ + ".text\n\t" \ + "IRQ" #nr "_interrupt:\n\t" \ + SAVE_ALL \ + "sIRQ" #nr "_interrupt:\n\t" /* shortcut for the multiple irq handler */ \ + "moveq "#nr",$r10\n\t" \ + "move.d $sp,$r11\n\t" \ + "jsr do_IRQ\n\t" /* irq.c, r10 and r11 are arguments */ \ + "moveq 0,$r9\n\t" /* make ret_from_intr realise we came from an irq */ \ + "jump ret_from_intr\n\t" \ + "bad_IRQ" #nr "_interrupt:\n\t" \ + "push $r0\n\t" \ + BLOCK_IRQ(mask,nr) \ + "pop $r0\n\t" \ + "reti\n\t" \ + "nop\n"); #endif /* _ASM_IRQ_H */ diff -urN linux-2.5.3-pre2/include/asm-cris/page.h linux/include/asm-cris/page.h --- linux-2.5.3-pre2/include/asm-cris/page.h Tue Feb 13 14:13:44 2001 +++ linux/include/asm-cris/page.h Mon Jan 21 17:23:24 2002 @@ -105,8 +105,14 @@ * to arm and m68k I think) */ -#define virt_to_page(kaddr) (mem_map + (((unsigned long)kaddr - PAGE_OFFSET) >> PAGE_SHIFT)) -#define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define virt_to_page(kaddr) (mem_map + (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT)) +#define VALID_PAGE(page) (((page) - mem_map) < max_mapnr) + +/* convert a page (based on mem_map and forward) to a physical address + * do this by figuring out the virtual address and then use __pa + */ + +#define page_to_phys(page) __pa((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) /* from linker script */ diff -urN linux-2.5.3-pre2/include/asm-cris/pgalloc.h linux/include/asm-cris/pgalloc.h --- linux-2.5.3-pre2/include/asm-cris/pgalloc.h Tue May 1 16:05:00 2001 +++ linux/include/asm-cris/pgalloc.h Mon Jan 21 17:23:24 2002 @@ -21,7 +21,7 @@ * Allocate and free page tables. */ -extern __inline__ pgd_t *get_pgd_slow(void) +static inline pgd_t *get_pgd_slow(void) { pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL); @@ -33,12 +33,12 @@ return ret; } -extern __inline__ void free_pgd_slow(pgd_t *pgd) +static inline void free_pgd_slow(pgd_t *pgd) { free_page((unsigned long)pgd); } -extern __inline__ pgd_t *get_pgd_fast(void) +static inline pgd_t *get_pgd_fast(void) { unsigned long *ret; @@ -51,7 +51,7 @@ return (pgd_t *)ret; } -extern __inline__ void free_pgd_fast(pgd_t *pgd) +static inline void free_pgd_fast(pgd_t *pgd) { *(unsigned long *)pgd = (unsigned long) pgd_quicklist; pgd_quicklist = (unsigned long *) pgd; diff -urN linux-2.5.3-pre2/include/asm-cris/pgtable.h linux/include/asm-cris/pgtable.h --- linux-2.5.3-pre2/include/asm-cris/pgtable.h Mon Jan 21 17:22:37 2002 +++ linux/include/asm-cris/pgtable.h Mon Jan 21 17:23:24 2002 @@ -3,6 +3,12 @@ * HISTORY: * * $Log: pgtable.h,v $ + * Revision 1.14 2001/12/10 03:08:50 bjornw + * Added pgtable_cache_init dummy + * + * Revision 1.13 2001/11/12 18:05:38 pkj + * Added declaration of paging_init(). + * * Revision 1.12 2001/08/11 00:28:00 bjornw * PAGE_CHG_MASK and PAGE_NONE had somewhat untraditional values * @@ -106,6 +112,8 @@ * the CRIS page table tree. */ +extern void paging_init(void); + /* The cache doesn't need to be flushed when TLB entries change because * the cache is mapped to physical memory, not virtual memory */ @@ -507,6 +515,6 @@ /* * No page table caches to initialise */ -#define pgtable_cache_init() do { } while (0) +#define pgtable_cache_init() do { } while (0) #endif /* _CRIS_PGTABLE_H */ diff -urN linux-2.5.3-pre2/include/asm-cris/processor.h linux/include/asm-cris/processor.h --- linux-2.5.3-pre2/include/asm-cris/processor.h Mon Oct 8 11:43:54 2001 +++ linux/include/asm-cris/processor.h Mon Jan 21 17:23:24 2002 @@ -142,6 +142,6 @@ #define init_task (init_task_union.task) #define init_stack (init_task_union.stack) -#define cpu_relax() do { } while (0) +#define cpu_relax() do { } while (0) #endif /* __ASM_CRIS_PROCESSOR_H */ diff -urN linux-2.5.3-pre2/include/asm-cris/scatterlist.h linux/include/asm-cris/scatterlist.h --- linux-2.5.3-pre2/include/asm-cris/scatterlist.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-cris/scatterlist.h Mon Jan 21 17:23:24 2002 @@ -0,0 +1,18 @@ +#ifndef __ASM_CRIS_SCATTERLIST_H +#define __ASM_CRIS_SCATTERLIST_H + +struct scatterlist { + char * address; /* Location data is to be transferred to */ + unsigned int length; + + /* The following is i386 highmem junk - not used by us */ + struct page * page; /* Location for highmem page, if any */ + unsigned int offset;/* for highmem, page offset */ + +}; + +/* i386 junk */ + +#define ISA_DMA_THRESHOLD (0x1fffffff) + +#endif /* !(__ASM_CRIS_SCATTERLIST_H) */ diff -urN linux-2.5.3-pre2/include/asm-cris/unistd.h linux/include/asm-cris/unistd.h --- linux-2.5.3-pre2/include/asm-cris/unistd.h Fri Nov 9 14:11:15 2001 +++ linux/include/asm-cris/unistd.h Mon Jan 21 17:23:24 2002 @@ -378,6 +378,7 @@ static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) +static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) /* the following are just while developing the elinux port! */ diff -urN linux-2.5.3-pre2/include/asm-cris/user.h linux/include/asm-cris/user.h --- linux-2.5.3-pre2/include/asm-cris/user.h Thu Feb 8 16:32:44 2001 +++ linux/include/asm-cris/user.h Mon Jan 21 17:23:24 2002 @@ -28,8 +28,51 @@ * to write an integer number of pages. */ +/* User mode registers, used for core dumps. In order to keep ELF_NGREG + sensible we let all registers be 32 bits. The csr registers are included + for future use. */ +struct user_regs_struct { + unsigned long r0; /* General registers. */ + unsigned long r1; + unsigned long r2; + unsigned long r3; + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + unsigned long r13; + unsigned long sp; /* Stack pointer. */ + unsigned long pc; /* Program counter. */ + unsigned long p0; /* Constant zero (only 8 bits). */ + unsigned long vr; /* Version register (only 8 bits). */ + unsigned long p2; /* Reserved. */ + unsigned long p3; /* Reserved. */ + unsigned long p4; /* Constant zero (only 16 bits). */ + unsigned long ccr; /* Condition code register (only 16 bits). */ + unsigned long p6; /* Reserved. */ + unsigned long mof; /* Multiply overflow register. */ + unsigned long p8; /* Constant zero. */ + unsigned long ibr; /* Not accessible. */ + unsigned long irp; /* Not accessible. */ + unsigned long srp; /* Subroutine return pointer. */ + unsigned long bar; /* Not accessible. */ + unsigned long dccr; /* Dword condition code register. */ + unsigned long brp; /* Not accessible. */ + unsigned long usp; /* User-mode stack pointer. Same as sp when + in user mode. */ + unsigned long csrinstr; /* Internal status registers. */ + unsigned long csraddr; + unsigned long csrdata; +}; + + struct user { - struct pt_regs regs; /* entire machine state */ + struct user_regs_struct regs; /* entire machine state */ size_t u_tsize; /* text size (pages) */ size_t u_dsize; /* data size (pages) */ size_t u_ssize; /* stack size (pages) */ diff -urN linux-2.5.3-pre2/include/asm-i386/hw_irq.h linux/include/asm-i386/hw_irq.h --- linux-2.5.3-pre2/include/asm-i386/hw_irq.h Mon Jan 14 13:27:06 2002 +++ linux/include/asm-i386/hw_irq.h Mon Jan 21 17:23:24 2002 @@ -35,13 +35,14 @@ * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. * TLB, reschedule and local APIC vectors are performance-critical. * - * Vectors 0xf0-0xfa are free (reserved for future Linux use). + * Vectors 0xf0-0xf9 are free (reserved for future Linux use). */ #define SPURIOUS_APIC_VECTOR 0xff #define ERROR_APIC_VECTOR 0xfe #define INVALIDATE_TLB_VECTOR 0xfd #define RESCHEDULE_VECTOR 0xfc -#define CALL_FUNCTION_VECTOR 0xfb +#define TASK_MIGRATION_VECTOR 0xfb +#define CALL_FUNCTION_VECTOR 0xfa /* * Local APIC timer IRQ vector is on a different priority level, diff -urN linux-2.5.3-pre2/include/linux/affs_fs.h linux/include/linux/affs_fs.h --- linux-2.5.3-pre2/include/linux/affs_fs.h Thu Apr 19 22:57:06 2001 +++ linux/include/linux/affs_fs.h Mon Jan 21 17:23:24 2002 @@ -6,6 +6,8 @@ #include +#include + #define AFFS_SUPER_MAGIC 0xadff struct affs_date; diff -urN linux-2.5.3-pre2/include/linux/affs_fs_i.h linux/include/linux/affs_fs_i.h --- linux-2.5.3-pre2/include/linux/affs_fs_i.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/affs_fs_i.h Mon Jan 21 17:23:24 2002 @@ -3,9 +3,6 @@ #include -// move this to linux/coda.h!!! -#include - #define AFFS_CACHE_SIZE PAGE_SIZE //#define AFFS_CACHE_SIZE (4*4) @@ -48,10 +45,13 @@ unsigned char i_pad; s32 i_parent; /* parent ino */ #endif + struct inode vfs_inode; }; /* short cut to get to the affs specific inode data */ -#define AFFS_INODE (&inode->u.affs_i) -#define AFFS_DIR (&dir->u.affs_i) +static inline struct affs_inode_info *AFFS_I(struct inode *inode) +{ + return list_entry(inode, struct affs_inode_info, vfs_inode); +} #endif diff -urN linux-2.5.3-pre2/include/linux/amigaffs.h linux/include/linux/amigaffs.h --- linux-2.5.3-pre2/include/linux/amigaffs.h Tue Dec 25 15:39:20 2001 +++ linux/include/linux/amigaffs.h Mon Jan 21 17:23:24 2002 @@ -93,32 +93,32 @@ static inline void affs_lock_link(struct inode *inode) { - down(&AFFS_INODE->i_link_lock); + down(&AFFS_I(inode)->i_link_lock); } static inline void affs_unlock_link(struct inode *inode) { - up(&AFFS_INODE->i_link_lock); + up(&AFFS_I(inode)->i_link_lock); } static inline void affs_lock_dir(struct inode *inode) { - down(&AFFS_INODE->i_hash_lock); + down(&AFFS_I(inode)->i_hash_lock); } static inline void affs_unlock_dir(struct inode *inode) { - up(&AFFS_INODE->i_hash_lock); + up(&AFFS_I(inode)->i_hash_lock); } static inline void affs_lock_ext(struct inode *inode) { - down(&AFFS_INODE->i_ext_lock); + down(&AFFS_I(inode)->i_ext_lock); } static inline void affs_unlock_ext(struct inode *inode) { - up(&AFFS_INODE->i_ext_lock); + up(&AFFS_I(inode)->i_ext_lock); } #ifdef __LITTLE_ENDIAN diff -urN linux-2.5.3-pre2/include/linux/coda.h linux/include/linux/coda.h --- linux-2.5.3-pre2/include/linux/coda.h Wed Apr 25 16:18:54 2001 +++ linux/include/linux/coda.h Mon Jan 21 17:23:24 2002 @@ -99,6 +99,7 @@ #if defined(__linux__) +#include #define cdev_t u_quad_t #ifndef __KERNEL__ #if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2) diff -urN linux-2.5.3-pre2/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h --- linux-2.5.3-pre2/include/linux/coda_fs_i.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/coda_fs_i.h Mon Jan 21 17:23:24 2002 @@ -24,6 +24,7 @@ unsigned int c_contcount; /* refcount for container file */ struct coda_cred c_cached_cred; /* credentials of cached perms */ unsigned int c_cached_perm; /* cached access permissions */ + struct inode vfs_inode; }; /* flags */ diff -urN linux-2.5.3-pre2/include/linux/coda_linux.h linux/include/linux/coda_linux.h --- linux-2.5.3-pre2/include/linux/coda_linux.h Wed Dec 5 13:02:14 2001 +++ linux/include/linux/coda_linux.h Mon Jan 21 17:23:24 2002 @@ -111,7 +111,10 @@ /* inode to cnode access functions */ -#define ITOC(inode) (&((inode)->u.coda_i)) +static inline struct coda_inode_info *ITOC(struct inode *inode) +{ + return list_entry(inode, struct coda_inode_info, vfs_inode); +} static __inline__ struct ViceFid *coda_i2f(struct inode *inode) { diff -urN linux-2.5.3-pre2/include/linux/dnotify.h linux/include/linux/dnotify.h --- linux-2.5.3-pre2/include/linux/dnotify.h Fri Sep 22 14:21:22 2000 +++ linux/include/linux/dnotify.h Mon Jan 21 17:23:24 2002 @@ -4,6 +4,8 @@ * Copyright 2000 (C) Stephen Rothwell */ +#include + struct dnotify_struct { struct dnotify_struct * dn_next; int dn_magic; diff -urN linux-2.5.3-pre2/include/linux/efs_fs.h linux/include/linux/efs_fs.h --- linux-2.5.3-pre2/include/linux/efs_fs.h Mon Dec 11 13:26:51 2000 +++ linux/include/linux/efs_fs.h Mon Jan 21 17:23:24 2002 @@ -37,13 +37,11 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif -#ifdef _EFS_USE_GENERIC -#define INODE_INFO(i) (struct efs_inode_info *) &((i)->u.generic_ip) -#define SUPER_INFO(s) (struct efs_sb_info *) &((s)->u.generic_sbp) -#else -#define INODE_INFO(i) &((i)->u.efs_i) +static inline struct efs_inode_info *INODE_INFO(struct inode *inode) +{ + return list_entry(inode, struct efs_inode_info, vfs_inode); +} #define SUPER_INFO(s) &((s)->u.efs_sb) -#endif extern struct inode_operations efs_dir_inode_operations; extern struct file_operations efs_dir_operations; diff -urN linux-2.5.3-pre2/include/linux/efs_fs_i.h linux/include/linux/efs_fs_i.h --- linux-2.5.3-pre2/include/linux/efs_fs_i.h Thu May 13 23:50:15 1999 +++ linux/include/linux/efs_fs_i.h Mon Jan 21 17:23:24 2002 @@ -61,6 +61,7 @@ int lastextent; efs_extent extents[EFS_DIRECTEXTENTS]; + struct inode vfs_inode; }; #endif /* __EFS_FS_I_H__ */ diff -urN linux-2.5.3-pre2/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- linux-2.5.3-pre2/include/linux/ext2_fs.h Mon Jan 14 13:27:20 2002 +++ linux/include/linux/ext2_fs.h Mon Jan 21 17:23:24 2002 @@ -532,101 +532,4 @@ #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) -#ifdef __KERNEL__ -/* - * Function prototypes - */ - -/* - * Ok, these declarations are also in but none of the - * ext2 source programs needs to include it so they are duplicated here. - */ -# define NORET_TYPE /**/ -# define ATTRIB_NORET __attribute__((noreturn)) -# define NORET_AND noreturn, - -/* balloc.c */ -extern int ext2_bg_has_super(struct super_block *sb, int group); -extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); -extern int ext2_new_block (struct inode *, unsigned long, - __u32 *, __u32 *, int *); -extern void ext2_free_blocks (struct inode *, unsigned long, - unsigned long); -extern unsigned long ext2_count_free_blocks (struct super_block *); -extern void ext2_check_blocks_bitmap (struct super_block *); -extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, - unsigned int block_group, - struct buffer_head ** bh); - -/* dir.c */ -extern int ext2_add_link (struct dentry *, struct inode *); -extern ino_t ext2_inode_by_name(struct inode *, struct dentry *); -extern int ext2_make_empty(struct inode *, struct inode *); -extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **); -extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *); -extern int ext2_empty_dir (struct inode *); -extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); -extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *); - -/* fsync.c */ -extern int ext2_sync_file (struct file *, struct dentry *, int); -extern int ext2_fsync_inode (struct inode *, int); - -/* ialloc.c */ -extern struct inode * ext2_new_inode (const struct inode *, int); -extern void ext2_free_inode (struct inode *); -extern unsigned long ext2_count_free_inodes (struct super_block *); -extern void ext2_check_inodes_bitmap (struct super_block *); -extern unsigned long ext2_count_free (struct buffer_head *, unsigned); - -/* inode.c */ -extern void ext2_read_inode (struct inode *); -extern void ext2_write_inode (struct inode *, int); -extern void ext2_put_inode (struct inode *); -extern void ext2_delete_inode (struct inode *); -extern int ext2_sync_inode (struct inode *); -extern void ext2_discard_prealloc (struct inode *); -extern void ext2_truncate (struct inode *); - -/* ioctl.c */ -extern int ext2_ioctl (struct inode *, struct file *, unsigned int, - unsigned long); - -/* super.c */ -extern void ext2_error (struct super_block *, const char *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -extern NORET_TYPE void ext2_panic (struct super_block *, const char *, - const char *, ...) - __attribute__ ((NORET_AND format (printf, 3, 4))); -extern void ext2_warning (struct super_block *, const char *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -extern void ext2_update_dynamic_rev (struct super_block *sb); -extern void ext2_put_super (struct super_block *); -extern void ext2_write_super (struct super_block *); -extern int ext2_remount (struct super_block *, int *, char *); -extern struct super_block * ext2_read_super (struct super_block *,void *,int); -extern int ext2_statfs (struct super_block *, struct statfs *); - -/* - * Inodes and files operations - */ - -/* dir.c */ -extern struct file_operations ext2_dir_operations; - -/* file.c */ -extern struct inode_operations ext2_file_inode_operations; -extern struct file_operations ext2_file_operations; - -/* inode.c */ -extern struct address_space_operations ext2_aops; - -/* namei.c */ -extern struct inode_operations ext2_dir_inode_operations; - -/* symlink.c */ -extern struct inode_operations ext2_fast_symlink_inode_operations; - -#endif /* __KERNEL__ */ - #endif /* _LINUX_EXT2_FS_H */ diff -urN linux-2.5.3-pre2/include/linux/ext2_fs_i.h linux/include/linux/ext2_fs_i.h --- linux-2.5.3-pre2/include/linux/ext2_fs_i.h Mon Sep 17 13:16:30 2001 +++ linux/include/linux/ext2_fs_i.h Wed Dec 31 16:00:00 1969 @@ -1,41 +0,0 @@ -/* - * linux/include/linux/ext2_fs_i.h - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/include/linux/minix_fs_i.h - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -#ifndef _LINUX_EXT2_FS_I -#define _LINUX_EXT2_FS_I - -/* - * second extended file system inode data in memory - */ -struct ext2_inode_info { - __u32 i_data[15]; - __u32 i_flags; - __u32 i_faddr; - __u8 i_frag_no; - __u8 i_frag_size; - __u16 i_osync; - __u32 i_file_acl; - __u32 i_dir_acl; - __u32 i_dtime; - __u32 i_block_group; - __u32 i_next_alloc_block; - __u32 i_next_alloc_goal; - __u32 i_prealloc_block; - __u32 i_prealloc_count; - __u32 i_dir_start_lookup; - int i_new_inode:1; /* Is a freshly allocated inode */ -}; - -#endif /* _LINUX_EXT2_FS_I */ diff -urN linux-2.5.3-pre2/include/linux/ext3_fs.h linux/include/linux/ext3_fs.h --- linux-2.5.3-pre2/include/linux/ext3_fs.h Sat Jan 5 12:58:11 2002 +++ linux/include/linux/ext3_fs.h Mon Jan 21 17:23:24 2002 @@ -443,7 +443,10 @@ #ifdef __KERNEL__ #define EXT3_SB(sb) (&((sb)->u.ext3_sb)) -#define EXT3_I(inode) (&((inode)->u.ext3_i)) +static inline struct ext3_inode_info *EXT3_I(struct inode *inode) +{ + return list_entry(inode, struct ext3_inode_info, vfs_inode); +} #else /* Assume that user mode programs are passing in an ext3fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test @@ -451,7 +454,7 @@ #define EXT3_SB(sb) (sb) #endif -#define NEXT_ORPHAN(inode) (inode)->u.ext3_i.i_dtime +#define NEXT_ORPHAN(inode) EXT3_I(inode)->i_dtime /* * Codes for operating systems @@ -620,7 +623,7 @@ extern int ext3_sync_file (struct file *, struct dentry *, int); /* ialloc.c */ -extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int); +extern struct inode * ext3_new_inode (handle_t *, struct inode *, int); extern void ext3_free_inode (handle_t *, struct inode *); extern struct inode * ext3_orphan_get (struct super_block *, ino_t); extern unsigned long ext3_count_free_inodes (struct super_block *); diff -urN linux-2.5.3-pre2/include/linux/ext3_fs_i.h linux/include/linux/ext3_fs_i.h --- linux-2.5.3-pre2/include/linux/ext3_fs_i.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/ext3_fs_i.h Mon Jan 21 17:23:24 2002 @@ -73,6 +73,7 @@ * by other means, so we have truncate_sem. */ struct rw_semaphore truncate_sem; + struct inode vfs_inode; }; #endif /* _LINUX_EXT3_FS_I */ diff -urN linux-2.5.3-pre2/include/linux/ext3_jbd.h linux/include/linux/ext3_jbd.h --- linux-2.5.3-pre2/include/linux/ext3_jbd.h Sat Jan 5 12:58:14 2002 +++ linux/include/linux/ext3_jbd.h Mon Jan 21 17:23:24 2002 @@ -289,7 +289,7 @@ return 1; if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA) return 1; - if (inode->u.ext3_i.i_flags & EXT3_JOURNAL_DATA_FL) + if (EXT3_I(inode)->i_flags & EXT3_JOURNAL_DATA_FL) return 1; return 0; } diff -urN linux-2.5.3-pre2/include/linux/fs.h linux/include/linux/fs.h --- linux-2.5.3-pre2/include/linux/fs.h Mon Jan 21 17:22:37 2002 +++ linux/include/linux/fs.h Mon Jan 21 17:23:24 2002 @@ -288,25 +288,15 @@ #include #include -#include -#include -#include #include #include #include #include -#include #include -#include -#include -#include -#include #include -#include #include #include #include -#include #include #include #include @@ -477,25 +467,15 @@ __u32 i_generation; union { struct minix_inode_info minix_i; - struct ext2_inode_info ext2_i; - struct ext3_inode_info ext3_i; - struct hpfs_inode_info hpfs_i; struct ntfs_inode_info ntfs_i; struct msdos_inode_info msdos_i; struct umsdos_inode_info umsdos_i; struct iso_inode_info isofs_i; - struct nfs_inode_info nfs_i; struct sysv_inode_info sysv_i; - struct affs_inode_info affs_i; - struct ufs_inode_info ufs_i; - struct efs_inode_info efs_i; struct romfs_inode_info romfs_i; - struct shmem_inode_info shmem_i; - struct coda_inode_info coda_i; struct smb_inode_info smbfs_i; struct hfs_inode_info hfs_i; struct adfs_inode_info adfs_i; - struct qnx4_inode_info qnx4_i; struct reiserfs_inode_info reiserfs_i; struct bfs_inode_info bfs_i; struct udf_inode_info udf_i; @@ -507,6 +487,12 @@ } u; }; +#include +/* will die */ +#include +#include +#include + struct fown_struct { int pid; /* pid or -pgrp where SIGIO should be sent */ uid_t uid, euid; /* uid/euid of process setting the owner */ @@ -562,6 +548,9 @@ * Lockd stuffs a "host" pointer into this. */ typedef struct files_struct *fl_owner_t; + +/* that will die - we need it for nfs_lock_info */ +#include struct file_lock { struct file_lock *fl_next; /* singly linked list for this inode */ diff -urN linux-2.5.3-pre2/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h --- linux-2.5.3-pre2/include/linux/hpfs_fs_i.h Thu Feb 10 12:17:00 2000 +++ linux/include/linux/hpfs_fs_i.h Mon Jan 21 17:23:24 2002 @@ -18,24 +18,7 @@ unsigned i_dirty : 1; struct semaphore i_sem; /* semaphore */ loff_t **i_rddir_off; + struct inode vfs_inode; }; - -#define i_hpfs_dno u.hpfs_i.i_dno -#define i_hpfs_parent_dir u.hpfs_i.i_parent_dir -#define i_hpfs_n_secs u.hpfs_i.i_n_secs -#define i_hpfs_file_sec u.hpfs_i.i_file_sec -#define i_hpfs_disk_sec u.hpfs_i.i_disk_sec -#define i_hpfs_dpos u.hpfs_i.i_dpos -#define i_hpfs_dsubdno u.hpfs_i.i_dsubdno -#define i_hpfs_ea_size u.hpfs_i.i_ea_size -#define i_hpfs_conv u.hpfs_i.i_conv -#define i_hpfs_ea_mode u.hpfs_i.i_ea_mode -#define i_hpfs_ea_uid u.hpfs_i.i_ea_uid -#define i_hpfs_ea_gid u.hpfs_i.i_ea_gid -/*#define i_hpfs_lock u.hpfs_i.i_lock*/ -/*#define i_hpfs_queue u.hpfs_i.i_queue*/ -#define i_hpfs_sem u.hpfs_i.i_sem -#define i_hpfs_rddir_off u.hpfs_i.i_rddir_off -#define i_hpfs_dirty u.hpfs_i.i_dirty #endif diff -urN linux-2.5.3-pre2/include/linux/netdevice.h linux/include/linux/netdevice.h --- linux-2.5.3-pre2/include/linux/netdevice.h Mon Jan 14 13:28:11 2002 +++ linux/include/linux/netdevice.h Mon Jan 21 17:23:24 2002 @@ -278,6 +278,10 @@ struct net_device_stats* (*get_stats)(struct net_device *dev); struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); + /* List of functions to handle Wireless Extensions (instead of ioctl). + * See for details. Jean II */ + struct iw_handler_def * wireless_handlers; + /* * This marks the end of the "visible" part of the structure. All * fields hereafter are internal to the system, and may change at diff -urN linux-2.5.3-pre2/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- linux-2.5.3-pre2/include/linux/nfs_fs.h Mon Jan 14 13:28:01 2002 +++ linux/include/linux/nfs_fs.h Mon Jan 21 17:23:24 2002 @@ -63,67 +63,154 @@ */ #define NFS_SUPER_MAGIC 0x6969 -static inline struct nfs_inode_info *NFS_I(struct inode *inode) +/* + * These are the default flags for swap requests + */ +#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) + +/* Flags in the RPC client structure */ +#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */ + +#define NFS_RW_SYNC 0x0001 /* O_SYNC handling */ +#define NFS_RW_SWAP 0x0002 /* This is a swap request */ + +/* + * When flushing a cluster of dirty pages, there can be different + * strategies: + */ +#define FLUSH_AGING 0 /* only flush old buffers */ +#define FLUSH_SYNC 1 /* file being synced, or contention */ +#define FLUSH_WAIT 2 /* wait for completion */ +#define FLUSH_STABLE 4 /* commit to stable storage */ + +#ifdef __KERNEL__ + +/* + * nfs fs inode data in memory + */ +struct nfs_inode { + /* + * The 64bit 'inode number' + */ + __u64 fsid; + __u64 fileid; + + /* + * NFS file handle + */ + struct nfs_fh fh; + + /* + * Various flags + */ + unsigned short flags; + + /* + * read_cache_jiffies is when we started read-caching this inode, + * and read_cache_mtime is the mtime of the inode at that time. + * attrtimeo is for how long the cached information is assumed + * to be valid. A successful attribute revalidation doubles + * attrtimeo (up to acregmax/acdirmax), a failure resets it to + * acregmin/acdirmin. + * + * We need to revalidate the cached attrs for this inode if + * + * jiffies - read_cache_jiffies > attrtimeo + * + * and invalidate any cached data/flush out any dirty pages if + * we find that + * + * mtime != read_cache_mtime + */ + unsigned long read_cache_jiffies; + __u64 read_cache_ctime; + __u64 read_cache_mtime; + __u64 read_cache_isize; + unsigned long attrtimeo; + unsigned long attrtimeo_timestamp; + + /* + * This is the cookie verifier used for NFSv3 readdir + * operations + */ + __u32 cookieverf[2]; + + /* + * This is the list of dirty unwritten pages. + */ + struct list_head read; + struct list_head dirty; + struct list_head commit; + struct list_head writeback; + + unsigned int nread, + ndirty, + ncommit, + npages; + + /* Flush daemon info */ + struct inode *hash_next, + *hash_prev; + unsigned long nextscan; + + /* Credentials for shared mmap */ + struct rpc_cred *mm_cred; + + struct inode vfs_inode; +}; + +/* + * Legal inode flag values + */ +#define NFS_INO_STALE 0x0001 /* possible stale inode */ +#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ +#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ +#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */ +#define NFS_INO_FLUSH 0x0020 /* inode is due for flushing */ +#define NFS_INO_NEW 0x0040 /* hadn't been filled yet */ + +static inline struct nfs_inode *NFS_I(struct inode *inode) { - return &inode->u.nfs_i; + return list_entry(inode, struct nfs_inode, vfs_inode); } -#define NFS_FH(inode) (&(inode)->u.nfs_i.fh) +#define NFS_FH(inode) (&NFS_I(inode)->fh) #define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server) #define NFS_CLIENT(inode) (NFS_SERVER(inode)->client) #define NFS_PROTO(inode) (NFS_SERVER(inode)->rpc_ops) #define NFS_REQUESTLIST(inode) (NFS_SERVER(inode)->rw_requests) #define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode))) #define NFS_CONGESTED(inode) (RPC_CONGESTED(NFS_CLIENT(inode))) -#define NFS_COOKIEVERF(inode) ((inode)->u.nfs_i.cookieverf) -#define NFS_READTIME(inode) ((inode)->u.nfs_i.read_cache_jiffies) -#define NFS_CACHE_CTIME(inode) ((inode)->u.nfs_i.read_cache_ctime) -#define NFS_CACHE_MTIME(inode) ((inode)->u.nfs_i.read_cache_mtime) -#define NFS_CACHE_ISIZE(inode) ((inode)->u.nfs_i.read_cache_isize) -#define NFS_NEXTSCAN(inode) ((inode)->u.nfs_i.nextscan) +#define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf) +#define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) +#define NFS_CACHE_CTIME(inode) (NFS_I(inode)->read_cache_ctime) +#define NFS_CACHE_MTIME(inode) (NFS_I(inode)->read_cache_mtime) +#define NFS_CACHE_ISIZE(inode) (NFS_I(inode)->read_cache_isize) +#define NFS_NEXTSCAN(inode) (NFS_I(inode)->nextscan) #define NFS_CACHEINV(inode) \ do { \ NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \ } while (0) -#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo) +#define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo) #define NFS_MINATTRTIMEO(inode) \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ : NFS_SERVER(inode)->acregmin) #define NFS_MAXATTRTIMEO(inode) \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \ : NFS_SERVER(inode)->acregmax) -#define NFS_ATTRTIMEO_UPDATE(inode) ((inode)->u.nfs_i.attrtimeo_timestamp) +#define NFS_ATTRTIMEO_UPDATE(inode) (NFS_I(inode)->attrtimeo_timestamp) -#define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags) +#define NFS_FLAGS(inode) (NFS_I(inode)->flags) #define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) #define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE) +#define NFS_NEW(inode) (NFS_FLAGS(inode) & NFS_INO_NEW) -#define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid) -#define NFS_FSID(inode) ((inode)->u.nfs_i.fsid) +#define NFS_FILEID(inode) (NFS_I(inode)->fileid) +#define NFS_FSID(inode) (NFS_I(inode)->fsid) /* Inode Flags */ #define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0) -/* - * These are the default flags for swap requests - */ -#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) - -/* Flags in the RPC client structure */ -#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */ - -#define NFS_RW_SYNC 0x0001 /* O_SYNC handling */ -#define NFS_RW_SWAP 0x0002 /* This is a swap request */ - -/* - * When flushing a cluster of dirty pages, there can be different - * strategies: - */ -#define FLUSH_AGING 0 /* only flush old buffers */ -#define FLUSH_SYNC 1 /* file being synced, or contention */ -#define FLUSH_WAIT 2 /* wait for completion */ -#define FLUSH_STABLE 4 /* commit to stable storage */ - static inline loff_t page_offset(struct page *page) { @@ -136,7 +223,6 @@ return page->index; } -#ifdef __KERNEL__ /* * linux/fs/nfs/inode.c */ @@ -220,13 +306,13 @@ static inline int nfs_have_read(struct inode *inode) { - return !list_empty(&inode->u.nfs_i.read); + return !list_empty(&NFS_I(inode)->read); } static inline int nfs_have_writebacks(struct inode *inode) { - return !list_empty(&inode->u.nfs_i.writeback); + return !list_empty(&NFS_I(inode)->writeback); } static inline int diff -urN linux-2.5.3-pre2/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h --- linux-2.5.3-pre2/include/linux/nfs_fs_i.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/nfs_fs_i.h Mon Jan 21 17:23:24 2002 @@ -6,87 +6,6 @@ #include /* - * nfs fs inode data in memory - */ -struct nfs_inode_info { - /* - * The 64bit 'inode number' - */ - __u64 fsid; - __u64 fileid; - - /* - * NFS file handle - */ - struct nfs_fh fh; - - /* - * Various flags - */ - unsigned short flags; - - /* - * read_cache_jiffies is when we started read-caching this inode, - * and read_cache_mtime is the mtime of the inode at that time. - * attrtimeo is for how long the cached information is assumed - * to be valid. A successful attribute revalidation doubles - * attrtimeo (up to acregmax/acdirmax), a failure resets it to - * acregmin/acdirmin. - * - * We need to revalidate the cached attrs for this inode if - * - * jiffies - read_cache_jiffies > attrtimeo - * - * and invalidate any cached data/flush out any dirty pages if - * we find that - * - * mtime != read_cache_mtime - */ - unsigned long read_cache_jiffies; - __u64 read_cache_ctime; - __u64 read_cache_mtime; - __u64 read_cache_isize; - unsigned long attrtimeo; - unsigned long attrtimeo_timestamp; - - /* - * This is the cookie verifier used for NFSv3 readdir - * operations - */ - __u32 cookieverf[2]; - - /* - * This is the list of dirty unwritten pages. - */ - struct list_head read; - struct list_head dirty; - struct list_head commit; - struct list_head writeback; - - unsigned int nread, - ndirty, - ncommit, - npages; - - /* Flush daemon info */ - struct inode *hash_next, - *hash_prev; - unsigned long nextscan; - - /* Credentials for shared mmap */ - struct rpc_cred *mm_cred; -}; - -/* - * Legal inode flag values - */ -#define NFS_INO_STALE 0x0001 /* possible stale inode */ -#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ -#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ -#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */ -#define NFS_INO_FLUSH 0x0020 /* inode is due for flushing */ - -/* * NFS lock info */ struct nfs_lock_info { diff -urN linux-2.5.3-pre2/include/linux/qnx4_fs.h linux/include/linux/qnx4_fs.h --- linux-2.5.3-pre2/include/linux/qnx4_fs.h Sun Dec 16 12:23:05 2001 +++ linux/include/linux/qnx4_fs.h Mon Jan 21 17:23:24 2002 @@ -97,6 +97,12 @@ #define QNX4DEBUG(X) (void) 0 #endif +struct qnx4_inode_info { + struct qnx4_inode_entry raw; + unsigned long mmu_private; + struct inode vfs_inode; +}; + extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry); extern unsigned long qnx4_count_free_blocks(struct super_block *sb); extern unsigned long qnx4_block_map(struct inode *inode, long iblock); @@ -119,6 +125,16 @@ extern int qnx4_sync_file(struct file *file, struct dentry *dentry, int); extern int qnx4_sync_inode(struct inode *inode); extern int qnx4_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create); + +static inline struct qnx4_inode_info *qnx4_i(struct inode *inode) +{ + return list_entry(inode, struct qnx4_inode_info, vfs_inode); +} + +static inline struct qnx4_inode_entry *qnx4_raw_inode(struct inode *inode) +{ + return &qnx4_i(inode)->raw; +} #endif /* __KERNEL__ */ diff -urN linux-2.5.3-pre2/include/linux/qnx4_fs_i.h linux/include/linux/qnx4_fs_i.h --- linux-2.5.3-pre2/include/linux/qnx4_fs_i.h Thu Feb 10 12:17:00 2000 +++ linux/include/linux/qnx4_fs_i.h Wed Dec 31 16:00:00 1969 @@ -1,39 +0,0 @@ -/* - * Name : qnx4_fs_i.h - * Author : Richard Frowijn - * Function : qnx4 inode definitions - * Version : 1.0.2 - * Last modified : 2000-01-06 - * - * History : 23-03-1998 created - * - */ -#ifndef _QNX4_FS_I -#define _QNX4_FS_I - -#include - -/* - * qnx4 fs inode entry - */ -struct qnx4_inode_info { - char i_reserved[16]; /* 16 */ - qnx4_off_t i_size; /* 4 */ - qnx4_xtnt_t i_first_xtnt; /* 8 */ - __u32 i_xblk; /* 4 */ - __s32 i_ftime; /* 4 */ - __s32 i_mtime; /* 4 */ - __s32 i_atime; /* 4 */ - __s32 i_ctime; /* 4 */ - qnx4_nxtnt_t i_num_xtnts; /* 2 */ - qnx4_mode_t i_mode; /* 2 */ - qnx4_muid_t i_uid; /* 2 */ - qnx4_mgid_t i_gid; /* 2 */ - qnx4_nlink_t i_nlink; /* 2 */ - __u8 i_zero[4]; /* 4 */ - qnx4_ftype_t i_type; /* 1 */ - __u8 i_status; /* 1 */ - unsigned long mmu_private; -}; - -#endif diff -urN linux-2.5.3-pre2/include/linux/sched.h linux/include/linux/sched.h --- linux-2.5.3-pre2/include/linux/sched.h Mon Jan 21 17:22:37 2002 +++ linux/include/linux/sched.h Mon Jan 21 17:23:24 2002 @@ -141,6 +141,7 @@ extern void sched_init(void); extern void init_idle(void); +extern void idle_startup_done(void); extern void show_state(void); extern void cpu_init (void); extern void trap_init(void); @@ -148,6 +149,8 @@ extern void update_one_process(struct task_struct *p, unsigned long user, unsigned long system, int cpu); extern void scheduler_tick(struct task_struct *p); +extern void sched_task_migrated(struct task_struct *p); +extern void smp_migrate_task(int cpu, task_t *task); #define MAX_SCHEDULE_TIMEOUT LONG_MAX extern signed long FASTCALL(schedule_timeout(signed long timeout)); diff -urN linux-2.5.3-pre2/include/linux/shmem_fs.h linux/include/linux/shmem_fs.h --- linux-2.5.3-pre2/include/linux/shmem_fs.h Mon Jan 14 10:10:43 2002 +++ linux/include/linux/shmem_fs.h Mon Jan 21 17:23:24 2002 @@ -28,7 +28,7 @@ unsigned long swapped; int locked; /* into memory */ struct list_head list; - struct inode *inode; + struct inode vfs_inode; }; struct shmem_sb_info { @@ -39,6 +39,9 @@ spinlock_t stat_lock; }; -#define SHMEM_I(inode) (&inode->u.shmem_i) +static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) +{ + return list_entry(inode, struct shmem_inode_info, vfs_inode); +} #endif diff -urN linux-2.5.3-pre2/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h --- linux-2.5.3-pre2/include/linux/ufs_fs.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/ufs_fs.h Mon Jan 21 17:23:24 2002 @@ -223,7 +223,7 @@ #define UFS_MAXNAMLEN 255 #define UFS_MAXMNTLEN 512 -#define UFS_MAXCSBUFS 31 +/* #define UFS_MAXCSBUFS 31 */ #define UFS_LINK_MAX 32000 /* @@ -508,6 +508,218 @@ #define UFS_SF_APPEND 0x00040000 /* append-only */ #define UFS_SF_NOUNLINK 0x00100000 /* can't be removed or renamed */ +/* + * This structure is used for reading disk structures larger + * than the size of fragment. + */ +struct ufs_buffer_head { + unsigned fragment; /* first fragment */ + unsigned count; /* number of fragments */ + struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */ +}; + +struct ufs_cg_private_info { + struct ufs_cylinder_group ucg; + __u32 c_cgx; /* number of cylidner group */ + __u16 c_ncyl; /* number of cyl's this cg */ + __u16 c_niblk; /* number of inode blocks this cg */ + __u32 c_ndblk; /* number of data blocks this cg */ + __u32 c_rotor; /* position of last used block */ + __u32 c_frotor; /* position of last used frag */ + __u32 c_irotor; /* position of last used inode */ + __u32 c_btotoff; /* (__u32) block totals per cylinder */ + __u32 c_boff; /* (short) free block positions */ + __u32 c_iusedoff; /* (char) used inode map */ + __u32 c_freeoff; /* (u_char) free block map */ + __u32 c_nextfreeoff; /* (u_char) next available space */ + __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */ + __u32 c_clusteroff; /* (u_int8) free cluster map */ + __u32 c_nclusterblks; /* number of clusters this cg */ +}; + +struct ufs_sb_private_info { + struct ufs_buffer_head s_ubh; /* buffer containing super block */ + __u32 s_sblkno; /* offset of super-blocks in filesys */ + __u32 s_cblkno; /* offset of cg-block in filesys */ + __u32 s_iblkno; /* offset of inode-blocks in filesys */ + __u32 s_dblkno; /* offset of first data after cg */ + __u32 s_cgoffset; /* cylinder group offset in cylinder */ + __u32 s_cgmask; /* used to calc mod fs_ntrak */ + __u32 s_size; /* number of blocks (fragments) in fs */ + __u32 s_dsize; /* number of data blocks in fs */ + __u32 s_ncg; /* number of cylinder groups */ + __u32 s_bsize; /* size of basic blocks */ + __u32 s_fsize; /* size of fragments */ + __u32 s_fpb; /* fragments per block */ + __u32 s_minfree; /* minimum percentage of free blocks */ + __u32 s_bmask; /* `blkoff'' calc of blk offsets */ + __u32 s_fmask; /* s_fsize mask */ + __u32 s_bshift; /* `lblkno'' calc of logical blkno */ + __u32 s_fshift; /* s_fsize shift */ + __u32 s_fpbshift; /* fragments per block shift */ + __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + __u32 s_sbsize; /* actual size of super block */ + __u32 s_csmask; /* csum block offset */ + __u32 s_csshift; /* csum block number */ + __u32 s_nindir; /* value of NINDIR */ + __u32 s_inopb; /* value of INOPB */ + __u32 s_nspf; /* value of NSPF */ + __u32 s_npsect; /* # sectors/track including spares */ + __u32 s_interleave; /* hardware sector interleave */ + __u32 s_trackskew; /* sector 0 skew, per track */ + __u32 s_csaddr; /* blk addr of cyl grp summary area */ + __u32 s_cssize; /* size of cyl grp summary area */ + __u32 s_cgsize; /* cylinder group size */ + __u32 s_ntrak; /* tracks per cylinder */ + __u32 s_nsect; /* sectors per track */ + __u32 s_spc; /* sectors per cylinder */ + __u32 s_ipg; /* inodes per group */ + __u32 s_fpg; /* fragments per group */ + __u32 s_cpc; /* cyl per cycle in postbl */ + __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */ + __s64 s_qbmask; /* ~usb_bmask */ + __s64 s_qfmask; /* ~usb_fmask */ + __s32 s_postblformat; /* format of positional layout tables */ + __s32 s_nrpos; /* number of rotational positions */ + __s32 s_postbloff; /* (__s16) rotation block list head */ + __s32 s_rotbloff; /* (__u8) blocks for each rotation */ + + __u32 s_fpbmask; /* fragments per block mask */ + __u32 s_apb; /* address per block */ + __u32 s_2apb; /* address per block^2 */ + __u32 s_3apb; /* address per block^3 */ + __u32 s_apbmask; /* address per block mask */ + __u32 s_apbshift; /* address per block shift */ + __u32 s_2apbshift; /* address per block shift * 2 */ + __u32 s_3apbshift; /* address per block shift * 3 */ + __u32 s_nspfshift; /* number of sector per fragment shift */ + __u32 s_nspb; /* number of sector per block */ + __u32 s_inopf; /* inodes per fragment */ + __u32 s_sbbase; /* offset of NeXTstep superblock */ + __u32 s_bpf; /* bits per fragment */ + __u32 s_bpfshift; /* bits per fragment shift*/ + __u32 s_bpfmask; /* bits per fragment mask */ + + __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */ +}; + +/* + * Sizes of this structures are: + * ufs_super_block_first 512 + * ufs_super_block_second 512 + * ufs_super_block_third 356 + */ +struct ufs_super_block_first { + __u32 fs_link; + __u32 fs_rlink; + __u32 fs_sblkno; + __u32 fs_cblkno; + __u32 fs_iblkno; + __u32 fs_dblkno; + __u32 fs_cgoffset; + __u32 fs_cgmask; + __u32 fs_time; + __u32 fs_size; + __u32 fs_dsize; + __u32 fs_ncg; + __u32 fs_bsize; + __u32 fs_fsize; + __u32 fs_frag; + __u32 fs_minfree; + __u32 fs_rotdelay; + __u32 fs_rps; + __u32 fs_bmask; + __u32 fs_fmask; + __u32 fs_bshift; + __u32 fs_fshift; + __u32 fs_maxcontig; + __u32 fs_maxbpg; + __u32 fs_fragshift; + __u32 fs_fsbtodb; + __u32 fs_sbsize; + __u32 fs_csmask; + __u32 fs_csshift; + __u32 fs_nindir; + __u32 fs_inopb; + __u32 fs_nspf; + __u32 fs_optim; + union { + struct { + __u32 fs_npsect; + } fs_sun; + struct { + __s32 fs_state; + } fs_sunx86; + } fs_u1; + __u32 fs_interleave; + __u32 fs_trackskew; + __u32 fs_id[2]; + __u32 fs_csaddr; + __u32 fs_cssize; + __u32 fs_cgsize; + __u32 fs_ntrak; + __u32 fs_nsect; + __u32 fs_spc; + __u32 fs_ncyl; + __u32 fs_cpg; + __u32 fs_ipg; + __u32 fs_fpg; + struct ufs_csum fs_cstotal; + __s8 fs_fmod; + __s8 fs_clean; + __s8 fs_ronly; + __s8 fs_flags; + __s8 fs_fsmnt[UFS_MAXMNTLEN - 212]; + +}; + +struct ufs_super_block_second { + __s8 fs_fsmnt[212]; + __u32 fs_cgrotor; + __u32 fs_csp[UFS_MAXCSBUFS]; + __u32 fs_maxcluster; + __u32 fs_cpc; + __u16 fs_opostbl[82]; +}; + +struct ufs_super_block_third { + __u16 fs_opostbl[46]; + union { + struct { + __s32 fs_sparecon[53];/* reserved for future constants */ + __s32 fs_reclaim; + __s32 fs_sparecon2[1]; + __s32 fs_state; /* file system state time stamp */ + __u32 fs_qbmask[2]; /* ~usb_bmask */ + __u32 fs_qfmask[2]; /* ~usb_fmask */ + } fs_sun; + struct { + __s32 fs_sparecon[53];/* reserved for future constants */ + __s32 fs_reclaim; + __s32 fs_sparecon2[1]; + __u32 fs_npsect; /* # sectors/track including spares */ + __u32 fs_qbmask[2]; /* ~usb_bmask */ + __u32 fs_qfmask[2]; /* ~usb_fmask */ + } fs_sunx86; + struct { + __s32 fs_sparecon[50];/* reserved for future constants */ + __s32 fs_contigsumsize;/* size of cluster summary array */ + __s32 fs_maxsymlinklen;/* max length of an internal symlink */ + __s32 fs_inodefmt; /* format of on-disk inodes */ + __u32 fs_maxfilesize[2]; /* max representable file size */ + __u32 fs_qbmask[2]; /* ~usb_bmask */ + __u32 fs_qfmask[2]; /* ~usb_fmask */ + __s32 fs_state; /* file system state time stamp */ + } fs_44; + } fs_u2; + __s32 fs_postblformat; + __s32 fs_nrpos; + __s32 fs_postbloff; + __s32 fs_rotbloff; + __s32 fs_magic; + __u8 fs_space[1]; +}; + #ifdef __KERNEL__ /* balloc.c */ @@ -539,7 +751,7 @@ /* ialloc.c */ extern void ufs_free_inode (struct inode *inode); -extern struct inode * ufs_new_inode (const struct inode *, int); +extern struct inode * ufs_new_inode (struct inode *, int); /* inode.c */ extern int ufs_frag_map (struct inode *, int); @@ -566,6 +778,13 @@ /* truncate.c */ extern void ufs_truncate (struct inode *); + +#include + +static inline struct ufs_inode_info *UFS_I(struct inode *inode) +{ + return list_entry(inode, struct ufs_inode_info, vfs_inode); +} #endif /* __KERNEL__ */ diff -urN linux-2.5.3-pre2/include/linux/ufs_fs_i.h linux/include/linux/ufs_fs_i.h --- linux-2.5.3-pre2/include/linux/ufs_fs_i.h Mon Nov 19 14:55:46 2001 +++ linux/include/linux/ufs_fs_i.h Mon Jan 21 17:23:24 2002 @@ -26,6 +26,7 @@ __u32 i_oeftflag; __u16 i_osync; __u32 i_lastfrag; + struct inode vfs_inode; }; #endif /* _LINUX_UFS_FS_I_H */ diff -urN linux-2.5.3-pre2/include/linux/ufs_fs_sb.h linux/include/linux/ufs_fs_sb.h --- linux-2.5.3-pre2/include/linux/ufs_fs_sb.h Mon Jan 14 13:27:06 2002 +++ linux/include/linux/ufs_fs_sb.h Mon Jan 21 17:23:24 2002 @@ -14,107 +14,15 @@ #ifndef __LINUX_UFS_FS_SB_H #define __LINUX_UFS_FS_SB_H -#include - -/* - * This structure is used for reading disk structures larger - * than the size of fragment. - */ -struct ufs_buffer_head { - unsigned fragment; /* first fragment */ - unsigned count; /* number of fragments */ - struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */ -}; - -struct ufs_cg_private_info { - struct ufs_cylinder_group ucg; - __u32 c_cgx; /* number of cylidner group */ - __u16 c_ncyl; /* number of cyl's this cg */ - __u16 c_niblk; /* number of inode blocks this cg */ - __u32 c_ndblk; /* number of data blocks this cg */ - __u32 c_rotor; /* position of last used block */ - __u32 c_frotor; /* position of last used frag */ - __u32 c_irotor; /* position of last used inode */ - __u32 c_btotoff; /* (__u32) block totals per cylinder */ - __u32 c_boff; /* (short) free block positions */ - __u32 c_iusedoff; /* (char) used inode map */ - __u32 c_freeoff; /* (u_char) free block map */ - __u32 c_nextfreeoff; /* (u_char) next available space */ - __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */ - __u32 c_clusteroff; /* (u_int8) free cluster map */ - __u32 c_nclusterblks; /* number of clusters this cg */ -}; - -struct ufs_sb_private_info { - struct ufs_buffer_head s_ubh; /* buffer containing super block */ - __u32 s_sblkno; /* offset of super-blocks in filesys */ - __u32 s_cblkno; /* offset of cg-block in filesys */ - __u32 s_iblkno; /* offset of inode-blocks in filesys */ - __u32 s_dblkno; /* offset of first data after cg */ - __u32 s_cgoffset; /* cylinder group offset in cylinder */ - __u32 s_cgmask; /* used to calc mod fs_ntrak */ - __u32 s_size; /* number of blocks (fragments) in fs */ - __u32 s_dsize; /* number of data blocks in fs */ - __u32 s_ncg; /* number of cylinder groups */ - __u32 s_bsize; /* size of basic blocks */ - __u32 s_fsize; /* size of fragments */ - __u32 s_fpb; /* fragments per block */ - __u32 s_minfree; /* minimum percentage of free blocks */ - __u32 s_bmask; /* `blkoff'' calc of blk offsets */ - __u32 s_fmask; /* s_fsize mask */ - __u32 s_bshift; /* `lblkno'' calc of logical blkno */ - __u32 s_fshift; /* s_fsize shift */ - __u32 s_fpbshift; /* fragments per block shift */ - __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */ - __u32 s_sbsize; /* actual size of super block */ - __u32 s_csmask; /* csum block offset */ - __u32 s_csshift; /* csum block number */ - __u32 s_nindir; /* value of NINDIR */ - __u32 s_inopb; /* value of INOPB */ - __u32 s_nspf; /* value of NSPF */ - __u32 s_npsect; /* # sectors/track including spares */ - __u32 s_interleave; /* hardware sector interleave */ - __u32 s_trackskew; /* sector 0 skew, per track */ - __u32 s_csaddr; /* blk addr of cyl grp summary area */ - __u32 s_cssize; /* size of cyl grp summary area */ - __u32 s_cgsize; /* cylinder group size */ - __u32 s_ntrak; /* tracks per cylinder */ - __u32 s_nsect; /* sectors per track */ - __u32 s_spc; /* sectors per cylinder */ - __u32 s_ipg; /* inodes per group */ - __u32 s_fpg; /* fragments per group */ - __u32 s_cpc; /* cyl per cycle in postbl */ - __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */ - __s64 s_qbmask; /* ~usb_bmask */ - __s64 s_qfmask; /* ~usb_fmask */ - __s32 s_postblformat; /* format of positional layout tables */ - __s32 s_nrpos; /* number of rotational positions */ - __s32 s_postbloff; /* (__s16) rotation block list head */ - __s32 s_rotbloff; /* (__u8) blocks for each rotation */ - - __u32 s_fpbmask; /* fragments per block mask */ - __u32 s_apb; /* address per block */ - __u32 s_2apb; /* address per block^2 */ - __u32 s_3apb; /* address per block^3 */ - __u32 s_apbmask; /* address per block mask */ - __u32 s_apbshift; /* address per block shift */ - __u32 s_2apbshift; /* address per block shift * 2 */ - __u32 s_3apbshift; /* address per block shift * 3 */ - __u32 s_nspfshift; /* number of sector per fragment shift */ - __u32 s_nspb; /* number of sector per block */ - __u32 s_inopf; /* inodes per fragment */ - __u32 s_sbbase; /* offset of NeXTstep superblock */ - __u32 s_bpf; /* bits per fragment */ - __u32 s_bpfshift; /* bits per fragment shift*/ - __u32 s_bpfmask; /* bits per fragment mask */ - - __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */ -}; - #define UFS_MAX_GROUP_LOADED 8 #define UFS_CGNO_EMPTY ((unsigned)-1) +struct ufs_sb_private_info; +struct ufs_cg_private_info; +struct ufs_csum; +#define UFS_MAXCSBUFS 31 + struct ufs_sb_info { struct ufs_sb_private_info * s_uspi; struct ufs_csum * s_csp[UFS_MAXCSBUFS]; @@ -127,121 +35,4 @@ unsigned s_mount_opt; }; -/* - * Sizes of this structures are: - * ufs_super_block_first 512 - * ufs_super_block_second 512 - * ufs_super_block_third 356 - */ -struct ufs_super_block_first { - __u32 fs_link; - __u32 fs_rlink; - __u32 fs_sblkno; - __u32 fs_cblkno; - __u32 fs_iblkno; - __u32 fs_dblkno; - __u32 fs_cgoffset; - __u32 fs_cgmask; - __u32 fs_time; - __u32 fs_size; - __u32 fs_dsize; - __u32 fs_ncg; - __u32 fs_bsize; - __u32 fs_fsize; - __u32 fs_frag; - __u32 fs_minfree; - __u32 fs_rotdelay; - __u32 fs_rps; - __u32 fs_bmask; - __u32 fs_fmask; - __u32 fs_bshift; - __u32 fs_fshift; - __u32 fs_maxcontig; - __u32 fs_maxbpg; - __u32 fs_fragshift; - __u32 fs_fsbtodb; - __u32 fs_sbsize; - __u32 fs_csmask; - __u32 fs_csshift; - __u32 fs_nindir; - __u32 fs_inopb; - __u32 fs_nspf; - __u32 fs_optim; - union { - struct { - __u32 fs_npsect; - } fs_sun; - struct { - __s32 fs_state; - } fs_sunx86; - } fs_u1; - __u32 fs_interleave; - __u32 fs_trackskew; - __u32 fs_id[2]; - __u32 fs_csaddr; - __u32 fs_cssize; - __u32 fs_cgsize; - __u32 fs_ntrak; - __u32 fs_nsect; - __u32 fs_spc; - __u32 fs_ncyl; - __u32 fs_cpg; - __u32 fs_ipg; - __u32 fs_fpg; - struct ufs_csum fs_cstotal; - __s8 fs_fmod; - __s8 fs_clean; - __s8 fs_ronly; - __s8 fs_flags; - __s8 fs_fsmnt[UFS_MAXMNTLEN - 212]; - -}; - -struct ufs_super_block_second { - __s8 fs_fsmnt[212]; - __u32 fs_cgrotor; - __u32 fs_csp[UFS_MAXCSBUFS]; - __u32 fs_maxcluster; - __u32 fs_cpc; - __u16 fs_opostbl[82]; -}; - -struct ufs_super_block_third { - __u16 fs_opostbl[46]; - union { - struct { - __s32 fs_sparecon[53];/* reserved for future constants */ - __s32 fs_reclaim; - __s32 fs_sparecon2[1]; - __s32 fs_state; /* file system state time stamp */ - __u32 fs_qbmask[2]; /* ~usb_bmask */ - __u32 fs_qfmask[2]; /* ~usb_fmask */ - } fs_sun; - struct { - __s32 fs_sparecon[53];/* reserved for future constants */ - __s32 fs_reclaim; - __s32 fs_sparecon2[1]; - __u32 fs_npsect; /* # sectors/track including spares */ - __u32 fs_qbmask[2]; /* ~usb_bmask */ - __u32 fs_qfmask[2]; /* ~usb_fmask */ - } fs_sunx86; - struct { - __s32 fs_sparecon[50];/* reserved for future constants */ - __s32 fs_contigsumsize;/* size of cluster summary array */ - __s32 fs_maxsymlinklen;/* max length of an internal symlink */ - __s32 fs_inodefmt; /* format of on-disk inodes */ - __u32 fs_maxfilesize[2]; /* max representable file size */ - __u32 fs_qbmask[2]; /* ~usb_bmask */ - __u32 fs_qfmask[2]; /* ~usb_fmask */ - __s32 fs_state; /* file system state time stamp */ - } fs_44; - } fs_u2; - __s32 fs_postblformat; - __s32 fs_nrpos; - __s32 fs_postbloff; - __s32 fs_rotbloff; - __s32 fs_magic; - __u8 fs_space[1]; -}; - -#endif /* __LINUX_UFS_FS_SB_H */ +#endif diff -urN linux-2.5.3-pre2/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p --- linux-2.5.3-pre2/include/linux/umsdos_fs.p Wed Aug 2 12:06:11 2000 +++ linux/include/linux/umsdos_fs.p Mon Jan 21 17:23:24 2002 @@ -93,3 +93,8 @@ /* rdir.c 22/03/95 03.31.42 */ struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo); struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry); + +static inline struct umsdos_inode_info *UMSDOS_I(struct inode *inode) +{ + return &inode->u.umsdos_i; +} diff -urN linux-2.5.3-pre2/include/linux/usb.h linux/include/linux/usb.h --- linux-2.5.3-pre2/include/linux/usb.h Mon Jan 14 13:35:15 2002 +++ linux/include/linux/usb.h Mon Jan 21 17:23:24 2002 @@ -576,13 +576,12 @@ /* ... less overhead for QUEUE_BULK */ #define USB_TIMEOUT_KILLED 0x1000 /* only set by HCD! */ -typedef struct -{ +struct usb_iso_packet_descriptor { unsigned int offset; unsigned int length; /* expected length */ unsigned int actual_length; unsigned int status; -} iso_packet_descriptor_t; +}; struct urb; @@ -741,10 +740,8 @@ int timeout; /* (in) timeout, in jiffies */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ - iso_packet_descriptor_t iso_frame_desc[0]; /* (in) ISO ONLY */ + struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ }; - -typedef struct urb urb_t; /** * usb_fill_control_urb - initializes a control urb diff -urN linux-2.5.3-pre2/include/linux/usbdev_fs_i.h linux/include/linux/usbdev_fs_i.h --- linux-2.5.3-pre2/include/linux/usbdev_fs_i.h Mon Jan 10 11:32:48 2000 +++ linux/include/linux/usbdev_fs_i.h Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -struct usb_device; -struct usb_bus; - -struct usbdev_inode_info { - struct list_head dlist; - struct list_head slist; - union { - struct usb_device *dev; - struct usb_bus *bus; - } p; -}; diff -urN linux-2.5.3-pre2/include/linux/usbdev_fs_sb.h linux/include/linux/usbdev_fs_sb.h --- linux-2.5.3-pre2/include/linux/usbdev_fs_sb.h Tue May 22 10:25:36 2001 +++ linux/include/linux/usbdev_fs_sb.h Wed Dec 31 16:00:00 1969 @@ -1,13 +0,0 @@ -struct usbdev_sb_info { - struct list_head slist; - struct list_head ilist; - uid_t devuid; - gid_t devgid; - umode_t devmode; - uid_t busuid; - gid_t busgid; - umode_t busmode; - uid_t listuid; - gid_t listgid; - umode_t listmode; -}; diff -urN linux-2.5.3-pre2/include/linux/wireless.h linux/include/linux/wireless.h --- linux-2.5.3-pre2/include/linux/wireless.h Mon Jan 14 13:28:48 2002 +++ linux/include/linux/wireless.h Mon Jan 21 17:23:24 2002 @@ -1,9 +1,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 12 5.10.01 + * Version : 13 6.12.01 * * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H @@ -11,6 +12,8 @@ /************************** DOCUMENTATION **************************/ /* + * Initial APIs (1996 -> onward) : + * ----------------------------- * Basically, the wireless extensions are for now a set of standard ioctl * call + /proc/net/wireless * @@ -27,16 +30,27 @@ * We have the list of command plus a structure descibing the * data exchanged... * Note that to add these ioctl, I was obliged to modify : - * net/core/dev.c (two place + add include) - * net/ipv4/af_inet.c (one place + add include) + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) * * /proc/net/wireless is a copy of /proc/net/dev. * We have a structure for data passed from the driver to /proc/net/wireless * Too add this, I've modified : - * net/core/dev.c (two other places) - * include/linux/netdevice.h (one place) - * include/linux/proc_fs.h (one place) + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2001 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h * + * Note as well that /proc/net/wireless implementation has now moved in : + * # include/linux/wireless.c + * + * Other comments : + * -------------- * Do not add here things that are redundant with other mechanisms * (drivers init, ifconfig, /proc/net/dev, ...) and with are not * wireless specific. @@ -54,16 +68,14 @@ #include /* for "struct sockaddr" et al */ #include /* for IFNAMSIZ and co... */ -/**************************** CONSTANTS ****************************/ - -/* --------------------------- VERSION --------------------------- */ +/***************************** VERSION *****************************/ /* * This constant is used to know the availability of the wireless * extensions and to know which version of wireless extensions it is * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ -#define WIRELESS_EXT 12 +#define WIRELESS_EXT 13 /* * Changes : @@ -123,12 +135,20 @@ * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space * - Add new statistics (frag, retry, beacon) * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT */ +/**************************** CONSTANTS ****************************/ + /* -------------------------- IOCTL LIST -------------------------- */ /* Basic operations */ -#define SIOCSIWNAME 0x8B00 /* Unused */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ #define SIOCSIWNWID 0x8B02 /* set network id (the cell) */ #define SIOCGIWNWID 0x8B03 /* get network id */ @@ -414,13 +434,49 @@ /* ------------------------ IOCTL REQUEST ------------------------ */ /* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + + struct sockaddr ap_addr; /* Access point address */ + + struct iw_point data; /* Other large parameters */ +}; + +/* * The structure to exchange data for ioctl. * This structure is the same as 'struct ifreq', but (re)defined for * convenience... - * - * Note that it should fit on the same memory footprint ! - * You should check this when increasing the above structures (16 octets) - * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * Do I need to remind you about structure size (32 octets) ? */ struct iwreq { @@ -429,35 +485,8 @@ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ } ifr_ifrn; - /* Data part */ - union - { - /* Config - generic */ - char name[IFNAMSIZ]; - /* Name : used to verify the presence of wireless extensions. - * Name of the protocol/provider... */ - - struct iw_point essid; /* Extended network name */ - struct iw_param nwid; /* network id (or domain - the cell) */ - struct iw_freq freq; /* frequency or channel : - * 0-1000 = channel - * > 1000 = frequency in Hz */ - - struct iw_param sens; /* signal level threshold */ - struct iw_param bitrate; /* default bit rate */ - struct iw_param txpower; /* default transmit power */ - struct iw_param rts; /* RTS threshold threshold */ - struct iw_param frag; /* Fragmentation threshold */ - __u32 mode; /* Operation mode */ - struct iw_param retry; /* Retry limits & lifetime */ - - struct iw_point encoding; /* Encoding stuff : tokens */ - struct iw_param power; /* PM duration/timeout */ - - struct sockaddr ap_addr; /* Access point address */ - - struct iw_point data; /* Other large parameters */ - } u; + /* Data part (defined just above) */ + union iwreq_data u; }; /* -------------------------- IOCTL DATA -------------------------- */ diff -urN linux-2.5.3-pre2/include/net/iw_handler.h linux/include/net/iw_handler.h --- linux-2.5.3-pre2/include/net/iw_handler.h Wed Dec 31 16:00:00 1969 +++ linux/include/net/iw_handler.h Mon Jan 21 17:23:24 2002 @@ -0,0 +1,374 @@ +/* + * This file define the new driver API for Wireless Extensions + * + * Version : 2 6.12.01 + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _IW_HANDLER_H +#define _IW_HANDLER_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial driver API (1996 -> onward) : + * ----------------------------------- + * The initial API just sends the IOCTL request received from user space + * to the driver (via the driver ioctl handler). The driver has to + * handle all the rest... + * + * The initial API also defines a specific handler in struct net_device + * to handle wireless statistics. + * + * The initial APIs served us well and has proven a reasonably good design. + * However, there is a few shortcommings : + * o No events, everything is a request to the driver. + * o Large ioctl function in driver with gigantic switch statement + * (i.e. spaghetti code). + * o Driver has to mess up with copy_to/from_user, and in many cases + * does it unproperly. Common mistakes are : + * * buffer overflows (no checks or off by one checks) + * * call copy_to/from_user with irq disabled + * o The user space interface is tied to ioctl because of the use + * copy_to/from_user. + * + * New driver API (2001 -> onward) : + * ------------------------------- + * The new driver API is just a bunch of standard functions (handlers), + * each handling a specific Wireless Extension. The driver just export + * the list of handler it supports, and those will be called apropriately. + * + * I tried to keep the main advantage of the previous API (simplicity, + * efficiency and light weight), and also I provide a good dose of backward + * compatibility (most structures are the same, driver can use both API + * simultaneously, ...). + * Hopefully, I've also addressed the shortcomming of the initial API. + * + * The advantage of the new API are : + * o Handling of Extensions in driver broken in small contained functions + * o Tighter checks of ioctl before calling the driver + * o Flexible commit strategy (at least, the start of it) + * o Backward compatibility (can be mixed with old API) + * o Driver doesn't have to worry about memory and user-space issues + * The last point is important for the following reasons : + * o You are now able to call the new driver API from any API you + * want (including from within other parts of the kernel). + * o Common mistakes are avoided (buffer overflow, user space copy + * with irq disabled and so on). + * + * The Drawback of the new API are : + * o bloat (especially kernel) + * o need to migrate existing drivers to new API + * My initial testing shows that the new API adds around 3kB to the kernel + * and save between 0 and 5kB from a typical driver. + * Also, as all structures and data types are unchanged, the migration is + * quite straightforward (but tedious). + * + * --- + * + * The new driver API is defined below in this file. User space should + * not be aware of what's happening down there... + * + * A new kernel wrapper is in charge of validating the IOCTLs and calling + * the appropriate driver handler. This is implemented in : + * # net/core/wireless.c + * + * The driver export the list of handlers in : + * # include/linux/netdevice.h (one place) + * + * The new driver API is available for WIRELESS_EXT >= 13. + * Good luck with migration to the new API ;-) + */ + +/* ---------------------- THE IMPLEMENTATION ---------------------- */ +/* + * Some of the choice I've made are pretty controversials. Defining an + * API is very much weighting compromises. This goes into some of the + * details and the thinking behind the implementation. + * + * Implementation goals : + * -------------------- + * The implementation goals were as follow : + * o Obvious : you should not need a PhD to understand what's happening, + * the benefit is easier maintainance. + * o Flexible : it should accomodate a wide variety of driver + * implementations and be as flexible as the old API. + * o Lean : it should be efficient memory wise to minimise the impact + * on kernel footprint. + * o Transparent to user space : the large number of user space + * applications that use Wireless Extensions should not need + * any modifications. + * + * Array of functions versus Struct of functions + * --------------------------------------------- + * 1) Having an array of functions allow the kernel code to access the + * handler in a single lookup, which is much more efficient (think hash + * table here). + * 2) The only drawback is that driver writer may put their handler in + * the wrong slot. This is trivial to test (I set the frequency, the + * bitrate changes). Once the handler is in the proper slot, it will be + * there forever, because the array is only extended at the end. + * 3) Backward/forward compatibility : adding new handler just require + * extending the array, so you can put newer driver in older kernel + * without having to patch the kernel code (and vice versa). + * + * All handler are of the same generic type + * ---------------------------------------- + * That's a feature !!! + * 1) Having a generic handler allow to have generic code, which is more + * efficient. If each of the handler was individually typed I would need + * to add a big switch in the kernel (== more bloat). This solution is + * more scalable, adding new Wireless Extensions doesn't add new code. + * 2) You can use the same handler in different slots of the array. For + * hardware, it may be more efficient or logical to handle multiple + * Wireless Extensions with a single function, and the API allow you to + * do that. (An example would be a single record on the card to control + * both bitrate and frequency, the handler would read the old record, + * modify it according to info->cmd and rewrite it). + * + * Functions prototype uses union iwreq_data + * ----------------------------------------- + * Some would have prefered functions defined this way : + * static int mydriver_ioctl_setrate(struct net_device *dev, + * long rate, int auto) + * 1) The kernel code doesn't "validate" the content of iwreq_data, and + * can't do it (different hardware may have different notion of what a + * valid frequency is), so we don't pretend that we do it. + * 2) The above form is not extendable. If I want to add a flag (for + * example to distinguish setting max rate and basic rate), I would + * break the prototype. Using iwreq_data is more flexible. + * 3) Also, the above form is not generic (see above). + * 4) I don't expect driver developper using the wrong field of the + * union (Doh !), so static typechecking doesn't add much value. + * 5) Lastly, you can skip the union by doing : + * static int mydriver_ioctl_setrate(struct net_device *dev, + * struct iw_request_info *info, + * struct iw_param *rrq, + * char *extra) + * And then adding the handler in the array like this : + * (iw_handler) mydriver_ioctl_setrate, // SIOCSIWRATE + * + * Using functions and not a registry + * ---------------------------------- + * Another implementation option would have been for every instance to + * define a registry (a struct containing all the Wireless Extensions) + * and only have a function to commit the registry to the hardware. + * 1) This approach can be emulated by the current code, but not + * vice versa. + * 2) Some drivers don't keep any configuration in the driver, for them + * adding such a registry would be a significant bloat. + * 3) The code to translate from Wireless Extension to native format is + * needed anyway, so it would not reduce significantely the amount of code. + * 4) The current approach only selectively translate Wireless Extensions + * to native format and only selectively set, whereas the registry approach + * would require to translate all WE and set all parameters for any single + * change. + * 5) For many Wireless Extensions, the GET operation return the current + * dynamic value, not the value that was set. + * + * This header is + * --------------------------------- + * 1) This header is kernel space only and should not be exported to + * user space. Headers in "include/linux/" are exported, headers in + * "include/net/" are not. + * + * Mixed 32/64 bit issues + * ---------------------- + * The Wireless Extensions are designed to be 64 bit clean, by using only + * datatypes with explicit storage size. + * There are some issues related to kernel and user space using different + * memory model, and in particular 64bit kernel with 32bit user space. + * The problem is related to struct iw_point, that contains a pointer + * that *may* need to be translated. + * This is quite messy. The new API doesn't solve this problem (it can't), + * but is a step in the right direction : + * 1) Meta data about each ioctl is easily available, so we know what type + * of translation is needed. + * 2) The move of data between kernel and user space is only done in a single + * place in the kernel, so adding specific hooks in there is possible. + * 3) In the long term, it allows to move away from using ioctl as the + * user space API. + * + * So many comments and so few code + * -------------------------------- + * That's a feature. Comments won't bloat the resulting kernel binary. + */ + +/***************************** INCLUDES *****************************/ + +#include /* IOCTL user space API */ + +/***************************** VERSION *****************************/ +/* + * This constant is used to know which version of the driver API is + * available. Hopefully, this will be pretty stable and no changes + * will be needed... + * I just plan to increment with each new version. + */ +#define IW_HANDLER_VERSION 2 + +/**************************** CONSTANTS ****************************/ + +/* Special error message for the driver to indicate that we + * should do a commit after return from the iw_handler */ +#define EIWCOMMIT EINPROGRESS + +/* Flags available in struct iw_request_info */ +#define IW_REQUEST_FLAG_NONE 0x0000 /* No flag so far */ + +/* Type of headers we know about (basically union iwreq_data) */ +#define IW_HEADER_TYPE_NULL 0 /* Not available */ +#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ +#define IW_HEADER_TYPE_UINT 4 /* __u32 */ +#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ +#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ +#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ +#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ + +/* Handling flags */ +/* Most are not implemented. I just use them as a reminder of some + * cool features we might need one day ;-) */ +#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */ +/* Wrapper level flags */ +#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ +#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ +#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */ +/* Driver level flags */ +#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ + +/****************************** TYPES ******************************/ + +/* ----------------------- WIRELESS HANDLER ----------------------- */ +/* + * A wireless handler is just a standard function, that looks like the + * ioctl handler. + * We also define there how a handler list look like... As the Wireless + * Extension space is quite dense, we use a simple array, which is faster + * (that's the perfect hash table ;-). + */ + +/* + * Meta data about the request passed to the iw_handler. + * Most handlers can safely ignore what's in there. + * The 'cmd' field might come handy if you want to use the same handler + * for multiple command... + * This struct is also my long term insurance. I can add new fields here + * without breaking the prototype of iw_handler... + */ +struct iw_request_info +{ + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; + +/* + * This is how a function handling a Wireless Extension should look + * like (both get and set, standard and private). + */ +typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +/* + * This define all the handler that the driver export. + * As you need only one per driver type, please use a static const + * shared by all driver instances... Same for the members... + * This will be linked from net_device in + */ +struct iw_handler_def +{ + /* Number of handlers defined (more precisely, index of the + * last defined handler + 1) */ + __u16 num_standard; + __u16 num_private; + /* Number of private arg description */ + __u16 num_private_args; + + /* Array of handlers for standard ioctls + * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] + */ + iw_handler * standard; + + /* Array of handlers for private ioctls + * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] + */ + iw_handler * private; + + /* Arguments of private handler. This one is just a list, so you + * can put it in any order you want and should not leave holes... + * We will automatically export that to user space... */ + struct iw_priv_args * private_args; + + /* In the long term, get_wireless_stats will move from + * 'struct net_device' to here, to minimise bloat. */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Currently we don't support events, so let's just plan for the + * future... + */ + +/* + * A Wireless Event. + */ +// How do we define short header ? We don't want a flag on length. +// Probably a flag on event ? Highest bit to zero... +struct iw_event +{ + __u16 length; /* Lenght of this stuff */ + __u16 event; /* Wireless IOCTL */ + union iwreq_data header; /* IOCTL fixed payload */ + char extra[0]; /* Optional IOCTL data */ +}; + +/* ---------------------- IOCTL DESCRIPTION ---------------------- */ +/* + * One of the main goal of the new interface is to deal entirely with + * user space/kernel space memory move. + * For that, we need to know : + * o if iwreq is a pointer or contain the full data + * o what is the size of the data to copy + * + * For private IOCTLs, we use the same rules as used by iwpriv and + * defined in struct iw_priv_args. + * + * For standard IOCTLs, things are quite different and we need to + * use the stuctures below. Actually, this struct is also more + * efficient, but that's another story... + */ + +/* + * Describe how a standard IOCTL looks like. + */ +struct iw_ioctl_description +{ + __u8 header_type; /* NULL, iw_point or other */ + __u8 token_type; /* Future */ + __u16 token_size; /* Granularity of payload */ + __u16 min_tokens; /* Min acceptable token number */ + __u16 max_tokens; /* Max acceptable token number */ + __u32 flags; /* Special handling of the request */ +}; + +/* Need to think of short header translation table. Later. */ + +/**************************** PROTOTYPES ****************************/ +/* + * Functions part of the Wireless Extensions (defined in net/core/wireless.c). + * Those may be called only within the kernel. + */ + +/* First : function strictly used inside the kernel */ + +/* Handle /proc/net/wireless, called in net/code/dev.c */ +extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, + int length); + +/* Handle IOCTLs, called in net/code/dev.c */ +extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); + +/* Second : functions that may be called by driver modules */ +/* None yet */ + +#endif /* _LINUX_WIRELESS_H */ diff -urN linux-2.5.3-pre2/init/main.c linux/init/main.c --- linux-2.5.3-pre2/init/main.c Wed Jan 9 09:45:17 2002 +++ linux/init/main.c Mon Jan 21 17:23:24 2002 @@ -290,8 +290,6 @@ extern void setup_arch(char **); extern void cpu_idle(void); -unsigned long wait_init_idle; - #ifndef CONFIG_SMP #ifdef CONFIG_X86_LOCAL_APIC @@ -305,6 +303,16 @@ #else +static unsigned long __initdata wait_init_idle; + +void __init idle_startup_done(void) +{ + clear_bit(smp_processor_id(), &wait_init_idle); + while (wait_init_idle) { + cpu_relax(); + barrier(); + } +} /* Called by boot processor to activate the rest. */ static void __init smp_init(void) @@ -315,6 +323,7 @@ smp_threads_ready=1; smp_commence(); + idle_startup_done(); } #endif @@ -411,20 +420,13 @@ check_bugs(); printk("POSIX conformance testing by UNIFIX\n"); + init_idle(); /* * We count on the initial thread going ok * Like idlers init is an unlocked kernel thread, which will * make syscalls (and thus be locked). */ smp_init(); - - /* - * Finally, we wait for all other CPU's, and initialize this - * thread that will become the idle thread for the boot CPU. - * After this, the scheduler is fully initialized, and we can - * start creating and running new threads. - */ - init_idle(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); diff -urN linux-2.5.3-pre2/kernel/fork.c linux/kernel/fork.c --- linux-2.5.3-pre2/kernel/fork.c Mon Jan 21 17:22:37 2002 +++ linux/kernel/fork.c Mon Jan 21 17:23:24 2002 @@ -747,23 +747,16 @@ if (p->ptrace & PT_PTRACED) send_sig(SIGSTOP, p, 1); -#define RUN_CHILD_FIRST 1 -#if RUN_CHILD_FIRST wake_up_forked_process(p); /* do this last */ -#else - wake_up_process(p); /* do this last */ -#endif ++total_forks; if (clone_flags & CLONE_VFORK) wait_for_completion(&vfork); -#if RUN_CHILD_FIRST else /* * Let the child process run first, to avoid most of the * COW overhead when the child exec()s afterwards. */ current->need_resched = 1; -#endif fork_out: return retval; diff -urN linux-2.5.3-pre2/kernel/sched.c linux/kernel/sched.c --- linux-2.5.3-pre2/kernel/sched.c Mon Jan 21 17:22:37 2002 +++ linux/kernel/sched.c Mon Jan 21 17:23:24 2002 @@ -37,11 +37,7 @@ * * Locking rule: those places that want to lock multiple runqueues * (such as the load balancing or the process migration code), lock - * acquire operations must be ordered by the runqueue's cpu id. - * - * The RT event id is used to avoid calling into the the RT scheduler - * if there is a RT task active in an SMP system but there is no - * RT scheduling activity otherwise. + * acquire operations must be ordered by ascending &runqueue. */ struct runqueue { spinlock_t lock; @@ -57,24 +53,28 @@ #define this_rq() cpu_rq(smp_processor_id()) #define task_rq(p) cpu_rq((p)->cpu) #define cpu_curr(cpu) (cpu_rq(cpu)->curr) -#define rq_cpu(rq) ((rq) - runqueues) #define rt_task(p) ((p)->policy != SCHED_OTHER) -#define lock_task_rq(rq,p,flags) \ -do { \ -repeat_lock_task: \ - rq = task_rq(p); \ - spin_lock_irqsave(&rq->lock, flags); \ - if (unlikely(rq_cpu(rq) != (p)->cpu)) { \ - spin_unlock_irqrestore(&rq->lock, flags); \ - goto repeat_lock_task; \ - } \ -} while (0) +static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags) +{ + struct runqueue *__rq; -#define unlock_task_rq(rq,p,flags) \ - spin_unlock_irqrestore(&rq->lock, flags) +repeat_lock_task: + __rq = task_rq(p); + spin_lock_irqsave(&__rq->lock, *flags); + if (unlikely(__rq != task_rq(p))) { + spin_unlock_irqrestore(&__rq->lock, *flags); + goto repeat_lock_task; + } + return __rq; +} +static inline void unlock_task_rq(runqueue_t *rq, task_t *p, + unsigned long *flags) +{ + spin_unlock_irqrestore(&rq->lock, *flags); +} /* * Adding/removing a task to/from a priority array: */ @@ -95,21 +95,26 @@ } /* - * A task is 'heavily interactive' if it has reached the - * bottom 25% of the SCHED_OTHER priority range - in this + * A task is 'heavily interactive' if it either has reached the + * bottom 25% of the SCHED_OTHER priority range, or if it is below + * its default priority by at least 3 priority levels. In this * case we favor it by reinserting it on the active array, * even after it expired its current timeslice. * + * A task is a 'CPU hog' if it's either in the upper 25% of the + * SCHED_OTHER priority range, or if's not an interactive task. + * * A task can get a priority bonus by being 'somewhat * interactive' - and it will get a priority penalty for * being a CPU hog. * - * CPU-hog penalties cannot go more than 5 above the default - * priority level. Priority bonus cannot go below the minimum - * priority level. */ -#define PRIO_INTERACTIVE (MAX_RT_PRIO + MAX_USER_PRIO/3) -#define TASK_INTERACTIVE(p) ((p)->prio <= PRIO_INTERACTIVE) +#define PRIO_INTERACTIVE (MAX_RT_PRIO + MAX_USER_PRIO/4) +#define PRIO_CPU_HOG (MAX_RT_PRIO + MAX_USER_PRIO*3/4) + +#define TASK_INTERACTIVE(p) (((p)->prio <= PRIO_INTERACTIVE) || \ + (((p)->prio < PRIO_CPU_HOG) && \ + ((p)->prio <= NICE_TO_PRIO((p)->__nice)-3))) static inline int effective_prio(task_t *p) { @@ -117,9 +122,18 @@ /* * Here we scale the actual sleep average [0 .... MAX_SLEEP_AVG] - * into the 20 ... -20 bonus/penalty range. + * into the 19 ... -18 bonus/penalty range. + * + * We take off the 10% from the full 0...39 priority range so that: + * + * 1) nice +19 CPU hogs do not preempt nice 0 CPU hogs just yet. + * 2) nice -20 interactive tasks do not get preempted by + * nice 0 interactive tasks. + * + * Both properties are important to certain applications. */ - bonus = MAX_USER_PRIO * p->sleep_avg / MAX_SLEEP_AVG - MAX_USER_PRIO/2; + bonus = MAX_USER_PRIO*9/10 * p->sleep_avg / MAX_SLEEP_AVG - + MAX_USER_PRIO*9/10/2; prio = NICE_TO_PRIO(p->__nice) - bonus; if (prio < MAX_RT_PRIO) prio = MAX_RT_PRIO; @@ -130,9 +144,10 @@ static inline void activate_task(task_t *p, runqueue_t *rq) { + unsigned long sleep_time = jiffies - p->sleep_timestamp; prio_array_t *array = rq->active; - if (!rt_task(p)) { + if (!rt_task(p) && sleep_time) { /* * This code gives a bonus to interactive tasks. We update * an 'average sleep time' value here, based on @@ -140,7 +155,7 @@ * the higher the average gets - and the higher the priority * boost gets as well. */ - p->sleep_avg += jiffies - p->sleep_timestamp; + p->sleep_avg += sleep_time; if (p->sleep_avg > MAX_SLEEP_AVG) p->sleep_avg = MAX_SLEEP_AVG; p->prio = effective_prio(p); @@ -185,12 +200,26 @@ cpu_relax(); barrier(); } - lock_task_rq(rq, p, flags); + rq = lock_task_rq(p, &flags); if (unlikely(rq->curr == p)) { - unlock_task_rq(rq, p, flags); + unlock_task_rq(rq, p, &flags); goto repeat; } - unlock_task_rq(rq, p, flags); + unlock_task_rq(rq, p, &flags); +} + +/* + * The SMP message passing code calls this function whenever + * the new task has arrived at the target CPU. We move the + * new task into the local runqueue. + * + * This function must be called with interrupts disabled. + */ +void sched_task_migrated(task_t *new_task) +{ + wait_task_inactive(new_task); + new_task->cpu = smp_processor_id(); + wake_up_process(new_task); } /* @@ -223,7 +252,7 @@ int success = 0; runqueue_t *rq; - lock_task_rq(rq, p, flags); + rq = lock_task_rq(p, &flags); p->state = TASK_RUNNING; if (!p->array) { activate_task(p, rq); @@ -231,11 +260,11 @@ resched_task(rq->curr); success = 1; } - unlock_task_rq(rq, p, flags); + unlock_task_rq(rq, p, &flags); return success; } -inline int wake_up_process(task_t * p) +int wake_up_process(task_t * p) { return try_to_wake_up(p, 0); } @@ -246,9 +275,8 @@ p->state = TASK_RUNNING; if (!rt_task(p)) { - p->prio += MAX_USER_PRIO/10; - if (p->prio > MAX_PRIO-1) - p->prio = MAX_PRIO-1; + p->sleep_avg = (p->sleep_avg * 4) / 5; + p->prio = effective_prio(p); } spin_lock_irq(&rq->lock); activate_task(p, rq); @@ -312,18 +340,6 @@ return sum; } -static inline unsigned long max_rq_len(void) -{ - unsigned long i, curr, max = 0; - - for (i = 0; i < smp_num_cpus; i++) { - curr = cpu_rq(i)->nr_running; - if (curr > max) - max = curr; - } - return max; -} - /* * Current runqueue is empty, or rebalance tick: if there is an * inbalance (current runqueue is too short) then pull from @@ -406,7 +422,7 @@ * Ok, lets do some actual balancing: */ - if (rq_cpu(busiest) < this_cpu) { + if (busiest < this_rq) { spin_unlock(&this_rq->lock); spin_lock(&busiest->lock); spin_lock(&this_rq->lock); @@ -492,8 +508,7 @@ static inline void idle_tick(void) { - if ((jiffies % IDLE_REBALANCE_TICK) || - likely(this_rq()->curr == NULL)) + if (jiffies % IDLE_REBALANCE_TICK) return; spin_lock(&this_rq()->lock); load_balance(this_rq(), 1); @@ -509,7 +524,7 @@ unsigned long now = jiffies; runqueue_t *rq = this_rq(); - if (p == rq->idle || !rq->idle) + if (p == rq->idle) return idle_tick(); /* Task might have expired already, but not scheduled off yet */ if (p->array != rq->active) { @@ -538,7 +553,7 @@ * do not update a process's priority until it either * goes to sleep or uses up its timeslice. This makes * it possible for interactive tasks to use up their - * timeslices at their high priority levels. + * timeslices at their highest priority levels. */ if (p->sleep_avg) p->sleep_avg--; @@ -562,29 +577,26 @@ */ asmlinkage void schedule(void) { - task_t *prev, *next; + task_t *prev = current, *next; + runqueue_t *rq = this_rq(); prio_array_t *array; - runqueue_t *rq; list_t *queue; int idx; if (unlikely(in_interrupt())) BUG(); -need_resched_back: - prev = current; release_kernel_lock(prev, smp_processor_id()); - rq = this_rq(); spin_lock_irq(&rq->lock); switch (prev->state) { - case TASK_INTERRUPTIBLE: - if (unlikely(signal_pending(prev))) { - prev->state = TASK_RUNNING; - break; - } - default: - deactivate_task(prev, rq); - case TASK_RUNNING: + case TASK_INTERRUPTIBLE: + if (unlikely(signal_pending(prev))) { + prev->state = TASK_RUNNING; + break; + } + default: + deactivate_task(prev, rq); + case TASK_RUNNING: } pick_next_task: if (unlikely(!rq->nr_running)) { @@ -615,7 +627,6 @@ if (likely(prev != next)) { rq->nr_switches++; rq->curr = next; - next->cpu = prev->cpu; context_switch(prev, next); /* * The runqueue pointer might be from another CPU @@ -628,8 +639,6 @@ spin_unlock_irq(&rq->lock); reacquire_kernel_lock(current); - if (need_resched()) - goto need_resched_back; return; } @@ -782,47 +791,23 @@ */ void set_cpus_allowed(task_t *p, unsigned long new_mask) { - runqueue_t *this_rq = this_rq(), *target_rq; - unsigned long this_mask = 1UL << smp_processor_id(); - int target_cpu; - new_mask &= cpu_online_map; if (!new_mask) BUG(); + p->cpus_allowed = new_mask; /* * Can the task run on the current CPU? If not then * migrate the process off to a proper CPU. */ - if (new_mask & this_mask) + if (new_mask & (1UL << smp_processor_id())) return; - target_cpu = ffz(~new_mask); - target_rq = cpu_rq(target_cpu); - if (target_cpu < smp_processor_id()) { - spin_lock_irq(&target_rq->lock); - spin_lock(&this_rq->lock); - } else { - spin_lock_irq(&this_rq->lock); - spin_lock(&target_rq->lock); - } - dequeue_task(p, p->array); - this_rq->nr_running--; - target_rq->nr_running++; - enqueue_task(p, target_rq->active); - target_rq->curr->need_resched = 1; - spin_unlock(&target_rq->lock); +#if CONFIG_SMP + smp_migrate_task(ffz(~new_mask), current); - /* - * The easiest solution is to context switch into - * the idle thread - which will pick the best task - * afterwards: - */ - this_rq->nr_switches++; - this_rq->curr = this_rq->idle; - this_rq->idle->need_resched = 1; - context_switch(current, this_rq->idle); - barrier(); - spin_unlock_irq(&this_rq()->lock); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); +#endif } void scheduling_functions_end_here(void) { } @@ -839,7 +824,7 @@ * We have to be careful, if called from sys_setpriority(), * the task might be in the middle of scheduling on another CPU. */ - lock_task_rq(rq, p, flags); + rq = lock_task_rq(p, &flags); if (rt_task(p)) { p->__nice = nice; goto out_unlock; @@ -853,7 +838,7 @@ if (array) { enqueue_task(p, array); /* - * If the task is runnable and lowered its priority, + * If the task is running and lowered its priority, * or increased its priority then reschedule its CPU: */ if ((nice < p->__nice) || @@ -861,7 +846,7 @@ resched_task(rq->curr); } out_unlock: - unlock_task_rq(rq, p, flags); + unlock_task_rq(rq, p, &flags); } #ifndef __alpha__ @@ -938,7 +923,7 @@ * To be able to change p->policy safely, the apropriate * runqueue lock must be held. */ - lock_task_rq(rq,p,flags); + rq = lock_task_rq(p, &flags); if (policy < 0) policy = p->policy; @@ -981,7 +966,7 @@ activate_task(p, task_rq(p)); out_unlock: - unlock_task_rq(rq,p,flags); + unlock_task_rq(rq, p, &flags); out_unlock_tasklist: read_unlock_irq(&tasklist_lock); @@ -1224,14 +1209,12 @@ read_unlock(&tasklist_lock); } -extern unsigned long wait_init_idle; - static inline void double_rq_lock(runqueue_t *rq1, runqueue_t *rq2) { if (rq1 == rq2) spin_lock(&rq1->lock); else { - if (rq_cpu(rq1) < rq_cpu(rq2)) { + if (rq1 < rq2) { spin_lock(&rq1->lock); spin_lock(&rq2->lock); } else { @@ -1260,16 +1243,11 @@ this_rq->curr = this_rq->idle = current; deactivate_task(current, rq); current->array = NULL; - current->prio = MAX_PRIO-1; + current->prio = MAX_PRIO; current->state = TASK_RUNNING; - clear_bit(smp_processor_id(), &wait_init_idle); double_rq_unlock(this_rq, rq); - while (wait_init_idle) { - cpu_relax(); - barrier(); - } current->need_resched = 1; - __sti(); + __restore_flags(flags); } extern void init_timervecs(void); @@ -1308,7 +1286,7 @@ */ rq = this_rq(); rq->curr = current; - rq->idle = NULL; + rq->idle = current; wake_up_process(current); init_timervecs(); diff -urN linux-2.5.3-pre2/mm/oom_kill.c linux/mm/oom_kill.c --- linux-2.5.3-pre2/mm/oom_kill.c Mon Jan 7 12:55:16 2002 +++ linux/mm/oom_kill.c Mon Jan 21 17:23:24 2002 @@ -110,8 +110,7 @@ /* * Simple selection loop. We chose the process with the highest - * number of 'points'. We need the locks to make sure that the - * list of task structs doesn't change while we look the other way. + * number of 'points'. We expect the caller will lock the tasklist. * * (not docbooked, we don't want this one cluttering up the manual) */ @@ -121,7 +120,6 @@ struct task_struct *p = NULL; struct task_struct *chosen = NULL; - read_lock(&tasklist_lock); for_each_task(p) { if (p->pid) { int points = badness(p); @@ -131,7 +129,6 @@ } } } - read_unlock(&tasklist_lock); return chosen; } @@ -170,14 +167,16 @@ */ static void oom_kill(void) { - struct task_struct *p = select_bad_process(), *q; + struct task_struct *p, *q; + + read_lock(&tasklist_lock); + p = select_bad_process(); /* Found nothing?!?! Either we hang forever, or we panic. */ if (p == NULL) panic("Out of memory and no killable processes...\n"); /* kill all processes that share the ->mm (i.e. all threads) */ - read_lock(&tasklist_lock); for_each_task(q) { if(q->mm == p->mm) oom_kill_task(q); } @@ -197,7 +196,7 @@ */ void out_of_memory(void) { - static unsigned long first, last, count; + static unsigned long first, last, count, lastkill; unsigned long now, since; /* @@ -234,8 +233,18 @@ return; /* + * If we just killed a process, wait a while + * to give that task a chance to exit. This + * avoids killing multiple processes needlessly. + */ + since = now - lastkill; + if (since < HZ*5) + return; + + /* * Ok, really out of memory. Kill something. */ + lastkill = now; oom_kill(); reset: diff -urN linux-2.5.3-pre2/mm/shmem.c linux/mm/shmem.c --- linux-2.5.3-pre2/mm/shmem.c Wed Nov 21 09:57:57 2001 +++ linux/mm/shmem.c Mon Jan 21 17:23:24 2002 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -385,7 +386,7 @@ return 0; found: delete_from_swap_cache(page); - add_to_page_cache(page, info->inode->i_mapping, offset + idx); + add_to_page_cache(page, info->vfs_inode.i_mapping, offset + idx); SetPageDirty(page); SetPageUptodate(page); info->swapped--; @@ -685,9 +686,13 @@ inode->i_mapping->a_ops = &shmem_aops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; info = SHMEM_I(inode); - info->inode = inode; spin_lock_init (&info->lock); sema_init (&info->sem, 1); + info->next_index = 0; + memset (info->i_direct, 0, sizeof(info->i_direct)); + info->i_indirect = NULL; + info->swapped = 0; + info->locked = 0; switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); @@ -1308,7 +1313,48 @@ return sb; } +static kmem_cache_t * shmem_inode_cachep; +static struct inode *shmem_alloc_inode(struct super_block *sb) +{ + struct shmem_inode_info *p; + p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, SLAB_KERNEL); + if (!p) + return NULL; + return &p->vfs_inode; +} + +static void shmem_destroy_inode(struct inode *inode) +{ + kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct shmem_inode_info *p = (struct shmem_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + inode_init_once(&p->vfs_inode); + } +} + +static int init_inodecache(void) +{ + shmem_inode_cachep = kmem_cache_create("shmem_inode_cache", + sizeof(struct shmem_inode_info), + 0, SLAB_HWCACHE_ALIGN, + init_once, NULL); + if (shmem_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(shmem_inode_cachep)) + printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n"); +} static struct address_space_operations shmem_aops = { writepage: shmem_writepage, @@ -1350,6 +1396,8 @@ }; static struct super_operations shmem_ops = { + alloc_inode: shmem_alloc_inode, + destroy_inode: shmem_destroy_inode, #ifdef CONFIG_TMPFS statfs: shmem_statfs, remount_fs: shmem_remount_fs, @@ -1376,30 +1424,45 @@ int error; struct vfsmount * res; - if ((error = register_filesystem(&tmpfs_fs_type))) { + error = init_inodecache(); + if (error) + goto out3; + + error = register_filesystem(&tmpfs_fs_type); + if (error) { printk (KERN_ERR "Could not register tmpfs\n"); - return error; + goto out2; } #ifdef CONFIG_TMPFS - if ((error = register_filesystem(&shmem_fs_type))) { + error = register_filesystem(&shmem_fs_type); + if (error) { printk (KERN_ERR "Could not register shm fs\n"); - return error; + goto out1; } devfs_mk_dir (NULL, "shm", NULL); #endif res = kern_mount(&tmpfs_fs_type); if (IS_ERR (res)) { + error = PTR_ERR(res); printk (KERN_ERR "could not kern_mount tmpfs\n"); - unregister_filesystem(&tmpfs_fs_type); - return PTR_ERR(res); + goto out; } shm_mnt = res; /* The internal instance should not do size checking */ - if ((error = shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX))) - printk (KERN_ERR "could not set limits on internal tmpfs\n"); - + shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX); return 0; + +out: +#ifdef CONFIG_TMPFS + unregister_filesystem(&shmem_fs_type); +out1: +#endif + unregister_filesystem(&tmpfs_fs_type); +out2: + destroy_inodecache(); +out3: + return error; } static void __exit exit_shmem_fs(void) @@ -1409,6 +1472,7 @@ #endif unregister_filesystem(&tmpfs_fs_type); mntput(shm_mnt); + destroy_inodecache(); } module_init(init_shmem_fs) diff -urN linux-2.5.3-pre2/mm/swapfile.c linux/mm/swapfile.c --- linux-2.5.3-pre2/mm/swapfile.c Mon Jan 21 17:22:37 2002 +++ linux/mm/swapfile.c Mon Jan 21 17:23:24 2002 @@ -344,7 +344,7 @@ if (page) { page_cache_get(page); /* Only cache user (+us), or swap space full? Free it! */ - if (page_count(page) == 2 || vm_swap_full()) { + if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) { delete_from_swap_cache(page); SetPageDirty(page); } diff -urN linux-2.5.3-pre2/net/core/Makefile linux/net/core/Makefile --- linux-2.5.3-pre2/net/core/Makefile Tue Oct 30 15:08:12 2001 +++ linux/net/core/Makefile Mon Jan 21 17:23:24 2002 @@ -26,5 +26,8 @@ obj-$(CONFIG_NETFILTER) += netfilter.o obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_PROFILE) += profile.o +obj-$(CONFIG_NET_RADIO) += wireless.o +# Ugly. I wish all wireless drivers were moved in drivers/net/wireless +obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o include $(TOPDIR)/Rules.make diff -urN linux-2.5.3-pre2/net/core/dev.c linux/net/core/dev.c --- linux-2.5.3-pre2/net/core/dev.c Wed Nov 7 14:39:36 2001 +++ linux/net/core/dev.c Mon Jan 21 17:23:24 2002 @@ -102,6 +102,7 @@ #include #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) #include /* Note : will define WIRELESS_EXT */ +#include #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ #ifdef CONFIG_PLIP extern int plip_init(void); @@ -1796,122 +1797,6 @@ #endif /* CONFIG_PROC_FS */ -#ifdef WIRELESS_EXT -#ifdef CONFIG_PROC_FS - -/* - * Print one entry of /proc/net/wireless - * This is a clone of /proc/net/dev (just above) - */ -static int sprintf_wireless_stats(char *buffer, struct net_device *dev) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = (dev->get_wireless_stats ? - dev->get_wireless_stats(dev) : - (struct iw_statistics *) NULL); - int size; - - if (stats != (struct iw_statistics *) NULL) { - size = sprintf(buffer, - "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", - dev->name, - stats->status, - stats->qual.qual, - stats->qual.updated & 1 ? '.' : ' ', - stats->qual.level, - stats->qual.updated & 2 ? '.' : ' ', - stats->qual.noise, - stats->qual.updated & 4 ? '.' : ' ', - stats->discard.nwid, - stats->discard.code, - stats->discard.fragment, - stats->discard.retries, - stats->discard.misc, - stats->miss.beacon); - stats->qual.updated = 0; - } - else - size = 0; - - return size; -} - -/* - * Print info for /proc/net/wireless (print all entries) - * This is a clone of /proc/net/dev (just above) - */ -static int dev_get_wireless_info(char * buffer, char **start, off_t offset, - int length) -{ - int len = 0; - off_t begin = 0; - off_t pos = 0; - int size; - - struct net_device * dev; - - size = sprintf(buffer, - "Inter-| sta-| Quality | Discarded packets | Missed\n" - " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" - ); - - pos += size; - len += size; - - read_lock(&dev_base_lock); - for (dev = dev_base; dev != NULL; dev = dev->next) { - size = sprintf_wireless_stats(buffer + len, dev); - len += size; - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) - break; - } - read_unlock(&dev_base_lock); - - *start = buffer + (offset - begin); /* Start of wanted data */ - len -= (offset - begin); /* Start slop */ - if (len > length) - len = length; /* Ending slop */ - if (len < 0) - len = 0; - - return len; -} -#endif /* CONFIG_PROC_FS */ - -/* - * Allow programatic access to /proc/net/wireless even if /proc - * doesn't exist... Also more efficient... - */ -static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = (dev->get_wireless_stats ? - dev->get_wireless_stats(dev) : - (struct iw_statistics *) NULL); - - if (stats != (struct iw_statistics *) NULL) { - struct iwreq * wrq = (struct iwreq *)ifr; - - /* Copy statistics to the user buffer */ - if(copy_to_user(wrq->u.data.pointer, stats, - sizeof(struct iw_statistics))) - return -EFAULT; - - /* Check if we need to clear the update flag */ - if(wrq->u.data.flags != 0) - stats->qual.updated = 0; - return(0); - } else - return -EOPNOTSUPP; -} -#endif /* WIRELESS_EXT */ - /** * netdev_set_master - set up master/slave pair * @slave: slave device @@ -2209,11 +2094,6 @@ notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); return 0; -#ifdef WIRELESS_EXT - case SIOCGIWSTATS: - return dev_iwstats(dev, ifr); -#endif /* WIRELESS_EXT */ - /* * Unknown or private ioctl */ @@ -2239,17 +2119,6 @@ return -EOPNOTSUPP; } -#ifdef WIRELESS_EXT - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - if (dev->do_ioctl) { - if (!netif_device_present(dev)) - return -ENODEV; - return dev->do_ioctl(dev, ifr, cmd); - } - return -EOPNOTSUPP; - } -#endif /* WIRELESS_EXT */ - } return -EINVAL; } @@ -2431,7 +2300,8 @@ } dev_load(ifr.ifr_name); rtnl_lock(); - ret = dev_ifsioc(&ifr, cmd); + /* Follow me in net/core/wireless.c */ + ret = wireless_process_ioctl(&ifr, cmd); rtnl_unlock(); if (!ret && IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) @@ -2856,6 +2726,7 @@ proc_net_create("dev", 0, dev_get_info); create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL); #ifdef WIRELESS_EXT + /* Available in net/core/wireless.c */ proc_net_create("wireless", 0, dev_get_wireless_info); #endif /* WIRELESS_EXT */ #endif /* CONFIG_PROC_FS */ diff -urN linux-2.5.3-pre2/net/core/wireless.c linux/net/core/wireless.c --- linux-2.5.3-pre2/net/core/wireless.c Wed Dec 31 16:00:00 1969 +++ linux/net/core/wireless.c Mon Jan 21 17:23:24 2002 @@ -0,0 +1,733 @@ +/* + * This file implement the Wireless Extensions APIs. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +/************************** DOCUMENTATION **************************/ +/* + * API definition : + * -------------- + * See for details of the APIs and the rest. + * + * History : + * ------- + * + * v1 - 5.12.01 - Jean II + * o Created this file. + * + * v2 - 13.12.01 - Jean II + * o Move /proc/net/wireless stuff from net/core/dev.c to here + * o Make Wireless Extension IOCTLs go through here + * o Added iw_handler handling ;-) + * o Added standard ioctl description + * o Initial dumb commit strategy based on orinoco.c + */ + +/***************************** INCLUDES *****************************/ + +#include /* copy_to_user() */ +#include /* Not needed ??? */ +#include /* off_t */ +#include /* struct ifreq, dev_get_by_name() */ + +#include /* Pretty obvious */ +#include /* New driver API */ + +/**************************** CONSTANTS ****************************/ + +/* This will be turned on later on... */ +#undef WE_STRICT_WRITE /* Check write buffer size */ + +/* Debuging stuff */ +#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ + +/************************* GLOBAL VARIABLES *************************/ +/* + * You should not use global variables, because or re-entrancy. + * On our case, it's only const, so it's OK... + */ +static const struct iw_ioctl_description standard_ioctl[] = { + /* SIOCSIWCOMMIT (internal) */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWNAME */ + { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWNWID */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWNWID */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWFREQ */ + { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWFREQ */ + { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWMODE */ + { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWMODE */ + { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWSENS */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWSENS */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWRANGE */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWRANGE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP}, + /* SIOCSIWPRIV */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWPRIV (handled directly by us) */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCSIWSTATS */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWSTATS (handled directly by us) */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWSPY */ + { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, + /* SIOCGIWSPY */ + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCSIWAP */ + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + /* SIOCGIWAP */ + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWAPLIST */ + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCSIWESSID */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWESSID */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWNICKN */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, + /* SIOCGIWNICKN */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCSIWRATE */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWRATE */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWRTS */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWRTS */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWFRAG */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWFRAG */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWTXPOW */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWTXPOW */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWRETRY */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWRETRY */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWENCODE */ + { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, + /* SIOCGIWENCODE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT}, + /* SIOCSIWPOWER */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWPOWER */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, +}; + +/* Size (in bytes) of the various private data types */ +char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; + +/************************ COMMON SUBROUTINES ************************/ +/* + * Stuff that may be used in various place or doesn't fit in one + * of the section below. + */ + +/* ---------------------------------------------------------------- */ +/* + * Return the driver handler associated with a specific Wireless Extension. + * Called from various place, so make sure it remains efficient. + */ +static inline iw_handler get_handler(struct net_device *dev, + unsigned int cmd) +{ + unsigned int index; /* MUST be unsigned */ + + /* Check if we have some wireless handlers defined */ + if(dev->wireless_handlers == NULL) + return NULL; + + /* Try as a standard command */ + index = cmd - SIOCIWFIRST; + if(index < dev->wireless_handlers->num_standard) + return dev->wireless_handlers->standard[index]; + + /* Try as a private command */ + index = cmd - SIOCIWFIRSTPRIV; + if(index < dev->wireless_handlers->num_private) + return dev->wireless_handlers->private[index]; + + /* Not found */ + return NULL; +} + +/* ---------------------------------------------------------------- */ +/* + * Get statistics out of the driver + */ +static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) +{ + return (dev->get_wireless_stats ? + dev->get_wireless_stats(dev) : + (struct iw_statistics *) NULL); + /* In the future, get_wireless_stats may move from 'struct net_device' + * to 'struct iw_handler_def', to de-bloat struct net_device. + * Definitely worse a thought... */ +} + +/* ---------------------------------------------------------------- */ +/* + * Call the commit handler in the driver + * (if exist and if conditions are right) + * + * Note : our current commit strategy is currently pretty dumb, + * but we will be able to improve on that... + * The goal is to try to agreagate as many changes as possible + * before doing the commit. Drivers that will define a commit handler + * are usually those that need a reset after changing parameters, so + * we want to minimise the number of reset. + * A cool idea is to use a timer : at each "set" command, we re-set the + * timer, when the timer eventually fires, we call the driver. + * Hopefully, more on that later. + * + * Also, I'm waiting to see how many people will complain about the + * netif_running(dev) test. I'm open on that one... + * Hopefully, the driver will remember to do a commit in "open()" ;-) + */ +static inline int call_commit_handler(struct net_device * dev) +{ + if((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) { + /* Call the commit handler on the driver */ + return dev->wireless_handlers->standard[0](dev, NULL, + NULL, NULL); + } else + return 0; /* Command completed successfully */ +} + +/* ---------------------------------------------------------------- */ +/* + * Number of private arguments + */ +static inline int get_priv_size(__u16 args) +{ + int num = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + return num * priv_type_size[type]; +} + + +/******************** /proc/net/wireless SUPPORT ********************/ +/* + * The /proc/net/wireless file is a human readable user-space interface + * exporting various wireless specific statistics from the wireless devices. + * This is the most popular part of the Wireless Extensions ;-) + * + * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). + * The content of the file is basically the content of "struct iw_statistics". + */ + +#ifdef CONFIG_PROC_FS + +/* ---------------------------------------------------------------- */ +/* + * Print one entry (line) of /proc/net/wireless + */ +static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev) +{ + /* Get stats from the driver */ + struct iw_statistics *stats; + int size; + + stats = get_wireless_stats(dev); + if (stats != (struct iw_statistics *) NULL) { + size = sprintf(buffer, + "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", + dev->name, + stats->status, + stats->qual.qual, + stats->qual.updated & 1 ? '.' : ' ', + stats->qual.level, + stats->qual.updated & 2 ? '.' : ' ', + stats->qual.noise, + stats->qual.updated & 4 ? '.' : ' ', + stats->discard.nwid, + stats->discard.code, + stats->discard.fragment, + stats->discard.retries, + stats->discard.misc, + stats->miss.beacon); + stats->qual.updated = 0; + } + else + size = 0; + + return size; +} + +/* ---------------------------------------------------------------- */ +/* + * Print info for /proc/net/wireless (print all entries) + */ +int dev_get_wireless_info(char * buffer, char **start, off_t offset, + int length) +{ + int len = 0; + off_t begin = 0; + off_t pos = 0; + int size; + + struct net_device * dev; + + size = sprintf(buffer, + "Inter-| sta-| Quality | Discarded packets | Missed\n" + " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" + ); + + pos += size; + len += size; + + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { + size = sprintf_wireless_stats(buffer + len, dev); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + read_unlock(&dev_base_lock); + + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + if (len < 0) + len = 0; + + return len; +} +#endif /* CONFIG_PROC_FS */ + +/************************** IOCTL SUPPORT **************************/ +/* + * The original user space API to configure all those Wireless Extensions + * is through IOCTLs. + * In there, we check if we need to call the new driver API (iw_handler) + * or just call the driver ioctl handler. + */ + +/* ---------------------------------------------------------------- */ +/* + * Allow programatic access to /proc/net/wireless even if /proc + * doesn't exist... Also more efficient... + */ +static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) +{ + /* Get stats from the driver */ + struct iw_statistics *stats; + + stats = get_wireless_stats(dev); + if (stats != (struct iw_statistics *) NULL) { + struct iwreq * wrq = (struct iwreq *)ifr; + + /* Copy statistics to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, stats, + sizeof(struct iw_statistics))) + return -EFAULT; + + /* Check if we need to clear the update flag */ + if(wrq->u.data.flags != 0) + stats->qual.updated = 0; + return 0; + } else + return -EOPNOTSUPP; +} + +/* ---------------------------------------------------------------- */ +/* + * Export the driver private handler definition + * They will be picked up by tools like iwpriv... + */ +static inline int ioctl_export_private(struct net_device * dev, + struct ifreq * ifr) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + + /* Check if the driver has something to export */ + if((dev->wireless_handlers->num_private_args == 0) || + (dev->wireless_handlers->private_args == NULL)) + return -EOPNOTSUPP; + + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; +#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ + if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1)) + return -E2BIG; +#endif /* WE_STRICT_WRITE */ + + /* Set the number of available ioctls. */ + iwr->u.data.length = dev->wireless_handlers->num_private_args; + + /* Copy structure to the user buffer. */ + if (copy_to_user(iwr->u.data.pointer, + dev->wireless_handlers->private_args, + sizeof(struct iw_priv_args) * iwr->u.data.length)) + return -EFAULT; + + return 0; +} + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static inline int ioctl_standard_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", + ifr->ifr_name, cmd); + printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); +#endif /* WE_IOCTL_DEBUG */ + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), NULL); + } else { + char * extra; + int err; + + /* Check what user space is giving us */ + if(IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + /* Check if number of token fits within bounds */ + if(iwr->u.data.length > descr->max_tokens) + return -E2BIG; + if(iwr->u.data.length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; +#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ + if(iwr->u.data.length < descr->max_tokens) + return -E2BIG; +#endif /* WE_STRICT_WRITE */ + } + +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Malloc %d bytes\n", + descr->max_tokens * descr->token_size); +#endif /* WE_IOCTL_DEBUG */ + + /* Always allocate for max space. Easier, and won't last + * long... */ + extra = kmalloc(descr->max_tokens * descr->token_size, + GFP_KERNEL); + if (extra == NULL) { + return -ENOMEM; + } + + /* If it is a SET, get all the extra data in here */ + if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) { + kfree(extra); + return -EFAULT; + } +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Got %d bytes\n", + iwr->u.data.length * descr->token_size); +#endif /* WE_IOCTL_DEBUG */ + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); + if (err) + ret = -EFAULT; +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Wrote %d bytes\n", + iwr->u.data.length * descr->token_size); +#endif /* WE_IOCTL_DEBUG */ + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + /* Call commit handler if needed and defined */ + if(ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + /* Here, we will generate the appropriate event if needed */ + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a private Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + * It's not as nice and slimline as the standard wrapper. The cause + * is struct iw_priv_args, which was not really designed for the + * job we are going here. + * + * IMPORTANT : This function prevent to set and get data on the same + * IOCTL and enforce the SET/GET convention. Not doing it would be + * far too hairy... + * If you need to set and get data at the same time, please don't use + * a iw_handler but process it in your ioctl handler (i.e. use the + * old driver API). + */ +static inline int ioctl_private_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + struct iw_priv_args * descr = NULL; + struct iw_request_info info; + int extra_size = 0; + int i; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + for(i = 0; i < dev->wireless_handlers->num_private_args; i++) + if(cmd == dev->wireless_handlers->private_args[i].cmd) { + descr = &(dev->wireless_handlers->private_args[i]); + break; + } + +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n", + ifr->ifr_name, cmd); + if(descr) { + printk(KERN_DEBUG "Name %s, set %X, get %X\n", + descr->name, descr->set_args, descr->get_args); + } +#endif /* WE_IOCTL_DEBUG */ + + /* Compute the size of the set/get arguments */ + if(descr != NULL) { + if(IW_IS_SET(cmd)) { + /* Size of set arguments */ + extra_size = get_priv_size(descr->set_args); + + /* Does it fits in iwr ? */ + if((descr->set_args & IW_PRIV_SIZE_FIXED) && + (extra_size < IFNAMSIZ)) + extra_size = 0; + } else { + /* Size of set arguments */ + extra_size = get_priv_size(descr->get_args); + + /* Does it fits in iwr ? */ + if((descr->get_args & IW_PRIV_SIZE_FIXED) && + (extra_size < IFNAMSIZ)) + extra_size = 0; + } + } + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if(extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + char * extra; + int err; + + /* Check what user space is giving us */ + if(IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + + /* Does it fits within bounds ? */ + if(iwr->u.data.length > (descr->set_args & + IW_PRIV_SIZE_MASK)) + return -E2BIG; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; + } + +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Malloc %d bytes\n", extra_size); +#endif /* WE_IOCTL_DEBUG */ + + /* Always allocate for max space. Easier, and won't last + * long... */ + extra = kmalloc(extra_size, GFP_KERNEL); + if (extra == NULL) { + return -ENOMEM; + } + + /* If it is a SET, get all the extra data in here */ + if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + extra_size); + if (err) { + kfree(extra); + return -EFAULT; + } +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length); +#endif /* WE_IOCTL_DEBUG */ + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + err = copy_to_user(iwr->u.data.pointer, extra, + extra_size); + if (err) + ret = -EFAULT; +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "Wrote %d elem\n", + iwr->u.data.length); +#endif /* WE_IOCTL_DEBUG */ + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + + /* Call commit handler if needed and defined */ + if(ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Main IOCTl dispatcher. Called from the main networking code + * (dev_ioctl() in net/core/dev.c). + * Check the type of IOCTL and call the appropriate wrapper... + */ +int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + iw_handler handler; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + /* A bunch of special cases, then the generic case... + * Note that 'cmd' is already filtered in dev_ioctl() with + * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ + switch(cmd) + { + case SIOCGIWSTATS: + /* Get Wireless Stats */ + return dev_iwstats(dev, ifr); + + case SIOCGIWPRIV: + /* Check if we have some wireless handlers defined */ + if(dev->wireless_handlers != NULL) { + /* We export to user space the definition of + * the private handler ourselves */ + return ioctl_export_private(dev, ifr); + } + // ## Fall-through for old API ## + default: + /* Generic IOCTL */ + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + /* New driver API : try to find the handler */ + handler = get_handler(dev, cmd); + if(handler != NULL) { + /* Standard and private are not the same */ + if(cmd < SIOCIWFIRSTPRIV) + return ioctl_standard_call(dev, + ifr, + cmd, + handler); + else + return ioctl_private_call(dev, + ifr, + cmd, + handler); + } + /* Old driver API : call driver ioctl handler */ + if (dev->do_ioctl) { + return dev->do_ioctl(dev, ifr, cmd); + } + return -EOPNOTSUPP; + } + /* Not reached */ + return -EINVAL; +} diff -urN linux-2.5.3-pre2/net/khttpd/main.c linux/net/khttpd/main.c --- linux-2.5.3-pre2/net/khttpd/main.c Sun Mar 25 18:14:25 2001 +++ linux/net/khttpd/main.c Mon Jan 21 17:23:24 2002 @@ -390,3 +390,5 @@ module_init(khttpd_init) module_exit(khttpd_cleanup) + +MODULE_LICENSE("GPL"); diff -urN linux-2.5.3-pre2/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c --- linux-2.5.3-pre2/net/netlink/af_netlink.c Fri Jun 29 19:38:26 2001 +++ linux/net/netlink/af_netlink.c Mon Jan 21 17:23:24 2002 @@ -996,3 +996,5 @@ module_init(netlink_proto_init); module_exit(netlink_proto_exit); + +MODULE_LICENSE("GPL");