]> git.neil.brown.name Git - history.git/commitdiff
v2.5.2.2 -> v2.5.2.3
authorLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 08:16:39 +0000 (00:16 -0800)
committerLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 08:16:39 +0000 (00:16 -0800)
- Al Viro: VFS inode allocation moved down to filesystem, trim inodes
- Greg KH: USB update, hotplug documentation
- Kai Germaschewski: ISDN update
- Ingo Molnar: scheduler tweaking ("J2")
- Arnaldo: emu10k kdev_t updates
- Ben Collins: firewire updates
- Björn Wesen: cris arch update
- Hal Duston: ps2esdi driver bio/kdev_t fixes
- Jean Tourrilhes: move wireless drivers into drivers/net/wireless,
update wireless API #1
- Richard Gooch: devfs race fix
- OGAWA Hirofumi: FATFS update

316 files changed:
CREDITS
Documentation/Changes
Documentation/Configure.help
Documentation/DocBook/Makefile
Documentation/DocBook/kernel-api.tmpl
Documentation/IRQ-affinity.txt
Documentation/filesystems/devfs/ChangeLog
Documentation/filesystems/devfs/README
Documentation/kernel-parameters.txt
Documentation/pci.txt
MAINTAINERS
Makefile
arch/cris/Makefile
arch/cris/README.mm
arch/cris/boot/compressed/README
arch/cris/boot/compressed/misc.c
arch/cris/boot/rescue/head.S
arch/cris/boot/rescue/kimagerescue.S
arch/cris/boot/rescue/testrescue.S
arch/cris/config.in
arch/cris/drivers/Config.in
arch/cris/drivers/axisflashmap.c
arch/cris/drivers/bluetooth/Makefile [new file with mode: 0644]
arch/cris/drivers/ds1302.c
arch/cris/drivers/eeprom.c
arch/cris/drivers/ethernet.c
arch/cris/drivers/examples/kiobuftest.c
arch/cris/drivers/gpio.c
arch/cris/drivers/i2c.c
arch/cris/drivers/i2c.h
arch/cris/drivers/ide.c
arch/cris/drivers/lpslave/bintocarr.pl
arch/cris/drivers/lpslave/e100lpslave.S
arch/cris/drivers/lpslave/e100lpslavenet.c
arch/cris/drivers/parport.c
arch/cris/drivers/serial.c
arch/cris/drivers/serial.h
arch/cris/drivers/sync_serial.c
arch/cris/drivers/usb-host.c
arch/cris/kernel/Makefile
arch/cris/kernel/debugport.c
arch/cris/kernel/entry.S
arch/cris/kernel/head.S
arch/cris/kernel/irq.c
arch/cris/kernel/kgdb.c
arch/cris/kernel/ksyms.c
arch/cris/kernel/process.c
arch/cris/kernel/ptrace.c
arch/cris/kernel/setup.c
arch/cris/kernel/shadows.c
arch/cris/kernel/sys_cris.c
arch/cris/kernel/time.c
arch/cris/kernel/traps.c
arch/cris/lib/checksum.S
arch/cris/lib/checksumcopy.S
arch/cris/lib/dmacopy.c
arch/cris/lib/dram_init.S
arch/cris/lib/hw_settings.S
arch/cris/lib/old_checksum.c
arch/cris/mm/extable.c
arch/cris/mm/fault.c
arch/cris/mm/init.c
arch/i386/defconfig
arch/i386/kernel/i8259.c
arch/i386/kernel/smp.c
arch/i386/kernel/smpboot.c
drivers/block/ps2esdi.c
drivers/ieee1394/Config.in
drivers/ieee1394/Makefile
drivers/ieee1394/csr.c
drivers/ieee1394/dv1394-private.h [new file with mode: 0644]
drivers/ieee1394/dv1394.c [new file with mode: 0644]
drivers/ieee1394/dv1394.h [new file with mode: 0644]
drivers/ieee1394/highlevel.c
drivers/ieee1394/highlevel.h
drivers/ieee1394/hosts.c
drivers/ieee1394/hosts.h
drivers/ieee1394/ieee1394.h
drivers/ieee1394/ieee1394_core.c
drivers/ieee1394/ieee1394_core.h
drivers/ieee1394/ieee1394_syms.c [deleted file]
drivers/ieee1394/ieee1394_transactions.c
drivers/ieee1394/ieee1394_types.h
drivers/ieee1394/nodemgr.c
drivers/ieee1394/nodemgr.h
drivers/ieee1394/ohci1394.c
drivers/ieee1394/ohci1394.h
drivers/ieee1394/pcilynx.c
drivers/ieee1394/pcilynx.h
drivers/ieee1394/raw1394.c
drivers/ieee1394/sbp2.c
drivers/ieee1394/sbp2.h
drivers/ieee1394/video1394.c
drivers/ieee1394/video1394.h
drivers/isdn/Config.in
drivers/isdn/hisax/Makefile
drivers/isdn/hisax/avm_pci.c
drivers/isdn/hisax/callc.c
drivers/isdn/hisax/config.c
drivers/isdn/hisax/elsa_ser.c
drivers/isdn/hisax/hfc_2bds0.c
drivers/isdn/hisax/hfc_2bs0.c
drivers/isdn/hisax/hfc_pci.c
drivers/isdn/hisax/hfc_sx.c
drivers/isdn/hisax/hisax.h
drivers/isdn/hisax/hisax_fcclassic.c [new file with mode: 0644]
drivers/isdn/hisax/hisax_fcclassic.h [new file with mode: 0644]
drivers/isdn/hisax/hisax_hscx.c [new file with mode: 0644]
drivers/isdn/hisax/hisax_hscx.h [new file with mode: 0644]
drivers/isdn/hisax/hscx.c
drivers/isdn/hisax/icc.c
drivers/isdn/hisax/isac.c
drivers/isdn/hisax/isar.c
drivers/isdn/hisax/isdnl1.c
drivers/isdn/hisax/isdnl2.c
drivers/isdn/hisax/isdnl2.h
drivers/isdn/hisax/isdnl3.c
drivers/isdn/hisax/jade.c
drivers/isdn/hisax/l3_1tr6.c
drivers/isdn/hisax/l3dss1.c
drivers/isdn/hisax/l3ni1.c
drivers/isdn/hisax/netjet.c
drivers/isdn/hisax/st5481.h
drivers/isdn/hisax/st5481_d.c
drivers/isdn/hisax/st5481_usb.c
drivers/isdn/hisax/tei.c
drivers/isdn/hisax/w6692.c
drivers/isdn/isdn_common.c
drivers/isdn/isdn_ppp.c
drivers/isdn/isdn_ppp.h
drivers/media/video/cpia_usb.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/au1000_eth.c
drivers/net/dl2k.c
drivers/net/i82586.h [deleted file]
drivers/net/pcmcia/Config.in
drivers/net/pcmcia/Makefile
drivers/net/pcmcia/i82593.h [deleted file]
drivers/net/pcmcia/netwave_cs.c [deleted file]
drivers/net/pcmcia/wavelan.h [deleted file]
drivers/net/pcmcia/wavelan_cs.c [deleted file]
drivers/net/pcmcia/wavelan_cs.h [deleted file]
drivers/net/wavelan.c [deleted file]
drivers/net/wavelan.h [deleted file]
drivers/net/wavelan.p.h [deleted file]
drivers/net/wireless/Config.in
drivers/net/wireless/Makefile
drivers/net/wireless/i82586.h [new file with mode: 0644]
drivers/net/wireless/i82593.h [new file with mode: 0644]
drivers/net/wireless/netwave_cs.c [new file with mode: 0644]
drivers/net/wireless/todo.txt
drivers/net/wireless/wavelan.c [new file with mode: 0644]
drivers/net/wireless/wavelan.h [new file with mode: 0644]
drivers/net/wireless/wavelan.p.h [new file with mode: 0644]
drivers/net/wireless/wavelan_cs.c [new file with mode: 0644]
drivers/net/wireless/wavelan_cs.h [new file with mode: 0644]
drivers/net/wireless/wavelan_cs.p.h [new file with mode: 0644]
drivers/sound/emu10k1/audio.c
drivers/sound/emu10k1/midi.c
drivers/sound/emu10k1/mixer.c
drivers/usb/Makefile
drivers/usb/audio.c
drivers/usb/auerswald.c
drivers/usb/devio.c
drivers/usb/hcd.c
drivers/usb/hcd/Config.in
drivers/usb/hcd/Makefile
drivers/usb/hcd/ehci-sched.c
drivers/usb/hcd/ohci-dbg.c [new file with mode: 0644]
drivers/usb/hcd/ohci-hcd.c [new file with mode: 0644]
drivers/usb/hcd/ohci-hub.c [new file with mode: 0644]
drivers/usb/hcd/ohci-mem.c [new file with mode: 0644]
drivers/usb/hcd/ohci-q.c [new file with mode: 0644]
drivers/usb/hcd/ohci.h [new file with mode: 0644]
drivers/usb/ibmcam.h
drivers/usb/kaweth.c
drivers/usb/ov511.c
drivers/usb/ov511.h
drivers/usb/pegasus.c
drivers/usb/se401.c
drivers/usb/se401.h
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/io_edgeport.c
drivers/usb/serial/keyspan.c
drivers/usb/storage/transport.c
drivers/usb/stv680.c
drivers/usb/stv680.h
drivers/usb/uhci.c
drivers/usb/usb-ohci.c
drivers/usb/usb-ohci.h
drivers/usb/usb-uhci.c
drivers/usb/usb-uhci.h
drivers/usb/usb.c
drivers/usb/usbvideo.c
drivers/usb/usbvideo.h
drivers/usb/vicam.c
drivers/usb/vicam.h
fs/affs/amigaffs.c
fs/affs/bitmap.c
fs/affs/file.c
fs/affs/inode.c
fs/affs/super.c
fs/coda/inode.c
fs/coda/psdev.c
fs/devfs/base.c
fs/efs/super.c
fs/ext2/balloc.c
fs/ext2/bitmap.c
fs/ext2/dir.c
fs/ext2/ext2.h [new file with mode: 0644]
fs/ext2/file.c
fs/ext2/fsync.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/ioctl.c
fs/ext2/namei.c
fs/ext2/super.c
fs/ext2/symlink.c
fs/ext3/ialloc.c
fs/ext3/inode.c
fs/ext3/ioctl.c
fs/ext3/namei.c
fs/ext3/super.c
fs/ext3/symlink.c
fs/fat/dir.c
fs/fat/file.c
fs/fat/misc.c
fs/hpfs/anode.c
fs/hpfs/buffer.c
fs/hpfs/dir.c
fs/hpfs/dnode.c
fs/hpfs/ea.c
fs/hpfs/file.c
fs/hpfs/hpfs_fn.h
fs/hpfs/inode.c
fs/hpfs/name.c
fs/hpfs/namei.c
fs/hpfs/super.c
fs/intermezzo/journal_ext3.c
fs/intermezzo/journal_obdfs.c
fs/intermezzo/journal_reiserfs.c
fs/nfs/inode.c
fs/nfs/read.c
fs/nfs/write.c
fs/qnx4/bitmap.c
fs/qnx4/fsync.c
fs/qnx4/inode.c
fs/ufs/balloc.c
fs/ufs/ialloc.c
fs/ufs/inode.c
fs/ufs/namei.c
fs/ufs/super.c
fs/ufs/symlink.c
fs/ufs/truncate.c
fs/ufs/util.c
fs/umsdos/dir.c
fs/umsdos/inode.c
fs/umsdos/namei.c
fs/umsdos/rdir.c
include/asm-cris/bitops.h
include/asm-cris/elf.h
include/asm-cris/ethernet.h [new file with mode: 0644]
include/asm-cris/irq.h
include/asm-cris/page.h
include/asm-cris/pgalloc.h
include/asm-cris/pgtable.h
include/asm-cris/processor.h
include/asm-cris/scatterlist.h [new file with mode: 0644]
include/asm-cris/unistd.h
include/asm-cris/user.h
include/asm-i386/hw_irq.h
include/linux/affs_fs.h
include/linux/affs_fs_i.h
include/linux/amigaffs.h
include/linux/coda.h
include/linux/coda_fs_i.h
include/linux/coda_linux.h
include/linux/dnotify.h
include/linux/efs_fs.h
include/linux/efs_fs_i.h
include/linux/ext2_fs.h
include/linux/ext2_fs_i.h [deleted file]
include/linux/ext3_fs.h
include/linux/ext3_fs_i.h
include/linux/ext3_jbd.h
include/linux/fs.h
include/linux/hpfs_fs_i.h
include/linux/netdevice.h
include/linux/nfs_fs.h
include/linux/nfs_fs_i.h
include/linux/qnx4_fs.h
include/linux/qnx4_fs_i.h [deleted file]
include/linux/sched.h
include/linux/shmem_fs.h
include/linux/ufs_fs.h
include/linux/ufs_fs_i.h
include/linux/ufs_fs_sb.h
include/linux/umsdos_fs.p
include/linux/usb.h
include/linux/usbdev_fs_i.h [deleted file]
include/linux/usbdev_fs_sb.h [deleted file]
include/linux/wireless.h
include/net/iw_handler.h [new file with mode: 0644]
init/main.c
kernel/fork.c
kernel/sched.c
mm/oom_kill.c
mm/shmem.c
mm/swapfile.c
net/core/Makefile
net/core/dev.c
net/core/wireless.c [new file with mode: 0644]
net/khttpd/main.c
net/netlink/af_netlink.c

diff --git a/CREDITS b/CREDITS
index 5bdaddc54f1d657d4ad9048466a90399dfc2404c..b36c8b68f2cb2216c943612030db2160fd42b0e3 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1281,6 +1281,13 @@ W: http://www.carumba.com/
 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 @@ S: USA
 
 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: ul. Matemblewska 1B/10
 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: Puistokaari 1 E 18
 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: Wanniassa ACT 2903
 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
index 8551fc7c863ad45d79a0a049c0ab463f3091410b..955627be1931b267aae8c71ed4d22f781c94b0a6 100644 (file)
@@ -50,10 +50,10 @@ with pcmcia-cs.
 
 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 @@ o  <ftp://rawhide.redhat.com/pub/rawhide/SRPMS/SRPMS/>
 
 E2fsprogs
 ---------
-o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19.tar.gz>
-o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.19-0.src.rpm>
+o  <http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.25.tar.gz>
 
 Reiserfsprogs
 -------------
index 77b10d17e2c4ad7b74c50c8bfd10c5f16200c320..9c3c11539fc16b7223002c3c11e9ae142608d5a3 100644 (file)
@@ -8144,6 +8144,19 @@ CONFIG_IEEE1394_VIDEO1394
   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 <file:Documentation/modules.txt>.  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
index 693fa212527dd22db109700d7a5e9b749658710c..fbd7270420f5a7d461dd7a8c5569d1afb2a952cd 100644 (file)
@@ -93,6 +93,8 @@ APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \
                $(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 \
index 448f3234d44cfde6536a042d5f00406d0f50ff44..69bf70946026755adaeccdb421207db79c40269d 100644 (file)
      </sect1>
      <sect1><title>PCI Support Library</title>
 !Edrivers/pci/pci.c
+     </sect1>
+     <sect1><title>PCI Hotplug Support Library</title>
+!Edrivers/hotplug/pci_hotplug_core.c
+!Edrivers/hotplug/pci_hotplug_util.c
      </sect1>
      <sect1><title>MCA Architecture</title>
        <sect2><title>MCA Device Functions</title>
index 7c582f494c39be24f6c24e95c1812d670f2ff685..938d7dd054905edfae6d9bc76ca27ffdfbd42629 100644 (file)
@@ -8,7 +8,7 @@ to turn off all CPUs, and if an IRQ controller does not support IRQ
 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
index de386c4ea0b7740d8cbf040a76d54e5b7c34a64e..8a65ff6090f921bddf5c37388e7cf1a8f738f1a5 100644 (file)
@@ -1869,3 +1869,11 @@ Changes for patch v206
 - Added support for multiple Compaq cpqarray controllers
 
 - Fixed (rare, old) race in <devfs_lookup>
+===============================================================================
+Changes for patch v207
+
+- Fixed deadlock bug in <devfs_d_revalidate_wait>
+
+- Tag VFS deletable in <devfs_mk_symlink> if handle ignored
+
+- Updated README from master HTML file
index a34fcd3215536f3436c93f8f60e9230074af8bc0..9f0e99e9674064f18126cd81e23ebbca941a1b21 100644 (file)
@@ -3,7 +3,16 @@ Devfs (Device File System) FAQ
 
 Linux Devfs (Device File System) FAQ
 Richard Gooch
-21-DEC-2001
+20-JAN-2002
+
+
+Document languages:
+
+
+
+
+
+
 
 -----------------------------------------------------------------------------
 
@@ -69,6 +78,7 @@ Alternatives to devfs
 What I don't like about devfs
 How to report bugs
 Strange kernel messages
+Compilation problems with devfsd
 
 
 Other resources
@@ -837,8 +847,8 @@ device nodes. You can do this in your boot scripts. All your drivers
 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 @@ Alternatives to devfs
 What I don't like about devfs
 How to report bugs
 Strange kernel messages
+Compilation problems with devfsd
 
 
 
@@ -1677,7 +1688,7 @@ unallocated major numbers. USB will also need to push beyond the 8 bit
 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 @@ The solution is the same as above.
 
 
 
+
+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 @@ This document has been translated into other languages.
 
 
 
+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. 
index 97e4026cfbdaa785258c82e92a2ac42101e7ede5..e02079a8dcd5ed3fa266a457775743d6d41e8aa8 100644 (file)
@@ -125,6 +125,13 @@ running once the system is up.
 
        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] 
index dadb9a9dcd426e7bfb80f75e1d4e1bdc65fde5fc..5ac05854aa8976d99b34ff5d3fe03f4b74a4ffc9 100644 (file)
@@ -104,6 +104,10 @@ Tips:
        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)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 2ebeced0bf089062d21396169f8e79590c2518e3..1c4472ec57efcb5f8dfa2305e512621710f888d2 100644 (file)
@@ -412,6 +412,12 @@ W: http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html
 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 @@ L:     linux-LVM@sistina.com
 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 @@ P:       Andrew Veliath
 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 @@ L:        linux-scsi@vger.rutgers.edu
 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
 
index f168a156c07736883a719a8402be9800e97f87d8..e4a9258e6acbe4bba5a30fde0c287adcbd088153 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 3
-EXTRAVERSION =-pre2
+EXTRAVERSION =-pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
index 61db7aea665b13d52abc524b2b2fb17296941759..4a9dadc1aa6c7aa7fe6bc876b59a695b7c301c85 100644 (file)
@@ -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 @@ LD = if [ ! -e $(LD_SCRIPT).tmp -o $(LD_SCRIPT) -nt $(LD_SCRIPT).tmp ]; then \
               -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 @@ ifdef CONFIG_ETRAX_AXISFLASHMAP
 # 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)
index 6de4e7077a9ff1ef9d33a36b5d932b30bea5f4d7..195ca898b55ba65f979c84496825d524e202a63f 100644 (file)
@@ -3,6 +3,9 @@ Memory management for CRIS/MMU
 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
 
index 83e1f1e516802215961bb5417931e7541f1b3da5..349eb2ab4663196175ac15e046df73329ddc793f 100644 (file)
@@ -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.
 
index 31a0331d70a859b81b87d28b6a7e364fbf0249bb..cf7a174ff823165b3ec5933ea18f1af489543a8b 100644 (file)
@@ -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.
index 7904252a36d264e819711771e06916fd3e4eee86..40f9af6b6ef4fb26690dcfda2fabb2d89ace8ab2 100644 (file)
@@ -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
 
 #define NOP_DI 0xf025050f
 #define RAM_INIT_MAGIC 0x56902387
-               
+
        .text
        
        ;; This is the entry point of the rescue code
@@ -144,7 +144,13 @@ jtcd:      move.d  [jumptarget], $r0
 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"
 
index 500ed5ede8035d234a2e5a76904f147ef87fdb0e..270774108b7e7a1787d5ba316470bca3a5bf0583 100644 (file)
@@ -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.
index 2e0f0ce004d3d35b8ac45b9fb985a5973b5002ae..f39c69e264834696ddc95e50ec7e791fd53ff9f6 100644 (file)
@@ -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.
index 66013359742eefc05444013b34b800f5e6562be7..6a38f9bc84a6e9902c3fc6b95f10ab6ee6bcb2ce 100644 (file)
@@ -35,6 +35,9 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 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 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
   int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
-endmenu
 
 source lib/Config.in
+endmenu
index 7d934a60035bbaa426b061a2a2a7c5182d878346..a737862509928c746e4234fce777d7f2f9c64b96 100644 (file)
@@ -175,11 +175,11 @@ if [ "$CONFIG_ETRAX_GPIO" = "y" ]; then
   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
index 784c444bb9d156aaaa8f4e4c8a3d9fd3cfb5b2f6..9f4a0558a07c26673366be6035611c54a62c8e5f 100644 (file)
  * 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 __u32 flash_read32(struct map_info *map, unsigned long ofs)
 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 @@ init_axis_flash(void)
        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 --git a/arch/cris/drivers/bluetooth/Makefile b/arch/cris/drivers/bluetooth/Makefile
new file mode 100644 (file)
index 0000000..b3cbffc
--- /dev/null
@@ -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
index 177d9b119d53dc25a8da3fcc828146de44ace4b5..a0b55436aa15ca2f8426b4672f502f81e6bbd27b 100644 (file)
@@ -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 $
 *!
 *!***************************************************************************/
 
index ad3df73a28b163fa369d943aa3bcbd4203a17f88..cb39cf10319a4188ffd05f93e32a214739c9e3e2 100644 (file)
@@ -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.
index 707c295f353f972bb25654b71ea736bd7aaaa8e4..47fe0a6e78ffcbbc2ccc9e74cf14ebf1e28e0f07 100644 (file)
@@ -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.
  *
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <asm/ethernet.h>
 
 //#define ETHDEBUG
 #define D(x)
@@ -120,7 +139,7 @@ static const char* cardname = "ETRAX 100LX built-in ethernet controller";
 
 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 @@ struct net_local {
 };
 
 
+/* Duplex settings */
+enum duplex
+{
+       half,
+       full,
+       autoneg
+};
+
 /* Dma descriptors etc. */
 
 #define RX_BUF_SIZE 32768
@@ -148,9 +175,18 @@ struct net_local {
 /* 
 ** 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 @@ struct net_local {
 
 /* 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 @@ struct net_local {
 #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 etrax_dma_descr TxDesc __attribute__ ((aligned(4)));
 
 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 e100tx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 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_rx_stats(struct net_device_stats *);
 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 @@ etrax_ethernet_init(struct net_device *dev)
        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 @@ etrax_ethernet_init(struct net_device *dev)
 
        /* 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 @@ etrax_ethernet_init(struct net_device *dev)
        /* 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 @@ e100_set_mac_address(struct net_device *dev, void *p)
 
        /* 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 @@ e100_open(struct net_device *dev)
 
        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 @@ e100_open(struct net_device *dev)
         * 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 @@ e100_open(struct net_device *dev)
 
        *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 @@ e100_open(struct net_device *dev)
        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 @@ e100_check_speed(unsigned long dummy)
        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 @@ e100_get_mdio_reg(unsigned char reg_num)
        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 @@ e100_send_mdio_cmd(unsigned short cmd, int write_cmd)
        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 @@ e100_receive_mdio_bit()
 static void 
 e100_reset_tranceiver(void)
 {
-       unsigned long flags;
        unsigned short cmd;
        unsigned short data;
        int bitCounter;
@@ -619,7 +800,7 @@ e100_reset_tranceiver(void)
        
        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 @@ e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        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 @@ e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        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 @@ e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 
                *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 @@ e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        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 @@ static void
 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 @@ e100_rx(struct net_device *dev)
                /* 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 @@ e100_rx(struct net_device *dev)
        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 @@ e100_rx(struct net_device *dev)
 
        /* 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 @@ e100_close(struct net_device *dev)
        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 @@ set_multicast_list(struct net_device *dev)
        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 @@ set_multicast_list(struct net_device *dev)
                        }
                        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 @@ e100_hardware_send_packet(char *buf, int length)
                /* 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 @@ e100_hardware_send_packet(char *buf, int length)
 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 @@ etrax_init_module(void)
 
        d->init = etrax_ethernet_init;
 
-       if(register_netdev(d) == 0)
+       if (register_netdev(d) == 0)
                return 0;
        else
                return -ENODEV;
index c621966a09300565be5c68798e9285a5f3d03074..784418f9c4d697d62de4fd2ec3b17d05ab67c9bd 100644 (file)
@@ -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
  *
index a88b0f6e44bd49afee1e2370638fdc7212f8998c..c454e5de50465d48c7b5e46e1f9e42e9ea6598e0 100644 (file)
@@ -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 @@ gpio_poll(struct file *filp,
 {
        /* 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 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
        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 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
                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<<i)
@@ -190,7 +197,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
                                restore_flags(flags);
                        }
                } else {
-                       for (i = 0; i<=7;i++) {
+                       for (i = 0; i <= 7;i++) {
                                save_flags(flags); cli();
                                *priv->port = *priv->shadow &= ~clk_mask;
                                if (data & 1<<i)
@@ -212,13 +219,13 @@ gpio_open(struct inode *inode, struct file *filp)
        struct gpio_private *priv;
        int p = MINOR(inode->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 @@ gpio_release(struct inode *inode, struct file *filp)
 
        /* 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 @@ gpio_ioctl(struct inode *inode, struct file *file,
 {
        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 @@ gpio_ioctl(struct inode *inode, struct file *file,
                        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 @@ gpio_ioctl(struct inode *inode, struct file *file,
                        }
                        break;
                default:
-                       if(priv->minor == LEDS)
+                       if (priv->minor == LEDS)
                                return gpio_leds_ioctl(cmd, arg);
                         else
                                return -EINVAL;
@@ -375,6 +382,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
 {
        unsigned char green;
        unsigned char red;
+
        switch (_IOC_NR(cmd)) {
                case IO_LEDACTIVE_SET:
                        green = ((unsigned char) arg) & 1;
@@ -382,14 +390,20 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
                        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 @@ struct file_operations gpio_fops = {
 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
index d3b32b62646420f2eff4f5854436896c6fd679bc..a890238310d7222683fbc2ed073b40e68b198677 100644 (file)
@@ -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 <linux/module.h>
index a820f9d9b2845cdcb370f3020fc8caae80879120..d789274adc71e88026188919ff086ff2bbda9f9e 100644 (file)
@@ -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);
index 45164a60577f3fcd2c1a6b8757ec9003c6700b7d..9cb576949257bf073925f653276f7743ea4e197b 100644 (file)
@@ -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 <asm/dma.h>
  *
index a400fa16853bc560ffc665723fe9166d9872fc2b..c03dd8b718b70daddda645f63a8b39a5951f192a 100644 (file)
@@ -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
index 6e2991debdea683e53df7e64a3b81f83f4b626d3..44efe0771fc257cb428c0dcefd436d94e6a8fff3 100644 (file)
@@ -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
        ;;
index f8ab69367ccf4a8a4427dc2639d18db478aabc9c..54aa0bacec5117b4da76cc66ae0056a35f532e9e 100644 (file)
@@ -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
  *
index 1d792968f149a2f7bfe562557369249a7f8360cf..443e51a111b87f6cca1eecaab3c28335d3a1cfc2 100644 (file)
@@ -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!
index 6de63c4ac822c6d6e9d77596798474165d376ff6..412f7519c48806e55fff4efb7a46a0108d2133ba 100644 (file)
@@ -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).
  * 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.
  * 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...
  *
  */
 
-static char *serial_version = "$Revision: 1.23 $";
+static char *serial_version = "$Revision: 1.3 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -272,6 +300,7 @@ static char *serial_version = "$Revision: 1.23 $";
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #if (LINUX_VERSION_CODE >= 131343)
 #include <linux/init.h>
 #endif
@@ -302,6 +331,8 @@ static char *serial_version = "$Revision: 1.23 $";
 #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 @@ static int serial_refcount;
 #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 @@ static int serial_refcount;
 
 #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 @@ static int serial_refcount;
 #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 int rs_write(struct tty_struct * tty, int from_user,
 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 @@ static struct e100_serial rs_table[] = {
 #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 @@ static struct e100_serial rs_table[] = {
 
        { 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 @@ static struct e100_serial rs_table[] = {
 
        { 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 tty_struct *serial_table[NR_PORTS];
 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 @@ static struct ser_statistics_type ser_stat[NR_PORTS];
 
 #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 @@ static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT;
 /* 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 @@ struct control_pins
 
 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 @@ e100_disable_rx(struct e100_serial *info)
 {
 #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 @@ e100_enable_rx(struct e100_serial *info)
 {
 #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 @@ e100_enable_rs485(struct tty_struct *tty,struct rs485_control *r)
 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 @@ e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
         * 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 @@ transmit_chars(struct e100_serial *info)
        }
        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 @@ transmit_chars(struct e100_serial *info)
 
 #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 @@ transmit_chars(struct e100_serial *info)
        *info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
        
        /* DMA is now running (hopefully) */
-
 }
 
 static void 
@@ -1220,13 +1232,122 @@ start_transmit(struct e100_serial *info)
        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 @@ receive_chars(struct e100_serial *info)
        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 @@ start_receive(struct e100_serial *info)
        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 @@ tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                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 @@ rec_interrupt(int irq, void *dev_id, struct pt_regs * regs)
        }
 }
 
-#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 @@ void _INLINE_ check_flush_timeout(struct e100_serial *info)
                /* 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 @@ timed_flush_handler(unsigned long ptr)
 {
        struct e100_serial *info;
        int i;
-       unsigned int magic;
 
 #ifdef CONFIG_SVINTO_SIM
        return;
@@ -1689,35 +1737,8 @@ timed_flush_handler(unsigned long ptr)
        
        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 @@ If RXD pin is 0 we can expect another character (see 2. below).
 
 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 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
 /*     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 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
                        }
                        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 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
                        }
                        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 @@ do_serial_hangup(void *private_)
 {
        struct e100_serial      *info = (struct e100_serial *) private_;
        struct tty_struct       *tty;
-       
+
        tty = info->tty;
        if (!tty)
                return;
@@ -1985,43 +2004,46 @@ static int
 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 @@ startup(struct e100_serial * info)
        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 @@ startup(struct e100_serial * info)
         * 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 @@ startup(struct e100_serial * info)
        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 @@ shutdown(struct e100_serial * info)
        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 @@ shutdown(struct e100_serial * info)
        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 @@ rs_flush_chars(struct tty_struct *tty)
        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 @@ rs_write(struct tty_struct * tty, int from_user,
 #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 @@ rs_write(struct tty_struct * tty, int from_user,
         * 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 @@ get_serial_info(struct e100_serial * info,
        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 @@ const struct state_str control_state_str[] = {
 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 @@ static int
 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 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
                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 @@ rs_open(struct tty_struct *tty, struct file * filp)
        }
 
        /*
-        * 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 @@ rs_open(struct tty_struct *tty, struct file * filp)
 
 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 @@ static inline int line_info(char *buf, struct e100_serial *info)
        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 @@ rs_init(void)
                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 @@ rs_init(void)
                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",
index 119f2fb85e8494fb19bdb31890de07e9fc92e7b1..c06d473429c76145aa8c0b068af086bd141a232b 100644 (file)
  * 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 @@ struct e100_serial {
 
 #endif /* __KERNEL__ */
 
-#endif /* !(_ETRAX_SERIAL_H) */
+#endif /* !_ETRAX_SERIAL_H */
index 4d3f9c01b2066453312dc1a56456a2679d82ffc5..6974945fa25a7b9188e1bb1b826c504295a95778 100644 (file)
@@ -711,7 +711,7 @@ static void start_dma(struct sync_port* port, const char* data, int count)
 {
        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;
index b2a93ce4d5ea5ed501138b9af14ae3f59bb3b77b..8e4e9e932ae87b3c2eea7b57409d203f18db24f4 100644 (file)
@@ -192,15 +192,15 @@ static USB_EP_Desc_t TxBulkEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
 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 int etrax_usb_allocate_epid(void);
 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_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
 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 void init_tx_intr_ep(void)
 }
 
 
-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 @@ void etrax_usb_do_intr_recover(int epid)
        } 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 etrax_usb_submit_intr_urb(urb_t *urb)
 
 static int handle_intr_transfer_attn(char epid, int status)
 {
-       urb_t *old_urb;
+       struct urb *old_urb;
 
        DBFENTER;
 
@@ -696,7 +696,7 @@ static int handle_intr_transfer_attn(char epid, int status)
        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 @@ static int etrax_rh_unlink_urb (urb_t *urb)
        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 @@ static void etrax_rh_send_irq(urb_t *urb)
 /*     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_init_int_timer(urb_t *urb)
 
 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 @@ static int etrax_usb_allocate_epid(void)
        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 @@ static int etrax_usb_submit_bulk_urb(urb_t *urb)
        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 @@ static int etrax_usb_submit_bulk_urb(urb_t *urb)
        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 etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
 
 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 handle_bulk_transfer_attn(char epid, int status)
 
 /* ---------------------------------------------------------------------------- */
 
-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 @@ static int etrax_usb_submit_ctrl_urb(urb_t *urb)
        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 @@ static int etrax_usb_submit_ctrl_urb(urb_t *urb)
        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 @@ static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
        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 @@ static int etrax_usb_submit_urb(urb_t *urb)
        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 @@ static int etrax_usb_unlink_urb(urb_t *urb)
        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 @@ static int etrax_usb_unlink_urb(urb_t *urb)
                                        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 @@ static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
        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_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
 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 void cleanup_sb(USB_SB_Desc_t *sb)
 
 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 int handle_control_transfer_attn(char epid, int status)
 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 @@ static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs)
        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;
index be2063e2fa53959ca9e2e1d209092228bfb88df1..9a92e3b2bb1f4ca0a55285cd17f5a594a0d59cdc 100644 (file)
@@ -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.
 #
index c6b1b880531023e1928d8ecc6fc3cd0bea77a824..059a2b4122f4c89ca585539d37675ac94c21fdc6 100644 (file)
@@ -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 @@ enableDebugIRQ(void)
 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 @@ console_setup(struct console *co, char *options)
 }
 
 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
 };
 
 /*
index a33a1c12afb88ea907d515ad5f34be8a23a7dab1..8512ba94323db21520fb72cb80254b4ff3002f8c 100644 (file)
@@ -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 @@ _handle_softirq:
        
 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 @@ mmu_bus_fault:
        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 @@ mmu_bus_fault:
        ;; 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 @@ _killed_by_death:
 ;; 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 @@ Watchdog_bite:
                | 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 @@ Watchdog_bite:
        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".
index 3eef976e5613402545504e55dbf4fcd884cb6b6d..bbd4eb3dd936fbfa7dd97317696e6b120d19ee6f 100644 (file)
@@ -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.
  *
 
 #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 @@ _inflash0:
        ;; 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?
index 9dd53469b7dfb246beeebc2881c6d34134950618..5f2dda7349848eeb10753ef236e094d510dc939e 100644 (file)
@@ -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 @@ set_break_vector(int n, irqvectptr addr)
 /* 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)
index e5c6428d7ca8e140b03cc742c3639dd41f9cf186..5d5c9d4bbfdcf646b5c72cfeb2244dae290dd051 100644 (file)
@@ -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
 *!
index aaf66ee05ec7b6528d4b8e1652f6fc4ab2471000..ef798138fcc385c1e948dc666989d83abef4b694 100644 (file)
 
 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_dir_shadow);
 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 @@ EXPORT_SYMBOL(__copy_user);
 
 #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);
 
index 00be67c79c728987aa11abba8fb8cfdffb062a7d..37263479cc2bfb505af7c62901a3fba79f530408 100644 (file)
@@ -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 <linux/slab.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/elfcore.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
@@ -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 @@ void hard_reset_now (void)
         * 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 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
  */
 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);
@@ -265,6 +276,12 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
 #endif 
 }
 
+/* Fill in the fpu structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+        return 0;
+}
+
 /* 
  * Be aware of the "magic" 7th argument in the four system-calls below.
  * They need the latest stackframe, which is put as the 7th argument by
index fd0f231596f676a25aac01cebdbd55c7d5203dd5..59f7d38fe4cd1e02bdbf41065109e7c14597f987 100644 (file)
@@ -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 long get_reg(struct task_struct *task, unsigned int regno)
 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 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        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 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        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 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        /* 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 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
                        wake_up_process(child);
                        ret = 0;
                        break;
-               }
 
                case PTRACE_DETACH:
                        ret = ptrace_detach(child, data);
index 2a7f45cc6e5b2915f80897ff94313d58363f95bf..820359196678fac3b976783876cb3daf76e293f5 100644 (file)
@@ -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
  *
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/pgtable.h>
 #include <asm/types.h>
 #include <asm/svinto.h>
 
@@ -72,10 +74,10 @@ extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S *
 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 @@ setup_arch(char **cmdline_p)
 
        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 @@ setup_arch(char **cmdline_p)
 #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 @@ static struct cpu_info {
        { "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 @@ int get_cpuinfo(char *buffer, unsigned *cpu_np)
                       (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 */
index ff2373a6678a9bb93c32bcd29ed2c1e0f689d502..25fac64945e463667511541680bf31b562335329 100644 (file)
@@ -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
  */
index 7fbb2e8038871c5c279af6ac17be33e09415354c..6db339ce21e760d41eb428beecdec6f18957a4e3 100644 (file)
@@ -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
  *
index 00b8ff6e889a11a593a48ca94eed16f8fc2a5352..537040f95a6dd870d6805154d6f2da21fd8238da 100644 (file)
@@ -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 @@ unsigned short cris_timer0_value_us[TIMER0_DIV+1];
 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 @@ static unsigned long do_slow_gettimeoffset(void)
  */
        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 @@ static int set_rtc_mmss(unsigned long nowtime)
 {
        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 @@ static int set_rtc_mmss(unsigned long nowtime)
 /* 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 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        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 @@ void print_timestamp(const char *s)
 {
        unsigned long flags;
        unsigned int newjiff;
+
        save_flags(flags);
        cli();
        newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); 
@@ -337,7 +345,6 @@ unsigned long
 get_cmos_time(void)
 {
        unsigned int year, mon, day, hour, min, sec;
-       int i;
 
        sec = CMOS_READ(RTC_SECONDS);
        min = CMOS_READ(RTC_MINUTES);
index 9fcda3abd2b8d4d501d6ac38e6125d488df8c9af..af7d639de3b6259dae2431eb35935eefe9103614 100644 (file)
@@ -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
  *
 
 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 @@ int kstack_depth_to_print = 24;
 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 @@ show_stack(unsigned long *sp)
 
         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 @@ show_stack(unsigned long *sp)
                        /* 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 @@ show_registers(struct pt_regs * regs)
               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 @@ bad:
         }
 }
 
+/* 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 */
 }
index 134aac28d9e5b2e936e0292225077da661567e21..fd15a76f78d63c3e00d93ba38d48caf6e7d697df 100644 (file)
@@ -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
  *
index ad3a8f5dbbaf129fb4492e3543580d2c9aca8b52..4d8bc348da3968172313f318261ad033fc4afdcf 100644 (file)
@@ -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
  *
index 318577a2de849492da032fee6e62602624efdc0a..fe8f091ed09c455e7b592758f55f252a4693f4c5 100644 (file)
@@ -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
  */
index 0d59ce119e41c3965384fbbe50e11dfa59906b4f..d46efd1aef1fd3d2c78a44d30aea049c090221c5 100644 (file)
@@ -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.
  *
index aca3b351a18b24e856565a0f764a14a9b9abe045..b35bb3baa5aa76b2c7825222ebae1cbe3f95ecad 100644 (file)
@@ -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.
index 6035a48ae2797c54c4bcf143002529e798a84eb0..ae0f7b9eb2997a1ff4f9b8ad45b24e1aa5057452 100644 (file)
@@ -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
index 2431ee1433bd09b34fc4a1d3602c413b31b41d7c..7351f59f8bb993eaeb14e5abfa2256c627d08916 100644 (file)
@@ -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
  *
index 7e1f6d7c2e8657616705bec4f076c83f1cc8ebd9..e62134d47572b9bbf5854e9079bd1aebccc64cc1 100644 (file)
@@ -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 @@ asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs,
                              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 @@ void
 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 @@ handle_mmu_bus_fault(struct pt_regs *regs)
        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 @@ handle_mmu_bus_fault(struct pt_regs *regs)
        } 
 
        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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
        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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
         * 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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
         */
 
        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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
         *  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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
 
                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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
         */
 
  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 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
        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 @@ vmalloc_fault:
                 * 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 @@ vmalloc_fault:
                 * 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;
+       }
 }
index b9dd26a8c9293bb66f4091d4c0b50109a790a2a1..b7544841b03e391330dc1c83f9293ab2ce445763 100644 (file)
@@ -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_initmem(void)
                 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;
 }
index dc485035368faf0d22087f0b095cd452c1ba3409..b20f83cd15affa6bcc5c6aa1598ec742c2317c8e 100644 (file)
@@ -491,8 +491,6 @@ CONFIG_PCMCIA_PCNET=y
 # 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 @@ CONFIG_USB=y
 # 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
 
index 0a4412a2fc02f301b076ea9921821b8ae6510e37..bfa7b8bfbb467b5c0ced49246fd99f04428c4112 100644 (file)
@@ -79,6 +79,7 @@ BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd)
  * 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)
@@ -473,6 +474,9 @@ void __init init_IRQ(void)
         */
        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);
 
index c0b3a94a17c263233626147aa613b0c5d38a9230..af1dc738720634449776c9539f2e61df44d2b4ad 100644 (file)
@@ -485,6 +485,35 @@ void flush_tlb_all(void)
        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
index 97663b228d214babfaf15291ea0bcf2a3e47f74a..34ca9174abeffe371e302c6bfa2794bfa4c84ba6 100644 (file)
@@ -462,6 +462,7 @@ int __init start_secondary(void *unused)
         * 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 @@ int __init start_secondary(void *unused)
         * the local TLBs too.
         */
        local_flush_tlb();
+       idle_startup_done();
 
-       init_idle();
        return cpu_idle();
 }
 
index 6e14b496e5bc07e4a0f3e0e44d1c55e2833a88e6..f23918b051d8f23c9834063c83e773546dd41fad 100644 (file)
@@ -422,7 +422,7 @@ static void __init ps2esdi_geninit(void)
        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 @@ static void do_ps2esdi_request(request_queue_t * q)
 #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 @@ static void do_ps2esdi_request(request_queue_t * q)
        }                       /* 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 @@ static void do_ps2esdi_request(request_queue_t * q)
        /* 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 @@ static void ps2esdi_normal_interrupt_handler(u_int int_ret_code)
        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);
index 558317daa5c3c9232bf47a77b23e7f2b26f5d36c..0ded2b74c151d405ffc311b405bd531c8b5f7a23 100644 (file)
@@ -19,6 +19,7 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
        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
index b6cdcb6bf0a4427d84a7ed78749ea1c791ce8a31..9c8aca29502bb7255db85d5ebba709a917e54446 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o
 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
 
index dfad289ded3e0214198ab07bb0bab7216d11679b..57b731c83fc8e8d80bb60e1616417dc202368e81 100644 (file)
@@ -70,7 +70,7 @@ static void add_host(struct hpsb_host *host)
 {
         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 @@ static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
         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 @@ static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
         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 @@ static int read_regs(struct hpsb_host *host, int nodeid, quadlet_t *buf,
                 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 @@ static int write_regs(struct hpsb_host *host, int nodeid, int destid,
                 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 @@ static int write_regs(struct hpsb_host *host, int nodeid, int destid,
         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 @@ static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
         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 --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
new file mode 100644 (file)
index 0000000..f5d81c4
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *   video1394.h - driver for OHCI 1394 boards
+ *   Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *                          Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * 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 <linux/pci.h>
+#include <asm/scatterlist.h>
+
+/* 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 --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
new file mode 100644 (file)
index 0000000..6a67e29
--- /dev/null
@@ -0,0 +1,2722 @@
+/*
+ * dv1394.c - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *  video1394.c - video driver for OHCI 1394 boards
+ *  Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *
+ * 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/tqueue.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+
+#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 <dmaas@dcine.com>, Dan Dennedy <dan@dennedy.org>");
+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 --git a/drivers/ieee1394/dv1394.h b/drivers/ieee1394/dv1394.h
new file mode 100644 (file)
index 0000000..d427cfd
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * dv1394.h - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *   video1394.h - driver for OHCI 1394 boards
+ *   Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *                          Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * 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( <a raw DV file>, buf, DV1394_NTSC_FRAME_SIZE );
+                 write( <the dv1394 FD>, 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  |
+        *--------------------------------------*
+                   <ACTIVE> 
+
+       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 */
index 3f337eb5d599963ce6ddf678bc65515adbb964ee..8520403cfb4ee500750a38df01e1a3a72e94e729 100644 (file)
@@ -44,10 +44,11 @@ struct hpsb_highlevel *hpsb_register_highlevel(const char *name,
         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 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
 
         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 hpsb_register_addrspace(struct hpsb_highlevel *hl,
         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 @@ void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
                          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 @@ void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
                            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);                  \
+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);
 }
 
-DEFINE_MULTIPLEXER(add_host)
-DEFINE_MULTIPLEXER(remove_host)
-DEFINE_MULTIPLEXER(host_reset)
-#undef DEFINE_MULTIPLEXER
+void highlevel_remove_host(struct hpsb_host *host)
+{
+        struct list_head *entry;
+        struct hpsb_highlevel *hl;
 
-/* Add one host to our list */
-void highlevel_add_one_host (struct hpsb_host *host)
+        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)
 {
-       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++;
+        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 @@ int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
 
         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 @@ int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
 
         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;
                         }
index f650a300f2b278174696d2b117469517b4f19408..84f927925e5b65338fad891b0acc0fd78216079b 100644 (file)
@@ -91,7 +91,6 @@ struct hpsb_address_ops {
 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);
 
index 01019eddafc688d0326aa38fdf6a0ccc0a4a031d..074db620a0f1c75237eea15bb3e1b46750869464 100644 (file)
  */
 
 #include <linux/config.h>
-
 #include <linux/types.h>
+#include <linux/list.h>
 #include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
+#include <linux/slab.h>
 
 #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)
-{
-       struct list_head *tlh, *hlh;
-        struct hpsb_host_template *tmpl;
-        struct hpsb_host *host;
 
-        spin_lock(&templates_lock);
-
-       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 int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
+{
+        return 0;
+}
 
-        spin_unlock(&templates_lock);
+static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
+{
+        return -1;
 }
 
-int hpsb_inc_host_usage(struct hpsb_host *host)
+static struct hpsb_host_operations dummy_ops = {
+        transmit_packet:  dummy_transmit_packet,
+        devctl:           dummy_devctl
+};
+
+
+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 @@ struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl,
         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;
-}
+        struct list_head *lh;
+        struct hpsb_host *host;
 
-void hpsb_unregister_lowlevel(struct hpsb_host_template *tmpl)
-{
-        shutdown_hosts(tmpl);
-
-        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);
 }
index a58d415ab0b373ad21da15c05494012826317f45..cd4ebe57485e471c3a404054d5903bb20d2af714 100644 (file)
 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 hpsb_host {
         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 @@ struct hpsb_host {
         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 @@ struct hpsb_host {
         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 @@ enum devctl_cmd {
          * 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 @@ enum reset_types {
         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 @@ struct hpsb_host_template {
                                  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 */
index 485b1657bda168f559f46839a2bb539e9cd9eafd..c68a2ace66ce9a065816e4ca8597887e78871089 100644 (file)
 #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 @@ struct ext_selfid {
 
 /*
  * 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 {
index 95ec4af660fe1d1635df782bcedce1c9fbd9e151..487bc71fa416e3e7d9d03e4dd32bcaead18010fa 100644 (file)
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/proc_fs.h>
 #include <asm/bitops.h>
 #include <asm/byteorder.h>
 #include <asm/semaphore.h>
+#include <asm/smplock.h>
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
@@ -135,12 +137,8 @@ void free_hpsb_packet(struct hpsb_packet *packet)
 
 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_reset_bus(struct hpsb_host *host, int type)
 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 @@ int hpsb_bus_reset(struct hpsb_host *host)
  * 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_received(struct hpsb_host *host, quadlet_t sid)
 
 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 @@ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
         }
 
         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 @@ int hpsb_send_packet(struct hpsb_packet *packet)
 {
         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 @@ int hpsb_send_packet(struct hpsb_packet *packet)
         }
 #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 @@ void abort_requests(struct hpsb_host *host)
         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 @@ void abort_timedouts(struct hpsb_host *host)
 }
 
 
+/*
+ * character device dispatching (see ieee1394_core.h)
+ * Dan Maas <dmaas@dcine.com>
+ */
+
+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 @@ static void __exit ieee1394_cleanup(void)
 
        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 @@ module_exit(ieee1394_cleanup);
 /* 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_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);
@@ -876,3 +1104,9 @@ EXPORT_SYMBOL(hpsb_guid_fill_packet);
 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);
index fa5d65fb785b4be5f213a94da4ee3404539197b8..e3d1975d977d64d5650ddc14a3eddfd7b2e2eee6 100644 (file)
@@ -4,6 +4,8 @@
 
 #include <linux/tqueue.h>
 #include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/proc_fs.h>
 #include <asm/semaphore.h>
 #include "hosts.h"
 
@@ -96,7 +98,6 @@ static inline unsigned int get_hpsb_generation(struct hpsb_host *host)
         return atomic_read(&host->generation);
 }
 
-
 /*
  * Queue packet for transmitting, return 0 for failure.
  */
@@ -152,4 +153,65 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet,
 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 --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c
deleted file mode 100644 (file)
index af0b2bc..0000000
+++ /dev/null
@@ -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 <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#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");
index 7086bc7e2caa7b8038362890a3e178978201695e..ca4c26fa88f7a34aaa11089e7fac68796f16d64a 100644 (file)
@@ -246,7 +246,7 @@ int hpsb_packet_success(struct hpsb_packet *packet)
                                  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 @@ int hpsb_packet_success(struct hpsb_packet *packet)
                 return -EAGAIN;
         }
 
-        HPSB_PANIC("reached unreachable code 2 in " __FUNCTION__);
+        HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__);
 }
 
 
index cbeaf43958c58d55e33fd09250f56dfa272894d0..5a77d179283bec86f2146e63b5a9cc9bd27a68de 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/types.h>
 #include <linux/version.h>
 #include <linux/list.h>
+#include <linux/init.h>
 #include <asm/byteorder.h>
 
 
 #define INIT_TQ_HEAD(tq) INIT_LIST_HEAD(&(tq))
 #endif
 
+/* The great kdev_t changeover in 2.5.x */
+#include <linux/kdev_t.h>
+#ifndef minor
+#define minor(dev) MINOR(dev)
+#endif
+
+#ifndef __devexit_p
+#define __devexit_p(x) x
+#endif
+
 /* This showed up around this time */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,12)
 
index d32219154426837b123a2ee49a76c3677c4f5690..0dcb0621a47003561074536b3fa76f5be93a89b6 100644 (file)
 #include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
+#include <linux/completion.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
 
 #include "ieee1394_types.h"
 #include "ieee1394.h"
@@ -59,13 +63,200 @@ static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
 
 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 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption
         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 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption
 
        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 @@ static struct node_entry *find_entry_by_nodeid(nodeid_t nodeid)
        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 @@ static void nodemgr_process_unit_directory(struct node_entry *ne,
                                           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 @@ static void nodemgr_process_unit_directory(struct node_entry *ne,
                        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 @@ static void nodemgr_process_unit_directory(struct node_entry *ne,
                        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 @@ static void dump_directories (struct node_entry *ne)
 #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 @@ static void nodemgr_call_policy(char *verb, struct unit_directory *ud)
        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 @@ nodemgr_call_policy(char *verb, struct unit_directory *ud)
 {
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
        HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled");
-#else
-       return;
 #endif
+       return;
 } 
 
 #endif /* CONFIG_HOTPLUG */
@@ -582,13 +905,13 @@ static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
                                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 @@ static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
        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 @@ static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid,
        int header_count;
        unsigned header_size;
        quadlet_t quad;
+       int ret;
 
 retry_configrom:
 
@@ -636,9 +958,10 @@ retry_configrom:
         * 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 @@ retry_configrom:
        }
 
        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 @@ static void nodemgr_remove_node(struct node_entry *ne)
 {
        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 @@ static void nodemgr_remove_node(struct node_entry *ne)
 
 /* 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 @@ static void nodemgr_node_probe(void *data)
        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 @@ static void nodemgr_add_host(struct hpsb_host *host)
         * 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 @@ static void nodemgr_host_reset(struct hpsb_host *host)
                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 @@ static void nodemgr_remove_host(struct hpsb_host *host)
        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_ops nodemgr_ops = {
 
 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 init_ieee1394_nodemgr(void)
 void cleanup_ieee1394_nodemgr(void)
 {
         hpsb_unregister_highlevel(hl);
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);
+#endif
 }
index 46f574e64155a36b00331fc63f3a1f8ef71902ef..7d67add26a300aa926a88e2b21fa32c27dbfb95f 100644 (file)
 #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 @@ struct bus_options {
 #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 @@ struct unit_directory {
        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 @@ struct unit_directory {
 
        /* 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 @@ struct node_entry {
        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)
index 752a6216c3a4c21bef5decb1a31d258fd3fbd434..7838f495f450f67ff2e569b0fbd5c190eeded93b 100644 (file)
  *  . Config ROM generation
  */
 
+/* Issues:
+ *
+ * - devctl BUS_RESET should treat arg as reset type
+ *
+ */
+
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/init.h>
 
 #ifdef CONFIG_ALL_PPC
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 #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 @@ printk(KERN_INFO "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
 #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 @@ printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
 #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 <bcollins@debian.org>";
+       "$Revision: 1.91 $ Ben Collins <bcollins@debian.org>";
 
 /* 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 @@ static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
        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 <jamesg@Filanet.com>:
                 * We had an error, generate another bus reset in response.  */
@@ -328,6 +318,9 @@ static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
                }
                return;
        }
+
+       /* SelfID Ok, reset error counter. */
+       ohci->self_id_errors = 0;
        
        size = ((self_id_count & 0x00001FFC) >> 2) - 1;
        q++;
@@ -361,7 +354,7 @@ static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
        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 @@ static int ohci_soft_reset(struct ti_ohci *ohci) {
                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 @@ static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq)
        for (i=0; i<d->num_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 int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
 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 @@ static int ohci_initialize(struct hpsb_host *host)
        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 @@ static int ohci_initialize(struct hpsb_host *host)
        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;i<ohci->nb_iso_rcv_ctx;i++) {
                reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i,
                          0xffffffff);
@@ -580,10 +554,6 @@ static int ohci_initialize(struct hpsb_host *host)
        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;i<ohci->nb_iso_xmit_ctx;i++) {
                reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i,
                          0xffffffff);
@@ -645,32 +615,19 @@ static int ohci_initialize(struct hpsb_host *host)
                  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 @@ static void insert_packet(struct ti_ohci *ohci,
        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 @@ static void insert_packet(struct ti_ohci *ohci,
                         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 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
        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 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                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 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                 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 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                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 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                 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 @@ static void ohci_irq_handler(int irq, void *dev_id,
         * 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 @@ static void ohci_irq_handler(int irq, void *dev_id,
        /* 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 @@ static void ohci_irq_handler(int irq, void *dev_id,
                 * 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 @@ static void ohci_irq_handler(int irq, void *dev_id,
                        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 @@ static void ohci_irq_handler(int irq, void *dev_id,
                        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 @@ static void ohci_irq_handler(int irq, void *dev_id,
 
                /* 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 @@ static __inline__ int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *b
 {
        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 @@ static __inline__ int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *b
                        }
                        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 @@ static void dma_rcv_tasklet (unsigned long data)
        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 @@ static void dma_rcv_tasklet (unsigned long data)
                        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 @@ static void dma_rcv_tasklet (unsigned long data)
                                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 @@ static void dma_rcv_tasklet (unsigned long data)
                               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 @@ static int free_dma_rcv_ctx(struct dma_rcv_ctx **d)
        }
        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 int free_dma_rcv_ctx(struct dma_rcv_ctx **d)
 }
 
 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 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
 
        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 @@ static int free_dma_trm_ctx(struct dma_trm_ctx **d)
                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 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
        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; i<d->num_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 @@ struct config_rom_ptr {
 
 #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 @@ static void ohci_init_config_rom(struct ti_ohci *ohci)
        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 @@ static void ohci_init_config_rom(struct ti_ohci *ohci)
        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 @@ static size_t ohci_get_rom(struct hpsb_host *host, const quadlet_t **ptr)
        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 @@ int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data,
                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;
+
+\f
+
+/***********************************
+ * 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 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
                                     &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 @@ static int __devinit ohci1394_add_one(struct pci_dev *dev, const struct pci_devi
                       &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 @@ static void remove_card(struct ti_ohci *ohci)
 
                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),
+};
+
+\f
+
+/***********************************
+ * 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 @@ void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
        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<<channel;
-               csrSel = 2;
-       }
-       else {
-               chan = 1<<(channel-32);
-               csrSel = 3;
-       }
-       if (ohci_compare_swap(ohci, &data1, 0, csrSel)<0) {
-               PRINT(KERN_INFO, ohci->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 <sebastien.rougeaux@anu.edu.au>");
-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 <sebastien.rougeaux@anu.edu.au>");
+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;
+
+       ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops,
+                                                OHCI1394_DRIVER_NAME);
+       if (!ohci1394_driver) {
+               PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed");
+               return -ENOMEM;
        }
-       if ((ret = pci_module_init(&ohci1394_driver))) {
-               PRINT_G(KERN_ERR, "PCI module init failed");
-               hpsb_unregister_lowlevel(&ohci_template);
+
+       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;
index 3ab491270135363168937790e3f6291a74c447b9..141f485e2b6e245952b15bc01e32d2c7d866ee30 100644 (file)
@@ -76,9 +76,13 @@ struct at_dma_prg {
        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 @@ struct dma_rcv_ctx {
 
 /* 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 @@ struct dma_trm_ctx {
        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 ti_ohci {
        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 @@ struct ti_ohci {
 
        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 @@ static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
 /* 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 @@ static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
 #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 @@ static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
 
 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
 
index 1038837ca567983dd8ce7150aa8ba5dc9bd44259..0e6cebd5641b2187e8b82a63e2f1bce28f03c47d 100644 (file)
 #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 @@ static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid)
 #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 @@ static int get_phy_reg(struct ti_lynx *lynx, int addr)
         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 @@ static int get_phy_reg(struct ti_lynx *lynx, int addr)
                 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 @@ static int set_phy_reg(struct ti_lynx *lynx, int addr, int val)
         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 @@ static int sel_phy_reg_page(struct ti_lynx *lynx, int page)
         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 @@ static int sel_phy_reg_port(struct ti_lynx *lynx, int port)
         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 @@ static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host)
 
         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 @@ static void send_next(struct ti_lynx *lynx, int what)
 }
 
 
-#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 @@ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
         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 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                 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 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
                 } else {
                         MOD_DEC_USE_COUNT;
                 }
+
+                retval = 1;
                 break;
 
         case ISO_LISTEN_CHANNEL:
@@ -1010,8 +851,8 @@ static ssize_t mem_read(struct file *file, char *buffer, size_t count,
                 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 lynx_irq_handler(int irq, void *dev_id,
         }
 }
 
+
 static void iso_rcv_bh(struct ti_lynx *lynx)
 {
         unsigned int idx;
@@ -1335,44 +1177,97 @@ static void iso_rcv_bh(struct ti_lynx *lynx)
 }
 
 
+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");
+        error = -ENOMEM;
+
+       host = hpsb_alloc_host(lynx_driver, sizeof(struct ti_lynx));
+        if (!host) FAIL("failed to allocate control structure memory");
 
-        lynx->state = have_host_struct;
-       lynx->host->hostdata = lynx;
-        lynx->id = num_of_cards-1;
+        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 @@ static int __devinit add_card(struct pci_dev *dev,
         }
 #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 @@ static int __devinit add_card(struct pci_dev *dev,
                 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 @@ static size_t get_lynx_rom(struct hpsb_host *host, const quadlet_t **ptr)
         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 @@ static struct pci_device_id pci_table[] __devinitdata = {
        { }                     /* 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 <andreas.bombe@munich.netsurf.de>");
@@ -1635,30 +1551,52 @@ MODULE_LICENSE("GPL");
 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);
index 58a0703f304d9d113d99eb3077bc138c19b98a32..8797746453485a1638e63270cbbd5e653eafbc94 100644 (file)
@@ -40,8 +40,8 @@ struct ti_lynx {
                 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;
index 4be8872a409fcaf6a131afaa8ceb2528eeb7b11a..9d975a60f76c75e56f75114de2b2ead9537564d5 100644 (file)
@@ -467,7 +467,7 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
                                 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 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
                                 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 @@ static int raw1394_open(struct inode *inode, struct file *file)
 {
         struct file_info *fi;
 
-        if (minor(inode->i_rdev)) {
+        if (ieee1394_file_to_instance(file) > 0) {
                 return -ENXIO;
         }
 
@@ -983,7 +983,7 @@ static int raw1394_release(struct inode *inode, struct file *file)
                 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 @@ static int __init init_raw1394(void)
                 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 int __init init_raw1394(void)
 
 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);
 }
index 888e918e51d8de924345bb7403f4a0fce22e2e3f..907eba202b0eae998c8990aefb7ffcd3f6d0fea1 100644 (file)
@@ -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
  *     - 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
  *
  *       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:
  *
  *                 when we register our driver.  This change 
  *                 automtically adds hotplug support to the driver.
  *                                 Kristian Hogsberg <hogsberg@users.sf.net>
+ *
+ *      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 <hogsberg@users.sf.net>
+ *     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.
  */
-    
-\f
+
 
 /*
  * Includes
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
 #include <linux/smp_lock.h>
+#include <linux/init.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/io.h>
 #include <asm/scatterlist.h>
 
+#ifdef CONFIG_KBUILD_2_5
+#include <scsi.h>
+#include <hosts.h>
+#include <sd.h>
+#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"
 #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"
 
 /*
  * Module load parameter definitions
  */
 
-/*
- * 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 @@ MODULE_DEVICE_TABLE(ieee1394, sbp2_id_table);
  */
 
 #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 @@ static u32 global_outstanding_command_orbs = 0;
 
 #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 @@ static u32 global_outstanding_dmas = 0;
 #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 @@ static spinlock_t sbp2_host_info_lock = SPIN_LOCK_UNLOCKED;
  * 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 int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i
 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 @@ static void sbp2util_free_command_dma(struct sbp2_command_info *command)
        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 @@ void sbp2_cleanup(void)
                hpsb_unregister_highlevel(sbp2_hl_handle);
                sbp2_hl_handle = NULL;
        }
-       return;
 }
 
 static int sbp2_probe(struct unit_directory *ud)
@@ -889,8 +919,10 @@ static void sbp2_disconnect(struct unit_directory *ud)
        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 @@ static void sbp2_update(struct unit_directory *ud)
                 */
                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 @@ static void sbp2_add_host(struct hpsb_host *host)
 
        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 @@ static struct sbp2scsi_host_info *sbp2_find_host_info(struct hpsb_host *host)
 
        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 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 the host is removed
+ * 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 @@ static void sbp2_remove_host(struct hpsb_host *host)
 
        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 @@ alloc_fail_first:
         */
        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 @@ alloc_fail_first:
 }
 
 /*
- * 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 @@ static void sbp2_remove_device(struct sbp2scsi_host_info *hi,
                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 @@ static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_insta
 
        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 @@ static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_insta
 
        /*
         * 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 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id)
        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 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id)
 
                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 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id)
                         * 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 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id)
                         * 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 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id)
                         * 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 @@ static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id
        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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
                                   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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
         * 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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
                        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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
 
                        /* 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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
 
                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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
                        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 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
 
                        /* 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 @@ static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_i
        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 @@ static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_i
                }
 
                scsi_id->last_orb = command_orb;
+               scsi_id->last_orb_dma = command->command_orb_dma;
 
        } else {
 
@@ -2014,6 +2094,9 @@ static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_i
                        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 @@ static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_i
                }
 
                scsi_id->last_orb = command_orb;
+               scsi_id->last_orb_dma = command->command_orb_dma;
 
        }
                return(0);
@@ -2053,12 +2137,17 @@ static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_insta
                             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 @@ static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_insta
            (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
@@ -2087,15 +2176,25 @@ static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_insta
                return(-EIO);
        }
 
+       /*
+        * 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 @@ static int sbp2_send_split_command(struct sbp2scsi_host_info *hi, struct scsi_id
 
                        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 @@ static int sbp2_send_split_command(struct sbp2scsi_host_info *hi, struct scsi_id
 
        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 @@ static void sbp2_check_sbp2_response(struct sbp2scsi_host_info *hi,
                                     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 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
        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 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
                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 @@ static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi,
                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 @@ static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi
                        /*
                         * 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 @@ static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi
                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:
@@ -2732,19 +2858,6 @@ static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi
                sbp2_check_sbp2_response(hi, scsi_id, SCpnt);
        }
 
-       /*
-        * 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 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
        unsigned long flags;
 
        SBP2_ERR("aborting sbp2 command");
-
+       print_command (SCpnt->cmnd);
+        
        if (scsi_id) {
 
                /*
@@ -2797,6 +2911,14 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
                        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 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
                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 @@ static int sbp2scsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
                hpsb_reset_bus(hi->host, LONG_RESET);
        }
 
-       return(SCSI_RESET_SUCCESS);
+       return(SUCCESS);
 }
 
 /*
@@ -2861,126 +2983,146 @@ static int sbp2scsi_biosparam (Scsi_Disk *disk, kdev_t dev, int geom[])
 }
 
 /*
- * 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];
+
+       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;
+}
 
-       global_scsi_tpnt = tpnt;
+
+MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
+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";
-}
+       if (SCSI_UNREGISTER_HOST(&scsi_driver_template))
+               SBP2_ERR("sbp2_module_exit: couldn't unregister scsi driver");
 
-MODULE_AUTHOR("James Goodwin <jamesg@filanet.com>");
-MODULE_DESCRIPTION("IEEE-1394 SBP-2 protocol driver");
-MODULE_SUPPORTED_DEVICE(SBP2_DEVICE_NAME);
-MODULE_LICENSE("GPL");
-
-/* 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);
index 505c65a7564462e1f32c573a1a31ca7de1e457a0..559fa3951a1461e815617b7d83c7d745624eb9fe 100644 (file)
 #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 @@ struct sbp2_status_block {
 #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 @@ struct sbp2_status_block {
 #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 @@ struct sbp2_status_block {
 #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 @@ struct sbp2_request_packet {
 
 };
 
+
+/* 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 @@ struct scsi_id_instance_data {
         * 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;
@@ -402,6 +414,13 @@ struct sbp2scsi_host_info {
        spinlock_t sbp2_command_lock;
        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
@@ -480,7 +499,8 @@ static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi,
                                   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 @@ static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id
 /*
  * 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 */
index cc90d13d125b887c0008d458eb6829c499f083ec..b435f99dd3741f2dc93f11655d4361cabafaf9db 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <linux/sched.h>
+#include <asm/segment.h>
 #include <linux/types.h>
 #include <linux/wrapper.h>
 #include <linux/vmalloc.h>
+#include <linux/timex.h>
+#include <linux/mm.h>
 
 #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 it_dma_prg {
 
 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_iso_ctx {
         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 @@ struct dma_iso_ctx {
        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: " fmt "\n" , ## args)
 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 spinlock_t video1394_cards_lock = SPIN_LOCK_UNLOCKED;
 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 @@ static int free_dma_iso_ctx(struct dma_iso_ctx **d)
 {
        int i;
        struct ti_ohci *ohci;
+       unsigned long *usage;
        
        if ((*d)==NULL) return -1;
 
@@ -303,11 +306,19 @@ static int free_dma_iso_ctx(struct dma_iso_ctx **d)
 
        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 int free_dma_iso_ctx(struct dma_iso_ctx **d)
 }
 
 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 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
        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 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
 
        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 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
                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 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc,
                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 @@ static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags)
 }
 
 /* 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;i<ohci->nb_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;i<ohci->nb_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 @@ int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d)
                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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
 
                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; i<ISO_CHANNELS; i++) {
+                       if (!(ohci->ISO_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<<v.channel;
@@ -923,7 +975,7 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                        }
 
                        video->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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                        }
                        
                        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
        {
                int channel;
                u64 mask;
-               int i;
 
                if(copy_from_user(&channel, (void *)arg, sizeof(int)))
                        return -EFAULT;
@@ -1005,24 +1056,23 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                
        }
        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
                        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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
        {
                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 @@ static int video1394_ioctl(struct inode *inode, struct file *file,
        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 @@ int video1394_mmap(struct file *file, struct vm_area_struct *vma)
                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 @@ int video1394_mmap(struct file *file, struct vm_area_struct *vma)
         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 @@ int video1394_mmap(struct file *file, struct vm_area_struct *vma)
 
 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 @@ static int video1394_release(struct inode *inode, struct file *file)
                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 @@ static int video1394_release(struct inode *inode, struct file *file)
         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 @@ static int video1394_release(struct inode *inode, struct file *file)
                        mask = (u64)0x1<<video->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 @@ static int video1394_release(struct inode *inode, struct file *file)
                                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 int video1394_release(struct inode *inode, struct file *file)
 }
 
 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;i<video->ohci->nb_iso_rcv_ctx-1;i++)
-               if (isoRecvIntEvent & (1<<(i+1))) 
-                       wakeup_dma_ir_ctx(video->ohci,
-                                         video->ir_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 (isoXmitIntEvent & (1<<i)) 
-                       wakeup_dma_it_ctx(video->ohci,
-                                         video->it_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 @@ static int video1394_init(struct ti_ohci *ohci)
        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 @@ static int video1394_init(struct ti_ohci *ohci)
        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 @@ static void remove_card(struct video_card *video)
 {
        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 @@ static void video1394_remove_host (struct hpsb_host *host)
 {
        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 @@ static void video1394_add_host (struct hpsb_host *host)
        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 @@ static void __exit video1394_exit_module (void)
        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 @@ static int __init video1394_init_module (void)
        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;
 }
 
index f352ac7616bff3a9a76c34ee6a19b7fb5f0e30e6..4ef8f2b5e9cf0e4544d71b51a33fadab180a758c 100644 (file)
@@ -35,11 +35,12 @@ enum {
        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 @@ enum {
 #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_queue_variable {
 struct video1394_wait {
        unsigned int channel;
        unsigned int buffer;
+       struct timeval filltime;        /* time of buffer full */
 };
 
 
index 77e8488ce86e251d6aa050ecd2958876c0a15677..0dd3cc6eaae95bac2d528a734cd9d55a261c9abd 100644 (file)
@@ -83,6 +83,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
    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
 
index ef6aaca1a42dbbf0428d9836c6c9cf65a9460180..8fdf469432d8686451f2dbbd2c63861681da34c6 100644 (file)
@@ -10,7 +10,7 @@ EXTRA_CFLAGS      += -DHISAX_MAX_CARDS=$(CONFIG_HISAX_MAX_CARDS)
 
 # 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_SEDLBAUER_CS)      += sedlbauer_cs.o
 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)
index 9215b06ae57ca40df73f79b9ed2bc6aa167fd8fe..bed060a41eb0808b7c45777bd57b7f3e1eb72f26 100644 (file)
@@ -561,7 +561,7 @@ hdlc_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hdlc_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_hdlc(struct PStack *st, struct BCState *bcs)
        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);
index 47cee5b068109495ca1939308f86e0fdecf205ae..918a39b41c00dad959d7c42f51e287bf38e851ff 100644 (file)
@@ -250,7 +250,6 @@ lli_leased_in(struct FsmInst *fi, int event, void *arg)
        }
 }
 
-
 /*
  * Dial out
  */
@@ -264,7 +263,7 @@ lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
                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_prep_dialout(struct FsmInst *fi, int event, void *arg)
                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_resume(struct FsmInst *fi, int event, void *arg)
                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 @@ lli_deliver_call(struct FsmInst *fi, int event, void *arg)
                        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 @@ lli_send_dconnect(struct FsmInst *fi, int event, void *arg)
        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 @@ lli_send_alert(struct FsmInst *fi, int event, void *arg)
        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 @@ lli_send_redir(struct FsmInst *fi, int event, void *arg)
 {
        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 @@ lli_init_bchan_in(struct FsmInst *fi, int event, void *arg)
        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 @@ lli_setup_rsp(struct FsmInst *fi, int event, void *arg)
        } 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 @@ lli_suspend(struct FsmInst *fi, int event, void *arg)
 {
        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 @@ lli_disconnect_req(struct FsmInst *fi, int event, void *arg)
                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 @@ lli_disconnect_reject(struct FsmInst *fi, int event, void *arg)
                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 @@ lli_reject_req(struct FsmInst *fi, int event, void *arg)
 #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 @@ lli_disconn_bchan(struct FsmInst *fi, int event, void *arg)
 
        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 @@ lli_release_bchan(struct FsmInst *fi, int event, void *arg)
 
        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 @@ lli_abort(struct FsmInst *fi, int event, void *arg)
        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_release_req(struct FsmInst *fi, int event, void *arg)
                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 @@ lli_failure_a(struct FsmInst *fi, int event, void *arg)
        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 @@ dchan_l3l4(struct PStack *st, int pr, void *arg)
        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 @@ init_PStack(struct PStack **stp) {
        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 @@ init_d_st(struct Channel *chanp)
        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 @@ CallcNewChan(struct IsdnCardState *csta) {
        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 @@ init_b_st(struct Channel *chanp, int incoming)
                        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 @@ init_b_st(struct Channel *chanp, int incoming)
                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 @@ leased_l4l3(struct PStack *st, int pr, void *arg)
                        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_command(isdn_ctrl * ic)
                                                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_command(isdn_ctrl * ic)
                                                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 @@ HiSax_command(isdn_ctrl * ic)
                                                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 @@ HiSax_command(isdn_ctrl * ic)
                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 @@ HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb)
                        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
index 56f2e69bee9aa9026b24755f4e4e82f48164c140..25280172bb1b4716ed9e057a1582c69b07726560 100644 (file)
@@ -1776,7 +1776,7 @@ int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[],
        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 @@ static void hisax_bh(struct IsdnCardState *cs)
                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 @@ static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg)
                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 @@ static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
        // 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 @@ static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
                }
                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 @@ static void hisax_d_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs)
        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);
index 87783425773fae1257c98a7b8b9bb55a6f092fe6..6ef49f05b299b1e1928dfa3de77267d2502d93a8 100644 (file)
@@ -591,7 +591,7 @@ modem_l2l1(struct PStack *st, int pr, void *arg)
                }
        } 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 @@ setstack_elsa(struct PStack *st, struct BCState *bcs)
                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 @@ setstack_elsa(struct PStack *st, struct BCState *bcs)
                        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;
index cb8eb837f5c269c73b34f5b4502ab22066010b05..7695a2ccb4091ceabf03ba1ba6e7a99605077b52 100644 (file)
@@ -545,7 +545,7 @@ hfc_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hfc_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_2b(struct PStack *st, struct BCState *bcs)
        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 @@ HFCD_l1hw(struct PStack *st, int pr, void *arg)
 #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;
index e329f0480d0fd3c7c3a9391bcbc8339ef3f8a8d4..2b7e330bc343aaab02171d976f065c35007a539a 100644 (file)
@@ -507,7 +507,7 @@ hfc_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hfc_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_hfc(struct PStack *st, struct BCState *bcs)
        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);
index eb5cf7de2d694d5bec71c6e9706c93068bc65b9c..bcebb3b95c8abfb56307facb8ca6e6c895a288bc 100644 (file)
@@ -776,7 +776,7 @@ dch_nt_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
                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 @@ HFCPCI_l1hw(struct PStack *st, int pr, void *arg)
 #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 @@ hfcpci_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hfcpci_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_2b(struct PStack *st, struct BCState *bcs)
        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);
index ad9690ff010ee887bd6db3a8add50b24316cc261..a01628bb3eab444b1b3dbfcabdbe4dd41e463940 100644 (file)
@@ -632,7 +632,7 @@ dch_nt_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
                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 @@ HFCSX_l1hw(struct PStack *st, int pr, void *arg)
 #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 @@ hfcsx_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hfcsx_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_2b(struct PStack *st, struct BCState *bcs)
        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);
index cee7b10a29aa472ae63c977c6555af115c4c0798..4915d01ade5e06222babe1091eeb2cc8c8e13585 100644 (file)
@@ -203,9 +203,9 @@ struct Layer1 {
        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 Layer2 {
        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 Layer2 {
 };
 
 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 Layer3 {
 };
 
 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 @@ char *HiSax_getrev(const char *revision);
 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 --git a/drivers/isdn/hisax/hisax_fcclassic.c b/drivers/isdn/hisax/hisax_fcclassic.c
new file mode 100644 (file)
index 0000000..a506158
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Driver for AVM Fritz!classic (ISA) ISDN card
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * 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 <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#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 <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+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 --git a/drivers/isdn/hisax/hisax_fcclassic.h b/drivers/isdn/hisax/hisax_fcclassic.h
new file mode 100644 (file)
index 0000000..9a606d2
--- /dev/null
@@ -0,0 +1,18 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include "hisax_hscx.h"
+
+#include <linux/pci.h>
+
+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 --git a/drivers/isdn/hisax/hisax_hscx.c b/drivers/isdn/hisax/hisax_hscx.c
new file mode 100644 (file)
index 0000000..bbeecd3
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Driver for HSCX
+ * High-Level Serial Communcation Controller Extended
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#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 <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+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 --git a/drivers/isdn/hisax/hisax_hscx.h b/drivers/isdn/hisax/hisax_hscx.h
new file mode 100644 (file)
index 0000000..6177a68
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __HISAX_HSCX_H__
+#define __HISAX_HSCX_H__
+
+#include <linux/kernel.h>
+#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
index 6a261e0befa1927530793179a26afd433a5757d5..b5cb8e3c9ad4276ebf3ceba04d2b19a8af78be40 100644 (file)
@@ -134,7 +134,7 @@ hscx_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ hscx_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_hscx(struct PStack *st, struct BCState *bcs)
        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);
index 2f224215638a5482b136898077b107e8d8f4f5e8..e6d655574a49e37d0c2f5cf0384ba08cf66c0a65 100644 (file)
@@ -89,7 +89,7 @@ icc_bh(struct IsdnCardState *cs)
                        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 @@ ICC_l1hw(struct PStack *st, int pr, void *arg)
 #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 @@ dbusy_timer_handler(struct IsdnCardState *cs)
                        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 {
index 61a0fff4278ec673bf8a5470c87520f46a5fd670..3d6b67428bb925de39ef3854fd4a40e76cab0bba 100644 (file)
@@ -93,7 +93,7 @@ isac_bh(struct IsdnCardState *cs)
                        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 @@ ISAC_l1hw(struct PStack *st, int pr, void *arg)
 #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 @@ dbusy_timer_handler(struct IsdnCardState *cs)
                        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 {
index 01986d8ed454fd88273242f812650df4121622dc..766428ed9e9d4a7a78e0036b8f6dfeceb268937c 100644 (file)
@@ -1602,7 +1602,7 @@ isar_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ isar_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_isar(struct PStack *st, struct BCState *bcs)
        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);
index 35b36bb14d90d4728f44419273efed89d8d1250b..a191be34b8973207b8de80b253422f1139156e25 100644 (file)
@@ -160,9 +160,9 @@ L1activated(struct IsdnCardState *cs)
        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 @@ L1deactivated(struct IsdnCardState *cs)
        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 @@ DChannel_proc_xmt(struct IsdnCardState *cs)
        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 @@ DChannel_proc_rcv(struct IsdnCardState *cs)
                        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 @@ DChannel_proc_rcv(struct IsdnCardState *cs)
                        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 @@ BChannel_proc_xmt(struct BCState *bcs)
        }
 
        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 @@ BChannel_proc_rcv(struct BCState *bcs)
                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 @@ l1b_timer_act(struct FsmInst *fi, int event, void *arg)
        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 @@ l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
        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 @@ dch_l2l1(struct PStack *st, int pr, void *arg)
                                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_HiSax(struct PStack *st, struct IsdnCardState *cs)
        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);
 }
index 4b2a31196e19a53722895099eca8d7aae9fd970f..fb593d1f56980954b4362c2618e0130da60fb43a 100644 (file)
@@ -27,14 +27,14 @@ static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 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 @@ enqueue_super(struct PStack *st,
 {
        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 @@ st5_dl_release_l2l3(struct PStack *st)
                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 @@ l2_got_ui(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_release(struct FsmInst *fi, int event, void *arg)
 {
        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 @@ l2_start_multi(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_restart_multi(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_connected(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
                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 @@ invoke_retransmission(struct PStack *st, unsigned int nr)
                        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 @@ l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
                        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 @@ l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
                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 @@ l2_got_iframe(struct FsmInst *fi, int event, void *arg)
        }
 
        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 @@ l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
                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 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
                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 @@ l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
                        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 @@ l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
 
        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 @@ l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_tei_remove(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
        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 @@ l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
 
        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 @@ l2_persistant_da(struct FsmInst *fi, int event, void *arg)
        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 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg)
                                        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 @@ l2m_debug(struct FsmInst *fi, char *fmt, ...)
 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 @@ transl2_l3l2(struct PStack *st, int pr, void *arg)
        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 @@ transl2_l3l2(struct PStack *st, int pr, void *arg)
 void
 setstack_transl2(struct PStack *st)
 {
-       st->l3.l3l2 = transl2_l3l2;
+       st->l2.l3l2 = transl2_l3l2;
 }
 
 void
index 7e447fb8ed1deb36f7c0a5943da2c5040bc0a3c0..0cdab1b73fac7c167039f4e9a8ddfab86e9adc2e 100644 (file)
@@ -23,3 +23,4 @@
 #define RSP    1
 
 #define LC_FLUSH_WAIT 1
+
index 02d23b8b77782a67bd8cb809efc7bf84d6444a5f..4eccc838061290631cb67eda78ee35ec2b69f289 100644 (file)
@@ -163,7 +163,7 @@ newl3state(struct l3_process *pc, int state)
 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 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp)
        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 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp)
        } 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 @@ setstack_l3dc(struct PStack *st, struct Channel *chanp)
 
 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 @@ setstack_l3bc(struct PStack *st, struct Channel *chanp)
        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 @@ lc_activate(struct FsmInst *fi, int event, void *arg)
        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 @@ lc_connect(struct FsmInst *fi, int event, void *arg)
 
        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 @@ lc_connected(struct FsmInst *fi, int event, void *arg)
        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 @@ lc_release_req(struct FsmInst *fi, int event, void *arg)
                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 @@ l3_msg(struct PStack *st, int pr, void *arg)
        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;
 
index d866dd2777304b6036310e80e2f8ae34ffa72476..41b109ea7b5ecae4e45af228c1490109fe84e639 100644 (file)
@@ -177,7 +177,7 @@ jade_l2l1(struct PStack *st, int pr, void *arg)
        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 @@ jade_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ setstack_jade(struct PStack *st, struct BCState *bcs)
        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);
index 4cb998d2792fe78eb062fb4676b5b119eddf0108..93fb3e94e1a2b1e38436b23386878035e0729583 100644 (file)
@@ -223,7 +223,7 @@ l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
                        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 @@ l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
        }
        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 @@ l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
                }
                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 @@ l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
                }
                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 @@ l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
        }
        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 @@ l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg)
        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 @@ static void
 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 @@ l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg)
 {
         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 @@ l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg)
         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 @@ setstack_1tr6(struct PStack *st)
 {
        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;
 
index 2f13dd3cf8b4425432d7885c23e2fdfa819fdd9c..8d84776e73930c701daa2c7e8dadafe21c3db4b7 100644 (file)
@@ -449,7 +449,7 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
                             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 @@ l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
                             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 @@ l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_connect(struct l3_process *pc, u_char pr, void *arg)
        /* 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 @@ l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
                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_release(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_proceed_req(struct l3_process *pc, u_char pr,
 {
        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 @@ l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_notify(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
                /* 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 @@ l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
        } 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 @@ l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
        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 @@ static void
 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 @@ l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
        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 @@ static void
 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 @@ l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
                 * 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 @@ l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
         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 @@ l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg)
         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 @@ setstack_dss1(struct PStack *st)
        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;
index 8811f3ca15c69f67afba8b3d7bc0bad133ead322..a1e99dee887c127d5b561fc416041d8068bfb20c 100644 (file)
@@ -377,7 +377,7 @@ l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
                             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 @@ l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
                             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 @@ l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_connect(struct l3_process *pc, u_char pr, void *arg)
        /* 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 @@ l3ni1_alerting(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_setup(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg)
                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_release(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_proceed_req(struct l3_process *pc, u_char pr,
 {
        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 @@ l3ni1_progress(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_notify(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_information(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg)
                /* 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 @@ l3ni1_t302(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_t303(struct l3_process *pc, u_char pr, void *arg)
        } 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 @@ l3ni1_t304(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_t310(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_t313(struct l3_process *pc, u_char pr, void *arg)
        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 @@ static void
 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 @@ l3ni1_t318(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_t319(struct l3_process *pc, u_char pr, void *arg)
        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 @@ static void
 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 @@ l3ni1_status(struct l3_process *pc, u_char pr, void *arg)
                 * 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 @@ l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
                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 @@ l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg)
        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 @@ l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
         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 @@ l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg)
         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 @@ static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *sk
        {
                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 @@ static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *sk
        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 @@ static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg )
 
                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 @@ setstack_ni1(struct PStack *st)
        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;
index 7482de0f6d1e8bd666c963c184cf5d2a0dcdbf95..482957140b82cbb0d6737ed9de2aff434df4a9d1 100644 (file)
@@ -847,7 +847,7 @@ tiger_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ tiger_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_tiger(struct PStack *st, struct BCState *bcs)
        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);
index 2881487aab01187cba703d087a4b1d9e2228dd8c..3c6e70880408ec63a4c437edb20ee12c602ab123 100644 (file)
@@ -478,7 +478,7 @@ extern int st5481_debug;
   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;
index b0b3d06627395e814f01e1723d2119911ba576b6..cd9b0389f4917bb221aaf7e72fb2aa1c21e7502a 100644 (file)
@@ -297,7 +297,7 @@ static void usb_d_out(struct st5481_adapter *adapter, int buf_nr)
        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;
index 0aa5edf990549b1557153f561f5342141c7b28da..a3e776f4d3e8682fa0101dcbb21f8b4b377cfcbf 100644 (file)
@@ -235,7 +235,7 @@ int __devinit st5481_setup_usb(struct st5481_adapter *adapter)
        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 @@ void st5481_release_in(struct st5481_in *in)
  */
 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;
        
index e08446d44abd3c212fb1dc54ba3d0ff6cbf0fbf8..9439ba57fa8cb2bdc61358a10e48135973e062b1 100644 (file)
@@ -120,7 +120,7 @@ put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
        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 @@ tei_id_assign(struct FsmInst *fi, int event, void *arg)
        } 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 @@ tei_id_remove(struct FsmInst *fi, int event, void *arg)
        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 @@ tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
                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 @@ tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
        } 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 @@ tei_l2tei(struct PStack *st, int pr, void *arg)
                        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);
                }
index d8e82616c30a9d6be290ff295c3bd14f995bfd03..7e0e6cff798229ffb4925e584c2a59e36b373216 100644 (file)
@@ -108,7 +108,7 @@ W6692_bh(struct IsdnCardState *cs)
                        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 @@ W6692_l1hw(struct PStack *st, int pr, void *arg)
 #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 @@ dbusy_timer_handler(struct IsdnCardState *cs)
                        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 @@ W6692_l2l1(struct PStack *st, int pr, void *arg)
                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 @@ W6692_l2l1(struct PStack *st, int pr, void *arg)
                        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 @@ setstack_w6692(struct PStack *st, struct BCState *bcs)
        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);
index 933880c1638359f9545271f61bc554fa7ebac9c8..d6fa2e0ef2d7afe5c88c6bb9866d80277986b1d7 100644 (file)
@@ -1124,6 +1124,18 @@ isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 #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 @@ isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 #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)
-{
-       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)
+static struct file_operations isdn_ctrl_fops =
 {
-       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 @@ static void isdn_register_devfs(int k)
        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] =
index 5d60bb2d4f729c13aa13cb4b48446efb1ed43ba9..95d9f72a4f5e0555badff97d715c692c9fbaf975 100644 (file)
@@ -9,7 +9,7 @@
  *
  */
 
-#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/isdn.h>
 #include <linux/smp_lock.h>
 #include <linux/poll.h>
@@ -269,7 +269,7 @@ isdn_ppp_get_slot(void)
  * 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 @@ isdn_ppp_open(struct inode *ino, struct file *file)
 /*
  * 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 @@ set_arg(void *b, void *val,int len)
 /*
  * 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 @@ isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned
        return 0;
 }
 
-unsigned int
+static unsigned int
 isdn_ppp_poll(struct file *file, poll_table * wait)
 {
        unsigned int mask;
@@ -690,8 +690,8 @@ isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
  *           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 @@ isdn_ppp_read(struct file *file, char *buf, int count, loff_t *off)
  * 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;
@@ -830,6 +830,18 @@ isdn_ppp_write(struct file *file, const char *buf, int count, loff_t *off)
        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.
  */
index 9dad0b5eac1b6c48e6570f979ca0b201d1ea49cb..3b59bdcd2c32de40ceadb04dfb2fa573921c21fd 100644 (file)
 #include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
 #include <linux/isdn_ppp.h>    /* 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);
index 938d99ee2367135e06120633b1eec01b348f36a9..474b05c773ea211dc15a7f97d39f6dabfbdf8b70 100644 (file)
@@ -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 void cpia_usb_complete(struct urb *urb)
 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)
index 51a084f96bdbfbdbcc01d7d372ace8fb3a6146c6..b1028cf5b427435c3d8e2c9518c45aaa0200d30f 100644 (file)
@@ -292,7 +292,6 @@ comment 'Wireless LAN (non-hamradio)'
 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
index 089dabaf5a4da9347cd62c06e85035daaa4d037f..f38266557dce1152a165af3ccac4d288220b0ecf 100644 (file)
@@ -166,7 +166,6 @@ obj-$(CONFIG_EEXPRESS) += eexpress.o
 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
index a9a4ac16240c2190776f8519996eea0df1309a42..894e65f714ce17e2907cffecf7f5341a4aa7e846 100644 (file)
@@ -1141,6 +1141,8 @@ static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                //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:
index 1c232734fc51fa8eb68d485b5d13085848584a4c..707adf273215b6995049dad8faba34453e2f8811 100644 (file)
@@ -1040,6 +1040,8 @@ rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
                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 --git a/drivers/net/i82586.h b/drivers/net/i82586.h
deleted file mode 100644 (file)
index 5f65b25..0000000
+++ /dev/null
@@ -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.
- */
index e4be90c3d2da4fbae7a2284d1e3241bc3c6feee6..599913636e68d7bcdd0798973d2cca840e6f82e8 100644 (file)
@@ -28,8 +28,6 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then
    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
index c8300131540e3ffa267d47c9f5a4421eff8e1145..ab0268f875d9176c1f9c537e785cbcfc60fadaa8 100644 (file)
@@ -27,8 +27,6 @@ obj-$(CONFIG_PCMCIA_AXNET)    += axnet_cs.o
 
 # 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 --git a/drivers/net/pcmcia/i82593.h b/drivers/net/pcmcia/i82593.h
deleted file mode 100644 (file)
index 33acb8a..0000000
+++ /dev/null
@@ -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 <klemets@it.kth.se>
- *
- * This software may be freely distributed for noncommercial purposes
- * as long as this notice is retained.
- * 
- * HISTORY
- * i82593.h,v
- * Revision 1.1  1996/07/17 15:23:12  root
- * Initial revision
- *
- * Revision 1.3  1995/04/05  15:13:58  adj
- * Initial alpha release
- *
- * Revision 1.2  1994/06/16  23:57:31  klemets
- * Mirrored all the fields in the configuration block.
- *
- * Revision 1.1  1994/06/02  20:25:34  klemets
- * Initial revision
- *
- *
- */
-#ifndef        _I82593_H
-#define        _I82593_H
-
-/* Intel 82593 CSMA/CD Core LAN Controller */
-
-/* Port 0 Command Register definitions */
-
-/* Execution operations */
-#define OP0_NOP                        0       /* CHNL = 0 */
-#define OP0_SWIT_TO_PORT_1     0       /* CHNL = 1 */
-#define OP0_IA_SETUP           1
-#define OP0_CONFIGURE          2
-#define OP0_MC_SETUP           3
-#define OP0_TRANSMIT           4
-#define OP0_TDR                        5
-#define OP0_DUMP               6
-#define OP0_DIAGNOSE           7
-#define OP0_TRANSMIT_NO_CRC    9
-#define OP0_RETRANSMIT         12
-#define OP0_ABORT              13
-/* Reception operations */
-#define OP0_RCV_ENABLE         8
-#define OP0_RCV_DISABLE                10
-#define OP0_STOP_RCV           11
-/* Status pointer control operations */
-#define OP0_FIX_PTR            15      /* CHNL = 1 */
-#define OP0_RLS_PTR            15      /* CHNL = 0 */
-#define OP0_RESET              14
-
-#define CR0_CHNL               (1 << 4)        /* 0=Channel 0, 1=Channel 1 */
-#define CR0_STATUS_0           0x00
-#define CR0_STATUS_1           0x20
-#define CR0_STATUS_2           0x40
-#define CR0_STATUS_3           0x60
-#define CR0_INT_ACK            (1 << 7)        /* 0=No ack, 1=acknowledge */
-
-/* Port 0 Status Register definitions */
-
-#define SR0_NO_RESULT          0               /* dummy */
-#define SR0_EVENT_MASK         0x0f
-#define SR0_IA_SETUP_DONE      1
-#define SR0_CONFIGURE_DONE     2
-#define SR0_MC_SETUP_DONE      3
-#define SR0_TRANSMIT_DONE      4
-#define SR0_TDR_DONE           5
-#define SR0_DUMP_DONE          6
-#define SR0_DIAGNOSE_PASSED    7
-#define SR0_TRANSMIT_NO_CRC_DONE 9
-#define SR0_RETRANSMIT_DONE    12
-#define SR0_EXECUTION_ABORTED  13
-#define SR0_END_OF_FRAME       8
-#define SR0_RECEPTION_ABORTED  10
-#define SR0_DIAGNOSE_FAILED    15
-#define SR0_STOP_REG_HIT       11
-
-#define SR0_CHNL               (1 << 4)
-#define SR0_EXECUTION          (1 << 5)
-#define SR0_RECEPTION          (1 << 6)
-#define SR0_INTERRUPT          (1 << 7)
-#define SR0_BOTH_RX_TX         (SR0_EXECUTION | SR0_RECEPTION)
-
-#define SR3_EXEC_STATE_MASK    0x03
-#define SR3_EXEC_IDLE          0
-#define SR3_TX_ABORT_IN_PROGRESS 1
-#define SR3_EXEC_ACTIVE                2
-#define SR3_ABORT_IN_PROGRESS  3
-#define SR3_EXEC_CHNL          (1 << 2)
-#define SR3_STP_ON_NO_RSRC     (1 << 3)
-#define SR3_RCVING_NO_RSRC     (1 << 4)
-#define SR3_RCV_STATE_MASK     0x60
-#define SR3_RCV_IDLE           0x00
-#define SR3_RCV_READY          0x20
-#define SR3_RCV_ACTIVE         0x40
-#define SR3_RCV_STOP_IN_PROG   0x60
-#define SR3_RCV_CHNL           (1 << 7)
-
-/* Port 1 Command Register definitions */
-
-#define OP1_NOP                        0
-#define OP1_SWIT_TO_PORT_0     1
-#define OP1_INT_DISABLE                2
-#define OP1_INT_ENABLE         3
-#define OP1_SET_TS             5
-#define OP1_RST_TS             7
-#define OP1_POWER_DOWN         8
-#define OP1_RESET_RING_MNGMT   11
-#define OP1_RESET              14
-#define OP1_SEL_RST            15
-
-#define CR1_STATUS_4           0x00
-#define CR1_STATUS_5           0x20
-#define CR1_STATUS_6           0x40
-#define CR1_STOP_REG_UPDATE    (1 << 7)
-
-/* Receive frame status bits */
-
-#define        RX_RCLD                 (1 << 0)
-#define RX_IA_MATCH            (1 << 1)
-#define        RX_NO_AD_MATCH          (1 << 2)
-#define RX_NO_SFD              (1 << 3)
-#define RX_SRT_FRM             (1 << 7)
-#define RX_OVRRUN              (1 << 8)
-#define RX_ALG_ERR             (1 << 10)
-#define RX_CRC_ERR             (1 << 11)
-#define RX_LEN_ERR             (1 << 12)
-#define RX_RCV_OK              (1 << 13)
-#define RX_TYP_LEN             (1 << 15)
-
-/* Transmit status bits */
-
-#define TX_NCOL_MASK           0x0f
-#define TX_FRTL                        (1 << 4)
-#define TX_MAX_COL             (1 << 5)
-#define TX_HRT_BEAT            (1 << 6)
-#define TX_DEFER               (1 << 7)
-#define TX_UND_RUN             (1 << 8)
-#define TX_LOST_CTS            (1 << 9)
-#define TX_LOST_CRS            (1 << 10)
-#define TX_LTCOL               (1 << 11)
-#define TX_OK                  (1 << 13)
-#define TX_COLL                        (1 << 15)
-
-struct i82593_conf_block {
-  u_char fifo_limit : 4,
-        forgnesi   : 1,
-        fifo_32    : 1,
-        d6mod      : 1,
-        throttle_enb : 1;
-  u_char throttle   : 6,
-        cntrxint   : 1,
-        contin     : 1;
-  u_char addr_len   : 3,
-        acloc      : 1,
-        preamb_len : 2,
-        loopback   : 2;
-  u_char lin_prio   : 3,
-        tbofstop   : 1,
-        exp_prio   : 3,
-        bof_met    : 1;
-  u_char           : 4,
-        ifrm_spc   : 4;
-  u_char           : 5,
-        slottim_low : 3;
-  u_char slottim_hi : 3,
-                   : 1,
-        max_retr   : 4;
-  u_char prmisc     : 1,
-        bc_dis     : 1,
-                   : 1,
-        crs_1      : 1,
-        nocrc_ins  : 1,
-        crc_1632   : 1,
-                   : 1,
-        crs_cdt    : 1;
-  u_char cs_filter  : 3,
-        crs_src    : 1,
-        cd_filter  : 3,
-                   : 1;
-  u_char           : 2,
-        min_fr_len : 6;
-  u_char lng_typ    : 1,
-        lng_fld    : 1,
-        rxcrc_xf   : 1,
-        artx       : 1,
-        sarec      : 1,
-        tx_jabber  : 1,        /* why is this called max_len in the manual? */
-        hash_1     : 1,
-        lbpkpol    : 1;
-  u_char           : 6,
-        fdx        : 1,
-                   : 1;
-  u_char dummy_6    : 6,       /* supposed to be ones */
-        mult_ia    : 1,
-        dis_bof    : 1;
-  u_char dummy_1    : 1,       /* supposed to be one */
-        tx_ifs_retrig : 2,
-        mc_all     : 1,
-        rcv_mon    : 2,
-        frag_acpt  : 1,
-        tstrttrs   : 1;
-  u_char fretx     : 1,
-        runt_eop   : 1,
-        hw_sw_pin  : 1,
-        big_endn   : 1,
-        syncrqs    : 1,
-        sttlen     : 1,
-        tx_eop     : 1,
-        rx_eop     : 1;
-  u_char rbuf_size  : 5,
-        rcvstop    : 1,
-                   : 2;
-};
-
-#define I82593_MAX_MULTICAST_ADDRESSES 128     /* Hardware hashed filter */
-
-#endif /* _I82593_H */
diff --git a/drivers/net/pcmcia/netwave_cs.c b/drivers/net/pcmcia/netwave_cs.c
deleted file mode 100644 (file)
index 8119f11..0000000
+++ /dev/null
@@ -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 <johnm@cs.uit.no>
- *                Dag Brattli <dagb@cs.uit.no>
- *                David Hinds <dahinds@users.sourceforge.net>
- * Created at:    A long time ago!
- * Modified at:   Mon Nov 10 11:54:37 1997
- * Modified by:   Dag Brattli <dagb@cs.uit.no>
- * 
- *     Copyright (c) 1997 University of Tromsø, Norway
- *
- * Revision History:
- *
- *   08-Nov-97 15:14:47   John Markus Bjørndalen <johnm@cs.uit.no>
- *    - Fixed some bugs in netwave_rx and cleaned it up a bit. 
- *      (One of the bugs would have destroyed packets when receiving
- *      multiple packets per interrupt). 
- *    - Cleaned up parts of newave_hw_xmit. 
- *    - A few general cleanups. 
- *   24-Oct-97 13:17:36   Dag Brattli <dagb@cs.uit.no>
- *    - Fixed netwave_rx receive function (got updated docs)
- *   Others:
- *    - Changed name from xircnw to netwave, take a look at 
- *      http://www.netwave-wireless.com
- *    - Some reorganizing of the code
- *    - Removed possible race condition between interrupt handler and transmit
- *      function
- *    - Started to add wireless extensions, but still needs some coding
- *    - Added watchdog for better handling of transmission timeouts 
- *      (hopefully this works better)
- ********************************************************************/
-
-/* To have statistics (just packets sent) define this */
-#undef NETWAVE_STATS
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h>
-#endif
-
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/mem_op.h>
-
-#define NETWAVE_REGOFF         0x8000
-/* The Netwave IO registers, offsets to iobase */
-#define NETWAVE_REG_COR        0x0
-#define NETWAVE_REG_CCSR       0x2
-#define NETWAVE_REG_ASR        0x4
-#define NETWAVE_REG_IMR        0xa
-#define NETWAVE_REG_PMR        0xc
-#define NETWAVE_REG_IOLOW      0x6
-#define NETWAVE_REG_IOHI       0x7
-#define NETWAVE_REG_IOCONTROL  0x8
-#define NETWAVE_REG_DATA       0xf
-/* The Netwave Extended IO registers, offsets to RamBase */
-#define NETWAVE_EREG_ASCC      0x114
-#define NETWAVE_EREG_RSER      0x120
-#define NETWAVE_EREG_RSERW     0x124
-#define NETWAVE_EREG_TSER      0x130
-#define NETWAVE_EREG_TSERW     0x134
-#define NETWAVE_EREG_CB        0x100
-#define NETWAVE_EREG_SPCQ      0x154
-#define NETWAVE_EREG_SPU       0x155
-#define NETWAVE_EREG_LIF       0x14e
-#define NETWAVE_EREG_ISPLQ     0x156
-#define NETWAVE_EREG_HHC       0x158
-#define NETWAVE_EREG_NI        0x16e
-#define NETWAVE_EREG_MHS       0x16b
-#define NETWAVE_EREG_TDP       0x140
-#define NETWAVE_EREG_RDP       0x150
-#define NETWAVE_EREG_PA        0x160
-#define NETWAVE_EREG_EC        0x180
-#define NETWAVE_EREG_CRBP      0x17a
-#define NETWAVE_EREG_ARW       0x166
-
-/*
- * Commands used in the extended command buffer
- * NETWAVE_EREG_CB (0x100-0x10F) 
- */
-#define NETWAVE_CMD_NOP        0x00
-#define NETWAVE_CMD_SRC        0x01
-#define NETWAVE_CMD_STC        0x02
-#define NETWAVE_CMD_AMA        0x03
-#define NETWAVE_CMD_DMA        0x04
-#define NETWAVE_CMD_SAMA       0x05
-#define NETWAVE_CMD_ER         0x06
-#define NETWAVE_CMD_DR         0x07
-#define NETWAVE_CMD_TL         0x08
-#define NETWAVE_CMD_SRP        0x09
-#define NETWAVE_CMD_SSK        0x0a
-#define NETWAVE_CMD_SMD        0x0b
-#define NETWAVE_CMD_SAPD       0x0c
-#define NETWAVE_CMD_SSS        0x11
-/* End of Command marker */
-#define NETWAVE_CMD_EOC        0x00
-
-/* ASR register bits */
-#define NETWAVE_ASR_RXRDY   0x80
-#define NETWAVE_ASR_TXBA    0x01
-
-#define TX_TIMEOUT             ((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 --git a/drivers/net/pcmcia/wavelan.h b/drivers/net/pcmcia/wavelan.h
deleted file mode 100644 (file)
index da8acc0..0000000
+++ /dev/null
@@ -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 --git a/drivers/net/pcmcia/wavelan_cs.c b/drivers/net/pcmcia/wavelan_cs.c
deleted file mode 100644 (file)
index c4ba93d..0000000
+++ /dev/null
@@ -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 <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static u_short
-psa_crc(unsigned char *        psa,    /* The PSA */
-       int             size)   /* Number of short for CRC */
-{
-  int          byte_cnt;       /* Loop on the PSA */
-  u_short      crc_bytes = 0;  /* Data in the PSA */
-  int          bit_cnt;        /* Loop on the bits of the short */
-
-  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
-    {
-      crc_bytes ^= psa[byte_cnt];      /* Its an xor */
-
-      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
-       {
-         if(crc_bytes & 0x0001)
-           crc_bytes = (crc_bytes >> 1) ^ 0xA001;
-         else
-           crc_bytes >>= 1 ;
-        }
-    }
-
-  return crc_bytes;
-} /* psa_crc */
-#endif /* SET_PSA_CRC */
-
-/*------------------------------------------------------------------*/
-/*
- * update the checksum field in the Wavelan's PSA
- */
-static void
-update_psa_checksum(device *   dev)
-{
-#ifdef SET_PSA_CRC
-  psa_t                psa;
-  u_short      crc;
-
-  /* read the parameter storage area */
-  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
-
-  /* update the checksum */
-  crc = psa_crc((unsigned char *) &psa,
-               sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
-               - sizeof(psa.psa_crc_status));
-
-  psa.psa_crc[0] = crc & 0xFF;
-  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
-
-  /* Write it ! */
-  psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
-           (unsigned char *)&psa.psa_crc, 2);
-
-#ifdef DEBUG_IOCTL_INFO
-  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
-          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
-
-  /* Check again (luxury !) */
-  crc = psa_crc((unsigned char *) &psa,
-                sizeof(psa) - sizeof(psa.psa_crc_status));
-
-  if(crc != 0)
-    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
-#endif /* DEBUG_IOCTL_INFO */
-#endif /* SET_PSA_CRC */
-} /* update_psa_checksum */
-
-/*------------------------------------------------------------------*/
-/*
- * Write 1 byte to the MMC.
- */
-static inline void
-mmc_out(u_long         base,
-       u_short         o,
-       u_char          d)
-{
-  /* Wait for MMC to go idle */
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-
-  outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
-  outb(d, MMD(base));
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to write bytes to the Modem Management Controller.
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_write(u_long       base,
-         u_char        o,
-         u_char *      b,
-         int           n)
-{
-  o += n;
-  b += n;
-
-  while(n-- > 0 )
-    mmc_out(base, --o, *(--b));
-} /* mmc_write */
-
-/*------------------------------------------------------------------*/
-/*
- * Read 1 byte from the MMC.
- * Optimised version for 1 byte, avoid using memory...
- */
-static inline u_char
-mmc_in(u_long  base,
-       u_short o)
-{
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-  outb(o << 1, MMR(base));             /* Set the read address */
-
-  outb(0, MMD(base));                  /* Required dummy write */
-
-  while(inb(HASR(base)) & HASR_MMI_BUSY)
-    ;
-  return (u_char) (inb(MMD(base)));    /* Now do the actual read */
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Routine to read bytes from the Modem Management Controller.
- * The implementation is complicated by a lack of address lines,
- * which prevents decoding of the low-order bit.
- * (code has just been moved in the above function)
- * We start by the end because it is the way it should be !
- */
-static inline void
-mmc_read(u_long                base,
-        u_char         o,
-        u_char *       b,
-        int            n)
-{
-  o += n;
-  b += n;
-
-  while(n-- > 0)
-    *(--b) = mmc_in(base, --o);
-} /* mmc_read */
-
-/*------------------------------------------------------------------*/
-/*
- * Get the type of encryption available...
- */
-static inline int
-mmc_encr(u_long                base)   /* i/o port of the card */
-{
-  int  temp;
-
-  temp = mmc_in(base, mmroff(0, mmr_des_avail));
-  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
-    return 0;
-  else
-    return temp;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Wait for the frequency EEprom to complete a command...
- * I hope this one will be optimally inlined...
- */
-static inline void
-fee_wait(u_long                base,   /* i/o port of the card */
-        int            delay,  /* Base delay to wait for */
-        int            number) /* Number of time to wait */
-{
-  int          count = 0;      /* Wait only a limited time */
-
-  while((count++ < number) &&
-       (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
-    udelay(delay);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Read bytes from the Frequency EEprom (frequency select cards).
- */
-static void
-fee_read(u_long                base,   /* i/o port of the card */
-        u_short        o,      /* destination offset */
-        u_short *      b,      /* data buffer */
-        int            n)      /* number of registers */
-{
-  b += n;              /* Position at the end of the area */
-
-  /* Write the address */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-  /* Loop on all buffer */
-  while(n-- > 0)
-    {
-      /* Write the read command */
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
-
-      /* Wait until EEprom is ready (should be quick !) */
-      fee_wait(base, 10, 100);
-
-      /* Read the value */
-      *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
-             mmc_in(base, mmroff(0, mmr_fee_data_l)));
-    }
-}
-
-#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
-
-/*------------------------------------------------------------------*/
-/*
- * Write bytes from the Frequency EEprom (frequency select cards).
- * This is a bit complicated, because the frequency eeprom has to
- * be unprotected and the write enabled.
- * Jean II
- */
-static void
-fee_write(u_long       base,   /* i/o port of the card */
-         u_short       o,      /* destination offset */
-         u_short *     b,      /* data buffer */
-         int           n)      /* number of registers */
-{
-  b += n;              /* Position at the end of the area */
-
-#ifdef EEPROM_IS_PROTECTED     /* disabled */
-#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
-  /* Ask to read the protected register */
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
-
-  fee_wait(base, 10, 100);
-
-  /* Read the protected register */
-  printk("Protected 2 : %02X-%02X\n",
-        mmc_in(base, mmroff(0, mmr_fee_data_h)),
-        mmc_in(base, mmroff(0, mmr_fee_data_l)));
-#endif /* DOESNT_SEEM_TO_WORK */
-
-  /* Enable protected register */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
-
-  fee_wait(base, 10, 100);
-
-  /* Unprotect area */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
-#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
-  /* Or use : */
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
-#endif /* DOESNT_SEEM_TO_WORK */
-
-  fee_wait(base, 10, 100);
-#endif /* EEPROM_IS_PROTECTED */
-
-  /* Write enable */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
-  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
-
-  fee_wait(base, 10, 100);
-
-  /* Write the EEprom address */
-  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
-
-  /* Loop on all buffer */
-  while(n-- > 0)
-    {
-      /* Write the value */
-      mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
-      mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
-
-      /* Write the write command */
-      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
-
-      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
-      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;i<num_missed;i++)
-      {
-       wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
-       wavepoint->qualptr %=WAVEPOINT_HISTORY;    /* in the ringbuffer. */
-      }
-  wavepoint->last_seen=jiffies;                 /* Add beacon to history */
-  wavepoint->last_seq=seq;     
-  wavepoint->sigqual[wavepoint->qualptr++]=sigqual;          
-  wavepoint->qualptr %=WAVEPOINT_HISTORY;
-  ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
-  
-  for(i=0;i<WAVEPOINT_FAST_HISTORY;i++)       /* Update running averages */
-    {
-      average_fast+=wavepoint->sigqual[ptr++];
-      ptr %=WAVEPOINT_HISTORY;
-    }
-  
-  average_slow=average_fast;
-  for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
-    {
-      average_slow+=wavepoint->sigqual[ptr++];
-      ptr %=WAVEPOINT_HISTORY;
-    }
-  
-  wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
-  wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;      
-}
-
-/* Perform a handover to a new WavePoint */
-void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
-{
-  ioaddr_t              base = lp->dev->base_addr;  
-  mm_t                  m;
-  unsigned long         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;                            /* <Mutex> */
-  
-  wavepoint=wl_roam_check(nwid,lp);            /* Find WavePoint table entry */
-  if(wavepoint==NULL)                    /* If no entry, Create a new one... */
-    {
-      wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
-      if(wavepoint==NULL)
-       goto out;
-    }
-  if(lp->curr_point==NULL)             /* If this is the only WavePoint, */
-    wv_roam_handover(wavepoint, lp);            /* Jump on it! */
-  
-  wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
-                                                        stats. */
-  
-  if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
-    if(!lp->cell_search)                  /* WavePoint is getting faint, */
-      wv_nwid_filter(NWID_PROMISC,lp);    /* start looking for a new one */
-  
-  if(wavepoint->average_slow > 
-     lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
-    wv_roam_handover(wavepoint, lp);   /* Handover to a better WavePoint */
-  
-  if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
-    if(lp->cell_search)  /* getting better, drop out of cell search mode */
-      wv_nwid_filter(!NWID_PROMISC,lp);
-  
-out:
-  lp->wavepoint_table.locked=0;                        /* </MUTEX>   :-) */
-}
-
-/* Test this MAC frame a WavePoint beacon */
-static inline int WAVELAN_BEACON(unsigned char *data)
-{
-  wavepoint_beacon *beacon= (wavepoint_beacon *)data;
-  static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
-  
-  if(memcmp(beacon,&beacon_template,9)==0)
-    return 1;
-  else
-    return 0;
-}
-#endif /* WAVELAN_ROAMING */
-
-/************************ I82593 SUBROUTINES *************************/
-/*
- * 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, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-      
-#ifdef DEBUG_CONFIG_INFO
-  printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
-        dev->name, (u_int) reg.Value);
-#endif
-
-  reg.Action = CS_WRITE;
-  reg.Value = reg.Value | COR_SW_RESET;
-  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-      
-  reg.Action = CS_WRITE;
-  reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
-  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
-  if(i != CS_SUCCESS)
-    {
-      cs_error(link->handle, AccessConfigurationRegister, i);
-      return FALSE;
-    }
-
-#ifdef DEBUG_CONFIG_TRACE
-  printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
-#endif
-  return TRUE;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * wavelan_hw_config() is called after a CARD_INSERTION event is
- * received, to configure the wavelan hardware.
- * Note that the reception will be enabled in wavelan->open(), so the
- * device is configured but idle...
- * Performs the following actions:
- *     1. A pcmcia software reset (using wv_pcmcia_reset())
- *     2. A power reset (reset DMA)
- *     3. Reset the LAN controller
- *     4. Initialize the radio modem (using wv_mmc_init)
- *     5. Configure LAN controller (using wv_82593_config)
- *     6. Perform a diagnostic on the LAN controller
- * (called by wavelan_event() & wv_hw_reset())
- */
-static int
-wv_hw_config(device *  dev)
-{
-  net_local *          lp = (net_local *) dev->priv;
-  ioaddr_t             base = dev->base_addr;
-  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 --git a/drivers/net/pcmcia/wavelan_cs.h b/drivers/net/pcmcia/wavelan_cs.h
deleted file mode 100644 (file)
index b0f6e41..0000000
+++ /dev/null
@@ -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 <linux/wireless.h>
- *     Other compile options can also reduce the size of it...
- *
- * MAC address and hardware detection :
- * ----------------------------------
- *     The detection code of the wavelan chech that the first 3
- *     octets of the MAC address fit the company code. This type of
- *     detection work well for AT&T cards (because the AT&T code is
- *     hardcoded in wavelan.h), but of course will fail for other
- *     manufacturer.
- *
- *     If you are sure that your card is derived from the wavelan,
- *     here is the way to configure it :
- *     1) Get your MAC address
- *             a) With your card utilities (wfreqsel, instconf, ...)
- *             b) With the driver :
- *                     o compile the kernel with DEBUG_CONFIG_INFO enabled
- *                     o Boot and look the card messages
- *     2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
- *     3) Compile & verify
- *     4) Send me the MAC code - I will include it in the next version...
- *
- */
-
-/* --------------------- WIRELESS EXTENSIONS --------------------- */
-/*
- * This driver is the first one to support "wireless extensions".
- * This set of extensions provide you some way to control the wireless
- * caracteristics of the hardware in a standard way and support for
- * applications for taking advantage of it (like Mobile IP).
- *
- * You will need to enable the CONFIG_NET_RADIO define in the kernel
- * configuration to enable the wireless extensions (this is the one
- * giving access to the radio network device choice).
- *
- * It might also be a good idea as well to fetch the wireless tools to
- * configure the device and play a bit.
- */
-
-/* ---------------------------- FILES ---------------------------- */
-/*
- * wavelan_cs.c :      The actual code for the driver - C functions
- *
- * wavelan_cs.h :      Private header : local types / vars for the driver
- *
- * wavelan.h :         Description of the hardware interface & structs
- *
- * i82593.h :          Description if the Ethernet 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 <klemets@paul.rutgers.edu>,
- * writting a Wavelan ISA driver for the MACH microkernel. Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modify this for the Pcmcia hardware.
- * 
- * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
- * and add specific Pcmcia support (there is currently no equivalent
- * of the PCMCIA package under BSD...).
- *
- * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * correctly 2.00 cards (2.4 GHz with frequency selection).
- * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
- * Pcmcia package (+ bug corrections).
- *
- * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
- * patchs to the Pcmcia driver. After, I added code in the ISA driver
- * for Wireless Extensions and full support of frequency selection
- * cards. Now, I'm doing the same to the Pcmcia driver + some
- * reorganisation.
- * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
- * much needed informations on the Wavelan hardware.
- */
-
-/* By the way : for the copyright & legal stuff :
- * Almost everybody wrote code under GNU or BSD license (or alike),
- * and want that their original copyright remain somewhere in the
- * code (for myself, I go with the GPL).
- * Nobody want to take responsibility for anything, except the fame...
- */
-
-/* --------------------------- CREDITS --------------------------- */
-/*
- * Credits:
- *    Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
- *     Loeke Brederveld of Lucent for providing extremely useful
- *     information about WaveLAN PCMCIA hardware
- *
- *    This driver is based upon several other drivers, in particular:
- *     David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
- *     Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
- *     Anders Klemets' PCMCIA WaveLAN adapter driver
- *     Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
- *
- * Additional Credits:
- *
- *    This software was originally developed under Linux 1.2.3
- *     (Slackware 2.0 distribution).
- *    And then under Linux 2.0.x (Debian 1.1 -> 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 <jrb@cs.pdx.edu>,
- *     Loeke Brederveld <lbrederv@wavelan.com>,
- *     Allan Creighton (allanc@cs.su.oz.au),
- *     Brent Elphick <belphick@uwaterloo.ca>,
- *     Joe Finney <joe@comp.lancs.ac.uk>,
- *     Matthew Geier (matthew@cs.su.oz.au),
- *     Remo di Giovanni (remo@cs.su.oz.au),
- *     Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- *     David Hinds <dahinds@users.sourceforge.net>,
- *     Jan Hoogendoorn (c/o marteijn@lucent.com),
- *      Bruce Janson <bruce@cs.usyd.edu.au>,
- *     Anthony D. Joseph <adj@lcs.mit.edu>,
- *     Anders Klemets (klemets@paul.rutgers.edu),
- *     Yunzhou Li <yunzhou@strat.iol.unh.edu>,
- *     Marc Meertens (mmeertens@lucent.com),
- *     Keith Moore,
- *     Robert Morris (rtm@das.harvard.edu),
- *     Ian Parkin (ian@cs.su.oz.au),
- *     John Rosenberg (johnr@cs.su.oz.au),
- *     George Rossi (george@phm.gov.au),
- *     Arthur Scott (arthur@cs.su.oz.au),
- *     Stanislav Sinyagin <stas@isf.ru>
- *     Peter Storey,
- *     Jean Tourrilhes <jt@hpl.hp.com>,
- *     Girish Welling (welling@paul.rutgers.edu)
- *     Clark Woodworth <clark@hiway1.exit109.com>
- *     Yongguang Zhang <ygz@isl.hrl.hac.com>...
- */
-
-/* ------------------------- IMPROVEMENTS ------------------------- */
-/*
- * I proudly present :
- *
- * Changes made in 2.8.22 :
- * ----------------------
- *     - improved wv_set_multicast_list
- *     - catch spurious interrupt
- *     - correct release of the device
- *
- * Changes mades in release :
- * ------------------------
- *     - Reorganisation of the code, function name change
- *     - Creation of private header (wavelan_cs.h)
- *     - Reorganised debug messages
- *     - More comments, history, ...
- *     - Configure earlier (in "insert" instead of "open")
- *        and do things only once
- *     - mmc_init : configure the PSA if not done
- *     - mmc_init : 2.00 detection better code for 2.00 init
- *     - better info at startup
- *     - Correct a HUGE bug (volatile & uncalibrated busy loop)
- *       in wv_82593_cmd => config speedup
- *     - Stop receiving & power down on close (and power up on open)
- *       use "ifconfig down" & "ifconfig up ; route add -net ..."
- *     - Send packets : add watchdog instead of pooling
- *     - Receive : check frame wrap around & try to recover some frames
- *     - wavelan_set_multicast_list : avoid reset
- *     - add wireless extensions (ioctl & get_wireless_stats)
- *       get/set nwid/frequency on fly, info for /proc/net/wireless
- *     - 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 <jseger@MIT.EDU> & David Hinds :
- * -----------------------------------------------------------------
- *     - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
- *     - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
- *     - Better initialisation of the i82593 controller
- *       from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
- *
- * Changes made for release in 3.0.10 :
- * ----------------------------------
- *     - Fix eject "hang" of the driver under 2.2.X :
- *             o create wv_flush_stale_links()
- *             o Rename wavelan_release to wv_pcmcia_release & move up
- *             o move unregister_netdev to wavelan_detach()
- *             o wavelan_release() no longer call wavelan_detach()
- *             o 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 <shadow@dementia.org>
- *     - Better handling of TX and RX normal failure conditions
- *     - #ifdef out all the roaming code
- *     - Add ESSID & "AP current address" ioctl stubs
- *     - General cleanup of the code
- *
- * Changes made for release in 3.0.13 :
- * ----------------------------------
- *     - Re-enable compilation of roaming code by default, but with
- *       do_roaming = 0
- *     - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
- *       at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
- *     - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
- *
- * Changes made for release in 3.0.15 :
- * ----------------------------------
- *     - Change e-mail and web page addresses
- *     - Watchdog timer is now correctly expressed in HZ, not in jiffies
- *     - Add channel number to the list of frequencies in range
- *     - Add the (short) list of bit-rates in range
- *     - Developp a new sensitivity... (sens.value & sens.fixed)
- *
- * Changes made for release in 3.1.2 :
- * ---------------------------------
- *     - Fix check for root permission (break instead of exit)
- *     - New nwid & encoding setting (Wireless Extension 9)
- *
- * 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 <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/fcntl.h>
-
-#ifdef CONFIG_NET_PCMCIA_RADIO
-#include <linux/wireless.h>            /* Wireless extensions */
-#endif
-
-/* Pcmcia headers that we need */
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/version.h>
-
-/* Wavelan declarations */
-#include "i82593.h"    /* Definitions for the Intel chip */
-
-#include "wavelan.h"   /* Others bits of the hardware */
-
-/************************** DRIVER OPTIONS **************************/
-/*
- * `#define' or `#undef' the following constant to change the behaviour
- * of the driver...
- */
-#define WAVELAN_ROAMING                /* Include experimental roaming code */
-#undef WAVELAN_ROAMING_EXT     /* Enable roaming wireless extensions */
-#undef SET_PSA_CRC             /* Set the CRC in PSA (slower) */
-#define USE_PSA_CONFIG         /* Use info from the PSA */
-#undef STRUCT_CHECK            /* Verify padding of structures */
-#undef EEPROM_IS_PROTECTED     /* Doesn't seem to be necessary */
-#define MULTICAST_AVOID                /* Avoid extra multicast (I'm sceptical) */
-#undef SET_MAC_ADDRESS         /* Experimental */
-
-#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
-/* Warning : these stuff will slow down the driver... */
-#define WIRELESS_SPY           /* Enable spying addresses */
-#undef HISTOGRAM               /* Enable histogram of sig level... */
-#endif
-
-/****************************** DEBUG ******************************/
-
-#undef DEBUG_MODULE_TRACE      /* Module insertion/removal */
-#undef DEBUG_CALLBACK_TRACE    /* Calls made by Linux */
-#undef DEBUG_INTERRUPT_TRACE   /* Calls to handler */
-#undef DEBUG_INTERRUPT_INFO    /* type of interrupt & so on */
-#define DEBUG_INTERRUPT_ERROR  /* problems */
-#undef DEBUG_CONFIG_TRACE      /* Trace the config functions */
-#undef DEBUG_CONFIG_INFO       /* What's going on... */
-#define DEBUG_CONFIG_ERRORS    /* Errors on configuration */
-#undef DEBUG_TX_TRACE          /* Transmission calls */
-#undef DEBUG_TX_INFO           /* Header of the 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 <var>=<value>'
- */
-
-/* Bit map of interrupts to choose from */
-/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
-static int     irq_mask = 0xdeb8;
-static int     irq_list[4] = { -1 };
-
-/* Shared memory speed, in ns */
-static int     mem_speed = 0;
-
-/* New module interface */
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-MODULE_PARM(mem_speed, "i");
-
-#ifdef WAVELAN_ROAMING         /* Conditional compile, see above in options */
-/* Enable roaming mode ? No ! Please keep this to 0 */
-static int     do_roaming = 0;
-MODULE_PARM(do_roaming, "i");
-#endif /* WAVELAN_ROAMING */
-
-MODULE_LICENSE("GPL");
-
-#endif /* WAVELAN_CS_H */
-
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
deleted file mode 100644 (file)
index 10805ef..0000000
+++ /dev/null
@@ -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 <NVALSTER@wcnd.nl.lucent.com> for the code
- * NOTE: By specifying a length including the CRC position the
- * returned value should be zero. (i.e. a correct checksum in the PSA)
- *
- * The Windows drivers don't use the CRC, but the AP and the PtP tool
- * depend on it.
- */
-static 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 --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h
deleted file mode 100644 (file)
index 27172cd..0000000
+++ /dev/null
@@ -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 --git a/drivers/net/wavelan.p.h b/drivers/net/wavelan.p.h
deleted file mode 100644 (file)
index 6bc145a..0000000
+++ /dev/null
@@ -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 <linux/wireless.h>
- *     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 <klemets@paul.rutgers.edu>
- * writing a WaveLAN ISA driver for the Mach microkernel.  Girish
- * Welling <welling@paul.rutgers.edu> had also worked on it.
- * Keith Moore modified this for the PCMCIA hardware.
- * 
- * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
- * and added specific PCMCIA support (there is currently no equivalent
- * of the PCMCIA package under BSD).
- *
- * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
- *
- * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
- *
- * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
- * (with help of the BSDI PCMCIA driver) for PCMCIA.
- * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
- * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
- * 2.00 cards correctly (2.4 GHz with frequency selection).
- * David Hinds <dhinds@hyper.stanford.edu> 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 <lbrederv@wavelan.com> 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 <bakre@paul.rutgers.edu>,
- *     Donald Becker <becker@cesdis.gsfc.nasa.gov>,
- *     Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
- *     Brent Elphick <belphick@uwaterloo.ca>,
- *     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@hpl.hp.com>,
- *     Girish Welling <welling@paul.rutgers.edu>,
- *     Clark Woodworth <clark@hiway1.exit109.com>
- *     Yongguang Zhang <ygz@isl.hrl.hac.com>
- *
- * 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>,
- *     Stanislav Sinyagin <stas@isf.ru>
- *     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       <linux/module.h>
-
-#include       <linux/kernel.h>
-#include       <linux/sched.h>
-#include       <linux/types.h>
-#include       <linux/fcntl.h>
-#include       <linux/interrupt.h>
-#include       <linux/stat.h>
-#include       <linux/ptrace.h>
-#include       <linux/ioport.h>
-#include       <linux/in.h>
-#include       <linux/string.h>
-#include       <linux/delay.h>
-#include       <asm/system.h>
-#include       <asm/bitops.h>
-#include       <asm/io.h>
-#include       <asm/dma.h>
-#include       <asm/uaccess.h>
-#include       <linux/errno.h>
-#include       <linux/netdevice.h>
-#include       <linux/etherdevice.h>
-#include       <linux/skbuff.h>
-#include       <linux/slab.h>
-#include       <linux/timer.h>
-#include       <linux/init.h>
-
-#include <linux/wireless.h>            /* 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 */
index aa404b045ce7728da90ca3c1890a4999ed735602..f4fabcff0806e170c3062c2a80a133c20ea77e44 100644 (file)
@@ -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 @@ fi
 
 # 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
index 06d9e29f208a3200966588d6589a4e88ba0ccd26..b3b8df628b5a6388b5265b0946469672b3ecb311 100644 (file)
@@ -14,6 +14,11 @@ obj-         :=
 # 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 --git a/drivers/net/wireless/i82586.h b/drivers/net/wireless/i82586.h
new file mode 100644 (file)
index 0000000..5f65b25
--- /dev/null
@@ -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 --git a/drivers/net/wireless/i82593.h b/drivers/net/wireless/i82593.h
new file mode 100644 (file)
index 0000000..33acb8a
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Definitions for Intel 82593 CSMA/CD Core LAN Controller
+ * The definitions are taken from the 1992 users manual with Intel
+ * order number 297125-001.
+ *
+ * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
+ *
+ * Copyright 1994, Anders Klemets <klemets@it.kth.se>
+ *
+ * This software may be freely distributed for noncommercial purposes
+ * as long as this notice is retained.
+ * 
+ * HISTORY
+ * i82593.h,v
+ * Revision 1.1  1996/07/17 15:23:12  root
+ * Initial revision
+ *
+ * Revision 1.3  1995/04/05  15:13:58  adj
+ * Initial alpha release
+ *
+ * Revision 1.2  1994/06/16  23:57:31  klemets
+ * Mirrored all the fields in the configuration block.
+ *
+ * Revision 1.1  1994/06/02  20:25:34  klemets
+ * Initial revision
+ *
+ *
+ */
+#ifndef        _I82593_H
+#define        _I82593_H
+
+/* Intel 82593 CSMA/CD Core LAN Controller */
+
+/* Port 0 Command Register definitions */
+
+/* Execution operations */
+#define OP0_NOP                        0       /* CHNL = 0 */
+#define OP0_SWIT_TO_PORT_1     0       /* CHNL = 1 */
+#define OP0_IA_SETUP           1
+#define OP0_CONFIGURE          2
+#define OP0_MC_SETUP           3
+#define OP0_TRANSMIT           4
+#define OP0_TDR                        5
+#define OP0_DUMP               6
+#define OP0_DIAGNOSE           7
+#define OP0_TRANSMIT_NO_CRC    9
+#define OP0_RETRANSMIT         12
+#define OP0_ABORT              13
+/* Reception operations */
+#define OP0_RCV_ENABLE         8
+#define OP0_RCV_DISABLE                10
+#define OP0_STOP_RCV           11
+/* Status pointer control operations */
+#define OP0_FIX_PTR            15      /* CHNL = 1 */
+#define OP0_RLS_PTR            15      /* CHNL = 0 */
+#define OP0_RESET              14
+
+#define CR0_CHNL               (1 << 4)        /* 0=Channel 0, 1=Channel 1 */
+#define CR0_STATUS_0           0x00
+#define CR0_STATUS_1           0x20
+#define CR0_STATUS_2           0x40
+#define CR0_STATUS_3           0x60
+#define CR0_INT_ACK            (1 << 7)        /* 0=No ack, 1=acknowledge */
+
+/* Port 0 Status Register definitions */
+
+#define SR0_NO_RESULT          0               /* dummy */
+#define SR0_EVENT_MASK         0x0f
+#define SR0_IA_SETUP_DONE      1
+#define SR0_CONFIGURE_DONE     2
+#define SR0_MC_SETUP_DONE      3
+#define SR0_TRANSMIT_DONE      4
+#define SR0_TDR_DONE           5
+#define SR0_DUMP_DONE          6
+#define SR0_DIAGNOSE_PASSED    7
+#define SR0_TRANSMIT_NO_CRC_DONE 9
+#define SR0_RETRANSMIT_DONE    12
+#define SR0_EXECUTION_ABORTED  13
+#define SR0_END_OF_FRAME       8
+#define SR0_RECEPTION_ABORTED  10
+#define SR0_DIAGNOSE_FAILED    15
+#define SR0_STOP_REG_HIT       11
+
+#define SR0_CHNL               (1 << 4)
+#define SR0_EXECUTION          (1 << 5)
+#define SR0_RECEPTION          (1 << 6)
+#define SR0_INTERRUPT          (1 << 7)
+#define SR0_BOTH_RX_TX         (SR0_EXECUTION | SR0_RECEPTION)
+
+#define SR3_EXEC_STATE_MASK    0x03
+#define SR3_EXEC_IDLE          0
+#define SR3_TX_ABORT_IN_PROGRESS 1
+#define SR3_EXEC_ACTIVE                2
+#define SR3_ABORT_IN_PROGRESS  3
+#define SR3_EXEC_CHNL          (1 << 2)
+#define SR3_STP_ON_NO_RSRC     (1 << 3)
+#define SR3_RCVING_NO_RSRC     (1 << 4)
+#define SR3_RCV_STATE_MASK     0x60
+#define SR3_RCV_IDLE           0x00
+#define SR3_RCV_READY          0x20
+#define SR3_RCV_ACTIVE         0x40
+#define SR3_RCV_STOP_IN_PROG   0x60
+#define SR3_RCV_CHNL           (1 << 7)
+
+/* Port 1 Command Register definitions */
+
+#define OP1_NOP                        0
+#define OP1_SWIT_TO_PORT_0     1
+#define OP1_INT_DISABLE                2
+#define OP1_INT_ENABLE         3
+#define OP1_SET_TS             5
+#define OP1_RST_TS             7
+#define OP1_POWER_DOWN         8
+#define OP1_RESET_RING_MNGMT   11
+#define OP1_RESET              14
+#define OP1_SEL_RST            15
+
+#define CR1_STATUS_4           0x00
+#define CR1_STATUS_5           0x20
+#define CR1_STATUS_6           0x40
+#define CR1_STOP_REG_UPDATE    (1 << 7)
+
+/* Receive frame status bits */
+
+#define        RX_RCLD                 (1 << 0)
+#define RX_IA_MATCH            (1 << 1)
+#define        RX_NO_AD_MATCH          (1 << 2)
+#define RX_NO_SFD              (1 << 3)
+#define RX_SRT_FRM             (1 << 7)
+#define RX_OVRRUN              (1 << 8)
+#define RX_ALG_ERR             (1 << 10)
+#define RX_CRC_ERR             (1 << 11)
+#define RX_LEN_ERR             (1 << 12)
+#define RX_RCV_OK              (1 << 13)
+#define RX_TYP_LEN             (1 << 15)
+
+/* Transmit status bits */
+
+#define TX_NCOL_MASK           0x0f
+#define TX_FRTL                        (1 << 4)
+#define TX_MAX_COL             (1 << 5)
+#define TX_HRT_BEAT            (1 << 6)
+#define TX_DEFER               (1 << 7)
+#define TX_UND_RUN             (1 << 8)
+#define TX_LOST_CTS            (1 << 9)
+#define TX_LOST_CRS            (1 << 10)
+#define TX_LTCOL               (1 << 11)
+#define TX_OK                  (1 << 13)
+#define TX_COLL                        (1 << 15)
+
+struct i82593_conf_block {
+  u_char fifo_limit : 4,
+        forgnesi   : 1,
+        fifo_32    : 1,
+        d6mod      : 1,
+        throttle_enb : 1;
+  u_char throttle   : 6,
+        cntrxint   : 1,
+        contin     : 1;
+  u_char addr_len   : 3,
+        acloc      : 1,
+        preamb_len : 2,
+        loopback   : 2;
+  u_char lin_prio   : 3,
+        tbofstop   : 1,
+        exp_prio   : 3,
+        bof_met    : 1;
+  u_char           : 4,
+        ifrm_spc   : 4;
+  u_char           : 5,
+        slottim_low : 3;
+  u_char slottim_hi : 3,
+                   : 1,
+        max_retr   : 4;
+  u_char prmisc     : 1,
+        bc_dis     : 1,
+                   : 1,
+        crs_1      : 1,
+        nocrc_ins  : 1,
+        crc_1632   : 1,
+                   : 1,
+        crs_cdt    : 1;
+  u_char cs_filter  : 3,
+        crs_src    : 1,
+        cd_filter  : 3,
+                   : 1;
+  u_char           : 2,
+        min_fr_len : 6;
+  u_char lng_typ    : 1,
+        lng_fld    : 1,
+        rxcrc_xf   : 1,
+        artx       : 1,
+        sarec      : 1,
+        tx_jabber  : 1,        /* why is this called max_len in the manual? */
+        hash_1     : 1,
+        lbpkpol    : 1;
+  u_char           : 6,
+        fdx        : 1,
+                   : 1;
+  u_char dummy_6    : 6,       /* supposed to be ones */
+        mult_ia    : 1,
+        dis_bof    : 1;
+  u_char dummy_1    : 1,       /* supposed to be one */
+        tx_ifs_retrig : 2,
+        mc_all     : 1,
+        rcv_mon    : 2,
+        frag_acpt  : 1,
+        tstrttrs   : 1;
+  u_char fretx     : 1,
+        runt_eop   : 1,
+        hw_sw_pin  : 1,
+        big_endn   : 1,
+        syncrqs    : 1,
+        sttlen     : 1,
+        tx_eop     : 1,
+        rx_eop     : 1;
+  u_char rbuf_size  : 5,
+        rcvstop    : 1,
+                   : 2;
+};
+
+#define I82593_MAX_MULTICAST_ADDRESSES 128     /* Hardware hashed filter */
+
+#endif /* _I82593_H */
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c
new file mode 100644 (file)
index 0000000..9823adc
--- /dev/null
@@ -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 <johnm@cs.uit.no>
+ *                Dag Brattli <dagb@cs.uit.no>
+ *                David Hinds <dahinds@users.sourceforge.net>
+ * Created at:    A long time ago!
+ * Modified at:   Mon Nov 10 11:54:37 1997
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1997 University of Tromsø, Norway
+ *
+ * Revision History:
+ *
+ *   08-Nov-97 15:14:47   John Markus Bjørndalen <johnm@cs.uit.no>
+ *    - Fixed some bugs in netwave_rx and cleaned it up a bit. 
+ *      (One of the bugs would have destroyed packets when receiving
+ *      multiple packets per interrupt). 
+ *    - Cleaned up parts of newave_hw_xmit. 
+ *    - A few general cleanups. 
+ *   24-Oct-97 13:17:36   Dag Brattli <dagb@cs.uit.no>
+ *    - Fixed netwave_rx receive function (got updated docs)
+ *   Others:
+ *    - Changed name from xircnw to netwave, take a look at 
+ *      http://www.netwave-wireless.com
+ *    - Some reorganizing of the code
+ *    - Removed possible race condition between interrupt handler and transmit
+ *      function
+ *    - Started to add wireless extensions, but still needs some coding
+ *    - Added watchdog for better handling of transmission timeouts 
+ *      (hopefully this works better)
+ ********************************************************************/
+
+/* To have statistics (just packets sent) define this */
+#undef NETWAVE_STATS
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+#define NETWAVE_REGOFF         0x8000
+/* The Netwave IO registers, offsets to iobase */
+#define NETWAVE_REG_COR        0x0
+#define NETWAVE_REG_CCSR       0x2
+#define NETWAVE_REG_ASR        0x4
+#define NETWAVE_REG_IMR        0xa
+#define NETWAVE_REG_PMR        0xc
+#define NETWAVE_REG_IOLOW      0x6
+#define NETWAVE_REG_IOHI       0x7
+#define NETWAVE_REG_IOCONTROL  0x8
+#define NETWAVE_REG_DATA       0xf
+/* The Netwave Extended IO registers, offsets to RamBase */
+#define NETWAVE_EREG_ASCC      0x114
+#define NETWAVE_EREG_RSER      0x120
+#define NETWAVE_EREG_RSERW     0x124
+#define NETWAVE_EREG_TSER      0x130
+#define NETWAVE_EREG_TSERW     0x134
+#define NETWAVE_EREG_CB        0x100
+#define NETWAVE_EREG_SPCQ      0x154
+#define NETWAVE_EREG_SPU       0x155
+#define NETWAVE_EREG_LIF       0x14e
+#define NETWAVE_EREG_ISPLQ     0x156
+#define NETWAVE_EREG_HHC       0x158
+#define NETWAVE_EREG_NI        0x16e
+#define NETWAVE_EREG_MHS       0x16b
+#define NETWAVE_EREG_TDP       0x140
+#define NETWAVE_EREG_RDP       0x150
+#define NETWAVE_EREG_PA        0x160
+#define NETWAVE_EREG_EC        0x180
+#define NETWAVE_EREG_CRBP      0x17a
+#define NETWAVE_EREG_ARW       0x166
+
+/*
+ * Commands used in the extended command buffer
+ * NETWAVE_EREG_CB (0x100-0x10F) 
+ */
+#define NETWAVE_CMD_NOP        0x00
+#define NETWAVE_CMD_SRC        0x01
+#define NETWAVE_CMD_STC        0x02
+#define NETWAVE_CMD_AMA        0x03
+#define NETWAVE_CMD_DMA        0x04
+#define NETWAVE_CMD_SAMA       0x05
+#define NETWAVE_CMD_ER         0x06
+#define NETWAVE_CMD_DR         0x07
+#define NETWAVE_CMD_TL         0x08
+#define NETWAVE_CMD_SRP        0x09
+#define NETWAVE_CMD_SSK        0x0a
+#define NETWAVE_CMD_SMD        0x0b
+#define NETWAVE_CMD_SAPD       0x0c
+#define NETWAVE_CMD_SSS        0x11
+/* End of Command marker */
+#define NETWAVE_CMD_EOC        0x00
+
+/* ASR register bits */
+#define NETWAVE_ASR_RXRDY   0x80
+#define NETWAVE_ASR_TXBA    0x01
+
+#define TX_TIMEOUT             ((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");
index f5fd819b702b035a3db9044cfe6cb3db4220f68f..a23e938419f5e73d6482bd0fb76e95208b9bd937 100644 (file)
@@ -4,16 +4,15 @@
 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 --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
new file mode 100644 (file)
index 0000000..10805ef
--- /dev/null
@@ -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 <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static 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 --git a/drivers/net/wireless/wavelan.h b/drivers/net/wireless/wavelan.h
new file mode 100644 (file)
index 0000000..27172cd
--- /dev/null
@@ -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 --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h
new file mode 100644 (file)
index 0000000..6bc145a
--- /dev/null
@@ -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 <linux/wireless.h>
+ *     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 <klemets@paul.rutgers.edu>
+ * writing a WaveLAN ISA driver for the Mach microkernel.  Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modified this for the PCMCIA hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> ported these two drivers to BSDI
+ * and added specific PCMCIA support (there is currently no equivalent
+ * of the PCMCIA package under BSD).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> ported both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> ported the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started to modify Bruce's driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished this work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * 2.00 cards correctly (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> 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 <lbrederv@wavelan.com> 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 <bakre@paul.rutgers.edu>,
+ *     Donald Becker <becker@cesdis.gsfc.nasa.gov>,
+ *     Loeke Brederveld <Loeke.Brederveld@Utrecht.NCR.com>,
+ *     Brent Elphick <belphick@uwaterloo.ca>,
+ *     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@hpl.hp.com>,
+ *     Girish Welling <welling@paul.rutgers.edu>,
+ *     Clark Woodworth <clark@hiway1.exit109.com>
+ *     Yongguang Zhang <ygz@isl.hrl.hac.com>
+ *
+ * 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>,
+ *     Stanislav Sinyagin <stas@isf.ru>
+ *     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       <linux/module.h>
+
+#include       <linux/kernel.h>
+#include       <linux/sched.h>
+#include       <linux/types.h>
+#include       <linux/fcntl.h>
+#include       <linux/interrupt.h>
+#include       <linux/stat.h>
+#include       <linux/ptrace.h>
+#include       <linux/ioport.h>
+#include       <linux/in.h>
+#include       <linux/string.h>
+#include       <linux/delay.h>
+#include       <asm/system.h>
+#include       <asm/bitops.h>
+#include       <asm/io.h>
+#include       <asm/dma.h>
+#include       <asm/uaccess.h>
+#include       <linux/errno.h>
+#include       <linux/netdevice.h>
+#include       <linux/etherdevice.h>
+#include       <linux/skbuff.h>
+#include       <linux/slab.h>
+#include       <linux/timer.h>
+#include       <linux/init.h>
+
+#include <linux/wireless.h>            /* 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 --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
new file mode 100644 (file)
index 0000000..b113323
--- /dev/null
@@ -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 <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static u_short
+psa_crc(unsigned char *        psa,    /* The PSA */
+       int             size)   /* Number of short for CRC */
+{
+  int          byte_cnt;       /* Loop on the PSA */
+  u_short      crc_bytes = 0;  /* Data in the PSA */
+  int          bit_cnt;        /* Loop on the bits of the short */
+
+  for(byte_cnt = 0; byte_cnt < size; byte_cnt++ )
+    {
+      crc_bytes ^= psa[byte_cnt];      /* Its an xor */
+
+      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+       {
+         if(crc_bytes & 0x0001)
+           crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+         else
+           crc_bytes >>= 1 ;
+        }
+    }
+
+  return crc_bytes;
+} /* psa_crc */
+#endif /* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void
+update_psa_checksum(device *   dev)
+{
+#ifdef SET_PSA_CRC
+  psa_t                psa;
+  u_short      crc;
+
+  /* read the parameter storage area */
+  psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+
+  /* update the checksum */
+  crc = psa_crc((unsigned char *) &psa,
+               sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1])
+               - sizeof(psa.psa_crc_status));
+
+  psa.psa_crc[0] = crc & 0xFF;
+  psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+  /* Write it ! */
+  psa_write(dev, (char *)&psa.psa_crc - (char *)&psa,
+           (unsigned char *)&psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+          dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+  /* Check again (luxury !) */
+  crc = psa_crc((unsigned char *) &psa,
+                sizeof(psa) - sizeof(psa.psa_crc_status));
+
+  if(crc != 0)
+    printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif /* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void
+mmc_out(u_long         base,
+       u_short         o,
+       u_char          d)
+{
+  /* Wait for MMC to go idle */
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+
+  outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base));
+  outb(d, MMD(base));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_write(u_long       base,
+         u_char        o,
+         u_char *      b,
+         int           n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0 )
+    mmc_out(base, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_long  base,
+       u_short o)
+{
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  outb(o << 1, MMR(base));             /* Set the read address */
+
+  outb(0, MMD(base));                  /* Required dummy write */
+
+  while(inb(HASR(base)) & HASR_MMI_BUSY)
+    ;
+  return (u_char) (inb(MMD(base)));    /* Now do the actual read */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_read(u_long                base,
+        u_char         o,
+        u_char *       b,
+        int            n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0)
+    *(--b) = mmc_in(base, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long                base)   /* i/o port of the card */
+{
+  int  temp;
+
+  temp = mmc_in(base, mmroff(0, mmr_des_avail));
+  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+    return 0;
+  else
+    return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
+ */
+static inline void
+fee_wait(u_long                base,   /* i/o port of the card */
+        int            delay,  /* Base delay to wait for */
+        int            number) /* Number of time to wait */
+{
+  int          count = 0;      /* Wait only a limited time */
+
+  while((count++ < number) &&
+       (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+    udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEprom (frequency select cards).
+ */
+static void
+fee_read(u_long                base,   /* i/o port of the card */
+        u_short        o,      /* destination offset */
+        u_short *      b,      /* data buffer */
+        int            n)      /* number of registers */
+{
+  b += n;              /* Position at the end of the area */
+
+  /* Write the address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the read command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
+
+      /* Wait until EEprom is ready (should be quick !) */
+      fee_wait(base, 10, 100);
+
+      /* Read the value */
+      *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) |
+             mmc_in(base, mmroff(0, mmr_fee_data_l)));
+    }
+}
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void
+fee_write(u_long       base,   /* i/o port of the card */
+         u_short       o,      /* destination offset */
+         u_short *     b,      /* data buffer */
+         int           n)      /* number of registers */
+{
+  b += n;              /* Position at the end of the area */
+
+#ifdef EEPROM_IS_PROTECTED     /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Ask to read the protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+  fee_wait(base, 10, 100);
+
+  /* Read the protected register */
+  printk("Protected 2 : %02X-%02X\n",
+        mmc_in(base, mmroff(0, mmr_fee_data_h)),
+        mmc_in(base, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
+
+  /* Enable protected register */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Unprotect area */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Or use : */
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
+
+  fee_wait(base, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+
+  /* Write enable */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+  fee_wait(base, 10, 100);
+
+  /* Write the EEprom address */
+  mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the value */
+      mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+      mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+      /* Write the write command */
+      mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
+
+      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+      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;i<num_missed;i++)
+      {
+       wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */
+       wavepoint->qualptr %=WAVEPOINT_HISTORY;    /* in the ringbuffer. */
+      }
+  wavepoint->last_seen=jiffies;                 /* Add beacon to history */
+  wavepoint->last_seq=seq;     
+  wavepoint->sigqual[wavepoint->qualptr++]=sigqual;          
+  wavepoint->qualptr %=WAVEPOINT_HISTORY;
+  ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY;
+  
+  for(i=0;i<WAVEPOINT_FAST_HISTORY;i++)       /* Update running averages */
+    {
+      average_fast+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  average_slow=average_fast;
+  for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++)
+    {
+      average_slow+=wavepoint->sigqual[ptr++];
+      ptr %=WAVEPOINT_HISTORY;
+    }
+  
+  wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY;
+  wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY;      
+}
+
+/* Perform a handover to a new WavePoint */
+void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
+{
+  ioaddr_t              base = lp->dev->base_addr;  
+  mm_t                  m;
+  unsigned long         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;                            /* <Mutex> */
+  
+  wavepoint=wl_roam_check(nwid,lp);            /* Find WavePoint table entry */
+  if(wavepoint==NULL)                    /* If no entry, Create a new one... */
+    {
+      wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp);
+      if(wavepoint==NULL)
+       goto out;
+    }
+  if(lp->curr_point==NULL)             /* If this is the only WavePoint, */
+    wv_roam_handover(wavepoint, lp);            /* Jump on it! */
+  
+  wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history
+                                                        stats. */
+  
+  if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */
+    if(!lp->cell_search)                  /* WavePoint is getting faint, */
+      wv_nwid_filter(NWID_PROMISC,lp);    /* start looking for a new one */
+  
+  if(wavepoint->average_slow > 
+     lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA)
+    wv_roam_handover(wavepoint, lp);   /* Handover to a better WavePoint */
+  
+  if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */
+    if(lp->cell_search)  /* getting better, drop out of cell search mode */
+      wv_nwid_filter(!NWID_PROMISC,lp);
+  
+out:
+  lp->wavepoint_table.locked=0;                        /* </MUTEX>   :-) */
+}
+
+/* Test this MAC frame a WavePoint beacon */
+static inline int WAVELAN_BEACON(unsigned char *data)
+{
+  wavepoint_beacon *beacon= (wavepoint_beacon *)data;
+  static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00};
+  
+  if(memcmp(beacon,&beacon_template,9)==0)
+    return 1;
+  else
+    return 0;
+}
+#endif /* WAVELAN_ROAMING */
+
+/************************ I82593 SUBROUTINES *************************/
+/*
+ * 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, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
+        dev->name, (u_int) reg.Value);
+#endif
+
+  reg.Action = CS_WRITE;
+  reg.Value = reg.Value | COR_SW_RESET;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+      
+  reg.Action = CS_WRITE;
+  reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
+  i = CardServices(AccessConfigurationRegister, link->handle, &reg);
+  if(i != CS_SUCCESS)
+    {
+      cs_error(link->handle, AccessConfigurationRegister, i);
+      return FALSE;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
+#endif
+  return TRUE;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * wavelan_hw_config() is called after a CARD_INSERTION event is
+ * received, to configure the wavelan hardware.
+ * Note that the reception will be enabled in wavelan->open(), so the
+ * device is configured but idle...
+ * Performs the following actions:
+ *     1. A pcmcia software reset (using wv_pcmcia_reset())
+ *     2. A power reset (reset DMA)
+ *     3. Reset the LAN controller
+ *     4. Initialize the radio modem (using wv_mmc_init)
+ *     5. Configure LAN controller (using wv_82593_config)
+ *     6. Perform a diagnostic on the LAN controller
+ * (called by wavelan_event() & wv_hw_reset())
+ */
+static int
+wv_hw_config(device *  dev)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  ioaddr_t             base = dev->base_addr;
+  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 --git a/drivers/net/wireless/wavelan_cs.h b/drivers/net/wireless/wavelan_cs.h
new file mode 100644 (file)
index 0000000..755df6f
--- /dev/null
@@ -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 --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h
new file mode 100644 (file)
index 0000000..67b1993
--- /dev/null
@@ -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 <linux/wireless.h>
+ *     Other compile options can also reduce the size of it...
+ *
+ * MAC address and hardware detection :
+ * ----------------------------------
+ *     The detection code of the wavelan chech that the first 3
+ *     octets of the MAC address fit the company code. This type of
+ *     detection work well for AT&T cards (because the AT&T code is
+ *     hardcoded in wavelan_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 <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Now, I'm doing the same to the Pcmcia driver + some
+ * reorganisation.
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * Credits:
+ *    Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and
+ *     Loeke Brederveld of Lucent for providing extremely useful
+ *     information about WaveLAN PCMCIA hardware
+ *
+ *    This driver is based upon several other drivers, in particular:
+ *     David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter
+ *     Bruce Janson's Linux driver for the AT-bus WaveLAN adapter
+ *     Anders Klemets' PCMCIA WaveLAN adapter driver
+ *     Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter
+ *
+ * Additional Credits:
+ *
+ *    This software was originally developed under Linux 1.2.3
+ *     (Slackware 2.0 distribution).
+ *    And then under Linux 2.0.x (Debian 1.1 -> 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 <jrb@cs.pdx.edu>,
+ *     Loeke Brederveld <lbrederv@wavelan.com>,
+ *     Allan Creighton (allanc@cs.su.oz.au),
+ *     Brent Elphick <belphick@uwaterloo.ca>,
+ *     Joe Finney <joe@comp.lancs.ac.uk>,
+ *     Matthew Geier (matthew@cs.su.oz.au),
+ *     Remo di Giovanni (remo@cs.su.oz.au),
+ *     Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *     David Hinds <dahinds@users.sourceforge.net>,
+ *     Jan Hoogendoorn (c/o marteijn@lucent.com),
+ *      Bruce Janson <bruce@cs.usyd.edu.au>,
+ *     Anthony D. Joseph <adj@lcs.mit.edu>,
+ *     Anders Klemets (klemets@paul.rutgers.edu),
+ *     Yunzhou Li <yunzhou@strat.iol.unh.edu>,
+ *     Marc Meertens (mmeertens@lucent.com),
+ *     Keith Moore,
+ *     Robert Morris (rtm@das.harvard.edu),
+ *     Ian Parkin (ian@cs.su.oz.au),
+ *     John Rosenberg (johnr@cs.su.oz.au),
+ *     George Rossi (george@phm.gov.au),
+ *     Arthur Scott (arthur@cs.su.oz.au),
+ *     Stanislav Sinyagin <stas@isf.ru>
+ *     Peter Storey,
+ *     Jean Tourrilhes <jt@hpl.hp.com>,
+ *     Girish Welling (welling@paul.rutgers.edu)
+ *     Clark Woodworth <clark@hiway1.exit109.com>
+ *     Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes made in 2.8.22 :
+ * ----------------------
+ *     - improved wv_set_multicast_list
+ *     - catch spurious interrupt
+ *     - correct release of the device
+ *
+ * Changes mades in release :
+ * ------------------------
+ *     - Reorganisation of the code, function name change
+ *     - Creation of private header (wavelan_cs.h)
+ *     - Reorganised debug messages
+ *     - More comments, history, ...
+ *     - Configure earlier (in "insert" instead of "open")
+ *        and do things only once
+ *     - mmc_init : configure the PSA if not done
+ *     - mmc_init : 2.00 detection better code for 2.00 init
+ *     - better info at startup
+ *     - Correct a HUGE bug (volatile & uncalibrated busy loop)
+ *       in wv_82593_cmd => config speedup
+ *     - Stop receiving & power down on close (and power up on open)
+ *       use "ifconfig down" & "ifconfig up ; route add -net ..."
+ *     - Send packets : add watchdog instead of pooling
+ *     - Receive : check frame wrap around & try to recover some frames
+ *     - wavelan_set_multicast_list : avoid reset
+ *     - add wireless extensions (ioctl & get_wireless_stats)
+ *       get/set nwid/frequency on fly, info for /proc/net/wireless
+ *     - 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 <jseger@MIT.EDU> & David Hinds :
+ * -----------------------------------------------------------------
+ *     - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk>
+ *     - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu>
+ *     - Better initialisation of the i82593 controller
+ *       from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu>
+ *
+ * Changes made for release in 3.0.10 :
+ * ----------------------------------
+ *     - Fix eject "hang" of the driver under 2.2.X :
+ *             o create wv_flush_stale_links()
+ *             o Rename wavelan_release to wv_pcmcia_release & move up
+ *             o move unregister_netdev to wavelan_detach()
+ *             o wavelan_release() no longer call wavelan_detach()
+ *             o 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 <shadow@dementia.org>
+ *     - Better handling of TX and RX normal failure conditions
+ *     - #ifdef out all the roaming code
+ *     - Add ESSID & "AP current address" ioctl stubs
+ *     - General cleanup of the code
+ *
+ * Changes made for release in 3.0.13 :
+ * ----------------------------------
+ *     - Re-enable compilation of roaming code by default, but with
+ *       do_roaming = 0
+ *     - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather
+ *       at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu>
+ *     - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff.
+ *
+ * Changes made for release in 3.0.15 :
+ * ----------------------------------
+ *     - Change e-mail and web page addresses
+ *     - Watchdog timer is now correctly expressed in HZ, not in jiffies
+ *     - Add channel number to the list of frequencies in range
+ *     - Add the (short) list of bit-rates in range
+ *     - Developp a new sensitivity... (sens.value & sens.fixed)
+ *
+ * Changes made for release in 3.1.2 :
+ * ---------------------------------
+ *     - Fix check for root permission (break instead of exit)
+ *     - New nwid & encoding setting (Wireless Extension 9)
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>            /* Wireless extensions */
+#endif
+
+/* Pcmcia headers that we need */
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/version.h>
+
+/* Wavelan declarations */
+#include "i82593.h"    /* Definitions for the Intel chip */
+
+#include "wavelan_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 <var>=<value>'
+ */
+
+/* Bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */
+static int     irq_mask = 0xdeb8;
+static int     irq_list[4] = { -1 };
+
+/* Shared memory speed, in ns */
+static int     mem_speed = 0;
+
+/* New module interface */
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(mem_speed, "i");
+
+#ifdef WAVELAN_ROAMING         /* Conditional compile, see above in options */
+/* Enable roaming mode ? No ! Please keep this to 0 */
+static int     do_roaming = 0;
+MODULE_PARM(do_roaming, "i");
+#endif /* WAVELAN_ROAMING */
+
+MODULE_LICENSE("GPL");
+
+#endif /* WAVELAN_CS_P_H */
+
index 0228a725e3a68fc892ffae948744cebe47bbba3d..f2e6b8d5ad949c2dd8256c0a1b5772547c451b83 100644 (file)
@@ -1098,7 +1098,7 @@ static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma)
 
 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;
index 935b58dc289361a2e48d14badb5955812bd92e29..b67349ba47e1f32e358127df41b200288cd123c1 100644 (file)
@@ -87,7 +87,7 @@ static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hd
 
 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;
index ea673d06ac69f6c1b7b7edb41251722ef29dea04..d8f82941746f989113ee217d5097a15421191801 100644 (file)
@@ -640,7 +640,7 @@ static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned
 
 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;
 
index 54883b59b006e190970ee6bd4c5650809d70c861..6eaecc1bfeb40b2530a37ea2cd4c7d432d4fb77c 100644 (file)
@@ -46,6 +46,10 @@ ifeq ($(CONFIG_USB_EHCI_HCD),y)
        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 @@ obj-$(CONFIG_USB_AUERSWALD)   += auerswald.o
 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
 
index 8fec3378590224a12cd67a0bcb8620242816432d..281b769fddf11343491c301ec085f5b1e8307aac 100644 (file)
@@ -297,13 +297,13 @@ struct usb_audio_state;
 #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];
 };
 
 
index 073b0c80d3339cf336e205f05227fc0106a5c1ee..24490c47d4089a08ef2316efbb80531c97f2790c 100644 (file)
@@ -176,7 +176,7 @@ struct  auerchain;                      /* forward for circular reference */
 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 @@ typedef struct
         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 @@ typedef struct
        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 @@ typedef struct
 
 /*-------------------------------------------------------------------*/
 /* 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 @@ static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp);
 /* --------------------------                                        */
 
 /* 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 @@ static void auerchain_complete (urb_t * urb)
    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 @@ static int auerchain_submit_urb_list (pauerchain_t acp, urb_t * urb, int early)
 /* 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 @@ static int auerchain_submit_urb (pauerchain_t acp, urb_t * urb)
    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 int auerchain_unlink_urb (pauerchain_t acp, urb_t * urb)
 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 @@ ac_fail:/* free the elements */
 
 
 /* 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 @@ static void auerchain_blocking_completion (urb_t *urb)
 
 
 /* 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 @@ static int auerchain_control_msg (pauerchain_t acp, struct usb_device *dev, unsi
 {
        int ret;
        struct usb_ctrlrequest *dr;
-       urb_t *urb;
+       struct urb *urb;
         int length;
 
         dbg ("auerchain_control_msg");
@@ -858,7 +858,7 @@ static int auerswald_status_retry (int status)
 }
 
 /* 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 @@ static void auerchar_ctrlwrite_complete (urb_t * urb)
 }
 
 /* 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 @@ static void auerswald_ctrlread_wretcomplete (urb_t * urb)
 }
 
 /* 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 @@ static void auerswald_ctrlread_complete (urb_t * urb)
    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;
index 0ddbffdee5a7a76051b5526beff7d0c2759952fb..d75364cff6fa8e7d3e159c7422eaec541a844958 100644 (file)
@@ -53,7 +53,7 @@ struct async {
        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 @@ extern inline unsigned int ld2(unsigned int x)
 
 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;
index 1e6cefb31d2f7cfc80c4e0d93ff0e8c921a1143e..882b4d559cddaa2a895e78ece519cd3d5a280c44 100644 (file)
@@ -606,7 +606,7 @@ clean_2:
                        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 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
        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 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
        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 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
        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?) */
index 6f64bd8b3239b4100c64056c150be124982758aa..bc1100521ab54a792a3a7596a8a6a6ecc1e0427c 100644 (file)
@@ -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
 
index 453c9cc3c676563acb53b048693730cd7be629a7..0c99da88a5d75161a06006952e360aa913bffa00 100644 (file)
@@ -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.
index 1328913d6f29fce2894a6b0def0b04e0941dfdb9..62754b5a2bac481a480d187c448c2e9c75fc5ad1 100644 (file)
@@ -630,7 +630,7 @@ itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags)
        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 --git a/drivers/usb/hcd/ohci-dbg.c b/drivers/usb/hcd/ohci-dbg.c
new file mode 100644 (file)
index 0000000..350ae3b
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * 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 (&regs->revision) & 0xff;
+       if (temp != 0x10)
+               dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+       temp = readl (&regs->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 (&regs->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 (&regs->intrstatus));
+       ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+       // intrdisable always same as intrenable
+       // ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
+
+       maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+       maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+       maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+       maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+       maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+       maybe_print_eds ("donehead", readl (&regs->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 --git a/drivers/usb/hcd/ohci-hcd.c b/drivers/usb/hcd/ohci-hcd.c
new file mode 100644 (file)
index 0000000..0f14a60
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * [ 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>  /* 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 <linux/usb.h>
+#include "../hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#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 <weissg@vienna.at>, 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 (&regs->intrstatus)
+                       & readl (&regs->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, &regs->intrdisable);     
+               dl_done_list (ohci, dl_reverse_done_list (ohci));
+               writel (OHCI_INTR_WDH, &regs->intrenable); 
+       }
+  
+       if (ints & OHCI_INTR_SO) {
+               dbg ("USB Schedule overrun");
+               writel (OHCI_INTR_SO, &regs->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, &regs->intrdisable);      
+               if (ohci->ed_rm_list [!frame] != NULL) {
+                       dl_del_list (ohci, !frame);
+               }
+               if (ohci->ed_rm_list [frame] != NULL)
+                       writel (OHCI_INTR_SF, &regs->intrenable);       
+       }
+
+       writel (ints, &regs->intrstatus);
+       writel (OHCI_INTR_MIE, &regs->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 --git a/drivers/usb/hcd/ohci-hub.c b/drivers/usb/hcd/ohci-hub.c
new file mode 100644 (file)
index 0000000..f54fcd4
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * 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 --git a/drivers/usb/hcd/ohci-mem.c b/drivers/usb/hcd/ohci-mem.c
new file mode 100644 (file)
index 0000000..e41c7bd
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * 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 --git a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c
new file mode 100644 (file)
index 0000000..f89cdc5
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * 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 --git a/drivers/usb/hcd/ohci.h b/drivers/usb/hcd/ohci.h
new file mode 100644 (file)
index 0000000..8b2fbff
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * 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)
+
index b2511dbdc8f15edf5fecc83100c140c8f86edee1..6a5e1cd029805ae0d3f4a000fb3063e9ab4a2831 100644 (file)
@@ -147,7 +147,7 @@ struct usb_device;
 
 struct ibmcam_sbuf {
        char *data;
-       urb_t *urb;
+       struct urb *urb;
 };
 
 struct ibmcam_frame {
index a261006554db546a355f7e466d6d9638acaef4d5..046674d3437e0bcad1bb121ec47e4236fd014d14 100644 (file)
@@ -953,7 +953,7 @@ struct usb_api_data {
 /*-------------------------------------------------------------------*
  * 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 @@ static void usb_api_blocking_completion(urb_t *urb)
  *-------------------------------------------------------------------*/
 
 // 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 @@ int kaweth_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;
 
index 7a44e9310061a94eac69ef64607b057cfd839fe7..5a61edd2cbdfd2d2165b94c6ce7324b1f9de536d 100644 (file)
@@ -3875,7 +3875,7 @@ ov511_postprocess(struct usb_ov511 *ov511, struct ov511_frame *frame)
  **********************************************************************/
 
 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 @@ check_middle:
 }
 
 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 @@ ov511_isoc_irq(struct urb *urb)
 static int 
 ov511_init_isoc(struct usb_ov511 *ov511)
 {
-       urb_t *urb;
+       struct urb *urb;
        int fx, err, n, size;
 
        PDEBUG(3, "*** Initializing capture ***");
index a3f55f7e5536f021c2435691260ef78672712639..0bdf18885253129a491a47df78dc5bc4c1efccab 100644 (file)
@@ -366,7 +366,7 @@ struct ov511_i2c_struct {
 
 struct ov511_sbuf {
        char *data;
-       urb_t *urb;
+       struct urb *urb;
 };
 
 enum {
index 5b1b9fb66d49cbeb571d27ca76dada9c6e6a8794..ce187fb485bdf0cde2679802311e6dcd72354ec4 100644 (file)
@@ -94,7 +94,7 @@ MODULE_DEVICE_TABLE (usb, pegasus_ids);
 
 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;
 
index c6747eb9c8470b80ad3df42d35575f44c280001e..7029693e2d73c912da318786af073fb3ffdda71b 100644 (file)
@@ -610,7 +610,7 @@ static void se401_send_size(struct usb_se401 *se401, int width, int height)
 */
 static int se401_start_stream(struct usb_se401 *se401)
 {
-       urb_t *urb;
+       struct urb *urb;
        int err=0, i;
        se401->streaming=1;
 
index e1fd7d2417b5dbb109a2df04579886204505c50a..861210147ca9f7b3a84c65c31110892c805802c7 100644 (file)
@@ -197,8 +197,8 @@ struct usb_se401 {
 
        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;
index ff4e8e101629faf87a633a24a95482732b7e25b3..d323a742f7efbfd68251a9a54669526cf0686022 100644 (file)
@@ -138,6 +138,7 @@ static __devinitdata struct usb_device_id id_table_sio [] = {
    
 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_8U232AM [] = {
 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 */
 };
 
index 321ae1914799dde80781256c7a1b56856dd45cd9..5254d49243929f6768fd759ca7c1670310bf1e97 100644 (file)
@@ -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 */
index d7968e6192808e1721520a22398c755058f0ab91..de7463529c3ef700710009bf65408f7802057f50 100644 (file)
@@ -2456,7 +2456,7 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
 {
        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);
index f736290afefa985f1968e727117a0aae69201482..43bc791c6cdefdfa86613666584ba2ef41b65998 100644 (file)
@@ -109,11 +109,11 @@ struct keyspan_serial_private {
 
        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 @@ struct keyspan_port_private {
        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 @@ static int keyspan_write(struct usb_serial_port *port, int from_user,
        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 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp)
        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 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp)
        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 @@ static int keyspan_fake_startup (struct usb_serial *serial)
 }
 
 /* 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 urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
 }
 
 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 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
        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 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
        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 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
        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;
 
index df717345071412ba2cca948b9200564a03d70670..089672906f2785dc078daf94f461c48eb9d6bd8e 100644 (file)
@@ -369,7 +369,7 @@ int usb_stor_clear_halt(struct usb_device *dev, int pipe)
 /* 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;
 
index 31786c2fdf2b50e5ee7d037047cf7d66b4d32cf6..fad991e9a25032f657510643b40b75f7f8b956c6 100644 (file)
@@ -766,7 +766,7 @@ static void stv680_video_irq (struct urb *urb)
 
 static int stv680_start_stream (struct usb_stv *stv680)
 {
-       urb_t *urb;
+       struct urb *urb;
        int err = 0, i;
 
        stv680->streaming = 1;
index 69a4c33a1ba982ccba98e95d5f60a862f20ec42f..3c0bd3ecc09de9cab4339f6a20a384c2cbfeea0b 100644 (file)
@@ -118,7 +118,7 @@ struct usb_stv {
        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;
index de513c7ad7567250d462f16819d528a52f97d31f..2ced4412d671114373038bb8a286358b1500e8f5 100644 (file)
@@ -520,7 +520,8 @@ static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct u
 
        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 @@ static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
 
        /* 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 @@ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
 
                /* 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),
index e62ca8a87b3209ce2c3c63e000365833426a3372..6720ddfdaedcc4dc24e8712b4c7005a6a24818ba 100644 (file)
@@ -173,7 +173,7 @@ static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
        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_locked (urb_t * urb)
        }
 }
 
-static void urb_rm_priv (urb_t * urb)
+static void urb_rm_priv (struct urb * urb)
 {
        unsigned long flags;
 
@@ -224,7 +224,7 @@ static int sohci_get_current_frame_number (struct usb_device * dev);
 /* 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 @@ static void ohci_dump (ohci_t *controller, int verbose)
 
 /* 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 @@ static int sohci_return_urb (struct ohci *hc, urb_t * urb)
 
 /* 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 @@ static int sohci_submit_urb (urb_t * urb)
 /* 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 ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
 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 @@ td_fill (ohci_t * ohci, unsigned int info,
  
 /* 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 @@ static void dl_transfer_length(td_t * td)
 {
        __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 @@ static void dl_transfer_length(td_t * td)
 
 /* 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 @@ static void dl_del_list (ohci_t  * ohci, unsigned int frame)
                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 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list)
        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 @@ static void rh_int_timer_do (unsigned long ptr)
 {
        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 @@ static void rh_int_timer_do (unsigned long ptr)
 
 /* 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 @@ static int rh_init_int_timer (urb_t * urb)
 
 /* 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_submit_urb (urb_t * urb)
 
 /*-------------------------------------------------------------------------*/
 
-static int rh_unlink_urb (urb_t * urb)
+static int rh_unlink_urb (struct urb * urb)
 {
        ohci_t * ohci = urb->dev->bus->hcpriv;
  
index ba9758e2ddf52d41b03b0be710fc62d20b8c4e57..82457dd5850d12ad1aa35c5b94189c71cf1e61da 100644 (file)
@@ -111,7 +111,7 @@ struct td {
        __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 int ep_unlink(ohci_t * ohci, ed_t * ed);
 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);
 
 /*-------------------------------------------------------------------------*/
 
index e929b08f1aedfcf1985227cdaf83a315073333c5..e7acef2f275031ea1084d37eca30f97dc59a7b55 100644 (file)
 // 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 @@ _static void uhci_switch_timer_int(uhci_t *s)
 }
 /*-------------------------------------------------------------------*/
 #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 @@ _static void enable_desc_loop(uhci_t *s, urb_t *urb)
        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 @@ _static void disable_desc_loop(uhci_t *s, urb_t *urb)
 }
 #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 @@ _static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
        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 @@ _static void queue_urb (uhci_t *s, urb_t *urb)
        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 @@ _static int init_skel (uhci_t *s)
 //                         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 @@ fail_unmap_enomem:
 // 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 @@ _static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
  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 @@ _static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mo
                       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 @@ _static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mo
 }
 /*-------------------------------------------------------------------*/
 // 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_release_bandwidth(urb_t *urb)
        }       
 }
 
-_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 @@ _static void uhci_urb_dma_sync(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
                                    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 @@ _static void uhci_urb_dma_unmap(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv)
    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 @@ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode)
 }
 /*-------------------------------------------------------------------*/
 // 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 int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
 _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 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force)
 
        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 void uhci_cleanup_unlink(uhci_t *s, int force)
 }
  
 /*-------------------------------------------------------------------*/
-_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 @@ _static int uhci_unlink_urb (urb_t *urb)
 // 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 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
        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 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
 /*-------------------------------------------------------------------*/
 // 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 @@ _static int iso_find_start (urb_t *urb)
 // 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 @@ _static int uhci_submit_int_urb (urb_t *urb)
        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 @@ _static int uhci_submit_iso_urb (urb_t *urb)
 /*-------------------------------------------------------------------*/
 // 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 @@ _static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
        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 @@ _static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
        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 int uhci_submit_urb (urb_t *urb)
 _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 @@ _static void uhci_check_timeouts(uhci_t *s)
 
                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 @@ _static __u8 root_hub_hub_des[] =
 
 /*-------------------------------------------------------------------------*/
 /* 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 @@ _static int rh_send_irq (urb_t *urb)
 
 /*-------------------------------------------------------------------------*/
 /* 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 @@ _static void rh_int_timer_do (unsigned long ptr)
 /*-------------------------------------------------------------------------*/
 /* 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_init_int_timer (urb_t *urb)
  *************************/
 
 
-_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_submit_urb (urb_t *urb)
 }
 /*-------------------------------------------------------------------------*/
 
-_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 @@ _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_
        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 @@ struct usb_operations uhci_device_operations =
        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 @@ _static void correct_data_toggles(urb_t *urb)
  *       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 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode)
        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 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode)
        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 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
 // 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_iso (uhci_t *s, urb_t *urb, int mode)
 _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 @@ _static int process_urb (uhci_t *s, struct list_head *p)
 #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;
index 5850f7f28f6bd4ea692dad346c6c782d788aa7e6..2ae48765cee2afb4c84b9ef8030e5f83dbeccb3e 100644 (file)
@@ -158,8 +158,8 @@ typedef struct {
        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;
index 3e8099cc7f9a0a8d468ae737596be1a2c86a2932..0dff0a5875a51514ebfb8725fcb123f6384b224f 100644 (file)
@@ -1084,12 +1084,13 @@ void usb_inc_dev_use(struct usb_device *dev)
  *
  *     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 @@ urb_t *usb_alloc_urb(int iso_packets)
  *     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 @@ void usb_free_urb(urb_t* urb)
  * 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 @@ int usb_submit_urb(urb_t *urb)
  * 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 @@ struct usb_api_data {
        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 @@ static void usb_api_blocking_completion(urb_t *urb)
 }
 
 // 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 @@ static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
 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_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u
 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;
index d6094f3b068cb3982ace165d8864f4b2b378c4cf..c5c30d3d1e8d5d335a0bf60579639b3bb39a219b 100644 (file)
@@ -1776,7 +1776,7 @@ read_done:
 /*
  * 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 @@ int usbvideo_StartDataPump(uvd_t *uvd)
        /* 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);
index 5ff4cf3ec426695e178f133ad579c3cf0d019b83..17456ed46b0dafeb11d2d5c5cc3f3e89b0e8cf6d 100644 (file)
@@ -165,7 +165,7 @@ struct usb_device;
 /* This structure represents one Isoc request - URB and buffer */
 typedef struct {
        char *data;
-       urb_t *urb;
+       struct urb *urb;
 } usbvideo_sbuf_t;
 
 typedef struct {
index dce980c1d28483af48343edb1966642fb72647a6..82eef90a5929303bcb7fa2a81437ec39798118f5 100644 (file)
@@ -468,29 +468,23 @@ static int vicam_v4l_open(struct video_device *vdev, int flags)
        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 @@ static void vicam_v4l_close(struct video_device *vdev)
        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 @@ static int vicam_v4l_init(struct video_device *dev)
 
 /* 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 @@ static void vicam_disconnect(struct usb_device *udev, void *ptr)
 
 /* 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 @@ static void __exit usb_vicam_exit(void)
 
 module_init(usb_vicam_init);
 module_exit(usb_vicam_exit);
+
index 46b4aaddfa936c8cf2f4b53105c74c5487a2d6ab..b483befbfdaef4125ae87602630644e0c49f333a 100644 (file)
@@ -68,7 +68,7 @@ struct usb_vicam
        /* v4l stuff */
        char *camera_name;
        char *fbuf;
-       urb_t *urb[VICAM_NUMSBUF];
+       struct urb *urb[VICAM_NUMSBUF];
        int sizes;
        int *width;
        int *height;
index 1ac692e33a1f2cd9be2e1d1244b1cee7b0aa23dd..bf53d87351e53374181bf5699ab8110cbd142942 100644 (file)
@@ -419,7 +419,7 @@ prot_to_mode(u32 prot)
 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 @@ mode_to_prot(struct inode *inode)
        if (mode & S_IWOTH)
                prot |= FIBF_OTR_WRITE;
 
-       AFFS_INODE->i_protect = prot;
+       AFFS_I(inode)->i_protect = prot;
 }
 
 void
index 697c8b47df9724b98a0bdb29c1d507a25f5a3a09..919309b100b167b0d9981d8487e20f6549576b6b 100644 (file)
@@ -155,16 +155,16 @@ affs_alloc_block(struct inode *inode, u32 goal)
 
        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 @@ find_bit:
        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);
 
index 6a3602298fdbb943adbc75727f7507dc03c94fca..510dffaf9c67d082435fa1cd230a4347e0cd7f8c 100644 (file)
@@ -62,8 +62,8 @@ affs_file_open(struct inode *inode, struct file *filp)
 {
        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 @@ affs_file_release(struct inode *inode, struct file *filp)
 {
        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 @@ affs_grow_extcache(struct inode *inode, u32 lc_idx)
        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_grow_extcache(struct inode *inode, u32 lc_idx)
                        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_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
        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 @@ static inline struct buffer_head *
 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 @@ affs_get_extblock_slow(struct inode *inode, u32 ext)
        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 @@ affs_get_extblock_slow(struct inode *inode, u32 ext)
                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 @@ affs_get_extblock_slow(struct inode *inode, u32 ext)
 
 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 @@ again:
 
        /* 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 @@ again:
        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 @@ find_ext:
 
        /* 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 @@ read_ext:
 
 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 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
        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 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
                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_readpage(struct file *file, struct page *page)
 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 @@ affs_extent_file_ofs(struct file *file, u32 newsize)
                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 @@ done:
        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 @@ affs_free_prealloc(struct inode *inode)
 
        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 @@ affs_truncate(struct inode *inode)
                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 @@ affs_truncate(struct inode *inode)
                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_truncate(struct inode *inode)
        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) {
index 2104b91b9e81b6faec0ccacd030a2522ecdb5259..b804256a8da55df3c6b8b04f07889a2722902a83 100644 (file)
@@ -68,12 +68,20 @@ affs_read_inode(struct inode *inode)
        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 @@ affs_read_inode(struct inode *inode)
        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 @@ affs_write_inode(struct inode *inode, int unused)
        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 @@ affs_put_inode(struct inode *inode)
        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 @@ affs_delete_inode(struct inode *inode)
 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 @@ affs_new_inode(struct inode *dir)
        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);
 
index b4653c37fa894bbc4b8895e4dea330c3a7422bc5..e824a71af7a7379fbef91a418851731bfe48ff68 100644 (file)
@@ -78,7 +78,54 @@ affs_write_super(struct super_block *sb)
        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 DECLARE_FSTYPE_DEV(affs_fs_type, "affs", affs_read_super);
 
 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;
index 3918709b08176da950eb1be14df39a76f0c6ed89..365fb633e88f06218783950c67fb8221d8153df5 100644 (file)
@@ -39,9 +39,60 @@ static void coda_clear_inode(struct inode *);
 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 @@ static void coda_read_inode(struct inode *inode)
 
         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 @@ static void coda_clear_inode(struct inode *inode)
         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)
index 7300732d467420cc6791e622758e91c0a8c3c5ed..add25fd1c6afbb0016734994001c55b24fab43ae 100644 (file)
@@ -407,24 +407,35 @@ static int init_coda_psdev(void)
 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
 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 @@ static void __exit exit_coda(void)
        devfs_unregister(devfs_handle);
        devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev");
        coda_sysctl_clean();
+       coda_destroy_inodecache();
 }
 
 module_init(init_coda);
index 19c4066007e377e2b9f17444a46b1c2149d35d9c..e7b2eec05fe2df704ecece5acc2a29f24b0cd3ef 100644 (file)
@@ -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
     20020113   Richard Gooch <rgooch@atnf.csiro.au>
               Fixed (rare, old) race in <devfs_lookup>.
   v1.9
+    20020120   Richard Gooch <rgooch@atnf.csiro.au>
+              Fixed deadlock bug in <devfs_d_revalidate_wait>.
+              Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
+  v1.10
 */
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "1.9 (20020113)"
+#define DEVFS_VERSION            "1.10 (20020120)"
 
 #define DEVFS_NAME "devfs"
 
@@ -781,7 +785,7 @@ struct devfs_entry
     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 @@ int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
     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 @@ devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)
        {
            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 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
     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 @@ static int devfs_unlink (struct inode *dir, struct dentry *dentry)
     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 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry,
     DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %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 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
     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 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
     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 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
     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);
index 2d8f5afb9d81f7a50c1b502791b5647085d375eb..541d86f2c5cbb5b70da2a62e18750eb03339f23f 100644 (file)
 #include <linux/efs_fs.h>
 #include <linux/efs_vh.h>
 #include <linux/efs_fs_sb.h>
+#include <linux/slab.h>
 
 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;
index b54d2c4356fc6fdf68915207e34bf8429ca3de5f..5d3f1486f2a23c726d107b18a17c5cfd2624ec8a 100644 (file)
@@ -12,8 +12,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/quotaops.h>
 
index c30a3b075bf88fdd03f4ccf1c7a05721eb4db8fb..a007b4460cd1e5cca3cc20d56c731d0ad93276f0 100644 (file)
@@ -7,9 +7,7 @@
  * Universite Pierre et Marie Curie (Paris VI)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
-
+#include "ext2.h"
 
 static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
 
index 9fc9fd62b6035e0577a0cdf24044c82ce9aea642..d6416b9484f3e2cf13c54b673a09d39735c9e478 100644 (file)
@@ -21,8 +21,7 @@
  * and moved here. AV
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/pagemap.h>
 
 typedef struct ext2_dir_entry_2 ext2_dirent;
@@ -306,12 +305,13 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
        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 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
 
 found:
        *res_page = page;
-       dir->u.ext2_i.i_dir_start_lookup = n;
+       ei->i_dir_start_lookup = n;
        return de;
 }
 
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
new file mode 100644 (file)
index 0000000..09d13d6
--- /dev/null
@@ -0,0 +1,120 @@
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+/*
+ * 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 <linux/kernel.h> 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;
index 8d978bdf66bee00d4a140575998d8e2034414249..9843debce62f194f2e5a545615c2ce9d44933b25 100644 (file)
@@ -18,8 +18,7 @@
  *     (jj@sunsite.ms.mff.cuni.cz)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/sched.h>
 
 /*
index 211e52c7fc5b47832025f771d1a2c1641357736a..14c5db0d8cff72b29bb6211aeac696325a3cd850 100644 (file)
@@ -22,8 +22,7 @@
  * we can depend on generic_block_fdatasync() to sync the data blocks.
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/smp_lock.h>
 
index b492a6b22645a8a5616341c107ba520fbc12add2..dc40b916d37e20dce6b88c479207e49277848101 100644 (file)
@@ -13,8 +13,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/quotaops.h>
 
@@ -311,7 +310,7 @@ found:
        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 * ext2_new_inode (const struct inode * dir, int mode)
        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 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode)
        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 @@ repeat:
        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);
index 3c3040e7513d830b6be3593ade4e24fd6456af4e..a0b631578230172883ce44efb2283a9a1996be85 100644 (file)
@@ -22,8 +22,7 @@
  *  Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/locks.h>
 #include <linux/smp_lock.h>
 #include <linux/sched.h>
@@ -57,7 +56,7 @@ void ext2_delete_inode (struct inode * inode)
            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 @@ no_delete:
 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 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
 
 
 #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 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
                            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 @@ static Indirect *ext2_get_branch(struct inode *inode,
 
        *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 @@ no_block:
 
 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 @@ static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
         * 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 @@ static inline int ext2_find_goal(struct inode *inode,
                                 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 @@ static inline int ext2_find_goal(struct inode *inode,
                 * 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 @@ static int ext2_alloc_branch(struct inode *inode,
                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 @@ static inline int ext2_splice_branch(struct inode *inode,
                                     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 @@ static inline int ext2_splice_branch(struct inode *inode,
        /* 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 @@ static inline int ext2_splice_branch(struct inode *inode,
        /* 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 @@ static void ext2_free_branches(struct inode *inode, u32 *p, u32 *q, int depth)
 
 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 @@ void ext2_read_inode (struct inode * inode)
        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 @@ void ext2_read_inode (struct inode * inode)
        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 @@ void ext2_read_inode (struct inode * inode)
        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 @@ void ext2_read_inode (struct inode * inode)
                                   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 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
        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 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
  * 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 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
        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 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
        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);
index 6413da21b82a2c8da703fed127e1c524d4e3c602..07e9aafbc15927d6369193abe386e0a928931ee1 100644 (file)
@@ -7,8 +7,7 @@
  * Universite Pierre et Marie Curie (Paris VI)
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/sched.h>
 #include <asm/uaccess.h>
 
 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 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
                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 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 
                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;
index 364deca4f8902883651fd2c840ee7d8ba397eea0..24afae18f3f6ea1e76057ecede711f721e5173f2 100644 (file)
@@ -29,8 +29,7 @@
  *        David S. Miller (davem@caip.rutgers.edu), 1995
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/pagemap.h>
 
 /*
@@ -134,7 +133,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
        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 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
        } 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);
index 05ae46d5cd145ee47aab7911d09739c8416c797b..bb14991cf85f29bb4831d3b31848fae66029a922 100644 (file)
@@ -19,8 +19,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#include "ext2.h"
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/locks.h>
@@ -148,7 +147,51 @@ void ext2_put_super (struct super_block * sb)
        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 DECLARE_FSTYPE_DEV(ext2_fs_type, "ext2", ext2_read_super);
 
 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;
index 05a4e585a8eea03ebd9f7b6cad1a28c6e4ebe890..c642c53c6181deb534369a9c44d4cff56457bb47 100644 (file)
  *  ext2 symlink handling code
  */
 
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
+#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 = {
index 0800f6845d32bd0b3f60aae27c682dcffc5427d2..30e309d0f284dfe466fa6372d10d36ce21fb5b6a 100644 (file)
@@ -293,7 +293,7 @@ error_return:
  * 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 inode * ext3_new_inode (handle_t *handle,
        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 @@ struct inode * ext3_new_inode (handle_t *handle,
        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 @@ repeat:
                /*
                 * 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 @@ repeat:
                        /*
                         * 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 @@ repeat:
        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;
        
index e0ad97fe37124d179e5bbd6838757e02a2d77f12..4398759a120c5aa9fa7a4b173cf3cb537e0f61f6 100644 (file)
@@ -196,7 +196,7 @@ void ext3_delete_inode (struct inode * inode)
         * (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 @@ no_delete:
 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 @@ static int ext3_alloc_block (handle_t *handle,
        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 @@ static int ext3_alloc_block (handle_t *handle,
                            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 @@ static Indirect *ext3_get_branch(struct inode *inode, int depth, int *offsets,
 
        *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 @@ no_block:
 
 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 @@ static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind)
         * 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 inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind)
 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 @@ static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],
                 * 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 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,
 {
        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 @@ static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,
        /* 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 @@ static int ext3_get_block_handle(handle_t *handle, struct inode *inode,
        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 @@ out:
        /*
         * 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 @@ out:
        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 @@ out:
         * 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 *ext3_bread(handle_t *handle, struct inode * inode,
                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 @@ static int ext3_commit_write(struct file *file, struct page *page,
                        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 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode,
 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 @@ void ext3_truncate(struct inode * inode)
         * 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 @@ do_indirects:
                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 @@ void ext3_read_inode(struct inode * inode)
 {
        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 @@ void ext3_read_inode(struct inode * inode)
                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 @@ void ext3_read_inode(struct inode * inode)
        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 @@ void ext3_read_inode(struct inode * inode)
                                         * 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 @@ void ext3_read_inode(struct inode * inode)
                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 @@ static int ext3_do_update_inode(handle_t *handle,
                                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 @@ static int ext3_do_update_inode(handle_t *handle,
  * 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 @@ static int ext3_do_update_inode(handle_t *handle,
                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 @@ static int ext3_do_update_inode(handle_t *handle,
                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 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
                }
                
                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 @@ int ext3_change_inode_journal_flag(struct inode *inode, int val)
         */
 
        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);
 
index 767bf65705ad3043d12c8f077ffe02a26d953648..07f7fdd93828170d67ce26be31c2c8780cc0030a 100644 (file)
 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 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
                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 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
                
                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;
index d7502fd79b8bf38a5659ed40316ecd4fa7e26159..ca49f46f9b03d318c0769c3573ba6577bbb0fc30 100644 (file)
@@ -124,7 +124,7 @@ static struct buffer_head * ext3_find_entry (struct dentry *dentry,
        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 @@ restart:
                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 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
                                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 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
                         * 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 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 
        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 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        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 ext3_orphan_add(handle_t *handle, struct inode *inode)
        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 @@ int ext3_orphan_add(handle_t *handle, struct inode *inode)
         * 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 @@ out_unlock:
 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 @@ int ext3_orphan_del(handle_t *handle, struct inode *inode)
        } 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 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
        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 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
        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 @@ static int ext3_symlink (struct inode * dir,
        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 @@ static int ext3_symlink (struct inode * dir,
                        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 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
                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 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
                        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);
                }
        }
index 46664b11aa5e3d5bb2acea3f7aa07584743f1a0b..013bb132adda9af4d4b1c5aeedba9306b9bf4d7f 100644 (file)
@@ -376,7 +376,10 @@ static int ext3_blkdev_remove(struct ext3_sb_info *sbi)
        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 @@ void ext3_put_super (struct super_block * sb)
        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 DECLARE_FSTYPE_DEV(ext3_fs_type, "ext3", ext3_read_super);
 
 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;
index 87f91adbd2acbd0b3410eb9471c38a329e994aad..46f04cf69d3f931860b0bd1c07934968cf74a69e 100644 (file)
 
 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 = {
index 2b474f2977fa910714df25bc2dcf78b4cf93cc85..29bb6bf791c87e898380077227537c5755371737 100644 (file)
@@ -744,8 +744,8 @@ int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
        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 @@ int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
        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];
index 9a3b45bcda5fb3e518abfd923a39b92cf0aea977..e18c537d06c4421e80271517147a6955775d801c 100644 (file)
@@ -65,8 +65,11 @@ int fat_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_r
                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);
index 2f34195b595d0dfaa3d27081a71f63b3b15bad42..87b913b032909ac8b9916ba6a0376a44ce22ecac 100644 (file)
@@ -131,14 +131,13 @@ int fat_add_cluster(struct inode *inode)
 {
        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 @@ int fat_add_cluster(struct inode *inode)
        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 @@ int fat_add_cluster(struct inode *inode)
        */
        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 @@ int fat_add_cluster(struct inode *inode)
                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 @@ struct buffer_head *fat_extend_dir(struct inode *inode)
 
        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 @@ struct buffer_head *fat_extend_dir(struct inode *inode)
                                        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 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
     **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)*
index 408427e9b4ba625e0832f86ebe32fb0e7b927217..f62612bb97673da8990f2be04a13c4be126d0f43 100644 (file)
@@ -42,9 +42,10 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
                                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;
index 66067c2a599d7c16fbc6ce7f35acd3c15cce3827..0049e5ee48fa5cbf0b5fb24ddf2ba52799b8b69e 100644 (file)
@@ -47,38 +47,72 @@ void hpfs_unlock_iget(struct super_block *s)
 
 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 @@ void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
        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 @@ void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
        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);
 }
index f2a732869ef9f614319a4021e163036d12d963e1..e9c1706f1d7e96be65688ef3087f81283cd5b1a1 100644 (file)
@@ -27,11 +27,12 @@ loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)
        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 @@ loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)
 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 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        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 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        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 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        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 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
        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 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
         * '.' 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 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
                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 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
                        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
index 78286ad36d118bc0e8f18f6153befee148998b42..2ddea3546c4c991c18dcdfa8235829f3f1b31168 100644 (file)
@@ -23,39 +23,43 @@ static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
 
 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 @@ void hpfs_del_pos(struct inode *inode, loff_t *pos)
 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 @@ int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, uns
        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_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, uns
 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 secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
 
 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 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno)
                                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;
                }
index 6c21acfb0c107c15e6be99d1e352af615badf9cf..f49fe4ee5e9d572ea172206a8a19593ecf699dd1 100644 (file)
@@ -355,7 +355,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data
        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)
index a6ea7c41b7c0f540bab8138c81a0860698f5610a..d841654edaa0b5b079884310cfc17d6cd2ad1394 100644 (file)
@@ -45,12 +45,13 @@ int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
 
 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 @@ secno hpfs_bmap(struct inode *inode, unsigned file_secno)
 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 @@ int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
                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 @@ int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
                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_readpage(struct file *file, struct page *page)
 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 @@ ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t
        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;
 }
index d7588317f0053a4d5a91706cbead85b043f72f5a..ce0c2b0f24a671e1d01030678057f2568959fa13 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/fs.h>
 #include <linux/hpfs_fs.h>
+#include <linux/hpfs_fs_i.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
@@ -303,6 +304,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 */
 
 void hpfs_error(struct super_block *, char *, ...);
index a4e03e28ef9f07f9ee24708fc88359b1799f8f0b..a45a3303be12a63993270f94b2614744cc29f286 100644 (file)
@@ -59,31 +59,32 @@ void hpfs_read_inode(struct inode *i)
        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 @@ void hpfs_read_inode(struct inode *i)
                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 @@ void hpfs_read_inode(struct inode *i)
                        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 @@ void hpfs_read_inode(struct inode *i)
                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_ea(struct inode *i, struct fnode *fnode)
 
 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(struct inode *i)
 
 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 @@ void hpfs_write_inode_nolock(struct inode *i)
                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 @@ int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
 
 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)
index f9fa94c7ef8665fbc075a9381906b99564a68c76..a50b0b004c5bd34a3d26747a13acae156746579f 100644 (file)
@@ -20,8 +20,9 @@ char *text_prefix[]={
 
 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 @@ void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
                        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;
 }
 
index 0947673627120d0aee41ca81fa3597cc81cadab7..3c5333c378ae3051cdfdaa30667c102026c1b55e 100644 (file)
@@ -25,7 +25,7 @@ int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
        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 hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
        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 @@ int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
        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 hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
        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 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
        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 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry)
        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 hpfs_rmdir(struct inode *dir, struct dentry *dentry)
        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 @@ int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
                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 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                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 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        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 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        }
        
        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 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        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 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                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);
index 6d8f486be2b9d5158d6a3a649f96de3122486adb..2483e920b5970e571fbfab60e856d95e09ef3fc8 100644 (file)
@@ -148,10 +148,56 @@ int hpfs_statfs(struct super_block *s, struct statfs *buf)
        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 @@ struct super_block *hpfs_read_super(struct super_block *s, void *options,
                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 @@ DECLARE_FSTYPE_DEV(hpfs_fs_type, "hpfs", hpfs_read_super);
 
 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;
index 43a4740dd9a7469448cd5ac0efec7137e89e6eb4..eadd1d857e196040def075955b368d33ff40303a 100644 (file)
@@ -188,7 +188,7 @@ void presto_e3_trans_commit(struct presto_file_set *fset, void *handle)
 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
index 3fdb83156b5f7d46cc21bd129ec0f5596714059d..bda86f328a4445fb88e5ab1d616770ea94bc0444 100644 (file)
@@ -163,7 +163,7 @@ void presto_obdfs_trans_commit(struct presto_file_set *fset, void *handle)
 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
index 578251ff9456c8ce23f73dc13154805f775d6f0b..780d9daa176e24c34a7c3e0cd4a37a7b5bdcb89b 100644 (file)
@@ -101,7 +101,7 @@ void presto_reiserfs_trans_commit(struct presto_file_set *fset, void *handle)
 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
index 64f549723884ac402541b8867bb260a9702c4862..dc50e798c642d12eb7e759bcf56170d13bc38858 100644 (file)
@@ -45,6 +45,8 @@ static struct inode * __nfs_fhget(struct super_block *, struct nfs_fh *, struct
 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_statfs(struct super_block *, struct statfs *);
 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 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
 
 /*
  * 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_invalidate_inode(struct inode *inode)
        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 @@ nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
                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 @@ nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fat
                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 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
        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_destroy_readpagecache(void);
 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 @@ static int __init init_nfs_fs(void)
 
        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");
index 936ce1dba3238b6d9e25d325e1d912d2fa6a4a95..919b2c5b57d653eaab8a0ea08d67a0fe6a5b6389 100644 (file)
@@ -156,10 +156,11 @@ static inline void
 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 @@ nfs_mark_request_read(struct nfs_page *req)
 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 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
                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 @@ nfs_pagein_list(struct list_head *head, int rpages)
 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 @@ nfs_scan_lru_read_timeout(struct nfs_server *server, struct list_head *dst)
 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 @@ nfs_scan_lru_read(struct nfs_server *server, struct list_head *dst)
 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;
 }
index 096ea12f8c2308f89b7dd28b02291b6a8847cae2..b3774c964c3843aee29e66ca597edd5ea6648519 100644 (file)
@@ -315,14 +315,15 @@ region_locked(struct inode *inode, struct nfs_page *req)
 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 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
 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 @@ nfs_inode_remove_request(struct nfs_page *req)
        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 @@ nfs_inode_remove_request(struct nfs_page *req)
 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 @@ static inline void
 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 @@ nfs_mark_request_dirty(struct nfs_page *req)
 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 @@ static inline void
 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 @@ nfs_mark_request_commit(struct nfs_page *req)
 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 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
                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 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
 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 @@ nfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst)
 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 @@ nfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst)
 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 @@ nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, un
 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 @@ nfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst)
 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 @@ nfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst)
 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 @@ nfs_strategy(struct inode *inode)
 {
        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 @@ nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data)
 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 @@ nfs_flush_one(struct list_head *head, struct inode *inode, int how)
        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;
index f544a436c822f374d56b3e33d4d204e62bc70cdf..b06d5c1d9424741377c0d1e18d19c523dd80c478 100644 (file)
@@ -142,13 +142,13 @@ int qnx4_set_bitmap(struct super_block *sb, long block, int busy)
 
 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)
index 7d17225f0d664e2dff7bb92c3c89d2a00f0a171a..f3d2301f1b4777dc5124fd47fe02693d15d9e0d1 100644 (file)
@@ -90,7 +90,7 @@ static int sync_direct(struct inode *inode, int wait)
 
        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)
index b74d341c2fa366fcafb9b219edea6b204d06cbf6..972fa8ed9760700f0969e08038e88c18224221df 100644 (file)
@@ -121,12 +121,16 @@ static void qnx4_write_inode(struct inode *inode, int unused)
 
 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 qnx4_block_map( struct inode *inode, long iblock )
        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 @@ static int qnx4_readpage(struct file *file, struct page *page)
 {
        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 @@ static void qnx4_read_inode(struct inode *inode)
        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 @@ static void qnx4_read_inode(struct inode *inode)
        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;
index 31c1bdd0ed466a30eb3649cd2908d0168ca9a3f7..8f82baf1f3b7a6144e39b094463b14370a42e7c9 100644 (file)
@@ -271,7 +271,7 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
                        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 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
                        *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 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
        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 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
                *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)
index 25f22378bba93a95ce83c290d8a6abc16913131d..d1eea1f58f69a0df49948741ef3c15ebf5a60262 100644 (file)
@@ -142,7 +142,7 @@ void ufs_free_inode (struct inode * inode)
  * 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 inode * ufs_new_inode (const struct inode * dir,     int mode)
        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 @@ struct inode * ufs_new_inode (const struct inode * dir,     int mode)
        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 @@ cg_found:
        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);
index d2d009aeeb6c949f4364f3b1583259b9b9932816..cc08c9febd2968764c25cdf9c58bb38c51295017 100644 (file)
@@ -84,6 +84,7 @@ static int ufs_block_to_path(struct inode *inode, long i_block, int offsets[4])
 
 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 @@ int ufs_frag_map(struct inode *inode, int frag)
        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 @@ static struct buffer_head * ufs_inode_getfrag (struct inode *inode,
        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 @@ static struct buffer_head * ufs_inode_getfrag (struct inode *inode,
        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 @@ repeat:
                 * 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 @@ repeat:
         * 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 @@ struct address_space_operations ufs_aops = {
 
 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 @@ void ufs_read_inode (struct inode * inode)
        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 @@ bad_inode:
 
 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 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
        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 @@ int ufs_sync_inode (struct inode *inode)
 
 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));
index b6b1833c43963a596b8b19b5bffc7fd937280213..0a95f62f7780088c0d20a6a83c31edff15064bd5 100644 (file)
@@ -138,7 +138,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
        } 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);
index 8d1260c601cc7a0361ef5285b003977a574617fb..8d57fb9d69d67d46fde22445b23b04f93afa137b 100644 (file)
@@ -953,7 +953,51 @@ int ufs_statfs (struct super_block * sb, struct statfs * buf)
        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 DECLARE_FSTYPE_DEV(ufs_fs_type, "ufs", ufs_read_super);
 
 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");
index 59a44e22e94b1b96b6ce93c54b7cd98e2a5018ab..2602f506d6af8bee098da473bd1394b19b89f94e 100644 (file)
  */
 
 #include <linux/fs.h>
+#include <linux/ufs_fs.h>
 
 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 = {
index 6fd8df562547373c42364b54b8466e51b44449fe..448466b31e01733de45c78957b37ac90e184894f 100644 (file)
@@ -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 @@ static int ufs_trunc_direct (struct inode * inode)
        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 @@ static int ufs_trunc_direct (struct inode * inode)
        /*
         * 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 @@ next1:
         * 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 @@ next2:;
        /*
         * 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_dindirect (struct inode * inode, unsigned offset, u32 * p)
 
 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 @@ static int ufs_trunc_tindirect (struct inode * inode)
        
        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 @@ static int ufs_trunc_tindirect (struct inode * inode)
                
 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 @@ void ufs_truncate (struct inode * inode)
        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 @@ void ufs_truncate (struct inode * inode)
                }
        }
        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"))
 }
index 2d94ed5531d79d986b4b36ce8d4808d58a406cdf..3f4a191556e4af323259ca4a2bd05772ec862560 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/locks.h>
+#include <linux/ufs_fs.h>
 
 #include "swab.h"
 #include "util.h"
index b2a140ce01271f1c061f91554b44f9f3a588df36..8d797bb6dafc0e12ea1a63e379e31e03a0426ead 100644 (file)
@@ -204,7 +204,7 @@ filp->f_dentry->d_name.name, entry.name);
                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 @@ dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
 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 @@ void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
        /*
         * 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 @@ dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
 
        /* 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 @@ dir->d_parent->d_name.name, dir->d_name.name, start, real);
        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);
index 9ecd1dbaa30dc351dace879ad05fb80f0c91828d..befdd474f7d80579db55e641bb9187ef29834531 100644 (file)
@@ -34,7 +34,7 @@ void UMSDOS_put_inode (struct inode *inode)
        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 @@ void UMSDOS_put_inode (struct inode *inode)
        }
 
        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_put_super (struct super_block *sb)
 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 @@ void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
        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 umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
        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 @@ dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched)
 
        /* 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);
index 9049610da7ac0df3f4ae178b8d24e9955b3978f1..1e40fd8e96ef7e37f9544ef697e4eb37f6f12784 100644 (file)
 
 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 @@ static int umsdos_waitcreate (struct inode *dir)
 {
        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 int umsdos_waitcreate (struct inode *dir)
  */
 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 @@ void umsdos_lockcreate (struct inode *dir)
         * 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 @@ static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
                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 @@ static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
 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_startlookup (struct inode *dir)
  */
 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_unlockcreate (struct inode *dir)
  */
 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 @@ temp->d_parent->d_name.name, temp->d_name.name, ret);
                        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 @@ olddentry->d_parent->d_name.name, olddentry->d_name.name));
         * 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 @@ out_unlock:
 
 
 #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
index 9fbca39419344662df24cd4d44b2817bcb230580..372454a2e112f4ec3aa12d940b142db946665c73 100644 (file)
@@ -113,7 +113,7 @@ Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n",
 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);
 
        }
index 86b07d5467596848d34a5962a7312a5a0583d0aa..4d9856538d92c4fa8b9c5a8735e49a4ae9c4560c 100644 (file)
@@ -22,6 +22,7 @@
 /* We use generic_ffs so get it; include guards resolve the possible
    mutually inclusion.  */
 #include <linux/bitops.h>
+#include <linux/compiler.h>
 
 /*
  * Some hacks to defeat gcc over-optimizations..
@@ -43,6 +44,8 @@ struct __dummy { unsigned long a[100]; };
 
 #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 @@ struct __dummy { unsigned long a[100]; };
 
 #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 @@ struct __dummy { unsigned long a[100]; };
  * 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 @@ static __inline__ int test_and_set_bit(int nr, void *addr)
        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 @@ static __inline__ int test_and_set_bit(int nr, void *addr)
  * 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 @@ static __inline__ int test_and_clear_bit(int nr, void *addr)
  * 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 @@ static __inline__ int __test_and_clear_bit(int nr, void *addr)
  * 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 @@ static __inline__ int test_and_change_bit(int nr, void *addr)
 
 /* 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 @@ static __inline__ int __test_and_change_bit(int nr, void *addr)
  * 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 @@ static __inline__ int test_bit(int nr, const void *addr)
  * 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 @@ static __inline__ unsigned long cris_swapnwbrlz(unsigned long w)
        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 @@ static __inline__ unsigned long cris_swapwbrlz(unsigned long w)
  * 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 @@ static __inline__ unsigned long ffz(unsigned long w)
  * 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 @@ static __inline__ unsigned long ffs(unsigned long w)
  * @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 @@ static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
 #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 @@ static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
 #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 */
index 085c43dd7b572f5bb042897acbcf439131f7c664..dba80d0609ef784634c833833688adb38bb9b821 100644 (file)
@@ -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 @@ typedef unsigned long elf_fpregset_t;
        (_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 --git a/include/asm-cris/ethernet.h b/include/asm-cris/ethernet.h
new file mode 100644 (file)
index 0000000..30da58a
--- /dev/null
@@ -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 */
index b1b5ada6ff5a2a2b2b5c89cf67a578e960f6ffc7..5e4fb8db67354c7e7ee5e671c118634ddb62b020 100644 (file)
@@ -126,6 +126,9 @@ void set_break_vector(int n, irqvectptr addr);
   /* 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 @@ __asm__ ( \
           "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 */
 
index 549345ff4f19a4bc2fef566a2fdf663b5616b6de..5950e9c1275fe71c1dc0c7f3c736b81bc9c3f08f 100644 (file)
@@ -105,8 +105,14 @@ typedef unsigned long pgprot_t;
  * 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 */
 
index 550c5fab2a39cfc187bf8feb41a3c727c7238365..80e73be0d2b05e777b46a7447a5ef722810eebc2 100644 (file)
@@ -21,7 +21,7 @@ extern struct pgtable_cache_struct {
  * 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 @@ extern __inline__ pgd_t *get_pgd_slow(void)
         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 @@ extern __inline__ pgd_t *get_pgd_fast(void)
         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;
index a99f35703c7ff3b1fa21f8eebece51a6e0450347..e533400551c03c30558f32af2d9dc691497bdcc8 100644 (file)
@@ -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
  *
  * 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 @@ static inline void update_mmu_cache(struct vm_area_struct * vma,
 /*
  * No page table caches to initialise
  */
-#define pgtable_cache_init()   do { } while (0)
+#define pgtable_cache_init()   do { } while (0)
 
 #endif /* _CRIS_PGTABLE_H */
index a9b11b89eced3ef75262b03114f6977cd32e80fa..7fba87a5aa1bc33afe530bd9d4ae204f18c6c7e5 100644 (file)
@@ -142,6 +142,6 @@ extern inline unsigned long thread_saved_pc(struct thread_struct *t)
 #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 --git a/include/asm-cris/scatterlist.h b/include/asm-cris/scatterlist.h
new file mode 100644 (file)
index 0000000..11a64f5
--- /dev/null
@@ -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) */
index d42c6b383fade9170e6dd43fa2dc821818b5da40..9fe719c8089e90b2d3b605eb040ec6df828ec55b 100644 (file)
@@ -378,6 +378,7 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
 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! */
 
index 79c7048a345d1d75a03284a12115ca65f8bfb9c2..982f4163bc9474a880746dee0360dbcb4d53c2f0 100644 (file)
  *     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) */
index fc8b17144adfde6fa9949cc666e2ab9f9ed104ca..e6d7377a70d8bafb57d24b680835c4d875707c3d 100644 (file)
  *  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,
index 68ba0f768bfaf831c86894489c3f712201c93815..7859a5e1883c8ecff66b2a5e6bf358d000936979 100644 (file)
@@ -6,6 +6,8 @@
 
 #include <linux/types.h>
 
+#include <linux/affs_fs_i.h>
+
 #define AFFS_SUPER_MAGIC 0xadff
 
 struct affs_date;
index 430ff682f6412860e12f5c71f81a1b23be39ade6..c32f69ca18f7a6f0cbdaf3e8ea0202781c584af4 100644 (file)
@@ -3,9 +3,6 @@
 
 #include <linux/a.out.h>
 
-// move this to linux/coda.h!!!
-#include <linux/time.h>
-
 #define AFFS_CACHE_SIZE                PAGE_SIZE
 //#define AFFS_CACHE_SIZE              (4*4)
 
@@ -48,10 +45,13 @@ struct affs_inode_info {
        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
index 1e7d6e8fa676f7794dbb9b01c5f6747af163656e..b4b1d430c306cfd5ac05b4b54df36aac00456ecd 100644 (file)
@@ -93,32 +93,32 @@ affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val)
 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
index f96edc7c7a37a2744f5af33dd8c1994ceac329b9..0d168b715ed79922c1dcc803c27645bb7bf65fd4 100644 (file)
@@ -99,6 +99,7 @@ typedef unsigned long long u_quad_t;
 
 
 #if defined(__linux__)
+#include <linux/time.h>
 #define cdev_t u_quad_t
 #ifndef __KERNEL__
 #if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
index 43d63d77697dcdbb098e939cf4407f67a6aad851..959fb24a94adba3f4d9f01fcd0d5e5828f0ab338 100644 (file)
@@ -24,6 +24,7 @@ struct coda_inode_info {
        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 */
index d514f908a3a59bd0b2b794253383664def3c6ef3..de66780b8d2c1634d9b5d8bd51f219d80c75e92a 100644 (file)
@@ -111,7 +111,10 @@ do {                                                                      \
 
 /* 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)
 {
index 5e231462b6451159c5054ea93e0332e2a60743d8..90813c5050644bd23d9a7e6f87dc129c358c81dc 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright 2000 (C) Stephen Rothwell
  */
 
+#include <linux/fs.h>
+
 struct dnotify_struct {
        struct dnotify_struct * dn_next;
        int                     dn_magic;
index ae049ed2f1478684893d8513efc0b78566e9a162..99d468975d0673e08693b2c8af61aeeccd13c781 100644 (file)
@@ -37,13 +37,11 @@ static const char cprt[] = "EFS: "EFS_VERSION" - (c) 1999 Al Smith <Al.Smith@aes
 #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;
index de55021ad24e9a65cc3760fa0aaff4b08547f336..64fe538acc88afdc18e14a068a843d731f210166 100644 (file)
@@ -61,6 +61,7 @@ struct efs_inode_info {
        int             lastextent;
 
        efs_extent      extents[EFS_DIRECTEXTENTS];
+       struct inode    vfs_inode;
 };
 
 #endif /* __EFS_FS_I_H__ */
index 03112a82fadb257e52d92b90ee6e602032b6c761..2b3624d801b31dc4ae8ebf1d961f08243aa1ed81 100644 (file)
@@ -532,101 +532,4 @@ enum {
 #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 <linux/kernel.h> 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 --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h
deleted file mode 100644 (file)
index 7f02e75..0000000
+++ /dev/null
@@ -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 */
index db5de1462107b261fa11234e1ef5d9a632ea81eb..c7fed24a7c76ba9d417b571509f07524460a9fba 100644 (file)
@@ -443,7 +443,10 @@ struct ext3_super_block {
 
 #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 @@ struct ext3_super_block {
 #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_check_dir_entry(const char *, struct inode *,
 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 *);
index 3c8d398a81039044765ad86adaf09d2018867fd1..104aea4e0c1958fe684c5fe02f33d02484adaa30 100644 (file)
@@ -73,6 +73,7 @@ struct ext3_inode_info {
         * by other means, so we have truncate_sem.
         */
        struct rw_semaphore truncate_sem;
+       struct inode vfs_inode;
 };
 
 #endif /* _LINUX_EXT3_FS_I */
index 88bb8a516bf62725d41720377c63f1e1f4ee63fb..ece9ec115665f3220f04059976f3fff81a954a84 100644 (file)
@@ -289,7 +289,7 @@ static inline int ext3_should_journal_data(struct inode *inode)
                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;
 }
index 4e5de1286d87969509d812eb9c2f813ae61fd252..6bda17aed79ac146466c42bd2355c246e4814d0e 100644 (file)
@@ -288,25 +288,15 @@ extern void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long
 
 #include <linux/pipe_fs_i.h>
 #include <linux/minix_fs_i.h>
-#include <linux/ext2_fs_i.h>
-#include <linux/ext3_fs_i.h>
-#include <linux/hpfs_fs_i.h>
 #include <linux/ntfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
 #include <linux/umsdos_fs_i.h>
 #include <linux/iso_fs_i.h>
-#include <linux/nfs_fs_i.h>
 #include <linux/sysv_fs_i.h>
-#include <linux/affs_fs_i.h>
-#include <linux/ufs_fs_i.h>
-#include <linux/efs_fs_i.h>
-#include <linux/coda_fs_i.h>
 #include <linux/romfs_fs_i.h>
-#include <linux/shmem_fs.h>
 #include <linux/smb_fs_i.h>
 #include <linux/hfs_fs_i.h>
 #include <linux/adfs_fs_i.h>
-#include <linux/qnx4_fs_i.h>
 #include <linux/reiserfs_fs_i.h>
 #include <linux/bfs_fs_i.h>
 #include <linux/udf_fs_i.h>
@@ -477,25 +467,15 @@ struct inode {
        __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 @@ struct inode {
        } u;
 };
 
+#include <linux/shmem_fs.h>
+/* will die */
+#include <linux/coda_fs_i.h>
+#include <linux/ext3_fs_i.h>
+#include <linux/efs_fs_i.h>
+
 struct fown_struct {
        int pid;                /* pid or -pgrp where SIGIO should be sent */
        uid_t uid, euid;        /* uid/euid of process setting the owner */
@@ -563,6 +549,9 @@ extern int init_private_file(struct file *, struct dentry *, int);
  */
 typedef struct files_struct *fl_owner_t;
 
+/* that will die - we need it for nfs_lock_info */
+#include <linux/nfs_fs_i.h>
+
 struct file_lock {
        struct file_lock *fl_next;      /* singly linked list for this inode  */
        struct list_head fl_link;       /* doubly linked list of all locks */
index 56a758b1627a10e06b5145a4f77b26b9a62b0a9a..c4d6cce5d6077cd3522d31e38f8dfea80e26a780 100644 (file)
@@ -18,24 +18,7 @@ struct hpfs_inode_info {
        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
index 97f3ecaf1f97605ebbca21eebf8722f5c00d96ba..8096e640c41679a5eda7a61a7979532c64cd437c 100644 (file)
@@ -278,6 +278,10 @@ struct net_device
        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 <net/iw_handler.h> 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
index efbbdba3a1bc91b56ebf454cb6ba6e8db65d42c0..93aae0c5fb6a07f9825014701542b10f24d8cec3 100644 (file)
  */
 #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 @@ unsigned long page_index(struct page *page)
        return page->index;
 }
 
-#ifdef __KERNEL__
 /*
  * linux/fs/nfs/inode.c
  */
@@ -220,13 +306,13 @@ extern int  nfs_scan_lru_commit_timeout(struct nfs_server *, struct list_head *)
 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
index 7fc3bae2278bce0edcc605b787c33ef1f1c8fb48..5a4fa8d5480115e58bf7300046d959dcb6795d01 100644 (file)
@@ -5,87 +5,6 @@
 #include <linux/list.h>
 #include <linux/nfs.h>
 
-/*
- * 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
  */
index 55ba2f99d9a23cc6642a700206bce6b639afc1dc..fc1912a3fd72a5ec9ef55385c350e8ab3a9f9f94 100644 (file)
@@ -97,6 +97,12 @@ struct qnx4_super_block {
 #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);
@@ -120,6 +126,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__ */
 
 #endif
diff --git a/include/linux/qnx4_fs_i.h b/include/linux/qnx4_fs_i.h
deleted file mode 100644 (file)
index b0fe846..0000000
+++ /dev/null
@@ -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 <linux/qnxtypes.h>
-
-/*
- * 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
index 3797423a4fbe5572c6b61a12a0e4cd7842f70575..b34544b8380f3a6f7c836df0b3f507c944ab1924 100644 (file)
@@ -141,6 +141,7 @@ typedef struct task_struct task_t;
 
 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_process_times(int user);
 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));
index 070eef7b5137e8f08e05df8d645588a3cc814e86..183b1e49dbe8d60eba0d266f5f0ee5ccfa2e464b 100644 (file)
@@ -28,7 +28,7 @@ struct shmem_inode_info {
        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 @@ struct shmem_sb_info {
        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
index f776030c8b0444bc239e11e726a6db302ee0e962..609d0dab2c6fdd5b8222b4fe1c456f6df4500756 100644 (file)
 
 #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 @@ struct ufs_inode {
 #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 @@ extern struct address_space_operations ufs_aops;
 
 /* 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);
@@ -567,6 +779,13 @@ extern struct inode_operations ufs_fast_symlink_inode_operations;
 /* truncate.c */
 extern void ufs_truncate (struct inode *);
 
+#include <linux/ufs_fs_i.h>
+
+static inline struct ufs_inode_info *UFS_I(struct inode *inode)
+{
+       return list_entry(inode, struct ufs_inode_info, vfs_inode);
+}
+
 #endif /* __KERNEL__ */
 
 #endif /* __LINUX_UFS_FS_H */
index 5f0287ab0139368e3407118a597c6f13ca485eb8..1b75864418223ee66b95848e351e422686604f89 100644 (file)
@@ -26,6 +26,7 @@ struct ufs_inode_info {
        __u32   i_oeftflag;
        __u16   i_osync;
        __u32   i_lastfrag;
+       struct inode vfs_inode;
 };
 
 #endif /* _LINUX_UFS_FS_I_H */
index 41b6ccc1eebe6e99965c023e419336906c69347e..c1be4c2264862e0f768b6d1cc129eab37bea3400 100644 (file)
 #ifndef __LINUX_UFS_FS_SB_H
 #define __LINUX_UFS_FS_SB_H
 
-#include <linux/ufs_fs.h>
-
-/*
- * 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 @@ struct ufs_sb_info {
        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
index 2752cb004c0f6cd2a4701b434f410c3e50b6e83a..99e6eefa822996cec53b33ad0a5725687d538aeb 100644 (file)
@@ -93,3 +93,8 @@ int UMSDOS_rename (struct inode *old_dir,
 /* 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;
+}
index d53844b91db39ceecea24cb8c066bd05a1c97ac6..5309034e511e5cfc995f2ed89fe50e4309ebc68f 100644 (file)
@@ -576,13 +576,12 @@ extern void usb_deregister(struct usb_driver *);
                                        /* ... 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,11 +740,9 @@ struct urb
        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
  * @urb: pointer to the urb to initialize.
diff --git a/include/linux/usbdev_fs_i.h b/include/linux/usbdev_fs_i.h
deleted file mode 100644 (file)
index 13bfad5..0000000
+++ /dev/null
@@ -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 --git a/include/linux/usbdev_fs_sb.h b/include/linux/usbdev_fs_sb.h
deleted file mode 100644 (file)
index 6027b1a..0000000
+++ /dev/null
@@ -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;
-};
index 3fe8709b469d66f20404ccbb574d27326e7591c3..fa3c64f78b32af8172ce9ac270479a3b35be1a4d 100644 (file)
@@ -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 - <jt@hpl.hp.com>
+ * 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
  *
  * 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.
 #include <linux/socket.h>              /* for "struct sockaddr" et al  */
 #include <linux/if.h>                  /* 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 :
  *     - 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 */
@@ -413,14 +433,50 @@ struct    iw_statistics
 };
 
 /* ------------------------ 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 @@ struct     iwreq
                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 --git a/include/net/iw_handler.h b/include/net/iw_handler.h
new file mode 100644 (file)
index 0000000..7151337
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * This file define the new driver API for Wireless Extensions
+ *
+ * Version :   2       6.12.01
+ *
+ * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * 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 <net/iw_handler.h>
+ * ---------------------------------
+ * 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 <linux/wireless.h>            /* 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 <linux/netdevice.h>
+ */
+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 */
index f36a314cf3f7c1027406cfcfd66cbc02b1320da6..0f5ea3ee4761d9115e2a2b1f23511dbbf71ed429 100644 (file)
@@ -290,8 +290,6 @@ static void __init parse_options(char *line)
 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 @@ static void __init smp_init(void)
 
 #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 @@ static void __init smp_init(void)
 
        smp_threads_ready=1;
        smp_commence();
+       idle_startup_done();
 }
 
 #endif
@@ -411,6 +420,7 @@ asmlinkage void __init start_kernel(void)
        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
@@ -418,14 +428,6 @@ asmlinkage void __init start_kernel(void)
         */
        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();
 }
index fc47857460de06e12db14ead17ebbf6bc8f05452..661f650e6ed215420082963a576340a2130d9870 100644 (file)
@@ -747,23 +747,16 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start,
        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;
index ac6c61912ac59713596e50699b6968f22d3f2358..7a8d2835ec28fe5de70fd9498ab68b1650a126c1 100644 (file)
@@ -37,11 +37,7 @@ struct prio_array {
  *
  * 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 @@ static struct runqueue runqueues[NR_CPUS] __cacheline_aligned;
 #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)
-
-#define unlock_task_rq(rq,p,flags)                             \
-       spin_unlock_irqrestore(&rq->lock, flags)
+static inline runqueue_t *lock_task_rq(task_t *p, unsigned long *flags)
+{
+       struct runqueue *__rq;
+
+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 @@ static inline void enqueue_task(struct task_struct *p, prio_array_t *array)
 }
 
 /*
- * 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 @@ static inline int effective_prio(task_t *p)
 
        /*
         * 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 int effective_prio(task_t *p)
 
 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 @@ static inline void activate_task(task_t *p, runqueue_t *rq)
                 * 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 @@ repeat:
                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 @@ static int try_to_wake_up(task_t * p, int synchronous)
        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 @@ static int try_to_wake_up(task_t * p, int synchronous)
                        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 @@ void wake_up_forked_process(task_t * p)
 
        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 @@ unsigned long nr_context_switches(void)
        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 @@ static void load_balance(runqueue_t *this_rq, int idle)
         * 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 @@ out_unlock:
 
 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 @@ void scheduler_tick(task_t *p)
        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 @@ void scheduler_tick(task_t *p)
         * 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 @@ void scheduling_functions_start_here(void) { }
  */
 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 @@ switch_tasks:
        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 @@ switch_tasks:
        spin_unlock_irq(&rq->lock);
 
        reacquire_kernel_lock(current);
-       if (need_resched())
-               goto need_resched_back;
        return;
 }
 
@@ -782,47 +791,23 @@ long sleep_on_timeout(wait_queue_head_t *q, long timeout)
  */
 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 @@ void set_user_nice(task_t *p, long nice)
         * 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 @@ void set_user_nice(task_t *p, long nice)
        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 @@ void set_user_nice(task_t *p, long nice)
                        resched_task(rq->curr);
        }
 out_unlock:
-       unlock_task_rq(rq, p, flags);
+       unlock_task_rq(rq, p, &flags);
 }
 
 #ifndef __alpha__
@@ -938,7 +923,7 @@ static int setscheduler(pid_t pid, int policy, struct sched_param *param)
         * 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 @@ static int setscheduler(pid_t pid, int policy, struct sched_param *param)
                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 @@ void show_state(void)
        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 @@ void __init init_idle(void)
        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 @@ void __init sched_init(void)
         */
        rq = this_rq();
        rq->curr = current;
-       rq->idle = NULL;
+       rq->idle = current;
        wake_up_process(current);
 
        init_timervecs();
index 1996d935c0bedddba27b5fad4671eadba99540c2..8c476d491dcdc9dee15d7f08c11dc2d61b88bb4c 100644 (file)
@@ -110,8 +110,7 @@ static int badness(struct task_struct *p)
 
 /*
  * 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 @@ static struct task_struct * select_bad_process(void)
        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 @@ static struct task_struct * select_bad_process(void)
                        }
                }
        }
-       read_unlock(&tasklist_lock);
        return chosen;
 }
 
@@ -170,14 +167,16 @@ void oom_kill_task(struct task_struct *p)
  */
 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 @@ static void oom_kill(void)
  */
 void out_of_memory(void)
 {
-       static unsigned long first, last, count;
+       static unsigned long first, last, count, lastkill;
        unsigned long now, since;
 
        /*
@@ -233,9 +232,19 @@ void out_of_memory(void)
        if (++count < 10)
                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:
index ddcac7a48a93bb022a7f03396ce5e072cb0f7816..a32676791b05bcfd9c4f3ba28a54e6855a2b5379 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pagemap.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/slab.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
@@ -385,7 +386,7 @@ static int shmem_unuse_inode (struct shmem_inode_info *info, swp_entry_t entry,
        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 @@ struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
                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 @@ static struct super_block *shmem_read_super(struct super_block * sb, void * data
        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 inode_operations shmem_dir_inode_operations = {
 };
 
 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 @@ static int __init init_shmem_fs(void)
        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 @@ static void __exit exit_shmem_fs(void)
 #endif
        unregister_filesystem(&tmpfs_fs_type);
        mntput(shm_mnt);
+       destroy_inodecache();
 }
 
 module_init(init_shmem_fs)
index ba70b95369321b4c11f1e4cb8b4dd6d9b4273bcd..925286b4f23e81e0418e5e057bff927a239d97db 100644 (file)
@@ -344,7 +344,7 @@ void free_swap_and_cache(swp_entry_t entry)
        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);
                }
index 65c973f26c9f18946b0827ac99b0d3535d53fc6c..5dacce00c2126271b90251d73ccb0cdfb44afe84 100644 (file)
@@ -26,5 +26,8 @@ obj-$(CONFIG_NET) += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o
 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
index 2ab8c806e4c7b990670f609a6fe5c09cbe12c775..04ce443e29fc35fd04a415a79e414386e48a17c1 100644 (file)
 #include <linux/module.h>
 #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
 #include <linux/wireless.h>            /* Note : will define WIRELESS_EXT */
+#include <net/iw_handler.h>
 #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
 #ifdef CONFIG_PLIP
 extern int plip_init(void);
@@ -1796,122 +1797,6 @@ static int dev_proc_stats(char *buffer, char **start, off_t offset,
 #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 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
                        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 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
                                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 @@ int dev_ioctl(unsigned int cmd, void *arg)
                                }
                                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 @@ int __init net_dev_init(void)
        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 --git a/net/core/wireless.c b/net/core/wireless.c
new file mode 100644 (file)
index 0000000..5d57ccc
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> 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 <asm/uaccess.h>               /* copy_to_user() */
+#include <linux/config.h>              /* Not needed ??? */
+#include <linux/types.h>               /* off_t */
+#include <linux/netdevice.h>           /* struct ifreq, dev_get_by_name() */
+
+#include <linux/wireless.h>            /* Pretty obvious */
+#include <net/iw_handler.h>            /* 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;
+}
index c0b4da1635e77af3ece6682b6034b33704570bb8..4065e8f8f38e94c2808444cc43dd707a97c7ee14 100644 (file)
@@ -390,3 +390,5 @@ void khttpd_cleanup(void)
 
        module_init(khttpd_init)
        module_exit(khttpd_cleanup)
+
+MODULE_LICENSE("GPL");
index 613e233fd886ecbd0bd73016da60c7d87554cc6a..49843fa2d58b05e3a0ad74e1fa560f7fb0123823 100644 (file)
@@ -996,3 +996,5 @@ static void __exit netlink_proto_exit(void)
 
 module_init(netlink_proto_init);
 module_exit(netlink_proto_exit);
+
+MODULE_LICENSE("GPL");