]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.33 2.1.33
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:04 +0000 (15:13 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:04 +0000 (15:13 -0500)
95 files changed:
CREDITS
Documentation/Configure.help
Documentation/devices.tex
Documentation/devices.txt
Documentation/digiboard.txt
Documentation/ioctl-number.txt
Documentation/parport.txt [new file with mode: 0644]
MAINTAINERS
Makefile
Rules.make
arch/alpha/config.in
arch/i386/boot/compressed/Makefile.debug [deleted file]
arch/i386/boot/compressed/misc.c
arch/i386/config.in
arch/i386/defconfig
arch/i386/kernel/i386_ksyms.c
arch/i386/kernel/irq.c
arch/i386/kernel/process.c
arch/i386/kernel/smp.c
arch/i386/kernel/traps.c
arch/i386/mm/fault.c
arch/i386/mm/init.c
drivers/Makefile
drivers/block/genhd.c
drivers/block/xd.h
drivers/char/Config.in
drivers/char/console.c
drivers/char/consolemap.c
drivers/char/lp.c
drivers/char/pcxx.c
drivers/char/pcxx.h
drivers/char/selection.c
drivers/char/tty_io.c
drivers/char/vt.c
drivers/net/3c505.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/eepro100.c [new file with mode: 0644]
drivers/net/net_init.c
drivers/net/plip.c
drivers/net/scc.c
drivers/pnp/BUGS-parport [new file with mode: 0644]
drivers/pnp/Config.in [new file with mode: 0644]
drivers/pnp/Makefile [new file with mode: 0644]
drivers/pnp/TODO-parport [new file with mode: 0644]
drivers/pnp/parport_init.c [new file with mode: 0644]
drivers/pnp/parport_ll_io.h [new file with mode: 0644]
drivers/pnp/parport_probe.c [new file with mode: 0644]
drivers/pnp/parport_procfs.c [new file with mode: 0644]
drivers/pnp/parport_share.c [new file with mode: 0644]
drivers/scsi/ChangeLog.ncr53c8xx
drivers/scsi/Config.in
drivers/scsi/README.ncr53c8xx
drivers/scsi/ncr53c8xx.c
drivers/scsi/ncr53c8xx.h
drivers/scsi/ppa.c
drivers/scsi/ppa.h
fs/Config.in
fs/inode.c
fs/isofs/inode.c
fs/lockd/clntproc.c
fs/nfs/inode.c
fs/nfs/nfs2xdr.c
fs/nfsd/stats.c
fs/proc/procfs_syms.c
include/asm-i386/irq.h
include/asm-i386/uaccess.h
include/linux/lp.h
include/linux/parport.h [new file with mode: 0644]
include/linux/proc_fs.h
include/linux/scc.h
include/linux/sunrpc/stats.h
include/linux/swapctl.h
init/main.c
kernel/exit.c
kernel/fork.c
kernel/printk.c
kernel/resource.c
kernel/sched.c
kernel/sys.c
kernel/sysctl.c
mm/vmscan.c
mm/vmscan.c.lock~ [deleted file]
net/core/dev.c
net/ipv4/proc.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/sunrpc/clnt.c
net/sunrpc/stats.c
net/sunrpc/sunrpc_syms.c
scripts/Configure
scripts/Menuconfig

diff --git a/CREDITS b/CREDITS
index 219dcaf342052928b4a83c1e26b86a72ae5debee..67ce66077518d8b422eaea7b50a967ba411a51e4 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -133,13 +133,12 @@ E: bir7@leland.Stanford.Edu
 D: Original author of the Linux networking code
 
 N: Philip Blundell
-E: pjb27@cam.ac.uk 
-E: pb@nexus.co.uk
-E: phil@tazenda.demon.co.uk
+E: Philip.Blundell@pobox.com
 D: Device driver hacking (especially EtherExpress16/3C505 net cards)
 D: Some Linux/ARM stuff
-S: Trinity College
-S: Cambridge, UK. CB2 1TQ
+D: Co-architect of the parallel port sharing system
+S: 201 Gilbert Road
+S: Cambridge, UK. CB4 3PA
 
 N: Thomas Bogendoerfer
 E: tsbogend@alpha.franken.de
@@ -510,6 +509,7 @@ S: USA
 N: Grant Guenther
 E: grant@torque.net
 D: drivers for parallel port devices: ppa, ez, bpcd
+D: original architect of the parallel-port sharing scheme. 
 S: 906-1001 Bay St.
 S: Toronto, Ontario, M5S 3A6 
 S: Canada
@@ -696,9 +696,11 @@ S: Chapel Hill, North Carolina 27514-4818
 S: USA
 
 N: Bernhard Kaindl
+E: bkaindl@netway.at
 E: edv@bartelt.via.at
 D: Author of a menu based configuration tool, kmenu, which 
 D: is the predecessor of 'make menuconfig' and 'make xconfig'.
+D: digiboard driver update(modularisation work and 2.1.x upd)
 S: Tallak 95
 S: 8103 Rein
 S: Austria
@@ -826,7 +828,8 @@ S: Australia
 
 N: Hans Lermen
 E: lermen@elserv.ffm.fgan.de
-D: Author of the LOADLIN Linux loader
+D: Author of the LOADLIN Linux loader, hacking on boot stuff
+D: Co-ordinator of DOSEMU releases
 S: Am Muehlenweg 38
 S: D53424 Remagen
 S: Germany
@@ -888,7 +891,7 @@ D: Initial implementation of VC's, pty's and select()
 N: James B. MacLean
 E: macleajb@ednet.ns.ca
 W: http://www.ednet.ns.ca/~macleajb/dosemu.html
-D: Coordinator of DOSEMU releases
+D: Former Co-ordinator of DOSEMU releases
 D: Program in DOSEMU
 S: PO BOX 220, HFX. CENTRAL
 S: Halifax, Nova Scotia
@@ -1521,6 +1524,13 @@ S: Kruislaan 419
 S: 1098 VA Amsterdam 
 S: The Netherlands
 
+N: Tim Waugh
+E: tmw20@cyberelk.demon.co.uk
+D: Co-architect of the parallel-port sharing system
+S: 51 Frensham Road
+S: Southsea
+S: Hampshire, UK. PO4 8AE
+
 N: Juergen Weigert
 E: jnweiger@immd4.informatik.uni-erlangen.de
 D: The Linux Support Team Erlangen
index 43fa43a3ff85be37c068b962fd5baad5f229457e..91901f70b938f2de6371e86de25733c9049d55be 100644 (file)
@@ -835,6 +835,23 @@ CONFIG_CPU_LITTLE_ENDIAN
   be necessary to run older Mips systems, such as the Sony News and
   MIPS RC3xxx, in big endian mode.
 
+Plug and Play support
+CONFIG_PNP
+  Plug and Play support allows the kernel to automatically configure some
+  peripheral devices.  Say Y to enable PnP.
+
+Parallel-port support
+CONFIG_PNP_PARPORT
+  If you want to use devices connected to your parallel port (printer,
+  Zip driver, PLIP link, ...) then you should enable this option and
+  read Documentation/parport.txt.
+
+Autoprobe for parallel device IDs
+CONFIG_PNP_PARPORT_AUTOPROBE
+  Some IEEE-1284 conformant parallel-port devices can identify themselves
+  when requested.  If this option is enabled the kernel will probe to see
+  what devices are connected at boot time.
+
 Enable loadable module support
 CONFIG_MODULES
   Kernel modules are small pieces of compiled code which can be 
@@ -2076,6 +2093,19 @@ CONFIG_SCSI_PPA
   drive: it will be supported automatically if you said Y to the
   generic "SCSI disk support", above.
 
+EPP FIFO Checking
+CONFIG_SCSI_PPA_HAVE_PEDANTIC
+  Some chipsets are slower then their motherboard. We have to control
+  the state of the FIFO now and then.  The values are 0 (don't check
+  FIFO), 1 (check FIFO every 4 bytes), 2 (check FIFO every other byte)
+  and 3 (check FIFO every time). If your EPP chipset is from the SMC
+  series, you are likely to have to set this value greater than 0.
+
+EPP Timing
+CONFIG_SCSI_PPA_EPP_TIME
+  This is the "reset time period", a delay time.  Too low a value may
+  cause all sorts of mid-level SCSI problems.
+
 Network device support?
 CONFIG_NETDEVICES
   You can say N here in case you don't intend to connect to any other
@@ -4091,24 +4121,12 @@ CONFIG_PRINTER
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  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
-  Documentation/modules.txt. The module will be called plip.o. If you
-  want to use both a parallel printer and PLIP, there are two cases:
-  1) If the printer and the PLIP cable are to use the same parallel
-  port (presumably because you have just one), it is best to compile
-  both drivers as modules and load and unload them as needed. 2) To
-  use different parallel ports for the printer and the PLIP cable, you
-  can say Y to this printer driver, specify the base address of the
-  parallel port(s) to use for the printer(s) with the "lp" kernel
-  command line option. (Try "man bootparam" or see the documentation
-  of your boot loader (lilo or loadlin) about how to pass options to
-  the kernel at boot time. The lilo procedure is also explained in the
-  SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses
-  as well as the syntax of the "lp" command line option can be found
-  in drivers/char/lp.c. You can then say Y to the PLIP driver or,
-  preferably, M in which case Documentation/networking/net-modules.txt
-  tells you how to specify the port and IRQ to be used by PLIP at
-  module load time.
+  Documentation/modules.txt.
+
+CONFIG_PRINTER_READBACK
+  If your printer conforms to IEEE 1284, it may be able to provide a status
+  indication when you read from it (for example, with `cat /dev/lp1').  To
+  use this feature, say Y here.
 
 Mouse Support (not serial mice)
 CONFIG_MOUSE
index 8983e7babbb888324ca33439ec2bb1ae61d515df..80dc0f9de0059e256285dbee8860b935a761d6c7 100644 (file)
@@ -46,7 +46,7 @@ foo \kill}%
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: March 3, 1997}
+\date{Last revised: April 7, 1997}
 \maketitle
 %
 \noindent
@@ -171,6 +171,7 @@ reply.
 \major{  }{}{block}{MicroSolutions BackPack parallel port CD-ROM}
 \major{42}{}{}{Demo/sample use}
 \major{43}{}{char }{isdn4linux virtual modem}
+\major{  }{}{block}{Network block devices}
 \major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
 \major{45}{}{char }{isdn4linux ISDN BRI driver}
 \major{46}{}{char }{Comtrol Rocketport serial card}
@@ -199,7 +200,14 @@ reply.
 \major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices}
 \major{73}{}{char }{Computone IntelliPort II serial card -- control devices}
 \major{74}{}{char }{SCI bridge}
-\major{75}{--119}{}{Unallocated}
+\major{75}{}{char }{Specialix IO8+ serial card}
+\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
+\major{77}{}{char }{ComScire Quantum Noise Generator}
+\major{78}{}{char }{PAM Software's multimodem boards}
+\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
+\major{80}{}{char }{Photometrics AT200 CCD camera}
+\major{81}{}{char }{Brooktree Bt848 frame grabbers}
+\major{82}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -494,6 +502,8 @@ physical disks.
        \minor{140}{/dev/relay8}{Berkshire Products Octal relay card}
        \minor{141}{/dev/relay16}{Berkshire Products ISO-16 relay card}
        \minor{142}{/dev/msr}{x86 model specific registers}
+       \minor{143}{/dev/pciconf}{PCI configuration space}
+       \minor{144}{/dev/nvram}{Non-volatile configuration RAM}
 \end{devicelist}
 
 \begin{devicelist}
@@ -659,6 +669,11 @@ See the Double documentation for an explanation of the ``mirror'' devices.
        \minordots
 \end{devicelist}
 
+\noindent
+Most distributions name these {\file /dev/sga}, {\file /dev/sgb}...
+This sets an unneccesary limit of 26 SCSI devices in the system, and
+is counter to standard Linux device-naming practice.
+
 \begin{devicelist}
 \major{22}{}{char }{Digiboard serial card}
        \minor{0}{/dev/ttyD0}{First Digiboard port}
@@ -1033,8 +1048,20 @@ driver with this number should not cause ill effects to the system
        \minor{0}{/dev/ttyI0}{First virtual modem}
        \minordots
        \minor{63}{/dev/ttyI63}{64th virtual modem}
+\\
+\major{  }{}{block}{Network block devices}
+       \minor{0}{/dev/nd0}{First network block device}
+       \minor{1}{/dev/nd1}{Second network block device}
+       \minordots
 \end{devicelist}
 
+\noindent
+Network Block Device is somehow similar to loopback devices: If you
+read from it, it sends packet accross network asking server for
+data. If you write to it, it sends packet telling server to write. It
+could be used to mounting filesystems over the net, swapping over the
+net, implementing block device in userland etc.
+
 \begin{devicelist}
 \major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
        \minor{0}{/dev/cui0}{Callout device corresponding to {\file ttyI0}}
@@ -1310,7 +1337,62 @@ supports the AVM B1 card.
 Currently for Dolphin Interconnect Solutions' PCI-SCI bridge.
 
 \begin{devicelist}
-\major{75}{--119}{}{Unallocated}
+\major{75}{}{char }{Specialix IO8+ serial card}
+       \minor{0}{/dev/ttyW0}{First IO8+ port, first card}
+       \minor{1}{/dev/ttyW1}{Second IO8+ port, first card}
+       \minordots
+       \minor{8}{/dev/ttyW8}{First IO8+ port, second card}
+       \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
+       \minor{0}{/dev/cuw0}{Callout device corresponding to {\file ttyW0}}
+       \minor{1}{/dev/cuw1}{Callout device corresponding to {\file ttyW1}}
+       \minordots
+       \minor{8}{/dev/cuw8}{Callout device corresponding to {\file ttyW8}}
+       \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{77}{}{char }{ComScire Quantum Noise Generator}
+       \minor{0}{/dev/qng}{ComScire Quantum Noise Generator}
+\end{devicelist}
+
+\begin{devicelist}
+\major{78}{}{char }{PAM Software's multimodem boards}
+       \minor{0}{/dev/ttyM0}{First PAM modem}
+       \minor{1}{/dev/ttyM1}{Second PAM modem}
+       \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
+       \minor{0}{/dev/cum0}{Callout device corresponding to {\file ttyM0}}
+       \minor{1}{/dev/cum1}{Callout device corresponding to {\file ttyM1}}
+       \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{80}{}{char }{Photometrics AT200 CCD camera}
+       \minor{0}{/dev/at200}{Photometrics AT200 CCD camera}
+\end{devicelist}
+
+\begin{devicelist}
+\major{81}{}{char }{Brooktree Bt848 frame grabbers}
+        \minor{0}{/dev/bttv0}{First Bt848 card} 
+        \minor{0}{/dev/bttv1}{Second Bt848 card} 
+        \minordots
+        \minor{16}{/dev/bttvc0}{Control for first Bt848 card} 
+        \minor{17}{/dev/bttvc1}{Control for second Bt848 card} 
+        \minordots
+        \minor{32}{/dev/bttv-vbi0}{VBI data of first Bt848 card}
+        \minor{33}{/dev/bttv-vbi1}{VBI data of second Bt848 card}
+        \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{82}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1467,6 +1549,9 @@ expected that multiple letters will be used; all letters will be upper
 case for the {\file tty} device and lower case for the {\file cu}
 device.
 
+The names {\file /dev/ttyQ$\#$} and {\file /dev/cuq$\#$} are reserved
+for local use.
+
 The alternate devices provide for kernel-based exclusion and somewhat
 different defaults than the primary devices.  Their main purpose is to
 allow the use of serial ports with programs with no inherent or broken
index 51104ba825d04da4d6d29d457f34cbd1cfd4f7c3..17a99de9548b46ac8ba9a037ab64d92548e41970 100644 (file)
@@ -1,8 +1,7 @@
                       LINUX ALLOCATED DEVICES
-
             Maintained by H. Peter Anvin <hpa@zytor.com>
 
-                    Last revised: March 3, 1997
+                    Last revised: April 7, 1997
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he got busy with other things in 1993.  It
@@ -285,6 +284,9 @@ reply.
                139 = /dev/openprom     SPARC OpenBoot PROM
                140 = /dev/relay8       Berkshire Products Octal relay card
                141 = /dev/relay16      Berkshire Products ISO-16 relay card
+               142 = /dev/msr          x86 model-specific registers
+               143 = /dev/pciconf      PCI configuration space
+               144 = /dev/nvram        Non-volatile configuration RAM
 
  11 char       Raw keyboard device
                  0 = /dev/kbd          Raw keyboard device
@@ -410,6 +412,11 @@ reply.
                  1 = /dev/sg1          Second generic SCSI device
                      ...
 
+               Most distributions name these /dev/sga, /dev/sgb...;
+               this sets an unnecessary limit of 26 SCSI devices in
+               the system and is counter to standard Linux
+               device-naming practice.
+
  22 char       Digiboard serial card
                  0 = /dev/ttyD0        First Digiboard port
                  1 = /dev/ttyD1        Second Digiboard port
@@ -707,6 +714,17 @@ reply.
                  0 = /dev/ttyI0        First virtual modem
                      ...
                 63 = /dev/ttyI63       64th virtual modem
+    block      Network block devices
+                 0 = /dev/nb0          First network block device
+                 1 = /dev/nb1          Second network block device
+                     ...
+
+               Network Block Device is somehow similar to loopback
+               devices: If you read from it, it sends packet accross
+               network asking server for data. If you write to it, it
+               sends packet telling server to write. It could be used
+               to mounting filesystems over the net, swapping over
+               the net, implementing block device in userland etc.
 
  44 char       isdn4linux virtual modem - alternate devices
                  0 = /dev/cui0         Callout device corresponding to ttyI0
@@ -920,7 +938,48 @@ reply.
                Currently for Dolphin Interconnect Solutions' PCI-SCI
                bridge.
 
- 75-119                UNALLOCATED
+ 75 char       Specialix IO8+ serial card
+                 0 = /dev/ttyW0        First IO8+ port, first card
+                 1 = /dev/ttyW1        Second IO8+ port, first card
+                     ...
+                 8 = /dev/ttyW8        First IO8+ port, second card
+                     ...
+
+ 76 char       Specialix IO8+ serial card - alternate devices
+                 0 = /dev/cuw0         Callout device corresponding to ttyW0
+                 1 = /dev/cuw1         Callout device corresponding to ttyW1
+                     ...
+                 8 = /dev/cuw8         Callout device corresponding to ttyW8
+                     ...
+
+ 77 char       ComScire Quantum Noise Generator
+                 0 = /dev/qng          ComScire Quantum Noise Generator
+
+ 78 char       PAM Software's multimodem boards
+                 0 = /dev/ttyM0        First PAM modem
+                 1 = /dev/ttyM1        Second PAM modem
+                     ...
+
+ 79 char       PAM Software's multimodem boards - alternate devices
+                 0 = /dev/cum0         Callout device corresponding to ttyM0
+                 1 = /dev/cum1         Callout device corresponding to ttyM1
+                     ...
+
+ 80 char       Photometrics AT200 CCD camera
+                 0 = /dev/at200        Photometrics AT200 CCD camera
+
+ 81 char       Brooktree Bt848 frame grabbers
+                 0 = /dev/bttv0        First Bt848 card
+                 1 = /dev/bttv1        Second Bt848 card
+                     ...
+                16 = /dev/bttvc0       Control for first Bt848 card
+                17 = /dev/bttvc1       Control for second Bt848 card
+                     ...
+                32 = /dev/bttv-vbi0    VBI data of first Bt848 card
+                33 = /dev/bttv-vbi1    VBI data of second Bt848 card
+                     ...
+
+ 82-119                UNALLOCATED
 
 120-127                LOCAL/EXPERIMENTAL USE
 
@@ -931,9 +990,6 @@ reply.
 255            RESERVED
 
 
-
-
-
  ****  ADDITIONAL /dev DIRECTORY ENTRIES
 
 This section details additional entries that should or may exist in
@@ -1054,6 +1110,8 @@ correspond to /dev/cua# and /dev/cub#.  In the future, it should be
 expected that multiple letters will be used; all letters will be upper
 case for the "tty" device and lower case for the "cu" device.
 
+The names /dev/ttyQ# and /dev/cuq# are reserved for local use.
+
 The alternate devices provide for kernel-based exclusion and somewhat
 different defaults than the primary devices.  Their main purpose is to
 allow the use of serial ports with programs with no inherent or broken
index 654d87d4bb1da58c6fe1332228a09abe91abc88d..0a49a427f52e3b71eb14ce2679d18120ba18982a 100644 (file)
@@ -3,37 +3,184 @@ The Linux Digiboard Driver
 
 The Digiboard Driver for Linux supports the following boards:
 
- DigiBoard PC/Xe, PC/Xi, PC/Xeve
+ DigiBoard PC/Xi, PC/Xe, PC/Xeve(which is the newer, smaller Xe with
+ a 8K window which is also known as PC/Xe(8K) and has no memory/irq
+ switches) You can use up to 4 cards with this driver and should work
+ on other architectures than intel also.
 
-Limitations:
-------------
-Currently the Driver does not do autoprobing!
+In case you have problems with this version(1.6.1) of this driver, please
+email directly to me as I made the last update. It you have a report about
+runnning it on other architectures than intel, email me, so I can document
+it here.
 
-The preconfigured I/O address is 0200h and the default memory address
-0D0000h, ALT-PIN feature on and 16 ports available.
+An version of this driver has been taken by Digiboard to make a driver
+software package which supports also PC/Xem cards and newer PCI cards
+but it don't support the old PC/Xi cards and it isn't yet ported to
+linux-2.1.x and may not be useable on other architectures than intel now.
+It is available from ftp.digi.com/ftp.digiboard.com. You can write me if
+you need an patch for this driver.
 
-Use them and you will not have to worry about configuring anything.
+  Bernhard Kaindl (bkaindl@netway.at)  6. April 1997.
 
-You can configure the driver via lilo. Have a look at the end of this
-message. The default settings vanish as soon as you give a digi= commandline.
-You can give multiple digi= commandline parameters to define multiple
-boards.
+Configuring the Driver
+----------------------
+
+The driver can be build direct into the kernel or as module.
+The pcxx driver can be configured using the command line feature while
+loading the kernel with LILO or LOADLIN or, if built as a module,
+with arguments to insmod and modprobe or with parameters in
+/etc/conf.modules for modprobe and kerneld.
+
+After configuring the driver you need to create the device special files
+as described in "Device file creation:" below and set the appropriate
+permissions for your application.
+
+As Module
+---------
+
+modprobe pcxx io=<io> \
+  membase=<membase> \
+  memsize=<memsize> \
+  numports=<numports>  \
+  altpin=<altpin> \
+  verbose=<verbose>
+
+or, if several cards are installed
+
+modprobe pcxx io=<io-1>,<io-2>,... \
+  membase=<membase-1>,<membase-2>,... \
+  memsize=<memsize-1>,<memsize-2>,... \
+  numports=<numports-1>,<numports-2>,... \
+  altpin=<altpin-1>,<altpin-2>,... \
+  verbose=<verbose>
+
+where <io-N> is the io address of the Nth card and <membase-N> is the
+memory base address of the Nth card, etc.
+
+The parameters can be specified in any order. For example, the numports
+parameter can precede the membase parameter, or vice versa. If several
+cards are installed the ordering within the comma separated parameter
+lists must be consistent, of course.
+
+io       - I/O port address of that card.
+membase  - Memory start address of that card.
+memsize  - Memory size of that card, in kilobytes. If given, this value
+           is compared against the card to verify configuration and
+           hinder the driver to use a misconfigured card. If the parameter
+           does not match the board it is disabled with a memory size error.
+numports - Number of ports on this card. This is the number of devices to
+           assign to this card or reserve if disabled.
+altpin   - 1: swap DCD and DSR for 8-pin RJ-45 with modems.
+          0: don't swap DCD and DSR.
+           other values count as 1.
+verbose  - 1: give nice verbose output during initialisation of the driver.
+              possibly helpful during board configuration.
+           0: normal terse output.
+
+Only the parameters which differ from the defaults need to be specified.
+If the io= parameter is not given, the default config is used. This is
+
+  io=0x200 membase=0xD0000 numports=16 altpin=0
+
+Only parameters applicable need be specified. For example to configure
+2 boards, first one at 0x200 with 8 ports, rest defaults, second one at
+0x120, memory at 0xD80000, altpin enabled, rest defaults, you can do this
+by using these parameters:
+
+  modprobe pcxx io=0x200,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+To disable a temporary unuseable board without changing the mapping of the
+devices following that board, you can empty the io-value for that board:
+
+  modprobe pcxx io=,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+The remainig board still uses ttyD8-ttyD15 and cud8-cud15.
+
+Example line for /etc/conf.modules for use with kerneld and as default
+parameters for modprobe:
+
+options pcxx           io=0x200 numports=8
+
+For kerneld to work you will likely need to add these two lines to your
+/etc/conf.modules:
+
+alias char-major-22    pcxx
+alias char-major-23    pcxx
+
+
+Boot-time configuration when linked into the kernel
+---------------------------------------------------
+
+Per Board to be configured, pass a digi= commandline parameter to the
+kernel using lilo or loadlin. It consists of a string of comma separated
+identifiers or integers.  The 6 values in order are:
+
+Card status:      Enable      - use that board
+                 Disable     - don't actually use that board.
+
+Card type:        PC/Xi       - the old ones with 64/128/256/512K RAM.
+                 PC/Xe       - PC/Xe(old ones with 64k mem range).
+                 PC/Xeve     - PC/Xe(newers with 8k mem range).
+
+Note: This is for documentation only, the type is detected from the board.
+
+Altpin setting:   Enable      - swap DCD and DSR for 8-pin RJ-45 with modems.
+                 Disable     - don't swap DCD and DSR.
+
+Number of ports:  1 ... 16    - Number of ports on this card. This is the
+                               number of devices to assign to this card.
+
+I/O port address: eg. 200     - I/O Port address where the card is configured.
+
+Memory base addr: eg. 80000   - Memory address where the board's memory starts.
+
+This is an example for a line which you can insert into you lilo.conf:
+
+   append="digi=Enable,PC/Xi,Disable,4,120,D0000"
+
+there is an alternate form, in which you must use decimal values only:
+
+   append="digi=1,0,0,16,512,851968"
+
+If you don't give a digi= commandline, the compiled-in defaults of
+board 1: io=0x200, membase=0xd0000, altpin=off and numports=16 are used.
+
+If you have the resources (io&mem) free for use, configure your board to
+these settings and you should be set up fine even if yours has not 16 ports.
+
+
+Sources of Information
+----------------------
+
+Webpage: http://private.fuller.edu/clameter/digi.html
+
+Mailing List: digiboard@list.fuller.edu
+
+(Write e-mail to that address to subscribe. Common ListServ commands work.
+Archive of messages available)
+
+Christoph Lameter (clameter@fuller.edu) 16. April 1996.
+
+Supporting Tools
+----------------
 
-Supporting Tools:
------------------
 Some tools and more detailed information can be found at
 ftp://ftp.fuller.edu/Linux/digi
 
-WARNING: Most of the stuff available right now uses the WRONG Major Device
-numbers and the wrong call out devices. Be careful and check them first.
-Better not use any of the software in that directory if you run a recent
-1.3.X Kernel or later!
-
 The "ditty" tool described in the Digiboard Manuals for other Unixes
 is also available.
 
+
+Device file creation
+--------------------
+
 Currently the Linux MAKEDEV command does not support generating the Digiboard
-Devices. Use the following script to generate the devices:
+Devices. 
+
+The /dev/cud devices behave like the /dev/cua devices
+and the ttyD devices are like the /dev/ttyS devices.
+
+Use the following script to generate the devices:
 
 ------------------ mkdigidev begin
 #!/bin/sh
@@ -66,8 +213,10 @@ do
   boardnum=`expr $boardnum + 1`
 done
 ------------------ mkdigidev end
+
 or apply the following patch to /dev/MAKEDEV and do a 
-make digi
+sh /dev/MAKEDEV digi
+
 ----- MAKEDEV Patch
 --- /dev/MAKEDEV       Sun Aug 13 15:48:23 1995
 +++ MAKEDEV    Tue Apr 16 17:53:27 1996
@@ -105,51 +254,3 @@ make digi
                ;;
        par[0-2])
 ----- End Makedev patch
-
-The /dev/cud?? devices behave like the /dev/cua?? devices
-and the ttyD devices are like the /dev/ttyS?? devices.
-
-Sources of Information
-----------------------
-
-Webpage: http://private.fuller.edu/clameter/digi.html
-
-Mailing List: digiboard@list.fuller.edu
-
-(Write e-mail to that address to subscribe. Common ListServ commands work.
-Archive of messages available)
-
-Christoph Lameter (clameter@fuller.edu) 16. April 1996.
-
------------------------------------------------------------------------------
-
-Changes v1.5.5:
-
-The ability to use the kernel's command line to pass in the configuration for 
-boards.  Using LILO's APPEND command, a string of comma separated identifiers 
-or integers can be used.  The 6 values in order are:
-
-   Enable/Disable this card,
-   Type of card: PC/Xi(0), PC/Xe(1), PC/Xeve(2), PC/Xem(3)
-   Enable/Disable alternate pin arrangement,
-   Number of ports on this card,
-   I/O Port where card is configured (in HEX if using string identifiers),
-   Base of memory window (in HEX if using string identifiers), 
-
-Samples:
-   append="digi=E,PC/Xi,D,16,200,D0000"
-   append="digi=1,0,0,16,512,(whatever D0000 is in base 10 :)
-
-Driver's minor device numbers are conserved. This means that instead of
-each board getting a block of 16 minors pre-assigned, it gets however
-many it should, with the next card following directly behind it.  A
-system with 4 2-port PC/Xi boards will use minor numbers 0-7.
-This conserves some memory, and removes a few hard coded constants.
-
-NOTE!! NOTE!! NOTE!!
-The definition of PC/Xem as a valid board type is the BEGINNING of support
-for this device.  The driver does not currently recognise the board, nor
-does it want to initialize it.  At least not the EISA version.
-
-Mike McLagan <mike.mclagan@linux.org> 5, April 1996.
-
index 7eb7108dcea8279e666df23edb42f6a4181ccec4..4e1eba41eb9c09420afc1f565cbf67e76591a76a 100644 (file)
@@ -1,5 +1,5 @@
 Ioctl Numbers
-12 Feb 1997
+5 Apr 1997
 Michael Chastain
 <mec@shout.net>
 
@@ -89,6 +89,8 @@ Code  Seq#    Include File            Comments
 'a'    all     various, see http://lrcwww.epfl.ch/linux-atm/magic.html
 'c'    all     linux/comstats.h
 'f'    all     linux/ext2_fs.h
+'l'    00-3F   linux/tcfs_fs.h         in development:
+                                       <http://mikonos.dia.unisa.it/tcfs>
 'm'    all     linux/mtio.h            conflict!
 'm'    all     linux/soundcard.h       conflict!
 'n'    all     linux/ncp_fs.h
@@ -105,8 +107,10 @@ Code       Seq#    Include File            Comments
 0x89   E0-EF   linux/sockios.h         SIOCPROTOPRIVATE range
 0x89   F0-FF   linux/sockios.h         SIOCDEVPRIVATE range
 0x8B   all     linux/wireless.h
+0x8C   00-3F   WiNRADiO driver         in development:
+                                       <mailto:brian@proximity.com.au>
 0x90   00      linux/sbpcd.h
-0x99   00-0F   537-Addinboard driver   in development, e-mail contact:
-                                       b.kohl@ipn-b.comlink.apc.org
-0xA0   all     Small Device Project    in development, e-mail contact:
-                                       khollis@northwest.com
+0x99   00-0F   537-Addinboard driver   in development:
+                                       <mailto:b.kohl@ipn-b.comlink.apc.org>
+0xA0   all     Small Device Project    in development:
+                                       <mailto:khollis@northwest.com>
diff --git a/Documentation/parport.txt b/Documentation/parport.txt
new file mode 100644 (file)
index 0000000..6b3ba37
--- /dev/null
@@ -0,0 +1,55 @@
+The `parport' code provides parallel-port support under Linux.  This
+includes the ability to share one port between multiple device
+drivers.
+
+You can pass parameters to the parport code to override its automatic
+detection of your hardware.  This is particularly useful if you want
+to use IRQs, since in general these can't be autoprobed successfully.
+
+If you load the parport code as a module, say
+
+       # insmod parport.o io=0x378,0x278 irq=7,5
+
+to tell the parport code that you want two ports, one at 0x378 using
+IRQ 7, and one at 0x278 using IRQ 5. 
+
+If you compile the parport code into the kernel, then you can use
+kernel boot parameters to get the same effect.  Add something like the
+following to your LILO command line:
+
+       parport=0x378,7 parport=0x278,5
+
+You can have many `parport=...' statements, one for each port you want
+to add.  Adding `parport=0' or just `parport=' to the command-line
+will disable parport support entirely.
+
+Once the parport code is initialised, you can attach device drivers to
+ports.  Normally this happens automatically; if the lp driver is
+loaded it will create one lp device for each port found.  You can
+override this, though, by using parameters either when you load the lp
+driver:
+
+       # insmod lp.o parport=0,2
+
+or on the LILO command line:
+
+       lp=parport0 lp=parport2
+
+Both the above examples would inform lp that you want /dev/lp0 to be
+the first parallel port, and /dev/lp1 to be the _third_ parallel port,
+with no lp device associated with the second port (parport1).  Note
+that this is different to the way older kernels worked; there used to
+be a static association between the I/O port address and the device
+name, so /dev/lp0 was always the port at 0x3bc.  This is no longer the
+case - if you only have one port, it will always be /dev/lp0,
+regardless of base address.
+
+Also:
+
+ * If you selected the device autoprobe at compile time, you can say
+`lp=auto' on the kernel command line, and lp will create devices only
+for those ports that seem to have printers attached.
+
+ * If you give PLIP the `timid' parameter, either with `plip=timid' on
+the command line, or with `insmod plip timid=1' when using modules, it
+will avoid any ports that seem to be in use by other devices.
index 5d5a26bf9c208a58968e857b569a7ea32443faf8..588898993187885f49a3c3526f5c02019fc1f8af 100644 (file)
@@ -407,6 +407,19 @@ M: emoenke@gwdg.de
 L:     linux-kernel@vger.rutgers.edu
 S:     Maintained
 
+PARALLEL PORT SHARING SUPPORT
+P:     Phil Blundell
+M:     Philip.Blundell@pobox.com
+P:     Tim Waugh
+M:     tmw20@cyberelk.demon.co.uk
+P:     David Campbell
+M:     campbell@tirian.che.curtin.edu.au
+L:     linux-parport@torque.net
+L:     pnp-list@redhat.com
+W:     http://www.cyberelk.demon.co.uk/parport.html
+W:     http://www.cage.curtin.edu.au/~campbell/parbus/
+S:     Maintained
+
 FPU EMULATOR
 P:     Bill Metzenthen
 M:     billm@suburbia.net
@@ -421,7 +434,7 @@ S:  Maintained
 KERNEL AUTOMOUNTER (AUTOFS)
 P:     H. Peter Anvin
 M:     hpa@zytor.com
-L:     linux-kernel@vger.rutgers.edu
+L:     autofs@linux.kernel.org
 S:     Maintained
 
 DEVICE NUMBER REGISTRY
index dc8687e5ffe16bbbfceb4f9fea8f63af09e74693..a6f2ff461a8ec90b99a9f004b8201d32bd07dc1c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 32
+SUBLEVEL = 33
 
 ARCH = i386
 
@@ -14,7 +14,7 @@ ARCH = i386
 SMP = 1
 #
 # SMP profiling options
-SMP_PROF = 1
+SMP_PROF = 1
 
 .EXPORT_ALL_VARIABLES:
 
@@ -144,6 +144,10 @@ ifdef CONFIG_PCI
 DRIVERS := $(DRIVERS) drivers/pci/pci.a
 endif
 
+ifdef CONFIG_PNP
+DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
+endif
+
 ifdef CONFIG_SBUS
 DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
 endif
index c8ac96d10d21726697b182a51eccd2823a9016ba..65cc6397f47fd3117df71f991359f3e90040f263 100644 (file)
@@ -22,11 +22,14 @@ unexport O_TARGET
 unexport O_OBJS
 unexport L_OBJS
 unexport M_OBJS
+# intermediate objects that form part of a module
+unexport MI_OBJS
 unexport ALL_MOBJS
 # objects that export symbol tables
 unexport OX_OBJS
 unexport LX_OBJS
 unexport MX_OBJS
+unexport MIX_OBJS
 unexport SYMTAB_OBJS
 
 unexport MOD_LIST_NAME
@@ -105,7 +108,7 @@ ALL_MOBJS = $(MX_OBJS) $(M_OBJS)
 ifneq "$(strip $(ALL_MOBJS))" ""
 PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
 endif
-modules: $(ALL_MOBJS) dummy
+modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy
 ifdef MOD_SUB_DIRS
        set -e; for i in $(MOD_SUB_DIRS); do $(MAKE) -C $$i modules; done
 endif
@@ -150,7 +153,7 @@ script:
 #
 ifdef CONFIG_MODULES
 
-SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS)
+SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS)
 
 ifdef CONFIG_MODVERSIONS
 ifneq "$(strip $(SYMTAB_OBJS))" ""
index 9fdad699b7558ecb20a5c96386ea5967f324f98d..cc523e4eaa5dbafc9447603a75d9ab9ef3b58c10 100644 (file)
@@ -95,8 +95,14 @@ bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
+fi
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
 endmenu
 
+source drivers/pnp/Config.in
+
 source drivers/block/Config.in
 
 if [ "$CONFIG_NET" = "y" ]; then
diff --git a/arch/i386/boot/compressed/Makefile.debug b/arch/i386/boot/compressed/Makefile.debug
deleted file mode 100644 (file)
index 83714b5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# linux/arch/i386/boot/compressed/Makefile
-#
-# create a compressed vmlinux image from the original vmlinux
-#
-
-OBJECTS = misc.o 
-
-CFLAGS = -g -O2 -DSTDC_HEADERS -DSTANDALONE_DEBUG -Wall
-
-test-gzip: piggy.o $(OBJECTS)
-       $(CC) -g -o test-gzip $(OBJECTS) piggy.o
-
-clean: 
-       $(RM) inflate.o misc.o test-gzip
-
-inflate.o: inflate.c gzip.h
-
-misc.o: misc.c gzip.h
-
-
index 76fef45669ded6260b922f221eda573183c691e3..b77b4f03e8be9e9da7e77cb29d802f561d6bbf55 100644 (file)
@@ -9,8 +9,6 @@
  * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
  */
 
-#include <string.h>
-
 #include <asm/segment.h>
 #include <asm/io.h>
 
@@ -97,7 +95,6 @@ static void error(char *m);
 static void gzip_mark(void **);
 static void gzip_release(void **);
  
-#ifndef STANDALONE_DEBUG
 static void puts(const char *);
   
 extern int end;
@@ -196,7 +193,7 @@ static void puts(const char *s)
        outb_p(0xff & (pos >> 1), vidport+1);
 }
 
-__ptr_t memset(__ptr_t s, int c, size_t n)
+void* memset(void* s, int c, size_t n)
 {
        int i;
        char *ss = (char*)s;
@@ -204,7 +201,7 @@ __ptr_t memset(__ptr_t s, int c, size_t n)
        for (i=0;i<n;i++) ss[i] = c;
 }
 
-__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
+void* memcpy(void* __dest, __const void* __src,
                            size_t __n)
 {
        int i;
@@ -212,7 +209,6 @@ __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
 
        for (i=0;i<__n;i++) d[i] = s[i];
 }
-#endif
 
 /* ===========================================================================
  * Fill the input buffer. This is called only when the buffer is empty
@@ -292,34 +288,6 @@ struct {
        short b;
        } stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS };
 
-#ifdef STANDALONE_DEBUG
-
-static void gzip_mark(void **ptr)
-{
-}
-
-static void gzip_release(void **ptr)
-{
-}
-
-char output_buffer[1024 * 800];
-
-int
-main(argc, argv)
-       int     argc;
-       char    **argv;
-{
-       output_data = output_buffer;
-
-       makecrc();
-       puts("Uncompressing Linux...");
-       gunzip();
-       puts("done.\n");
-       return 0;
-}
-
-#else
-
 void setup_normal_output_buffer()
 {
 #ifdef STANDARD_MEMORY_BIOS_CALL
@@ -388,8 +356,4 @@ int decompress_kernel(struct moveparams *mv)
        if (high_loaded) close_output_buffer_if_we_run_high(mv);
        return high_loaded;
 }
-#endif
-
-
-
 
index 517d938bb0439efad86331e94068c6cc7c928b4b..f95590407445b63902cb4c958fe3edce25f1c024 100644 (file)
@@ -44,8 +44,13 @@ choice 'Processor type' \
         Pentium        CONFIG_M586     \
         PPro           CONFIG_M686" Pentium
 bool 'Video mode selection support' CONFIG_VIDEO_SELECT
+
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
+
 endmenu
 
+source drivers/pnp/Config.in
+
 source drivers/block/Config.in
 
 if [ "$CONFIG_NET" = "y" ]; then
index e2c670246a2880407a8a511539f54f61151cc27a..70761baa6fc20e208be4d2011b3d84ab86b988f8 100644 (file)
@@ -156,9 +156,10 @@ CONFIG_NET_EISA=y
 # CONFIG_PCNET32 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
-CONFIG_DE4X5=y
+# CONFIG_DE4X5 is not set
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
+CONFIG_EEXPRESS_PRO100=y
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
@@ -192,9 +193,10 @@ CONFIG_VFAT_FS=y
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_ROOT_NFS is not set
 # CONFIG_SMB_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
index 661e17eb98f3903a3f4f69deb7cb950914ca6c4d..cbdfc763b82c822c558d6219546bcaf4779a81a9 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/uaccess.h>
 #include <asm/checksum.h>
 #include <asm/io.h>
+#include <asm/hardirq.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
@@ -26,6 +27,7 @@ EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL_NOVERS(__down_failed);
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
index 55dfdfceb6317252e2169b06e912aea5d15b8d5c..b640a563d1a16458f41c34c22e9c9153d8dc3848 100644 (file)
 #include <asm/smp.h>
 #include <asm/pgtable.h>
 
+#ifdef __SMP_PROF__
+extern volatile unsigned long smp_apic_timer_ticks[1+NR_CPUS];
+#endif
+
 #define CR0_NE 32
 
 static unsigned char cache_21 = 0xff;
@@ -137,6 +141,9 @@ BUILD_IRQ(SECOND,15,0x80)
 BUILD_SMP_INTERRUPT(reschedule_interrupt)
 BUILD_SMP_INTERRUPT(invalidate_interrupt)
 BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
+#ifdef __SMP_PROF__
+BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt)
+#endif
 #endif
 
 /*
@@ -252,6 +259,7 @@ int get_smp_prof_list(char *buf) {
        unsigned long sum_spins_syscall = 0;
        unsigned long sum_spins_sys_idle = 0;
        unsigned long sum_smp_idle_count = 0;
+       unsigned long sum_apic_timer_ticks = 0;
 
        for (i=0;i<smp_num_cpus;i++) {
                int cpunum = cpu_logical_map[i];
@@ -259,6 +267,7 @@ int get_smp_prof_list(char *buf) {
                sum_spins_syscall+=smp_spins_syscall[cpunum];
                sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
                sum_smp_idle_count+=smp_idle_count[cpunum];
+               sum_apic_timer_ticks+=smp_apic_timer_ticks[cpunum];
        }
 
        len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
@@ -315,6 +324,12 @@ int get_smp_prof_list(char *buf) {
 
        len +=sprintf(buf+len,"   idle ticks\n");
 
+       len+=sprintf(buf+len,"TICK %10lu",sum_apic_timer_ticks);
+       for (i=0;i<smp_num_cpus;i++)
+               len+=sprintf(buf+len," %10lu",smp_apic_timer_ticks[cpu_logical_map[i]]);
+
+       len +=sprintf(buf+len,"   local APIC timer ticks\n");
+
        len+=sprintf(buf+len, "IPI: %10lu   received\n",
                ipi_count);
 
@@ -734,14 +749,30 @@ void init_IRQ(void)
        outb_p(0x34,0x43);              /* binary, mode 2, LSB/MSB, ch 0 */
        outb_p(LATCH & 0xff , 0x40);    /* LSB */
        outb(LATCH >> 8 , 0x40);        /* MSB */
+
        for (i = 0; i < 16 ; i++)
                set_intr_gate(0x20+i,bad_interrupt[i]);
-       /* This bit is a hack because we don't send timer messages to all processors yet */
-       /* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */
+
+       /*
+        * This bit is a hack because we don't send timer messages to all
+        * processors yet. It has to be here .. it doesn't work if you put
+        * it down the bottom - assembler explodes 8)
+        */
+
 #ifdef __SMP__ 
-       set_intr_gate(0x20+i, reschedule_interrupt);    /* IRQ '16' - IPI for rescheduling */
-       set_intr_gate(0x21+i, invalidate_interrupt);    /* IRQ '17' - IPI for invalidation */
-       set_intr_gate(0x22+i, stop_cpu_interrupt);      /* IRQ '18' - IPI for CPU halt */
+       /* IRQ '16' - IPI for rescheduling */
+       set_intr_gate(0x20+i, reschedule_interrupt);
+
+       /* IRQ '17' - IPI for invalidation */
+       set_intr_gate(0x21+i, invalidate_interrupt);
+
+       /* IRQ '18' - IPI for CPU halt */
+       set_intr_gate(0x22+i, stop_cpu_interrupt);
+
+#ifdef __SMP_PROF__
+       /* IRQ '19' - self generated IPI for local APIC timer */
+       set_intr_gate(0x23+i, apic_timer_interrupt);
+#endif
 #endif 
        request_region(0x20,0x20,"pic1");
        request_region(0xa0,0x20,"pic2");
index 9bbde4136cc39d893b5c5118d1ca7ea7df03a2e3..fe4723951cd0274d597438f30be385d85228451c 100644 (file)
@@ -107,6 +107,7 @@ asmlinkage int sys_idle(void)
        if (current->pid != 0)
                goto out;
        /* endless idle loop with no priority at all */
+       current->priority = -100;
        current->counter = -100;
        for (;;) 
        {
@@ -145,6 +146,7 @@ out:
 
 int cpu_idle(void *unused)
 {
+       current->priority = -100;
        while(1)
        {
                if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
@@ -158,6 +160,8 @@ int cpu_idle(void *unused)
                        run_task_queue(&tq_scheduler);
                        unlock_kernel();
                }
+               /* endless idle loop with no priority at all */
+               current->counter = -100;
                schedule();
        }
 }
@@ -474,7 +478,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
        p->tss.ss = KERNEL_DS;
        p->tss.ds = KERNEL_DS;
        p->tss.fs = USER_DS;
-       p->tss.gs = KERNEL_DS;
+       p->tss.gs = USER_DS;
        p->tss.ss0 = KERNEL_DS;
        p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
        p->tss.tr = _TSS(nr);
index c97f27acc06370902decf61c2cf204a404bafea7..ffc9f0779fea6b01f50ddd2386fd268dfefb2f4d 100644 (file)
@@ -23,7 +23,8 @@
  *     Michel Lespinasse       :       Changes for 2.1 kernel map.
  *     Michael Chastain        :       Change trampoline.S to gnu as.
  *             Alan Cox        :       Dumb bug: 'B' step PPro's are fine
- *
+ *             Ingo Molnar     :       Added APIC timers, based on code
+ *                                     from Jose Renau
  */
 
 #include <linux/kernel.h>
@@ -45,6 +46,7 @@
 #include <asm/io.h>
 
 extern unsigned long start_kernel, _etext;
+void setup_APIC_clock (void);
 
 /*
  *     Some notes on processor bugs:
@@ -137,6 +139,10 @@ volatile unsigned long smp_spins_syscall[NR_CPUS]={0};  /* Count syscall spins
 volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall                 */
 volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle                            */
 volatile unsigned long smp_idle_count[1+NR_CPUS]={0,}; /* Count idle ticks                                     */
+
+/* Count local APIC timer ticks */
+volatile unsigned long smp_apic_timer_ticks[1+NR_CPUS]={0,};
+
 #endif
 #if defined (__SMP_PROF__)
 volatile unsigned long smp_idle_map=0;                 /* Map for idle processors                              */
@@ -621,6 +627,14 @@ void smp_callin(void)
        l=apic_read(APIC_SPIV);
        l|=(1<<8);              /* Enable */
        apic_write(APIC_SPIV,l);
+
+#ifdef __SMP_PROF__
+       /*
+        * Set up our APIC timer. 
+        */
+       setup_APIC_clock ();
+#endif
+
        sti();
        /*
         *      Get our bogomips.
@@ -767,7 +781,14 @@ void smp_boot_cpus(void)
        apic_write(APIC_SPIV,cfg);
 
        udelay(10);
-                       
+
+#ifdef __SMP_PROF__
+       /*
+        * Set up our local APIC timer:
+        */                     
+       setup_APIC_clock ();
+#endif
+
        /*
         *      Now scan the cpu present map and fire up the other CPUs.
         */
@@ -1305,3 +1326,274 @@ asmlinkage void smp_stop_cpu_interrupt(void)
                for(;;) __asm__("hlt");
        for  (;;) ;
 }
+
+#ifdef __SMP_PROF__
+
+extern void (*do_profile)(struct pt_regs *);
+static void (*default_do_profile)(struct pt_regs *) = NULL;
+
+/*
+ *     APIC timer interrupt
+ */
+void smp_apic_timer_interrupt(struct pt_regs * regs)
+{
+       int cpu = smp_processor_id();
+
+       if (!user_mode(regs)) {
+               unsigned long flags;
+
+               /*
+                * local timer interrupt is not NMI yet, so
+                * it's simple, we just aquire the global cli
+                * lock to mess around with profiling info.
+                *
+                * later, in the NMI version, we have to build
+                * our own 'current' pointer (as we could have
+                * interrupted code that just changes "current")
+                * and have to lock profiling info between NMI
+                * interrupts properly.
+                */
+               save_flags(flags);
+               cli();
+               default_do_profile(regs);
+               restore_flags(flags);
+       }
+
+       /*
+        * this is safe outside the lock.
+        */ 
+       smp_apic_timer_ticks[cpu]++;
+
+       apic_read (APIC_SPIV);
+       apic_write (APIC_EOI, 0);
+}
+
+/*
+ * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
+ * per second. We assume that the caller has already set up the local
+ * APIC at apic_addr.
+ *
+ * Later we might want to split HZ into two parts: introduce
+ * PROFILING_HZ and scheduling HZ. The APIC timer is not exactly
+ * sync with the external timer chip, it closely follows bus clocks.
+ */
+
+#define RTDSC(x)       __asm__ __volatile__ (  ".byte 0x0f,0x31" \
+                               :"=a" (((unsigned long*)&x)[0]),  \
+                                "=d" (((unsigned long*)&x)[1]))
+
+/*
+ * The timer chip is already set up at HZ interrupts per second here,
+ * but we do not accept timer interrupts yet. We only allow the BP
+ * to calibrate.
+ */
+unsigned int get_8254_timer_count (void)
+{
+       unsigned int count;
+
+        outb_p(0x00, 0x43);
+       count = inb_p(0x40);
+       count |= inb_p(0x40) << 8;
+
+       return count;
+}
+
+/*
+ * This function sets up the local APIC timer, with a timeout of
+ * 'clocks' APIC bus clock. During calibration we actually call
+ * this function twice, once with a bogus timeout value, second
+ * time for real. The other (noncalibrating) CPUs call this
+ * function only once, with the real value.
+ *
+ * We are strictly in irqs off mode here, as we do not want to
+ * get an APIC interrupt go off accidentally.
+ */
+
+#define APIC_DIVISOR 16
+
+/*
+ * We do reads before writes even if unnecessary, to get around the
+ * APIC double write 'bug'.
+ */
+
+void setup_APIC_timer (unsigned int clocks)
+{
+       unsigned long lvtt1_value; 
+       unsigned int tmp_value;
+
+       /*
+        * Unfortunately the local APIC timer cannot be set up into NMI
+        * mode. With the IO APIC we can re-route the external timer
+        * interrupt and broadcast it as an NMI to all CPUs, so no pain.
+        *
+        * NOTE: this irq vector 19 and the gate in BUILD_SMP_TIMER_INTERRUPT
+        * should be the same ;)
+        */
+       tmp_value = apic_read(APIC_LVTT);
+       lvtt1_value = APIC_LVT_TIMER_PERIODIC | (0x20+19);
+       apic_write(APIC_LVTT , lvtt1_value);
+
+       /*
+        * Divide PICLK by 16
+        */
+       tmp_value = apic_read(APIC_TDCR);
+       apic_write(APIC_TDCR , (tmp_value & ~APIC_TDR_DIV_1 )
+                                | APIC_TDR_DIV_16);
+
+       tmp_value = apic_read(APIC_TMICT);
+       apic_write(APIC_TMICT, clocks/APIC_DIVISOR);
+}
+
+void wait_8254_wraparound (void)
+{
+       unsigned int curr_count, prev_count=~0;
+       int delta;
+
+       curr_count = get_8254_timer_count();
+
+       do {
+               prev_count = curr_count;
+               curr_count = get_8254_timer_count();
+               delta = curr_count-prev_count;
+
+       /*
+        * This limit for delta seems arbitrary, but it isnt, it's
+        * slightly above the level of error a buggy Mercury/Neptune
+        * chipset timer can cause.
+        */
+
+       } while (delta<1000);
+}
+
+/*
+ * In this function we calibrate APIC bus clocks to the external
+ * timer here. Unfortunately we cannot use jiffies and
+ * the timer irq to calibrate, since some later bootup
+ * code depends on getting the first irq? Ugh.
+ *
+ * We want to do the calibration only once since we
+ * want to have local timer irqs syncron. CPUs connected
+ * by the same APIC bus have the very same bus frequency.
+ * And we want to have irqs off anyways, no accidental
+ * APIC irq that way.
+ */
+
+int calibrate_APIC_clock (void)
+{
+       unsigned long long t1,t2;
+       unsigned long tt1,tt2;
+       unsigned int prev_count, curr_count;
+       long calibration_result;
+
+       printk("calibrating APIC timer ... ");
+
+       /*
+        * Put whatever arbitrary (but long enough) timeout
+        * value into the APIC clock, we just want to get the
+        * counter running for calibration.
+        */
+       setup_APIC_timer(1000000000);
+
+       /*
+        * The timer chip counts down to zero. Lets wait
+        * for a wraparound to start exact measurement:
+        * (the current tick might have been already half done)
+        */
+
+       wait_8254_wraparound ();
+
+       /*
+        * We wrapped around just now, lets start:
+        */
+       RTDSC(t1);
+       tt1=apic_read(APIC_TMCCT);
+
+       /*
+        * lets wait until we get to the next wrapround:
+        */
+       wait_8254_wraparound ();
+
+       tt2=apic_read(APIC_TMCCT);
+       RTDSC(t2);
+
+       /*
+        * The APIC bus clock counter is 32 bits only, it
+        * might have overflown:
+        */
+       if (tt2<tt1) {
+               unsigned long tmp = tt2;
+               tt2=tt1;
+               tt1=tmp;
+       }
+
+       calibration_result = (tt2-tt1)*APIC_DIVISOR;
+
+       printk("\n..... %ld CPU clocks in 1 timer chip tick.\n",
+                        (unsigned long)(t2-t1));
+
+       printk("..... %ld APIC bus clocks in 1 timer chip tick.\n",
+                        calibration_result);
+
+
+       printk("..... CPU clock speed is %ld.%ld MHz.\n", 
+               ((long)(t2-t1))/(1000000/HZ),
+               ((long)(t2-t1))%(1000000/HZ)  );
+
+       printk("..... APIC bus clock speed is %ld.%ld MHz.\n", 
+               calibration_result/(1000000/HZ),
+               calibration_result%(1000000/HZ)  );
+
+       return calibration_result;
+}
+
+void setup_APIC_clock (void)
+{
+       unsigned long flags; 
+
+       static volatile int calibration_lock;
+       static unsigned int calibration_result;
+
+       return;
+       save_flags(flags);
+       cli();
+
+       printk("setup_APIC_clock() called.\n");
+
+       /*
+        * We use a private profiling function. (This is preparation
+        * for NMI local timer interrupts.)
+        *
+        * [ setup_APIC_clock() is called from all CPUs, but we want
+        *   to do this part of the setup only once ... and it fits
+        *   here best ]
+        */
+       if (!set_bit(0,&calibration_lock)) {
+
+               default_do_profile = do_profile;
+               do_profile = NULL;
+
+               calibration_result=calibrate_APIC_clock();
+               /*
+                * Signal completion to the other CPU[s]:
+                */
+               calibration_lock = 3;
+
+       } else {
+               /*
+                * Other CPU is calibrating, wait for finish:
+                */
+               printk("waiting for other CPU calibrating APIC timer ... ");
+               while (calibration_lock == 1);
+               printk("done, continuing.\n");
+       }
+
+       setup_APIC_timer (calibration_result);
+
+       restore_flags(flags);
+}
+
+#undef APIC_DIVISOR
+#undef RTDSC
+
+#endif
+
index dafdd2fa8020b474d4365269d983359ddc61a7fc..a89c0a3f512d4d711392be86f1eb948b937a444c 100644 (file)
@@ -114,7 +114,7 @@ int kstack_depth_to_print = 24;
 #define VMALLOC_OFFSET (8*1024*1024)
 #define MODULE_RANGE (8*1024*1024)
 
-static void show_regs(struct pt_regs *regs)
+static void show_registers(struct pt_regs *regs)
 {
        int i;
        unsigned long esp;
@@ -124,8 +124,6 @@ static void show_regs(struct pt_regs *regs)
 
        esp = (unsigned long) &regs->esp;
        ss = KERNEL_DS;
-       if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
-               return;
        if (regs->xcs & 3) {
                esp = regs->esp;
                ss = regs->xss & 0xffff;
@@ -184,9 +182,11 @@ static void show_regs(struct pt_regs *regs)
 
 /*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
+       if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
+               return;
        console_verbose();
        printk("%s: %04lx\n", str, err & 0xffff);
-       show_regs(regs);
+       show_registers(regs);
        do_exit(SIGSEGV);
 }
 
@@ -236,7 +236,7 @@ out:
 
 asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
 {
-       printk("NMI\n"); show_regs(regs);
+       printk("NMI\n"); show_registers(regs);
 #ifdef CONFIG_SMP_NMI_INVAL
        smp_flush_tlb_rcv();
 #else
index 7d0628d00212fcb0e417e990b25b548ae16cd8ef..65a4a67d3443763a3a0def0d336ac964ccbfd485 100644 (file)
@@ -187,11 +187,10 @@ bad_area:
  *
  * First we check if it was the bootup rw-test, though..
  */
-       if (wp_works_ok < 0 && !address && (error_code & 1)) {
+       if (wp_works_ok < 0 && address == 0xc0000000 && (error_code & 1)) {
                wp_works_ok = 1;
                pg0[0] = pte_val(mk_pte(0, PAGE_SHARED));
                flush_tlb();
-               printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
                goto out;
        }
        if (address < PAGE_SIZE) {
index 1fb61678bf9473b6555fcc698d0b33d7324553db..5779a66868130285da03ded75c8fde653ff234d2 100644 (file)
@@ -146,19 +146,19 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
        pg_dir = swapper_pg_dir;
        /* unmap the original low memory mappings */
        pgd_val(pg_dir[0]) = 0;
+
+       /* Map whole memory from 0xC0000000 */
+
        while (address < end_mem) {
-               /*
-                * The following code enabled 4MB page tables for the
-                * Intel Pentium cpu, unfortunately the SMP kernel can't
-                * handle the 4MB page table optimizations yet
-                */
-               /*
-                * This will create page tables that
-                * span up to the next 4MB virtual
-                * memory boundary, but that's ok,
-                * we won't use that memory anyway.
-                */
                if (x86_capability & 8) {
+                       /*
+                        * If we're running on a Pentium CPU, we can use the 4MB
+                        * page tables. 
+                        *
+                        * The page tables we create span up to the next 4MB
+                        * virtual memory boundary, but that's OK as we won't
+                        * use that memory anyway.
+                        */
 #ifdef GAS_KNOWS_CR4
                        __asm__("movl %%cr4,%%eax\n\t"
                                "orl $16,%%eax\n\t"
@@ -176,8 +176,10 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
                        address += 4*1024*1024;
                        continue;
                }
-               /* map the memory at virtual addr 0xC0000000 */
-               /* pg_table is physical at this point */
+               /*
+                * We're on a [34]86, use normal page tables.
+                * pg_table is physical at this point
+                */
                pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
                if (!pg_table) {
                        pg_table = (pte_t *) __pa(start_mem);
@@ -273,18 +275,25 @@ void mem_init(unsigned long start_mem, unsigned long end_mem)
 /* test if the WP bit is honoured in supervisor mode */
        if (wp_works_ok < 0) {
                unsigned char tmp_reg;
+               unsigned long old = pg0[0];
+               printk("Checking if this processor honours the WP bit even in supervisor mode... ");
                pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY));
                local_flush_tlb();
+               current->mm->mmap->vm_start += PAGE_SIZE;
                __asm__ __volatile__(
                        "movb %0,%1 ; movb %1,%0"
                        :"=m" (*(char *) __va(0)),
                         "=q" (tmp_reg)
                        :/* no inputs */
                        :"memory");
-               pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL));
+               pg0[0] = old;
                local_flush_tlb();
-               if (wp_works_ok < 0)
+               current->mm->mmap->vm_start -= PAGE_SIZE;
+               if (wp_works_ok < 0) {
                        wp_works_ok = 0;
+                       printk("No.\n");
+               } else
+                       printk("Ok.\n");
        }
        return;
 }
index 502f66d32ae9a43cb075b9207084707b1375578b..ef047d0406da86d8f431de7190a609e46b9c6fb1 100644 (file)
@@ -9,12 +9,17 @@
 
 SUB_DIRS     := block char net #streams
 MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
 endif
 
+ifdef CONFIG_PNP
+SUB_DIRS += pnp
+MOD_SUB_DIRS += pnp
+endif
+
 ifdef CONFIG_SBUS
 SUB_DIRS += sbus
 endif
index 54024d8d5fe12ee245b70827e34401b1d7c949ef..4c21bc9473924d6e933a10b9f433da19ece7eaeb 100644 (file)
@@ -765,9 +765,15 @@ static void setup_dev(struct gendisk *dev)
 void device_setup(void)
 {
        extern void console_map_init(void);
+#ifdef CONFIG_PNP_PARPORT
+       extern int pnp_parport_init(void);
+#endif
        struct gendisk *p;
        int nr=0;
 
+#ifdef CONFIG_PNP_PARPORT
+       pnp_parport_init();
+#endif
        chr_dev_init();
        blk_dev_init();
        sti();
index 02ee8ee0f32426ba02f16f6cdd1aa935376ec865..b3531e5cff3994afb93f6edf592cdedf75bb3739 100644 (file)
@@ -112,7 +112,7 @@ static void xd_geninit (struct gendisk *);
 static int xd_open (struct inode *inode,struct file *file);
 static void do_xd_request (void);
 static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
-static void xd_release (struct inode *inode,struct file *file);
+static int xd_release (struct inode *inode,struct file *file);
 static int xd_reread_partitions (kdev_t dev);
 static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count);
 static void xd_recalibrate (u_char drive);
index 1f297607f97024fa312292a2692edeaf6162c046..c32475be7dd7fbf3ba77ec9c3a98ede3dfaf751a 100644 (file)
@@ -19,7 +19,7 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
-   bool 'Digiboard PC/Xx Support' CONFIG_DIGI
+   tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
    tristate 'Cyclades async mux support' CONFIG_CYCLADES
    bool 'Stallion multiport serial support' CONFIG_STALDRV
    if [ "$CONFIG_STALDRV" = "y" ]; then
@@ -33,8 +33,12 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
      int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
    fi
 fi
-tristate 'Parallel printer support' CONFIG_PRINTER
-
+if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PNP_PARPORT
+  if [ "$CONFIG_PRINTER" != "n" ]; then
+    bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
+  fi
+fi
 
 bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
 if [ "$CONFIG_MOUSE" = "y" ]; then
index f563a5e98c4daa1c3f83bb3c4e4a5f454a8ec46f..763d3c71b3bc61daed74f68f769528e76c6ba647 100644 (file)
@@ -884,10 +884,10 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
         * related to the kernel should not use this.
         */
                        data = shift_state;
-                       return put_user(data, (char *) arg);
+                       return __put_user(data, (char *) arg);
                case 7:
                        data = mouse_reporting();
-                       return put_user(data, (char *) arg);
+                       return __put_user(data, (char *) arg);
                case 10:
                        set_vesa_blanking(arg);
                        return 0;
@@ -1337,11 +1337,17 @@ static int do_con_write(struct tty_struct * tty, int from_user,
        if (currcons == sel_cons)
                clear_selection();
 
+       if (from_user) {
+               /* just to make sure that noone lurks at places he shouldn't see. */
+               if (verify_area(VERIFY_READ, buf, count))
+                       return 0; /* ?? are error codes legal here ?? */
+       }
+
        disable_bh(CONSOLE_BH);
        while (!tty->stopped && count) {
                enable_bh(CONSOLE_BH);
                if (from_user)
-                       get_user(c, buf);
+                       __get_user(c, buf);
                else
                        c = *buf;
                buf++; n++; count--;
index 041a6a23e979ac9db8081bdbddb72f2a16a33075..c2d1648e69c526916c9a9a6223a0f04b606d81c5 100644 (file)
@@ -235,7 +235,7 @@ int con_set_trans_old(unsigned char * arg)
 
        for (i=0; i<E_TABSZ ; i++) {
                unsigned char uc;
-               get_user(uc, arg+i);
+               __get_user(uc, arg+i);
                p[i] = UNI_DIRECT_BASE | uc;
        }
 
@@ -255,7 +255,7 @@ int con_get_trans_old(unsigned char * arg)
        for (i=0; i<E_TABSZ ; i++)
          {
            ch = conv_uni_to_pc(p[i]);
-           put_user((ch & ~0xff) ? 0 : ch, arg+i);
+           __put_user((ch & ~0xff) ? 0 : ch, arg+i);
          }
        return 0;
 }
@@ -272,7 +272,7 @@ int con_set_trans_new(ushort * arg)
 
        for (i=0; i<E_TABSZ ; i++) {
                unsigned short us;
-               get_user(us, arg+i);
+               __get_user(us, arg+i);
                p[i] = us;
        }
 
@@ -291,7 +291,7 @@ int con_get_trans_new(ushort * arg)
                return i;
 
        for (i=0; i<E_TABSZ ; i++)
-         put_user(p[i], arg+i);
+         __put_user(p[i], arg+i);
        
        return 0;
 }
@@ -381,8 +381,8 @@ con_set_unimap(ushort ct, struct unipair *list)
   while( ct-- )
     {
       unsigned short unicode, fontpos;
-      get_user(unicode, &list->unicode);
-      get_user(fontpos, &list->fontpos);
+      __get_user(unicode, &list->unicode);
+      __get_user(fontpos, &list->fontpos);
       if ( (err1 = con_insert_unipair(unicode,fontpos)) != 0 )
        err = err1;
       list++;
@@ -434,15 +434,15 @@ con_get_unimap(ushort ct, ushort *uct, struct unipair *list){
                      {
                        if ( *p2 < MAX_GLYPH && ect++ < ct )
                          {
-                           put_user((u_short)((i<<11)+(j<<6)+k),
+                           __put_user((u_short)((i<<11)+(j<<6)+k),
                                     &list->unicode);
-                           put_user((u_short) *p2, &list->fontpos);
+                           __put_user((u_short) *p2, &list->fontpos);
                            list++;
                          }
                        p2++;
                      }
          }
-       put_user(ect, uct);
+       __put_user(ect, uct);
        return ((ect <= ct) ? 0 : -ENOMEM);
 }
 
index 6358711095bfd9c2419bba18c63eb102efd9c1c0..e87de84b06a064814bbc3c844a804d7ad1075eaa 100644 (file)
@@ -8,7 +8,8 @@
  * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
  * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
- * lp_read (Status readback) support added by Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * lp_read (Status readback) support added by Carsten Gross,
+ *                                             carsten@sol.wohnheim.uni-ulm.de
  */
 
 #include <linux/module.h>
@@ -17,7 +18,6 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
-#include <linux/lp.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
@@ -26,6 +26,8 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <linux/parport.h>
+#include <linux/lp.h>
 
 /* the BIOS manuals say there can be up to 4 lpt devices
  * but I have not seen a board where the 4th address is listed
  * please let me know if you have different equipment
  * if you have more than 3 printers, remember to increase LP_NO
  */
-struct lp_struct lp_table[] = {
-       { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
-       { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
-       { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
+struct lp_struct lp_table[] =
+{
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}}
 };
 #define LP_NO 3
 
+/* Device name */
+static char *dev_name = "lp";
+
 /* Test if printer is ready (and optionally has no error conditions) */
 #define LP_READY(minor, status) \
   ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
@@ -49,61 +58,58 @@ struct lp_struct lp_table[] = {
    (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
       (LP_PBUSY|LP_PSELECD|LP_PERRORP)
 
-/*
- * All my debugging code assumes that you debug with only one printer at
- * a time. RWWH
- * Debug info moved into stats area, so this is no longer true (Rob Janssen)
- */
-
 #undef LP_DEBUG
+#undef LP_READ_DEBUG
 
 static int lp_reset(int minor)
 {
-       outb_p(LP_PSELECP, LP_C(minor));
+       w_ctr(minor, LP_PSELECP);
        udelay(LP_DELAY);
-       outb_p(LP_PSELECP | LP_PINITP, LP_C(minor));
-       return LP_S(minor);
+       w_ctr(minor, LP_PSELECP | LP_PINITP);
+       return r_str(minor);
 }
 
 static inline int lp_char_polled(char lpchar, int minor)
 {
        int status, wait = 0;
-       unsigned long count  = 0;
+       unsigned long count = 0;
        struct lp_stats *stats;
 
        do {
-               status = LP_S(minor);
-               count ++;
-               if(need_resched)
+               status = r_str(minor);
+               count++;
+               if (need_resched)
                        schedule();
-       } while(!LP_READY(minor,status) && count < LP_CHAR(minor));
+       } while (!LP_READY(minor, status) && count < LP_CHAR(minor));
 
        if (count == LP_CHAR(minor)) {
                return 0;
                /* we timed out, and the character was /not/ printed */
        }
-       outb_p(lpchar, LP_B(minor));
+       w_dtr(minor, lpchar);
        stats = &LP_STAT(minor);
        stats->chars++;
        /* must wait before taking strobe high, and after taking strobe
           low, according spec.  Some printers need it, others don't. */
-       while(wait != LP_WAIT(minor)) wait++;
+       while (wait != LP_WAIT(minor))
+               wait++;
        /* control port takes strobe high */
-       outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-       while(wait) wait--;
+       w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+       while (wait)
+               wait--;
        /* take strobe low */
-       outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+       w_ctr(minor, LP_PSELECP | LP_PINITP);
        /* update waittime statistics */
        if (count > stats->maxwait) {
 #ifdef LP_DEBUG
-           printk(KERN_DEBUG "lp%d success after %d counts.\n",minor,count);
+               printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count);
 #endif
-           stats->maxwait = count;
+               stats->maxwait = count;
        }
        count *= 256;
-       wait = (count > stats->meanwait)? count - stats->meanwait :
-                                         stats->meanwait - count;
-       stats->meanwait = (255*stats->meanwait + count + 128) / 256;
+       wait = (count > stats->meanwait) ? count - stats->meanwait :
+           stats->meanwait - count;
+       stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
        stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
 
        return 1;
@@ -117,35 +123,37 @@ static inline int lp_char_interrupt(char lpchar, int minor)
        struct lp_stats *stats;
 
        do {
-           if(need_resched)
-               schedule();
-           if ((status = LP_S(minor)) & LP_PBUSY) {
-               if (!LP_CAREFUL_READY(minor, status))
-                       return 0;
-               outb_p(lpchar, LP_B(minor));
-               stats = &LP_STAT(minor);
-               stats->chars++;
-               /* must wait before taking strobe high, and after taking strobe
-                  low, according spec.  Some printers need it, others don't. */
-               wait = 0;
-               while(wait != LP_WAIT(minor)) wait++;
-               /* control port takes strobe high */
-               outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-               while(wait) wait--;
-               /* take strobe low */
-               outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
-               /* update waittime statistics */
-               if (count) {
-                   if (count > stats->maxwait)
-                       stats->maxwait = count;
-                   count *= 256;
-                   wait = (count > stats->meanwait)? count - stats->meanwait :
-                                                     stats->meanwait - count;
-                   stats->meanwait = (255*stats->meanwait + count + 128) / 256;
-                   stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
+               if(need_resched)
+                       schedule();
+               if ((status = r_str(minor)) & LP_PBUSY) {
+                       if (!LP_CAREFUL_READY(minor, status))
+                               return 0;
+                       w_dtr(minor, lpchar);
+                       stats = &LP_STAT(minor);
+                       stats->chars++;
+                       /* must wait before taking strobe high, and after taking strobe
+                          low, according spec.  Some printers need it, others don't. */
+                       wait = 0;
+                       while (wait != LP_WAIT(minor))
+                               wait++;
+                       /* control port takes strobe high */
+                       w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+                       while (wait)
+                               wait--;
+                       /* take strobe low */
+                       w_ctr(minor, LP_PSELECP | LP_PINITP);
+                       /* update waittime statistics */
+                       if (count) {
+                               if (count > stats->maxwait)
+                                       stats->maxwait = count;
+                               count *= 256;
+                               wait = (count > stats->meanwait) ? count - stats->meanwait :
+                                   stats->meanwait - count;
+                               stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
+                               stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
+                       }
+                       return 1;
                }
-               return 1;
-           }
        } while (count++ < LP_CHAR(minor));
 
        return 0;
@@ -153,17 +161,15 @@ static inline int lp_char_interrupt(char lpchar, int minor)
 
 static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       struct lp_struct *lp = &lp_table[0];
+       struct parport *pb = (struct parport *) dev_id;
+       struct ppd *pd = pb->cad;
+       struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
 
-       while (irq != lp->irq) {
-               if (++lp >= &lp_table[LP_NO])
-                       return;
-       }
-
-       wake_up(&lp->lp_wait_q);
+       if (lp_dev->lp_wait_q)
+               wake_up(&lp_dev->lp_wait_q);
 }
 
-static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count)
+static inline int lp_write_interrupt(unsigned int minor, const char *buf, int count)
 {
        unsigned long copy_size;
        unsigned long total_bytes_written = 0;
@@ -171,6 +177,11 @@ static inline int lp_write_interrupt(unsigned int minor, const char * buf, int c
        struct lp_struct *lp = &lp_table[minor];
        unsigned char status;
 
+       if (minor >= LP_NO)
+               return -ENXIO;
+       if (lp_table[minor].dev == NULL)
+               return -ENXIO;
+
        do {
                bytes_written = 0;
                copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
@@ -184,35 +195,35 @@ static inline int lp_write_interrupt(unsigned int minor, const char * buf, int c
                        } else {
                                int rc = total_bytes_written + bytes_written;
                                if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
-                                        LP_STAT(minor).maxrun = lp_table[minor].runchars;
-                               status = LP_S(minor);
+                                       LP_STAT(minor).maxrun = lp_table[minor].runchars;
+                               status = r_str(minor);
                                if ((status & LP_POUTPA)) {
                                        printk(KERN_INFO "lp%d out of paper\n", minor);
                                        if (LP_F(minor) & LP_ABORT)
-                                               return rc?rc:-ENOSPC;
+                                               return rc ? rc : -ENOSPC;
                                } else if (!(status & LP_PSELECD)) {
                                        printk(KERN_INFO "lp%d off-line\n", minor);
                                        if (LP_F(minor) & LP_ABORT)
-                                               return rc?rc:-EIO;
+                                               return rc ? rc : -EIO;
                                } else if (!(status & LP_PERRORP)) {
                                        printk(KERN_ERR "lp%d printer error\n", minor);
                                        if (LP_F(minor) & LP_ABORT)
-                                               return rc?rc:-EIO;
+                                               return rc ? rc : -EIO;
                                }
                                LP_STAT(minor).sleeps++;
                                cli();
-                               outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
-                               status = LP_S(minor);
+                               w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
+                               status = r_str(minor);
                                if ((!(status & LP_PACK) || (status & LP_PBUSY))
-                                 && LP_CAREFUL_READY(minor, status)) {
-                                       outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+                                   && LP_CAREFUL_READY(minor, status)) {
+                                       w_ctr(minor, LP_PSELECP | LP_PINITP);
                                        sti();
                                        continue;
                                }
-                               lp_table[minor].runchars=0;
+                               lp_table[minor].runchars = 0;
                                current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
                                interruptible_sleep_on(&lp->lp_wait_q);
-                               outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+                               w_ctr(minor, LP_PSELECP | LP_PINITP);
                                sti();
                                if (current->signal & ~current->blocked) {
                                        if (total_bytes_written + bytes_written)
@@ -232,9 +243,9 @@ static inline int lp_write_interrupt(unsigned int minor, const char * buf, int c
        return total_bytes_written;
 }
 
-static inline int lp_write_polled(unsigned int minor, const char * buf, int count)
+static inline int lp_write_polled(unsigned int minor, const char *buf, int count)
 {
-       int  retval,status;
+       int  retval, status;
        char c;
        const char *temp;
 
@@ -249,7 +260,7 @@ static inline int lp_write_polled(unsigned int minor, const char * buf, int coun
                } else { /* if printer timed out */
                        if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
                                 LP_STAT(minor).maxrun = lp_table[minor].runchars;
-                       status = LP_S(minor);
+                       status = r_str(minor);
 
                        if (status & LP_POUTPA) {
                                printk(KERN_INFO "lp%d out of paper\n", minor);
@@ -269,7 +280,7 @@ static inline int lp_write_polled(unsigned int minor, const char * buf, int coun
                        } else
                        /* not offline or out of paper. on fire? */
                        if (!(status & LP_PERRORP)) {
-                               printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
+                               printk(KERN_ERR "lp%d on fire\n", minor);
                                if(LP_F(minor) & LP_ABORT)
                                        return temp-buf?temp-buf:-EIO;
                                current->state = TASK_INTERRUPTIBLE;
@@ -302,15 +313,27 @@ static long lp_write(struct inode * inode, struct file * file,
        const char * buf, unsigned long count)
 {
        unsigned int minor = MINOR(inode->i_rdev);
+       int retv;
 
        if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
                lp_table[minor].runchars = 0;
+
        lp_table[minor].lastcall = jiffies;
 
-       if (LP_IRQ(minor))
-               return lp_write_interrupt(minor, buf, count);
-       else
-               return lp_write_polled(minor, buf, count);
+       /* Claim Parport or sleep until it becomes available
+        * (see lp_wakeup() for details)
+        */
+       if (parport_claim(lp_table[minor].dev)) {
+               sleep_on(&lp_table[minor].lp_wait_q);
+               lp_table[minor].lp_wait_q = NULL;
+       }
+       if (LP_IRQ(minor) > 0)
+               retv = lp_write_interrupt(minor, buf, count);
+       else
+               retv = lp_write_polled(minor, buf, count);
+       parport_release(lp_table[minor].dev);
+       return retv;
 }
 
 static long long lp_lseek(struct inode * inode, struct file * file,
@@ -319,38 +342,19 @@ static long long lp_lseek(struct inode * inode, struct file * file,
        return -ESPIPE;
 }
 
-/* Test if nibble mode for status readback is okay. 
- * returns false if not, otherwise true
- */
-static int lp_nibble_mode_ok(int minor) 
-{
-       int reply=1;
-
-       outb_p(0, LP_B(minor));         /* Request "nibble mode" */
-       udelay(LP_DELAY);               
-       outb((inb(LP_C(minor)) & ~8), LP_C(minor)); /* SelectIN low */
-       outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
-       udelay(LP_DELAY);
-       outb((inb(LP_C(minor)) | 1), LP_C(minor)); /* Strobe high */
-       if (( LP_S(minor) & ~0x80) != 0x38) reply=0; /* expected answer? */
-       outb((inb(LP_C(minor)) & ~1 ), LP_C(minor)); /* Strobe low */
-       outb((inb(LP_C(minor)) & ~2 ), LP_C(minor)); /* Autofeed low */
-       udelay(LP_DELAY);
-       return(reply);
-}
-
+#ifdef CONFIG_PRINTER_READBACK
 
 static int lp_read_nibble(int minor) 
 {
        unsigned char i;
-       i=LP_S(minor)>>3;
+       i=r_str(minor)>>3;
        i&=~8;
        if ( ( i & 0x10) == 0) i|=8;
        return(i & 0x0f);
 }
 
 static void lp_select_in_high(int minor) {
-       outb((inb(LP_C(minor)) | 8), LP_C(minor));
+       w_ctr(minor, (r_ctr(minor) | 8));
 }
 
 /* Status readback confirming to ieee1284 */
@@ -364,6 +368,14 @@ static long lp_read(struct inode * inode, struct file * file,
        unsigned int i;
        unsigned int minor=MINOR(inode->i_rdev);
        
+       /* Claim Parport or sleep until it becomes available
+        * (see lp_wakeup() for details)
+        */
+       if (parport_claim(lp_table[minor].dev)) {
+               sleep_on(&lp_table[minor].lp_wait_q);
+               lp_table[minor].lp_wait_q = NULL;
+       }
+
        temp=buf;       
 #ifdef LP_READ_DEBUG 
        printk(KERN_INFO "lp%d: read mode\n", minor);
@@ -372,14 +384,19 @@ static long lp_read(struct inode * inode, struct file * file,
        retval = verify_area(VERIFY_WRITE, buf, count);
        if (retval)
                return retval;
-       if (lp_nibble_mode_ok(minor)==0) {
+       if (parport_ieee1284_nibble_mode_ok(lp_table[minor].dev->port, 0)==0) {
+#ifdef LP_READ_DEBUG
+               printk(KERN_INFO "lp%d: rejected IEEE1284 negotiation.\n",
+                      minor);
+#endif
                lp_select_in_high(minor);
+               parport_release(lp_table[minor].dev);
                return temp-buf;          /*  End of file */
        }
        for (i=0; i<=(count*2); i++) {
-               outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+               w_ctr(minor, r_ctr(minor) | 2); /* AutoFeed high */
                do {
-                       status=(LP_S(minor) & 0x40);
+                       status=(r_str(minor) & 0x40);
                        udelay(50);
                        counter++;
                        if (need_resched)
@@ -389,15 +406,16 @@ static long lp_read(struct inode * inode, struct file * file,
 #ifdef LP_READ_DEBUG
                        printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
 #endif         
-                       outb((inb(LP_C(minor)) & ~2), LP_C(minor));
+                       w_ctr(minor, r_ctr(minor) & ~2);
                        lp_select_in_high(minor);
+                       parport_release(lp_table[minor].dev);
                        return temp-buf; /* end the read at timeout */
                }
                counter=0;
                z=lp_read_nibble(minor);
-               outb((inb(LP_C(minor)) & ~2), LP_C(minor)); /* AutoFeed low */
+               w_ctr(minor, r_ctr(minor) & ~2); /* AutoFeed low */
                do {
-                       status=(LP_S(minor) & 0x40);
+                       status=(r_str(minor) & 0x40);
                        udelay(20);
                        counter++;
                        if (need_resched)
@@ -409,6 +427,7 @@ static long lp_read(struct inode * inode, struct file * file,
 #endif
                        if (current->signal & ~current->blocked) {
                                lp_select_in_high(minor);
+                               parport_release(lp_table[minor].dev);
                                if (temp !=buf)
                                        return temp-buf;
                                else 
@@ -426,14 +445,15 @@ static long lp_read(struct inode * inode, struct file * file,
                } else Byte=z;
        }
        lp_select_in_high(minor);
+       parport_release(lp_table[minor].dev);
        return temp-buf;        
 }
 
+#endif
+
 static int lp_open(struct inode * inode, struct file * file)
 {
        unsigned int minor = MINOR(inode->i_rdev);
-       int ret;
-       unsigned int irq;
 
        if (minor >= LP_NO)
                return -ENXIO;
@@ -450,7 +470,7 @@ static int lp_open(struct inode * inode, struct file * file)
           a non-standard manner.  This is strictly a Linux hack, and
           should most likely only ever be used by the tunelp application. */
        if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
-               int status = LP_S(minor);
+               int status = r_str(minor);
                if (status & LP_POUTPA) {
                        printk(KERN_INFO "lp%d out of paper\n", minor);
                        MOD_DEC_USE_COUNT;
@@ -465,24 +485,13 @@ static int lp_open(struct inode * inode, struct file * file)
                        return -EIO;
                }
        }
-
-       if ((irq = LP_IRQ(minor))) {
+       if (LP_IRQ(minor) > 0) {
                lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
                if (!lp_table[minor].lp_buffer) {
                        MOD_DEC_USE_COUNT;
                        return -ENOMEM;
                }
-
-               ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
-               if (ret) {
-                       kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
-                       lp_table[minor].lp_buffer = NULL;
-                       printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
-                       MOD_DEC_USE_COUNT;
-                       return ret;
-               }
        }
-
        LP_F(minor) |= LP_BUSY;
        return 0;
 }
@@ -493,11 +502,9 @@ static int lp_release(struct inode * inode, struct file * file)
        unsigned int irq;
 
        if ((irq = LP_IRQ(minor))) {
-               free_irq(irq, NULL);
                kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
                lp_table[minor].lp_buffer = NULL;
        }
-
        LP_F(minor) &= ~LP_BUSY;
        MOD_DEC_USE_COUNT;
        return 0;
@@ -545,49 +552,9 @@ static int lp_ioctl(struct inode *inode, struct file *file,
                case LPWAIT:
                        LP_WAIT(minor) = arg;
                        break;
-               case LPSETIRQ: {
-                       int oldirq;
-                       int newirq = arg;
-                       struct lp_struct *lp = &lp_table[minor];
-
-                       if (!suser())
-                               return -EPERM;
-
-                       oldirq = LP_IRQ(minor);
-
-                       /* Allocate buffer now if we are going to need it */
-                       if (!oldirq && newirq) {
-                               lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
-                               if (!lp->lp_buffer)
-                                       return -ENOMEM;
-                       }
-
-                       if (oldirq) {
-                               free_irq(oldirq, NULL);
-                       }
-                       if (newirq) {
-                               /* Install new irq */
-                               if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer", NULL))) {
-                                       if (oldirq) {
-                                               /* restore old irq */
-                                               request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
-                                       } else {
-                                               /* We don't need the buffer */
-                                               kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
-                                               lp->lp_buffer = NULL;
-                                       }
-                                       return retval;
-                               }
-                       }
-                       if (oldirq && !newirq) {
-                               /* We don't need the buffer */
-                               kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
-                               lp->lp_buffer = NULL;
-                       }
-                       LP_IRQ(minor) = newirq;
-                       lp_reset(minor);
+               case LPSETIRQ: 
+                       return -EINVAL;
                        break;
-               }
                case LPGETIRQ:
                        retval = verify_area(VERIFY_WRITE, (void *) arg,
                            sizeof(int));
@@ -601,7 +568,7 @@ static int lp_ioctl(struct inode *inode, struct file *file,
                        if (retval)
                                return retval;
                        else {
-                               int status = LP_S(minor);
+                               int status = r_str(minor);
                                copy_to_user((int *) arg, &status, sizeof(int));
                        }
                        break;
@@ -638,7 +605,11 @@ static int lp_ioctl(struct inode *inode, struct file *file,
 
 static struct file_operations lp_fops = {
        lp_lseek,
+#ifdef CONFIG_PRINTER_READBACK
        lp_read,
+#else
+       NULL,
+#endif
        lp_write,
        NULL,           /* lp_readdir */
        NULL,           /* lp_poll */
@@ -648,130 +619,108 @@ static struct file_operations lp_fops = {
        lp_release
 };
 
-static int lp_probe(int offset)
-{
-       int base, size;
-       unsigned int testvalue;
-
-       base = LP_B(offset);
-       if (base == 0) 
-               return -1;              /* de-configured by command line */
-       if (LP_IRQ(offset) > 15) 
-               return -1;              /* bogus interrupt value */
-       size = (base == 0x3bc)? 3 : 8;
-       if (check_region(base, size) < 0)
-               return -1;
-       /* write to port & read back to check */
-       outb_p(LP_DUMMY, base);
-       udelay(LP_DELAY);
-       testvalue = inb_p(base);
-       if (testvalue == LP_DUMMY) {
-               LP_F(offset) |= LP_EXIST;
-               lp_reset(offset);
-               printk(KERN_INFO "lp%d at 0x%04x, ", offset, base);
-               request_region(base, size, "lp");
-               if (LP_IRQ(offset))
-                       printk("(irq = %d)\n", LP_IRQ(offset));
-               else
-                       printk("(polling)\n");
-               return 1;
-       } else
-               return 0;
-}
+static int parport[LP_NO] = { -1, };
+
+#ifdef MODULE
+#define lp_init init_module
+MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
 
-/* Command line parameters:
+#else
 
-   When the lp driver is built in to the kernel, you may use the
-   LILO/LOADLIN command line to set the port addresses and interrupts
-   that the driver will use.
+static int parport_ptr = 0;
 
-   Syntax:     lp=port0[,irq0[,port1[,irq1[,port2[,irq2]]]]]
+void lp_setup(char *str, int *ints)
+{
+       /* Ugh. */
+       if (!strncmp(str, "parport", 7)) {
+               int n = simple_strtoul(str+7, NULL, 10);
+               if (parport_ptr < LP_NO)
+                       parport[parport_ptr++] = n;
+               else
+                       printk(KERN_INFO "lp: too many ports, %s ignored.\n",
+                              str);
+       } else if (!strcmp(str, "auto")) {
+               parport[0] = -3;
+       } else {
+               if (ints[0] == 0 || ints[1] == 0) {
+                       /* disable driver on "parport=" or "parport=0" */
+                       parport[0] = -2;
+               } else {
+                       printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
+               }
+       }
+}
 
-   For example:   lp=0x378,0   or   lp=0x278,5,0x378,7
+#endif
 
-   Note that if this feature is used, you must specify *all* the ports
-   you want considered, there are no defaults.  You can disable a
-   built-in driver with lp=0 .
+int lp_wakeup(void *ref)
+{
+       struct lp_struct *lp_dev = (struct lp_struct *) ref;
 
-*/
+       if (!lp_dev->lp_wait_q)
+               return 1;       /* Wake up whom? */
 
-void   lp_setup(char *str, int *ints)
+       /* Claim the Parport */
+       if (parport_claim(lp_dev->dev))
+               return 1;       /* Shouldn't happen */
 
-{      
-        LP_B(0)   = ((ints[0] > 0) ? ints[1] : 0 );
-        LP_IRQ(0) = ((ints[0] > 1) ? ints[2] : 0 );
-        LP_B(1)   = ((ints[0] > 2) ? ints[3] : 0 );
-        LP_IRQ(1) = ((ints[0] > 3) ? ints[4] : 0 );
-        LP_B(2)   = ((ints[0] > 4) ? ints[5] : 0 );
-        LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : 0 );
+       wake_up(&lp_dev->lp_wait_q);
+       return 0;
 }
 
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-#define lp_init init_module
-#endif
+static int inline lp_searchfor(int list[], int a)
+{
+       int i;
+       for (i = 0; i < LP_NO && list[i] != -1; i++) {
+               if (list[i] == a) return 1;
+       }
+       return 0;
+}
 
 int lp_init(void)
 {
-       int offset = 0;
        int count = 0;
-#ifdef MODULE
-       int failed = 0;
-#endif
-
-       if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
-               printk("lp: unable to get major %d\n", LP_MAJOR);
-               return -EIO;
+       struct parport *pb;
+  
+       if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
+               printk("lp: unable to get major %d\n", LP_MAJOR);
+               return -EIO;
        }
-#ifdef MODULE
-       /* When user feeds parameters, use them */
-       for (offset=0; offset < LP_NO; offset++) {
-               int specified=0;
 
-               if (io[offset] != 0) {
-                       LP_B(offset) = io[offset];
-                       specified++;
-               }
-               if (irq[offset] != 0) {
-                       LP_IRQ(offset) = irq[offset];
-                       specified++;
-               }
-               if (specified) {
-                       if (lp_probe(offset) <= 0) {
-                               printk(KERN_INFO "lp%d: Not found\n", offset);
-                               failed++;
-                       } else
-                               count++;
+       if (parport[0] == -2) return 0;
+
+       pb = parport_enumerate();
+
+       while (pb) {
+               if (parport[0] == -1 || lp_searchfor(parport, count) ||
+                   (parport[0] == -3 &&
+                    pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
+                       lp_table[count].dev =
+                           parport_register_device(pb, dev_name, NULL, 
+                                           lp_wakeup,
+                                           lp_interrupt, PARPORT_DEV_TRAN,
+                                           (void *) &lp_table[count]);
+                       lp_table[count].flags |= LP_EXIST;
+                       printk(KERN_INFO "lp%d: using %s at 0x%x, ", count,
+                              pb->name, pb->base);
+                       if (pb->irq == -1)
+                               printk("polling.\n");
+                       else
+                               printk("irq %d.\n", pb->irq);
                }
-       }
-       /* Successful specified devices increase count
-        * Unsuccessful specified devices increase failed
-        */
-       if (count)
-               return 0;
-       if (failed) {
-               printk(KERN_INFO "lp: No override devices found.\n");
-               unregister_chrdev(LP_MAJOR,"lp");
-               return -EIO;
-       }
-       /* Only get here if there were no specified devices. To continue 
-        * would be silly since the above code has scribbled all over the
-        * probe list.
-        */
-#endif
-       /* take on all known port values */
-       for (offset = 0; offset < LP_NO; offset++) {
-               int ret = lp_probe(offset);
-               if (ret < 0)
-                       continue;
-               count += ret;
-       }
-       if (count == 0)
-               printk("lp: Driver configured but no interfaces found.\n");
-
-       return 0;
+               if (++count == LP_NO)
+                       break;
+               pb = pb->next;
+       }
+
+       /* Successful specified devices increase count
+        * Unsuccessful specified devices increase failed
+        */
+       if (count)
+               return 0;
+  
+       printk(KERN_INFO "lp: driver loaded but no devices found\n");
+       return 1;
 }
 
 #ifdef MODULE
@@ -779,13 +728,11 @@ void cleanup_module(void)
 {
        int offset;
 
-       unregister_chrdev(LP_MAJOR,"lp");
+       unregister_chrdev(LP_MAJOR, "lp");
        for (offset = 0; offset < LP_NO; offset++) {
-               int base, size;
-               base = LP_B(offset);
-               size = (base == 0x3bc)? 3 : 8;
-               if (LP_F(offset) & LP_EXIST)
-                       release_region(LP_B(offset),size);
+               if (lp_table[offset].dev == NULL)
+                       continue;
+               parport_unregister_device(lp_table[offset].dev);
        }
 }
 #endif
index 005f48a17872c15d0dacfd3685373ea692cd9740..5cfcb589ddf7ce55c3e7d634aae538d097d4f3f2 100644 (file)
  *  1.5.7 July 22, 1996 Martin Mares: CLOCAL fix, pcxe_table clearing.
  *             David Nugent: Bug in pcxe_open.
  *             Brian J. Murrell: Modem Control fixes, Majors correctly assigned
+ *  1.6.1 April 6, 1997 Bernhard Kaindl: fixed virtual memory access for 2.1
+ *              i386-kernels and use on other archtitectures, Allowing use
+ *              as module, added module parameters, added switch to enable
+ *              verbose messages to assist user during card configuration.
+ *              Currently only tested on a PC/Xi card, but should work on Xe
+ *              and Xeve also.
  *
  */
-#undef MODULE
-/* Module code is broken right now. Don't enable this unless you want to fix it */
 
 #undef SPEED_HACK
 /* If you define SPEED_HACK then you get the following Baudrate translation
 #include <linux/string.h>
 
 #ifndef MODULE
-
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
-
-#include <linux/ctype.h>
+#include <linux/ctype.h> /* We only need it for parsing the "digi="-line */
 #endif
 
 #include <asm/system.h>
@@ -81,8 +79,7 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 
-#define VERSION        "1.5.7"
-static char *banner = "Digiboard PC/X{i,e,eve} driver v1.5.7";
+#define VERSION        "1.6.1"
 
 #include "digi.h"
 #include "fep.h"
@@ -90,11 +87,45 @@ static char *banner = "Digiboard PC/X{i,e,eve} driver v1.5.7";
 #include "digi_fep.h"
 #include "digi_bios.h"
 
-/* Define one default setting if no digi= config line is used.
- * Default is ALTPIN = ON, PC/16E, 16 ports, I/O 200h Memory 0D0000h
+/*
+ * Define one default setting if no digi= config line is used.
+ * Default is altpin = disabled, 16 ports, I/O 200h, Memory 0D0000h
  */
-static struct board_info boards[MAX_DIGI_BOARDS] = { { ENABLED, 0, ON, 16, 0x200, 0xd0000,0 } };
+static struct board_info boards[MAX_DIGI_BOARDS] = { {
+/* Board is enabled       */   ENABLED,
+/* Type is auto-detected  */   0,
+/* altping is disabled    */    DISABLED,
+/* number of ports = 16   */   16,
+/* io address is 0x200    */   0x200,
+/* card memory at 0xd0000 */   0xd0000,
+/* first minor device no. */   0
+} };
  
+static int verbose = 0;
+static int debug   = 0;
+
+#ifdef MODULE
+/* Variables for insmod */
+static int io[]           = {0, 0, 0, 0};
+static int membase[]      = {0, 0, 0, 0};
+static int memsize[]      = {0, 0, 0, 0};
+static int altpin[]       = {0, 0, 0, 0};
+static int numports[]     = {0, 0, 0, 0};
+
+# if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Bernhard Kaindl");
+MODULE_DESCRIPTION("Digiboard PC/X{i,e,eve} driver");
+MODULE_PARM(verbose,     "i");
+MODULE_PARM(debug,       "i");
+MODULE_PARM(io,          "1-4i");
+MODULE_PARM(membase,     "1-4i");
+MODULE_PARM(memsize,     "1-4i");
+MODULE_PARM(altpin,      "1-4i");
+MODULE_PARM(numports,    "1-4i");
+# endif
+
+#endif MODULE
+
 static int numcards = 1;
 static int nbdevs = 0;
  
@@ -159,19 +190,14 @@ static inline void assertmemoff(struct channel *ch);
 
 /* function definitions */
 #ifdef MODULE
-int            init_module(void);
-void           cleanup_module(void);
 
 /*
- *     Loadable module initialization stuff.
+ * pcxe_init() is our init_module():
  */
+#define pcxe_init init_module
 
-int init_module()
-{
-
-       return pcxe_init();
+void   cleanup_module(void);
 
-}
 
 /*****************************************************************************/
 
@@ -184,7 +210,7 @@ void cleanup_module()
        struct board_info *bd;
        struct channel *ch;
 
-       printk(KERN_INFO "Unloading PC/Xx: version %s\n", VERSION);
+       printk(KERN_NOTICE "Unloading PC/Xx version %s\n", VERSION);
 
        save_flags(flags);
        cli();
@@ -287,7 +313,7 @@ static inline void assertmemoff(struct channel *ch)
 static inline void pcxe_sched_event(struct channel *info, int event)
 {
        info->event |= 1 << event;
-       queue_task_irq_off(&info->tqueue, &tq_pcxx);
+       queue_task(&info->tqueue, &tq_pcxx);
        mark_bh(DIGI_BH);
 }
 
@@ -861,12 +887,12 @@ static void pcxe_flush_chars(struct tty_struct *tty)
        }
 }
 
-/* Flag if lilo configuration option is used. If so the
- * default settings are removed
- */
-
-static int liloconfig=0;
+#ifndef MODULE
 
+/*
+ * Driver setup function when linked into the kernel to optionally parse multible
+ * "digi="-lines and initialize the driver at boot time. No probing.
+ */
 void pcxx_setup(char *str, int *ints)
 {
 
@@ -875,11 +901,7 @@ void pcxx_setup(char *str, int *ints)
        char              *temp, *t2;
        unsigned          len;
 
-#if 0
-       if (!numcards)
-               memset(&boards, 0, sizeof(boards));
-#endif
-       if (liloconfig==0) { liloconfig=1;numcards=0; }
+       numcards=0;
 
        memset(&board, 0, sizeof(board));
 
@@ -921,7 +943,6 @@ void pcxx_setup(char *str, int *ints)
                                return;
                }
 
-       
        while (str && *str) 
        {
                /* find the next comma or terminator */
@@ -985,11 +1006,6 @@ void pcxx_setup(char *str, int *ints)
 
                        case 4:
                                t2 = str;
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
                                while (isdigit(*t2))
                                        t2++;
 
@@ -998,39 +1014,27 @@ void pcxx_setup(char *str, int *ints)
                                        printk("PC/Xx: Invalid port count %s\n", str);
                                        return;
                                }
-#endif
 
                                board.numports = simple_strtoul(str, NULL, 0);
                                last = i;
                                break;
 
                        case 5:
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
                                t2 = str;
                                while (isxdigit(*t2))
                                        t2++;
 
                                if (*t2)
                                {
-                                       printk("PC/Xx: Invalid port count %s\n", str);
+                                       printk("PC/Xx: Invalid io port address %s\n", str);
                                        return;
                                }
-#endif
 
                                board.port = simple_strtoul(str, NULL, 16);
                                last = i;
                                break;
 
                        case 6:
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
                                t2 = str;
                                while (isxdigit(*t2))
                                        t2++;
@@ -1040,7 +1044,6 @@ void pcxx_setup(char *str, int *ints)
                                        printk("PC/Xx: Invalid memory base %s\n", str);
                                        return;
                                }
-#endif
 
                                board.membase = simple_strtoul(str, NULL, 16);
                                last = i;
@@ -1075,33 +1078,97 @@ void pcxx_setup(char *str, int *ints)
        /* yeha!  string parameter was successful! */
        numcards++;
 }
+#endif
 
-
+/*
+ * function to initialize the driver with the given parameters, which are either
+ * the default values from this file or the parameters given at boot.
+ */
 int pcxe_init(void)
 {
-       ulong flags, memory_seg=0, memory_size;
-       int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
+       ulong memory_seg=0, memory_size=0;
+       int lowwater, enabled_cards=0, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
        unchar *fepos, *memaddr, *bios, v;
        volatile struct global_data *gd;
-       struct board_info *bd;
        volatile struct board_chan *bc;
+       struct board_info *bd;
        struct channel *ch;
 
-       printk("%s\n", banner);
+       printk(KERN_NOTICE "Digiboard PC/X{i,e,eve} driver v%s\n", VERSION);
+
+#ifdef MODULE
+       for (i = 0; i < 4; i++) {
+               if (io[i]) {
+                       numcards = 0;
+                       break;
+               }
+       }
+       if (numcards == 0) {
+               int first_minor = 0;
+
+               for (i = 0; i < 4; i++) {
+                       if (io[i] == 0) {
+                               boards[i].port    = 0;
+                               boards[i].status  = DISABLED;
+                       }
+                       else {
+                               boards[i].port         = (ushort)io[i];
+                               boards[i].status       = ENABLED;
+                               boards[i].first_minor  = first_minor;
+                               numcards=i+1;
+                       }
+                       if (membase[i])
+                               boards[i].membase = (ulong)membase[i];
+                       else
+                               boards[i].membase = 0xD0000;
+
+                       if (memsize[i])
+                               boards[i].memsize = (ulong)(memsize[i] * 1024);
+                       else
+                               boards[i].memsize = 0;
+
+                       if (altpin[i])
+                               boards[i].altpin  = ON;
+                       else
+                               boards[i].altpin  = OFF;
+
+                       if (numports[i])
+                               boards[i].numports  = (ushort)numports[i];
+                       else
+                               boards[i].numports  = 16;
+
+                       first_minor += boards[i].numports;
+               }
+       }
+#endif
 
        if (numcards <= 0)
        {
-               printk("PC/Xx: No cards configured, exiting.\n");
-               return(0);
+               printk("PC/Xx: No cards configured, driver not active.\n");
+               return -EIO;
        }
+#if 1
+       if (debug)
+           for (i = 0; i < numcards; i++) {
+                   printk("Card %d:status=%d, port=0x%x, membase=0x%lx, memsize=0x%lx, altpin=%d, numports=%d, first_minor=%d\n",
+                           i+1,
+                           boards[i].status,
+                           boards[i].port,
+                           boards[i].membase,
+                           boards[i].memsize,
+                           boards[i].altpin,
+                           boards[i].numports,
+                           boards[i].first_minor);
+           }
+#endif
 
        for (i=0;i<numcards;i++)
                nbdevs += boards[i].numports;
 
        if (nbdevs <= 0)
        {
-               printk("PC/Xx: No devices activated, exiting.\n");
-               return(0);
+               printk("PC/Xx: No devices activated, driver not active.\n");
+               return -EIO;
        }
 
        /*
@@ -1175,22 +1242,22 @@ int pcxe_init(void)
        pcxe_callout.subtype = SERIAL_TYPE_CALLOUT;
        pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 
-       save_flags(flags);
-       cli();
-
        for(crd=0; crd < numcards; crd++) {
                bd = &boards[crd];
                outb(FEPRST, bd->port);
                pcxxdelay(1);
 
                for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) {
-                       if(i > 1000) {
+                       if(i > 100) {
                                printk("PC/Xx: Board not found at port 0x%x! Check switch settings.\n",
                                        bd->port);
                                bd->status = DISABLED;
                                break;
                        }
-                       pcxxdelay(1);
+#ifdef MODULE
+                       schedule();
+#endif
+                       pcxxdelay(10);
                }
                if(bd->status == DISABLED)
                        continue;
@@ -1237,18 +1304,38 @@ int pcxe_init(void)
                                memory_size = 0x10000;
                        }
                }
+               if (verbose)
+                       printk("Configuring card %d as a %s %ldK card. io=0x%x, mem=%lx-%lx\n",
+                               crd+1, board_desc[bd->type], memory_size/1024,
+                               bd->port,bd->membase,bd->membase+memory_size-1);
+
+               if (boards[crd].memsize == 0)
+                       boards[crd].memsize = memory_size;
+               else
+                       if (boards[crd].memsize != memory_size) {
+                           printk("PC/Xx: memory size mismatch:supplied=%lx(%ldK) probed=%ld(%ldK)\n",
+                                   boards[crd].memsize, boards[crd].memsize / 1024,
+                                   memory_size, memory_size / 1024);
+                           continue;
+                       }
 
-               memaddr = (unchar *) bd->membase;
+               memaddr = (unchar *)phys_to_virt(bd->membase);
+
+               if (verbose)
+                       printk("Resetting board and testing memory access:");
 
                outb(FEPRST|FEPMEM, bd->port);
 
                for(i=0; (inb(bd->port) & FEPMASK) != (FEPRST|FEPMEM); i++) {
-                       if(i > 10000) {
-                               printk("PC/Xx: %s not resetting at port 0x%x! Check switch settings.\n",
+                       if(i > 1000) {
+                               printk("\nPC/Xx: %s not resetting at port 0x%x! Check switch settings.\n",
                                        board_desc[bd->type], bd->port);
                                bd->status = DISABLED;
                                break;
                        }
+#ifdef MODULE
+                       schedule();
+#endif
                        pcxxdelay(1);
                }
                if(bd->status == DISABLED)
@@ -1265,6 +1352,8 @@ int pcxe_init(void)
                        bd->status = DISABLED;
                        continue;
                }
+               if (verbose)
+                       printk(" done.\n");
 
                for(i=0; i < 16; i++) {
                        memaddr[MISCGLOBAL+i] = 0;
@@ -1273,19 +1362,36 @@ int pcxe_init(void)
                if(bd->type == PCXI || bd->type == PCXE) {
                        bios = memaddr + BIOSCODE + ((0xf000 - memory_seg) << 4);
 
+                       if (verbose)
+                               printk("Downloading BIOS to 0x%lx:", virt_to_phys(bios));
+
                        memcpy(bios, pcxx_bios, pcxx_nbios);
 
+                       if (verbose)
+                               printk(" done.\n");
+
                        outb(FEPMEM, bd->port);
 
-                       for(i=0; i <= 10000; i++) {
+                       if (verbose)
+                               printk("Waiting for BIOS to become ready");
+
+                       for(i=1; i <= 30; i++) {
                                if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
                                        goto load_fep;
                                }
-                               pcxxdelay(1);
+                               if (verbose) {
+                                       printk(".");
+                                       if (i % 50 == 0)
+                                               printk("\n");
+                               }
+#ifdef MODULE
+                               schedule();
+#endif
+                               pcxxdelay(50);
                        }
 
-                       printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
-                                                       board_desc[bd->type], bd->port);
+                       printk("\nPC/Xx: BIOS download failed for board at 0x%x(addr=%lx-%lx)!\n",
+                                                       bd->port, bd->membase, bd->membase+bd->memsize);
                        bd->status = DISABLED;
                        continue;
                }
@@ -1299,14 +1405,22 @@ int pcxe_init(void)
                        outb(FEPCLR, bd->port);
                        memwinon(bd,0);
 
-                       for(i=0; i <= 10000; i++) {
+                       for(i=0; i <= 1000; i++) {
                                if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
                                        goto load_fep;
                                }
-                               pcxxdelay(1);
+                               if (verbose) {
+                                       printk(".");
+                                       if (i % 50 == 0)
+                                               printk("\n");
+                               }
+#ifdef MODULE
+                               schedule();
+#endif
+                               pcxxdelay(10);
                        }
 
-                       printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
+                       printk("\nPC/Xx: BIOS download failed on the %s at 0x%x!\n",
                                board_desc[bd->type], bd->port);
                        bd->status = DISABLED;
                        continue;
@@ -1317,10 +1431,16 @@ load_fep:
                if(bd->type == PCXEVE)
                        fepos = memaddr + (FEPCODE & 0x1fff);
 
+               if (verbose)
+                       printk(" ok.\nDownloading FEP/OS to 0x%lx:", virt_to_phys(fepos));
+
                memwinon(bd, (FEPCODE >> 13));
                memcpy(fepos, pcxx_cook, pcxx_ncook);
                memwinon(bd, 0);
 
+               if (verbose)
+                       printk(" done.\n");
+
                *(ushort *)((ulong)memaddr + MBOX +  0) = 2;
                *(ushort *)((ulong)memaddr + MBOX +  2) = memory_seg + FEPCODESEG;
                *(ushort *)((ulong)memaddr + MBOX +  4) = 0;
@@ -1338,12 +1458,18 @@ load_fep:
                                bd->status = DISABLED;
                                break;
                        }
+#ifdef MODULE
+                       schedule();
+#endif
                        pcxxdelay(1);
                }
 
                if(bd->status == DISABLED)
                        continue;
 
+               if (verbose)
+                       printk("Waiting for FEP/OS to become ready");
+
                *(ushort *)(memaddr + FEPSTAT) = 0;
                *(ushort *)(memaddr + MBOX + 0) = 1;
                *(ushort *)(memaddr + MBOX + 2) = FEPCODESEG;
@@ -1353,18 +1479,29 @@ load_fep:
                outb(FEPCLR, bd->port);
                memwinon(bd, 0);
 
-               for(i=0; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
-                       if(i > 10000) {
-                               printk("PC/Xx: FEP/OS download failed on the %s at 0x%x!\n",
+               for(i=1; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
+                       if(i > 1000) {
+                               printk("\nPC/Xx: FEP/OS download failed on the %s at 0x%x!\n",
                                        board_desc[bd->type], bd->port);
                                bd->status = DISABLED;
                                break;
                        }
+                       if (verbose) {
+                               printk(".");
+                               if (i % 50 == 0)
+                                       printk("\n%5d",i/50);
+                       }
+#ifdef MODULE
+                       schedule();
+#endif
                        pcxxdelay(1);
                }
                if(bd->status == DISABLED)
                        continue;
 
+               if (verbose)
+                       printk(" ok.\n");
+
                ch = digi_channels+bd->first_minor;
                pcxxassert(ch < digi_channels+nbdevs, "ch out of range");
 
@@ -1423,9 +1560,7 @@ load_fep:
 
                        ch->txbufsize = bc->tmax + 1;
                        ch->rxbufsize = bc->rmax + 1;
-
                        ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL);
-
                        lowwater = ch->txbufsize >= 2000 ? 1024 : ch->txbufsize/2;
                        fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
                        fepcmd(ch, SRXLWATER, ch->rxbufsize/4, 0, 10, 0);
@@ -1454,17 +1589,25 @@ load_fep:
                        ch->normal_termios = pcxe_driver.init_termios;
                        ch->open_wait = 0;
                        ch->close_wait = 0;
-                       /* zero out flags as it is unassigned at this point
-                        * brian@ilinx.com
-                        */
                        ch->asyncflags = 0;
                }
 
-               printk("PC/Xx: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
-                       board_desc[bd->type], board_mem[bd->type], bd->port, 
-                       bd->membase, bd->numports);
+               if (verbose)
+                   printk("Card No. %d ready: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
+                           crd+1, board_desc[bd->type], board_mem[bd->type], bd->port, 
+                           bd->membase, bd->numports);
+               else
+                   printk("PC/Xx: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
+                           board_desc[bd->type], board_mem[bd->type], bd->port, 
+                           bd->membase, bd->numports);
 
                memwinoff(bd, 0);
+               enabled_cards++;
+       }
+
+       if (enabled_cards <= 0) {
+               printk(KERN_NOTICE "PC/Xx: No cards enabled, no driver.\n");
+               return -EIO;
        }
 
        if(tty_register_driver(&pcxe_driver))
@@ -1473,15 +1616,13 @@ load_fep:
        if(tty_register_driver(&pcxe_callout))
                panic("Couldn't register PC/Xe callout");
 
-#if 0
-       loops_per_sec = save_loops_per_sec;  /* reset it to what it should be */
-#endif
-
        /*
         * Start up the poller to check for events on all enabled boards
         */
        timer_active |= 1 << DIGI_TIMER;
-       restore_flags(flags);
+
+       if (verbose)
+               printk(KERN_NOTICE "PC/Xx: Driver with %d card(s) ready.\n", enabled_cards);
 
        return 0;
 }
@@ -1546,7 +1687,7 @@ static void doevent(int crd)
 
        while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) {
                assertgwinon(chan0);
-               eventbuf = (volatile unchar *)bd->membase + tail + ISTART;
+               eventbuf = (volatile unchar *)phys_to_virt(bd->membase + tail + ISTART);
                channel = eventbuf[0];
                event = eventbuf[1];
                mstat = eventbuf[2];
@@ -1657,7 +1798,7 @@ fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds,
 
        assertgwinon(ch);
 
-       memaddr = (unchar *)ch->board->membase;
+       memaddr = (unchar *)phys_to_virt(ch->board->membase);
        head = ch->mailbox->cin;
 
        if(head >= (CMAX-CSTART) || (head & 03)) {
@@ -1698,6 +1839,7 @@ fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds,
 
                if(n <= ncmds * (sizeof(short)*4))
                        break;
+               /* Seems not to be good here: schedule(); */
        }
 }
 
index 936741cee2565b34a716d0e6fd2a84cf1f684d85..e688a8be24090a92d50394f630e1864114a6ab49 100644 (file)
@@ -49,6 +49,7 @@ struct board_info     {
        ushort numports;
        ushort port;
        ulong  membase;
+       ulong  memsize;
        ushort first_minor;
 };
 
index 8827dc7550598dc5bd88d22f7a7aa5505da05e5a..459d2e4686cb14b262248031308968ce1bb3acf6 100644 (file)
@@ -118,8 +118,9 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user)
        int sel_mode, new_sel_start, new_sel_end, spc;
        char *bp, *obp;
        int i, ps, pe;
-
+       
        do_unblank_screen();
+       poke_blanked_console();
 
        { unsigned short *args, xs, ys, xe, ye;
 
@@ -291,7 +292,7 @@ int paste_selection(struct tty_struct *tty)
        
        if (!bp || !c)
                return 0;
-       do_unblank_screen();
+       poke_blanked_console();
        add_wait_queue(&vt->paste_wait, &wait);
        do {
                current->state = TASK_INTERRUPTIBLE;
index 72ad883a0ccdee2343a52d0868c84d484d243d16..86b441c4a07c6b976048bb4d6fb5f857755ed61e 100644 (file)
  * 
  * Restrict vt switching via ioctl()
  *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Move console and virtual terminal code to more apropriate files,
+ * implement CONFIG_VT and generalize console device interface.
+ *     -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
  */
 
 #include <linux/config.h>
index f41caf2fa19fdc055b6d214a58512c7c7ed33adf..2b8378c0d77dde754dc0d8947e8cc1c2b967343f 100644 (file)
@@ -6,6 +6,7 @@
  *  Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
  *  Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
  *  Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
+ *  Some code moved for less code duplication - Andi Kleen - Mar 1997
  */
 
 #include <linux/types.h>
@@ -189,7 +190,271 @@ _kd_mksound(unsigned int hz, unsigned int ticks)
 }
 
 void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_mksound;
-       
+
+
+#define i (tmp.kb_index)
+#define s (tmp.kb_table)
+#define v (tmp.kb_value)
+static inline int
+do_kdsk_ioctl(int cmd, struct kbentry *user_kbe, int perm, struct kbd_struct *kbd)
+{
+       struct kbentry tmp;
+       ushort *key_map, val, ov;
+
+       if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+               return -EFAULT;
+       if (i >= NR_KEYS || s >= MAX_NR_KEYMAPS)
+               return -EINVAL; 
+
+       switch (cmd) {
+       case KDGKBENT:
+               key_map = key_maps[s];
+               if (key_map) {
+                   val = U(key_map[i]);
+                   if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+                       val = K_HOLE;
+               } else
+                   val = (i ? K_HOLE : K_NOSUCHMAP);
+               return __put_user(val, &user_kbe->kb_value);
+       case KDSKBENT:
+               if (!perm)
+                       return -EPERM;
+               if (!i && v == K_NOSUCHMAP) {
+                       /* disallocate map */
+                       key_map = key_maps[s];
+                       if (s && key_map) {
+                           key_maps[s] = 0;
+                           if (key_map[0] == U(K_ALLOCATED)) {
+                                       kfree_s(key_map, sizeof(plain_map));
+                                       keymap_count--;
+                           }
+                       }
+                       break;
+               }
+
+               if (KTYP(v) < NR_TYPES) {
+                   if (KVAL(v) > max_vals[KTYP(v)])
+                               return -EINVAL;
+               } else
+                   if (kbd->kbdmode != VC_UNICODE)
+                               return -EINVAL;
+
+               /* assignment to entry 0 only tests validity of args */
+               if (!i)
+                       break;
+
+               if (!(key_map = key_maps[s])) {
+                       int j;
+
+                       if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
+                               return -EPERM;
+
+                       key_map = (ushort *) kmalloc(sizeof(plain_map),
+                                                    GFP_KERNEL);
+                       if (!key_map)
+                               return -ENOMEM;
+                       key_maps[s] = key_map;
+                       key_map[0] = U(K_ALLOCATED);
+                       for (j = 1; j < NR_KEYS; j++)
+                               key_map[j] = U(K_HOLE);
+                       keymap_count++;
+               }
+               ov = U(key_map[i]);
+               if (v == ov)
+                       break;  /* nothing to do */
+               /*
+                * Attention Key.
+                */
+               if (((ov == K_SAK) || (v == K_SAK)) && !suser())
+                       return -EPERM;
+               key_map[i] = U(v);
+               if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+                       compute_shiftstate();
+               break;
+       }
+       return 0;
+}
+#undef i
+#undef s
+#undef v
+
+static inline int 
+do_kbkeycode_ioctl(int cmd, struct kbkeycode *user_kbkc, int perm)
+{
+       struct kbkeycode tmp;
+       int kc = 0;
+
+       if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+               return -EFAULT;
+       switch (cmd) {
+       case KDGETKEYCODE:
+               kc = getkeycode(tmp.scancode);
+               if (kc >= 0)
+                       kc = __put_user(kc, &user_kbkc->keycode);
+               break;
+       case KDSETKEYCODE:
+               if (!perm)
+                       return -EPERM;
+               kc = setkeycode(tmp.scancode, tmp.keycode);
+               break;
+       }
+       return kc;
+}
+
+static inline int
+do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm)
+{
+       struct kbsentry tmp;
+       char *p;
+       u_char *q;
+       int sz;
+       int delta;
+       char *first_free, *fj, *fnw;
+       int j, k, i = 0;
+
+       /* we mostly copy too much here (512bytes), but who cares ;) */
+       if (copy_from_user(&tmp, user_kdgkb, sizeof(struct kbsentry)))
+               return -EFAULT;
+       tmp.kb_string[sizeof(tmp.kb_string)-1] = '\0';
+       if (tmp.kb_func >= MAX_NR_FUNC)
+               return -EINVAL;
+
+       switch (cmd) {
+       case KDGKBSENT:
+               sz = sizeof(tmp.kb_string) - 1; /* sz should have been
+                                                 a struct member */
+               q = user_kdgkb->kb_string;
+               p = func_table[i];
+               if(p)
+                       for ( ; *p && sz; p++, sz--)
+                               __put_user(*p, q++);
+               __put_user('\0', q);
+               return ((p && *p) ? -EOVERFLOW : 0);
+       case KDSKBSENT:
+               if (!perm)
+                       return -EPERM;
+
+               q = func_table[i];
+               first_free = funcbufptr + (funcbufsize - funcbufleft);
+               for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) 
+                       ;
+               if (j < MAX_NR_FUNC)
+                       fj = func_table[j];
+               else
+                       fj = first_free;
+
+               delta = (q ? -strlen(q) : 1) + strlen(tmp.kb_string);
+               if (delta <= funcbufleft) {     /* it fits in current buf */
+                   if (j < MAX_NR_FUNC) {
+                       memmove(fj + delta, fj, first_free - fj);
+                       for (k = j; k < MAX_NR_FUNC; k++)
+                           if (func_table[k])
+                               func_table[k] += delta;
+                   }
+                   if (!q)
+                     func_table[i] = fj;
+                   funcbufleft -= delta;
+               } else {                        /* allocate a larger buffer */
+                   sz = 256;
+                   while (sz < funcbufsize - funcbufleft + delta)
+                     sz <<= 1;
+                   fnw = (char *) kmalloc(sz, GFP_KERNEL);
+                   if(!fnw)
+                     return -ENOMEM;
+
+                   if (!q)
+                     func_table[i] = fj;
+                   if (fj > funcbufptr)
+                       memmove(fnw, funcbufptr, fj - funcbufptr);
+                   for (k = 0; k < j; k++)
+                     if (func_table[k])
+                       func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+                   if (first_free > fj) {
+                       memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+                       for (k = j; k < MAX_NR_FUNC; k++)
+                         if (func_table[k])
+                           func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+                   }
+                   if (funcbufptr != func_buf)
+                     kfree_s(funcbufptr, funcbufsize);
+                   funcbufptr = fnw;
+                   funcbufleft = funcbufleft - delta + sz - funcbufsize;
+                   funcbufsize = sz;
+               }
+               strcpy(func_table[i], tmp.kb_string);
+               break;
+       }
+       return 0;
+}
+
+static inline int 
+do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm)
+{
+       int nchar;
+       struct consolefontdesc cfdarg;
+       int i = 0;
+
+       if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) 
+               return -EFAULT;
+       if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+               return -EINVAL;
+       
+       switch (cmd) {
+       case PIO_FONTX:
+               if (!perm)
+                       return -EPERM;
+               if ( cfdarg.charcount == 256 ||
+                    cfdarg.charcount == 512 ) {
+                       i = con_set_font(cfdarg.chardata,
+                               cfdarg.charcount == 512);
+                       if (i)
+                               return i;
+                       i = con_adjust_height(cfdarg.charheight);
+                       return (i <= 0) ? i : kd_size_changed(i, 0);
+               } else
+                       return -EINVAL;
+       case GIO_FONTX:
+               i = cfdarg.charcount;
+               cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256;
+               cfdarg.charheight = video_font_height;
+               __copy_to_user(user_cfd, &cfdarg,
+                           sizeof(struct consolefontdesc)); 
+               if ( cfdarg.chardata )
+               {
+                       if ( i < nchar )
+                               return -ENOMEM;
+                       return con_get_font(cfdarg.chardata);
+               } else
+                       return 0;
+       }
+       return 0;
+}
+
+static inline int 
+do_unimap_ioctl(int cmd, struct unimapdesc *user_ud,int perm)
+{
+       struct unimapdesc tmp;
+       int i = 0; 
+
+       if (copy_from_user(&tmp, user_ud, sizeof tmp))
+               return -EFAULT;
+       if (tmp.entries) {
+               i = verify_area(VERIFY_WRITE, tmp.entries, 
+                                               tmp.entry_ct*sizeof(struct unipair));
+               if (i) return i;
+       }
+       switch (cmd) {
+       case PIO_UNIMAP:
+               if (!perm)
+                       return -EPERM;
+               return con_set_unimap(tmp.entry_ct, tmp.entries);
+       case GIO_UNIMAP:
+               return con_get_unimap(tmp.entry_ct, &(user_ud->entry_ct), tmp.entries);
+       }
+       return 0;
+}
+
 /*
  * We handle the console-specific ioctl's here.  We allow the
  * capability to modify any console, not just the fg_console. 
@@ -215,7 +480,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        perm = 0;
        if (current->tty == tty || suser())
                perm = 1;
-
        kbd = kbd_table + console;
        switch (cmd) {
        case KIOCSOUND:
@@ -247,10 +512,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                /*
                 * this is naive.
                 */
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-               if (!i)
-                       put_user(KB_101, (char *) arg);
-               return i;
+               ucval = KB_101;
+               goto setchar;
 
 #ifndef __alpha__
                /*
@@ -307,10 +570,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return 0;
 
        case KDGETMODE:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-               if (!i)
-                       put_user(vt_cons[console]->vc_mode, (int *) arg);
-               return i;
+               ucval = vt_cons[console]->vc_mode;
+               goto setint;
 
        case KDMAPDISP:
        case KDUNMAPDISP:
@@ -346,15 +607,11 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return 0;
 
        case KDGKBMODE:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-               if (!i) {
-                       ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
+               ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
                                 (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
                                 (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
                                 K_XLATE);
-                       put_user(ucval, (int *) arg);
-               }
-               return i;
+               goto setint;
 
        /* this could be folded into KDSKBMODE, but for compatibility
           reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
@@ -372,253 +629,21 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return 0;
 
        case KDGKBMETA:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-               if (!i) {
-                       ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
-                                K_METABIT);
-                       put_user(ucval, (int *) arg);
-               }
-               return i;
+               ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
+       setint:
+               return put_user(ucval, (int *)arg); 
 
        case KDGETKEYCODE:
-       {
-               struct kbkeycode * const a = (struct kbkeycode *)arg;
-               unsigned int sc;
-               int kc;
-
-               i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbkeycode));
-               if (i)
-                       return i;
-               get_user(sc, &a->scancode);
-               kc = getkeycode(sc);
-               if (kc < 0)
-                       return kc;
-               put_user(kc, &a->keycode);
-               return 0;
-       }
-
        case KDSETKEYCODE:
-       {
-               struct kbkeycode * const a = (struct kbkeycode *)arg;
-               unsigned int sc, kc;
-
-               if (!perm)
-                       return -EPERM;
-               i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
-               if (i)
-                       return i;
-               get_user(sc, &a->scancode);
-               get_user(kc, &a->keycode);
-               return setkeycode(sc, kc);
-       }
+               return do_kbkeycode_ioctl(cmd, (struct kbkeycode *)arg, perm);
 
        case KDGKBENT:
-       {
-               struct kbentry * const a = (struct kbentry *)arg;
-               ushort *key_map, val;
-               u_char s;
-
-               i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
-               if (i)
-                       return i;
-               get_user(i, &a->kb_index);
-               if (i >= NR_KEYS)
-                       return -EINVAL;
-               get_user(s, &a->kb_table);
-               if (s >= MAX_NR_KEYMAPS)
-                       return -EINVAL;
-               key_map = key_maps[s];
-               if (key_map) {
-                   val = U(key_map[i]);
-                   if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
-                       val = K_HOLE;
-               } else
-                   val = (i ? K_HOLE : K_NOSUCHMAP);
-               put_user(val, &a->kb_value);
-               return 0;
-       }
-
        case KDSKBENT:
-       {
-               const struct kbentry * a = (struct kbentry *)arg;
-               ushort *key_map;
-               u_char s;
-               u_short v, ov;
-
-               if (!perm)
-                       return -EPERM;
-               i = verify_area(VERIFY_READ, (const void *)a, sizeof(struct kbentry));
-               if (i)
-                       return i;
-               get_user(i, &a->kb_index);
-               if (i >= NR_KEYS)
-                       return -EINVAL;
-               get_user(s, &a->kb_table);
-               if (s >= MAX_NR_KEYMAPS)
-                       return -EINVAL;
-               get_user(v, &a->kb_value);
-               if (!i && v == K_NOSUCHMAP) {
-                       /* disallocate map */
-                       key_map = key_maps[s];
-                       if (s && key_map) {
-                           key_maps[s] = 0;
-                           if (key_map[0] == U(K_ALLOCATED)) {
-                               kfree_s(key_map, sizeof(plain_map));
-                               keymap_count--;
-                           }
-                       }
-                       return 0;
-               }
-
-               if (KTYP(v) < NR_TYPES) {
-                   if (KVAL(v) > max_vals[KTYP(v)])
-                       return -EINVAL;
-               } else
-                   if (kbd->kbdmode != VC_UNICODE)
-                       return -EINVAL;
-
-               /* assignment to entry 0 only tests validity of args */
-               if (!i)
-                       return 0;
-
-               if (!(key_map = key_maps[s])) {
-                       int j;
-
-                       if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
-                               return -EPERM;
-
-                       key_map = (ushort *) kmalloc(sizeof(plain_map),
-                                                    GFP_KERNEL);
-                       if (!key_map)
-                               return -ENOMEM;
-                       key_maps[s] = key_map;
-                       key_map[0] = U(K_ALLOCATED);
-                       for (j = 1; j < NR_KEYS; j++)
-                               key_map[j] = U(K_HOLE);
-                       keymap_count++;
-               }
-               ov = U(key_map[i]);
-               if (v == ov)
-                       return 0;       /* nothing to do */
-               /*
-                * Only the Superuser can set or unset the Secure
-                * Attention Key.
-                */
-               if (((ov == K_SAK) || (v == K_SAK)) && !suser())
-                       return -EPERM;
-               key_map[i] = U(v);
-               if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
-                       compute_shiftstate();
-               return 0;
-       }
+               return do_kdsk_ioctl(cmd, (struct kbentry *)arg, perm, kbd);
 
        case KDGKBSENT:
-       {
-               struct kbsentry *a = (struct kbsentry *)arg;
-               char *p;
-               u_char *q;
-               int sz;
-
-               i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
-               if (i)
-                       return i;
-               get_user(i, &a->kb_func);
-               if (i >= MAX_NR_FUNC || i < 0)
-                       return -EINVAL;
-               sz = sizeof(a->kb_string) - 1; /* sz should have been
-                                                 a struct member */
-               q = a->kb_string;
-               p = func_table[i];
-               if(p)
-                       for ( ; *p && sz; p++, sz--)
-                               put_user(*p, q++);
-               put_user('\0', q);
-               return ((p && *p) ? -EOVERFLOW : 0);
-       }
-
        case KDSKBSENT:
-       {
-               struct kbsentry * const a = (struct kbsentry *)arg;
-               int delta;
-               char *first_free, *fj, *fnw;
-               int j, k, sz;
-               u_char *p;
-               char *q;
-
-               if (!perm)
-                       return -EPERM;
-               i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
-               if (i)
-                       return i;
-               get_user(i, &a->kb_func);
-               if (i >= MAX_NR_FUNC)
-                       return -EINVAL;
-               q = func_table[i];
-
-               first_free = funcbufptr + (funcbufsize - funcbufleft);
-               for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
-               if (j < MAX_NR_FUNC)
-                       fj = func_table[j];
-               else
-                       fj = first_free;
-
-               delta = (q ? -strlen(q) : 1);
-               sz = sizeof(a->kb_string);      /* sz should have been
-                                                  a struct member */
-               for (p = a->kb_string; sz; p++,sz--) {
-                       unsigned char uc;
-                       get_user(uc, p);
-                       if (!uc)
-                               break;
-                       delta++;
-               }
-               if (!sz)
-                       return -EOVERFLOW;
-               if (delta <= funcbufleft) {     /* it fits in current buf */
-                   if (j < MAX_NR_FUNC) {
-                       memmove(fj + delta, fj, first_free - fj);
-                       for (k = j; k < MAX_NR_FUNC; k++)
-                           if (func_table[k])
-                               func_table[k] += delta;
-                   }
-                   if (!q)
-                     func_table[i] = fj;
-                   funcbufleft -= delta;
-               } else {                        /* allocate a larger buffer */
-                   sz = 256;
-                   while (sz < funcbufsize - funcbufleft + delta)
-                     sz <<= 1;
-                   fnw = (char *) kmalloc(sz, GFP_KERNEL);
-                   if(!fnw)
-                     return -ENOMEM;
-
-                   if (!q)
-                     func_table[i] = fj;
-                   if (fj > funcbufptr)
-                       memmove(fnw, funcbufptr, fj - funcbufptr);
-                   for (k = 0; k < j; k++)
-                     if (func_table[k])
-                       func_table[k] = fnw + (func_table[k] - funcbufptr);
-
-                   if (first_free > fj) {
-                       memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
-                       for (k = j; k < MAX_NR_FUNC; k++)
-                         if (func_table[k])
-                           func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
-                   }
-                   if (funcbufptr != func_buf)
-                     kfree_s(funcbufptr, funcbufsize);
-                   funcbufptr = fnw;
-                   funcbufleft = funcbufleft - delta + sz - funcbufsize;
-                   funcbufsize = sz;
-               }
-               for (p = a->kb_string, q = func_table[i]; ; p++, q++) {
-                       get_user(*q, p);
-                       if (!*q)
-                               break;
-               }
-               return 0;
-       }
+               return do_kdgkb_ioctl(cmd, (struct kbsentry *)arg, perm);
 
        case KDGKBDIACR:
        {
@@ -627,8 +652,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
                if (i)
                        return i;
-               put_user(accent_table_size, &a->kb_cnt);
-               copy_to_user(a->kbdiacr, accent_table,
+               __put_user(accent_table_size, &a->kb_cnt);
+               __copy_to_user(a->kbdiacr, accent_table,
                            accent_table_size*sizeof(struct kbdiacr));
                return 0;
        }
@@ -643,23 +668,19 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
                if (i)
                        return i;
-               get_user(ct,&a->kb_cnt);
+               __get_user(ct,&a->kb_cnt);
                if (ct >= MAX_DIACR)
                        return -EINVAL;
                accent_table_size = ct;
-               copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
+               __copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
                return 0;
        }
 
        /* the ioctls below read/set the flags usually shown in the leds */
        /* don't use them - they will go away without warning */
        case KDGKBLED:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-               if (i)
-                       return i;
-               put_user(kbd->ledflagstate |
-                        (kbd->default_ledflagstate << 4), (char *) arg);
-               return 0;
+               ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
+               goto setchar;
 
        case KDSKBLED:
                if (!perm)
@@ -674,11 +695,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        /* the ioctls below only set the lights, not the functions */
        /* for those, see KDGKBLED and KDSKBLED above */
        case KDGETLED:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-               if (i)
-                       return i;
-               put_user(getledstate(), (char *) arg);
-               return 0;
+               ucval = getledstate();
+       setchar:
+               return put_user(ucval, (char*)arg);
 
        case KDSETLED:
                if (!perm)
@@ -710,21 +729,15 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
 
        case VT_SETMODE:
        {
-               struct vt_mode *vtmode = (struct vt_mode *)arg;
-               char mode;
+               struct vt_mode tmp;
 
                if (!perm)
                        return -EPERM;
-               i = verify_area(VERIFY_READ, (void *)vtmode, sizeof(struct vt_mode));
-               if (i)
-                       return i;
-               get_user(mode, &vtmode->mode);
-               if (mode != VT_AUTO && mode != VT_PROCESS)
+               if (copy_from_user(&tmp, (void*)arg, sizeof(struct vt_mode)))
+                       return -EFAULT;
+               if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS)
                        return -EINVAL;
-               vt_cons[console]->vt_mode.mode = mode;
-               get_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
-               get_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
-               get_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
+               vt_cons[console]->vt_mode = tmp;
                /* the frsig is ignored, so we set it to 0 */
                vt_cons[console]->vt_mode.frsig = 0;
                vt_cons[console]->vt_pid = current->pid;
@@ -734,19 +747,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        }
 
        case VT_GETMODE:
-       {
-               struct vt_mode *vtmode = (struct vt_mode *)arg;
-
-               i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
-               if (i)
-                       return i;
-               put_user(vt_cons[console]->vt_mode.mode, &vtmode->mode);
-               put_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
-               put_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
-               put_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
-               put_user(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
-               return 0;
-       }
+               return copy_to_user((void*)arg, &(vt_cons[console]->vt_mode), 
+                                                       sizeof(struct vt_mode)) ? -EFAULT : 0; 
 
        /*
         * Returns global vt state. Note that VT 0 is always open, since
@@ -761,27 +763,23 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
                if (i)
                        return i;
-               put_user(fg_console + 1, &vtstat->v_active);
+               __put_user(fg_console + 1, &vtstat->v_active);
                state = 1;      /* /dev/tty0 is always open */
                for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
                        if (VT_IS_IN_USE(i))
                                state |= mask;
-               put_user(state, &vtstat->v_state);
-               return 0;
+               return __put_user(state, &vtstat->v_state);
        }
 
        /*
         * Returns the first available (non-opened) console.
         */
        case VT_OPENQRY:
-               i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-               if (i)
-                       return i;
                for (i = 0; i < MAX_NR_CONSOLES; ++i)
                        if (! VT_IS_IN_USE(i))
                                break;
-               put_user(i < MAX_NR_CONSOLES ? (i+1) : -1, (int *) arg);
-               return 0;
+               ucval = i < MAX_NR_CONSOLES ? (i+1) : -1;
+               goto setint;             
 
        /*
         * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
@@ -904,8 +902,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
                if (i)
                        return i;
-               get_user(ll, &vtsizes->v_rows);
-               get_user(cc, &vtsizes->v_cols);
+               __get_user(ll, &vtsizes->v_rows);
+               __get_user(cc, &vtsizes->v_cols);
                i = vc_resize(ll, cc);
                return i ? i :  kd_size_changed(ll, cc);
        }
@@ -919,19 +917,19 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                i = verify_area(VERIFY_READ, (void *)vtconsize, sizeof(struct vt_consize));
                if (i)
                        return i;
-               get_user(ll, &vtconsize->v_rows);
-               get_user(cc, &vtconsize->v_cols);
-               get_user(vlin, &vtconsize->v_vlin);
-               get_user(clin, &vtconsize->v_clin);
-               get_user(vcol, &vtconsize->v_vcol);
-               get_user(ccol, &vtconsize->v_ccol);
+               __get_user(ll, &vtconsize->v_rows);
+               __get_user(cc, &vtconsize->v_cols);
+               __get_user(vlin, &vtconsize->v_vlin);
+               __get_user(clin, &vtconsize->v_clin);
+               __get_user(vcol, &vtconsize->v_vcol);
+               __get_user(ccol, &vtconsize->v_ccol);
                vlin = vlin ? vlin : video_scan_lines;
                if ( clin )
                  {
                    if ( ll )
                      {
                        if ( ll != vlin/clin )
-                         return EINVAL; /* Parameters don't add up */
+                         return -EINVAL; /* Parameters don't add up */
                      }
                    else 
                      ll = vlin/clin;
@@ -941,14 +939,14 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                    if ( cc )
                      {
                        if ( cc != vcol/ccol )
-                         return EINVAL;
+                         return -EINVAL;
                      }
                    else
                      cc = vcol/ccol;
                  }
 
                if ( clin > 32 )
-                 return EINVAL;
+                 return -EINVAL;
                    
                if ( vlin )
                  video_scan_lines = vlin;
@@ -989,30 +987,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                 /* con_get_cmap() defined in console.c */
 
        case PIO_FONTX:
-       {
-               struct consolefontdesc cfdarg;
-
-               if (!perm)
-                       return -EPERM;
-               if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-                       return -EINVAL;
-               i = verify_area(VERIFY_READ, (void *)arg,
-                               sizeof(struct consolefontdesc));
-               if (i) return i;
-               copy_from_user(&cfdarg, (void *)arg,
-                             sizeof(struct consolefontdesc)); 
-               
-               if ( cfdarg.charcount == 256 ||
-                    cfdarg.charcount == 512 ) {
-                       i = con_set_font(cfdarg.chardata,
-                               cfdarg.charcount == 512);
-                       if (i)
-                               return i;
-                       i = con_adjust_height(cfdarg.charheight);
-                       return (i <= 0) ? i : kd_size_changed(i, 0);
-               } else
-                       return -EINVAL;
-       }
+       case GIO_FONTX:
+               return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm);
 
        case PIO_FONTRESET:
        {
@@ -1038,32 +1014,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
 #endif
        }
 
-       case GIO_FONTX:
-       {
-               struct consolefontdesc cfdarg;
-               int nchar;
-
-               if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-                       return -EINVAL;
-               i = verify_area(VERIFY_WRITE, (void *)arg,
-                       sizeof(struct consolefontdesc));
-               if (i) return i;        
-               copy_from_user(&cfdarg, (void *) arg,
-                             sizeof(struct consolefontdesc)); 
-               i = cfdarg.charcount;
-               cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256;
-               cfdarg.charheight = video_font_height;
-               copy_to_user((void *) arg, &cfdarg,
-                           sizeof(struct consolefontdesc)); 
-               if ( cfdarg.chardata )
-               {
-                       if ( i < nchar )
-                               return -ENOMEM;
-                       return con_get_font(cfdarg.chardata);
-               } else
-                       return 0;
-       }
-
        case PIO_SCRNMAP:
                if (!perm)
                        return -EPERM;
@@ -1084,52 +1034,16 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
              { struct unimapinit ui;
                if (!perm)
                        return -EPERM;
-               i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
-               if (i)
-                 return i;
-               copy_from_user(&ui, (void *)arg, sizeof(struct unimapinit));
+               i = copy_from_user(&ui, (void *)arg, sizeof(struct unimapinit));
+               if (i) return -EFAULT;
                con_clear_unimap(&ui);
                return 0;
              }
 
        case PIO_UNIMAP:
-             { struct unimapdesc *ud;
-               u_short ct;
-               struct unipair *list;
-
-               if (!perm)
-                       return -EPERM;
-               i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
-               if (i == 0) {
-                   ud = (struct unimapdesc *) arg;
-                   get_user(ct, &ud->entry_ct);
-                   get_user(list, &ud->entries);
-                   i = verify_area(VERIFY_READ, (void *) list,
-                                   ct*sizeof(struct unipair));
-                   if(!i)
-                           return con_set_unimap(ct, list);
-               }
-               return i;
-             }
-
        case GIO_UNIMAP:
-             { struct unimapdesc *ud;
-               u_short ct;
-               struct unipair *list;
-
-               i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
-               if (i == 0) {
-                   ud = (struct unimapdesc *) arg;
-                   get_user(ct, &ud->entry_ct);
-                   get_user(list, &ud->entries);
-                   if (ct)
-                     i = verify_area(VERIFY_WRITE, (void *) list,
-                                     ct*sizeof(struct unipair));
-                   if(!i)
-                           return con_get_unimap(ct, &(ud->entry_ct), list);
-               }
-               return i;
-             }
+               return do_unimap_ioctl(cmd, (struct unimapdesc *)arg, perm);
+
        case VT_LOCKSWITCH:
                if (!suser())
                   return -EPERM;
index 4d97c497502574bf3c8dd16f773641e1c03f89d8..30dba3e1daa9d4fe9c5b8ff71933604d1dfcd980 100644 (file)
@@ -621,6 +621,7 @@ static void receive_packet(struct device *dev, int len)
        if (set_bit(0, (void *) &adapter->dmaing))
                printk("%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
 
+       skb->dev = dev;
        adapter->current_dma.direction = 0;
        adapter->current_dma.length = rlen;
        adapter->current_dma.skb = skb;
@@ -698,12 +699,12 @@ static void elp_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
                        } else {
                                struct sk_buff *skb = adapter->current_dma.skb;
                                if (skb) {
-                                 skb->dev = dev;
                                  if (adapter->current_dma.target) {
                                    /* have already done the skb_put() */
                                    memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
                                  }
                                  skb->protocol = eth_type_trans(skb,dev);
+                                 adapter->stats.rx_bytes += skb->len;
                                  netif_rx(skb);
                                }
                        }
@@ -1032,10 +1033,8 @@ static int send_packet(struct device *dev, struct sk_buff *skb)
                        printk("%s: transmit blocked\n", dev->name);
                return FALSE;
        }
-       adapter = dev->priv;
-
 
-       adapter->stats.tx_bytes+=nlen;
+       adapter->stats.tx_bytes += nlen;
        
        /*
         * send the adapter a transmit packet command. Ignore segment and offset
index 06f4c156648f2e3d203a78d2671af318ffb9bc5d..812aec76a32aa3e83b065cd76c665b45708c11a5 100644 (file)
@@ -79,6 +79,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
     tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
     tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
     tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
+    tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
@@ -113,7 +114,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   fi
 fi
 
-tristate 'PLIP (parallel port) support' CONFIG_PLIP
+if [ ! "$CONFIG_PNP_PARPORT" = "n" ]; then
+  dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PNP_PARPORT
+fi
 
 tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
index ab447440e69fc7d07d588afa082b3b94010531de..901e821af164775f442fb32de268fab756ed363d 100644 (file)
@@ -371,6 +371,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_EEXPRESS_PRO100),y)
+L_OBJS += eepro100.o
+else
+  ifeq ($(CONFIG_EEXPRESS_PRO100),m)
+  M_OBJS += eepro100.o
+  endif
+endif
+
 ifeq ($(CONFIG_WAVELAN),y)
 L_OBJS += wavelan.o
 else
index 364f6fff51a2e3a5edbb52e0f741c6124493bd8c..6bbd69cb9f3a6f58e11c15bf9a5f6e11034c57a0 100644 (file)
@@ -50,6 +50,7 @@ extern int hp_plus_probe(struct device *dev);
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int eepro_probe(struct device *);
+extern int eepro100_probe(struct device *);
 extern int el3_probe(struct device *);
 extern int at1500_probe(struct device *);
 extern int pcnet32_probe(struct device *);
@@ -178,6 +179,9 @@ static int ethif_probe(struct device *dev)
 #ifdef CONFIG_EEXPRESS_PRO     /* Intel EtherExpress Pro/10 */
        && eepro_probe(dev)
 #endif
+#ifdef CONFIG_EEXPRESS_PRO100  /* Intel EtherExpress Pro/100 */
+       && eepro100_probe(dev)
+#endif
 #ifdef CONFIG_DEPCA            /* DEC DEPCA */
        && depca_probe(dev)
 #endif
@@ -327,18 +331,6 @@ static struct device eth0_dev = {
 #   undef NEXT_DEV
 #   define NEXT_DEV    (&eth0_dev)
 
-#if defined(PLIP) || defined(CONFIG_PLIP)
-    extern int plip_init(struct device *);
-    static struct device plip2_dev = {
-       "plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
-    static struct device plip1_dev = {
-       "plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
-    static struct device plip0_dev = {
-       "plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, };
-#   undef NEXT_DEV
-#   define NEXT_DEV    (&plip0_dev)
-#endif  /* PLIP */
-
 #if defined(SLIP) || defined(CONFIG_SLIP)
        /* To be exact, this node just hooks the initialization
           routines to the device structures.                   */
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
new file mode 100644 (file)
index 0000000..77facf4
--- /dev/null
@@ -0,0 +1,1732 @@
+/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
+/*
+   NOTICE: this version tested with kernels 1.3.72 and later only!
+       Written 1996 by Donald Becker.
+
+       This software may be used and distributed according to the terms
+       of the GNU Public License, incorporated herein by reference.
+
+       This driver is for the Intel EtherExpress Pro 100B boards.
+       It should work with other i82557 boards (if any others exist).
+       To use a built-in driver, install as drivers/net/eepro100.c.
+       To use as a module, use the compile-command at the end of the file.
+
+       The author may be reached as becker@CESDIS.usra.edu, or C/O
+       Center of Excellence in Space Data and Information Sciences
+          Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
+       For updates see
+       <base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
+*/
+
+static const char *version =
+"eepro100.c:v0.31 3/29/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+
+/* A few user-configurable values that apply to all boards.
+   First set are undocumented and spelled per Intel recommendations. */
+
+static int congenb = 0;                /* Enable congestion control in the DP83840. */
+static int txfifo = 8;         /* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8;         /* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0;     /* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0;     /* Rx DMA length, 0 means no preemption. */
+
+/* If defined use the copy-only-tiny-buffer scheme for higher performance.
+   The value sets the copy breakpoint.  Lower uses more memory, but is
+   faster. */
+#define SKBUFF_RX_COPYBREAK 256
+
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+   LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP                             /* Warning: Bogus!  This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#endif
+
+#endif
+/* This should be in a header file. */
+#if (LINUX_VERSION_CODE < VERSION(1,3,44))
+struct device *init_etherdev(struct device *dev, int sizeof_priv,
+                                                        unsigned long *mem_startp);
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)                  /* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+/* The total I/O port extent of the board.  Nominally 0x18, but rounded up
+   for PCI allocation. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry eepro100_drv =
+{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef SPEEDO3_DEBUG
+int speedo_debug = SPEEDO3_DEBUG;
+#else
+int speedo_debug = 3;
+#endif
+
+/*
+                               Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
+single-chip fast ethernet controller for PCI, as used on the Intel
+EtherExpress Pro 100 adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line.  While it's
+possible to share PCI interrupt lines, it negatively impacts performance and
+only recent kernels support it. 
+
+III. Driver operation
+
+IIIA. General
+The Speedo3 is very similar to other Intel network chips, that is to say
+"apparently designed on a different planet".  This chips retains the complex
+Rx and Tx descriptors and multiple buffers pointers as previous chips, but
+also has simplified Tx and Rx buffer modes.  This driver uses the "flexible"
+Tx mode, but in a simplified lower-overhead manner: it associates only a
+single buffer descriptor with each frame descriptor.
+
+Despite the extra space overhead in each recieve skbuff, the driver must use
+the simplified Rx buffer mode to assure that only a single data buffer is
+associated with each RxFD. The driver implements this by reserving space
+for the Rx descriptor at the head of each Rx skbuff
+
+The Speedo-3 has receive and command unit base addresses that are added to
+almost all descriptor pointers.  The driver sets these to zero, so that all
+pointer fields are absolute addresses.
+
+The System Control Block (SCB) of some previous Intel chips exists on the
+chip in both PCI I/O and memory space.  This driver uses the I/O space
+registers, but might switch to memory mapped mode to better support non-x86
+processors.
+
+IIIB. Transmit structure
+
+The driver must use the complex Tx command+descriptor mode in order to
+have a indirect pointer to the skbuff data section.  Each Tx command block
+(TxCB) is associated with a single, immediately appended Tx buffer descriptor
+(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
+speedo_private data structure for each adapter instance.
+
+This ring structure is used for all normal transmit packets, but the
+transmit packet descriptors aren't long enough for most non-Tx commands such
+as CmdConfigure.  This is complicated by the possibility that the chip has
+already loaded the link address in the previous descriptor.  So for these
+commands we convert the next free descriptor on the ring to a NoOp, and point
+that descriptor's link to the complex command.
+
+An additional complexity of these non-transmit commands are that they may be
+added asynchronous to the normal transmit queue, so we disable interrupts
+whenever the Tx descriptor ring is manipulated.
+
+A notable aspect of the these special configure commands is that they do
+work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
+is done at interrupt time using the 'dirty_tx' index, and checking for the
+command-complete bit.  While the setup frames may have the NoOp command on the
+Tx ring marked as complete, but not have completed the setup command, this
+is not a problem.  The tx_ring entry can be still safely reused, as the
+tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
+
+Commands may have bits set e.g. CmdSuspend in the command word to either
+suspend or stop the transmit/command unit.  This driver always flags the last
+command with CmdSuspend, erases the CmdSuspend in the previous command, and
+then issues a CU_RESUME.
+Note: Watch out for the potential race condition here: imagine
+       erasing the previous suspend
+               the chip processes the previous command
+               the chip processes the final command, and suspends
+       doing the CU_RESUME
+               the chip processes the next-yet-valid post-final-command.
+So blindly sending a CU_RESUME is only safe if we do it immediately after
+after erasing the previous CmdSuspend, without the possibility of an
+intervening delay.  Thus the resume command is always within the
+interrupts-disabled region.  This is a timing dependence, but handling this
+condition in a timing-independent way would considerably complicate the code.
+
+Note: In previous generation Intel chips, restarting the command unit was a
+notoriously slow process.  This is presumably no longer true.
+
+IIIC. Receive structure
+
+Because of the bus-master support on the Speedo3 this driver uses the new
+SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
+This scheme allocates full-sized skbuffs as receive buffers.  The value
+SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
+trade-off the memory wasted by passing the full-sized skbuff to the queue
+layer for all frames vs. the copying cost of copying a frame to a
+correctly-sized skbuff.
+
+For small frames the copying cost is negligible (esp. considering that we
+are pre-loading the cache with immediately useful header information), so we
+allocate a new, minimally-sized skbuff.  For large frames the copying cost
+is non-trivial, and the larger copy might flush the cache of useful data, so
+we pass up the skbuff the packet was received into.
+
+IIID. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'sp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.)         After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero.     Iff the 'sp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
+that stated that I could disclose the information.  But I still resent
+having to sign an Intel NDA when I'm helping Intel sell their own product!
+
+*/
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE   16              /* Effectively 2 entries fewer. */
+#define RX_RING_SIZE   16
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ             1536
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK      16
+
+/* How to wait for the command unit to accept a command.
+   Typically this takes 0 ticks. */
+static inline void wait_for_cmd_done(int cmd_ioaddr)
+{
+  short wait = 100;
+  do   ;
+  while(inb(cmd_ioaddr) && --wait >= 0);
+}
+
+/* Operational parameter that usually are not changed. */
+
+#ifndef PCI_VENDOR_ID_INTEL            /* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_INTEL            0x8086 /* Hmmmm, how did they pick that? */
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82557
+#define PCI_DEVICE_ID_INTEL_82557      0x1229
+#endif
+
+/* The rest of these values should never change. */
+
+/* Offsets to the various registers.
+   All accesses need not be longword aligned. */
+enum speedo_offsets {
+       SCBStatus = 0, SCBCmd = 2,      /* Rx/Command Unit command and status. */
+       SCBPointer = 4,                         /* General purpose pointer. */
+       SCBPort = 8,                            /* Misc. commands and operands.  */
+       SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+       SCBCtrlMDI = 16,                        /* MDI interface control. */
+       SCBEarlyRx = 20,                        /* Early receive byte count. */
+};
+/* Commands that can be put in a command list entry. */
+enum commands {
+       CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+       CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
+       CmdSuspend = 0x4000,            /* Suspend after completion. */
+       CmdIntr = 0x2000,                       /* Interrupt after completion. */
+       CmdTxFlex = 0x0008,                     /* Use "Flexible mode" for CmdTx command. */
+};
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define         CU_START               0x0010
+#define         CU_RESUME              0x0020
+#define         CU_STATSADDR   0x0040
+#define         CU_SHOWSTATS   0x0050  /* Dump statistics counters. */
+#define         CU_CMD_BASE    0x0060  /* Base address to add to add CU commands. */
+#define         CU_DUMPSTATS   0x0070  /* Dump then reset stats counters. */
+
+#define         RX_START       0x0001
+#define         RX_RESUME      0x0002
+#define         RX_ABORT       0x0004
+#define         RX_ADDR_LOAD   0x0006
+#define         RX_RESUMENR    0x0007
+#define INT_MASK       0x0100
+#define DRVR_INT       0x0200          /* Driver generated interrupt. */
+
+/* The Speedo3 Rx and Tx frame/buffer descriptors. */
+struct descriptor {                    /* A generic descriptor. */
+       s16 status;             /* Offset 0. */
+       s16 command;            /* Offset 2. */
+       u32 link;                                       /* struct descriptor *  */
+       unsigned char params[0];
+};
+
+/* The Speedo3 Rx and Tx buffer descriptors. */
+struct RxFD {                                  /* Receive frame descriptor. */
+       s32 status;
+       u32 link;                                       /* struct RxFD * */
+       u32 rx_buf_addr;                        /* void * */
+       u16 count;
+       u16 size;
+};
+
+/* Elements of the RxFD.status word. */
+#define RX_COMPLETE 0x8000
+
+struct TxFD {                                  /* Transmit frame descriptor set. */
+       s32 status;
+       u32 link;                                       /* void * */
+       u32 tx_desc_addr;                       /* Always points to the tx_buf_addr element. */
+       s32 count;                                      /* # of TBD (=1), Tx start thresh., etc. */
+       /* This constitutes a single "TBD" entry -- we only use one. */
+       u32 tx_buf_addr;                        /* void *, frame to be transmitted.  */
+       s32 tx_buf_size;                        /* Length of Tx frame. */
+};
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+struct speedo_stats {
+       u32 tx_good_frames;
+       u32 tx_coll16_errs;
+       u32 tx_late_colls;
+       u32 tx_underruns;
+       u32 tx_lost_carrier;
+       u32 tx_deferred;
+       u32 tx_one_colls;
+       u32 tx_multi_colls;
+       u32 tx_total_colls;
+       u32 rx_good_frames;
+       u32 rx_crc_errs;
+       u32 rx_align_errs;
+       u32 rx_resource_errs;
+       u32 rx_overrun_errs;
+       u32 rx_colls_errs;
+       u32 rx_runt_errs;
+       u32 done_marker;
+};
+
+struct speedo_private {
+       char devname[8];                        /* Used only for kernel debugging. */
+       const char *product_name;
+       struct device *next_module;
+       struct TxFD     tx_ring[TX_RING_SIZE];  /* Commands (usually CmdTxPacket). */
+       /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+       struct sk_buff* tx_skbuff[TX_RING_SIZE];
+       struct descriptor  *last_cmd;   /* Last command sent. */
+       /* Rx descriptor ring & addresses of receive-in-place skbuffs. */
+       struct RxFD *rx_ringp[RX_RING_SIZE];
+       struct sk_buff* rx_skbuff[RX_RING_SIZE];
+#if (LINUX_VERSION_CODE < 0x10300)     /* Kernel v1.2.*. */
+       struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */
+#endif
+       struct RxFD *last_rxf;  /* Last command sent. */
+       struct enet_statistics stats;
+       struct speedo_stats lstats;
+       struct timer_list timer;        /* Media selection timer. */
+       long last_rx_time;                      /* Last Rx, in jiffies, to handle Rx hang. */
+       unsigned int cur_rx, cur_tx;            /* The next free ring entry */
+       unsigned int dirty_rx, dirty_tx;        /* The ring entries to be free()ed. */
+       struct descriptor config_cmd;   /* A configure command, with header... */
+       u8 config_cmd_data[22];                 /* .. and setup parameters. */
+       int mc_setup_frm_len;                           /* The length of an allocated.. */
+       struct descriptor *mc_setup_frm;        /* ..multicast setup frame. */
+       char rx_mode;                                           /* Current PROMISC/ALLMULTI setting. */
+       unsigned int tx_full:1;                         /* The Tx queue is full. */
+       unsigned int full_duplex:1;                     /* Full-duplex operation requested. */
+       unsigned int default_port:1;            /* Last dev->if_port value. */
+       unsigned int rx_bug:1;                          /* Work around receiver hang errata. */
+       unsigned int rx_bug10:1;                        /* Receiver might hang at 10mbps. */
+       unsigned int rx_bug100:1;                       /* Receiver might hang at 100mbps. */
+       unsigned short phy[2];                          /* PHY media interfaces available. */
+};
+
+/* The parameters for a CmdConfigure operation.
+   There are so many options that it would be difficult to document each bit.
+   We mostly use the default or recommended settings. */
+const char basic_config_cmd[22] = {
+       22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
+       0, 0x2E, 0,  0x60, 0,
+       0xf2, 0x48,   0, 0x40, 0xf2, 0x80,              /* 0x40=Force full-duplex */
+       0x3f, 0x05, };
+
+/* PHY media interface chips. */
+static const char *phys[] = { "None", "i82553-A/B", "i82553-C", "i82503",
+                                                                 "DP83840", "80c240", "80c24", "unknown" };
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+                                        S80C24, PhyUndefined, };
+static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options);
+
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static int mdio_write(int ioaddr, int phy_id, int location, int value);
+static int speedo_open(struct device *dev);
+static void speedo_timer(unsigned long data);
+static void speedo_init_rx_ring(struct device *dev);
+static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
+static int speedo_rx(struct device *dev);
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs);
+#endif
+static int speedo_close(struct device *dev);
+static struct enet_statistics *speedo_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+
+\f
+
+#ifdef MODULE
+/* The parameters that may be passed in... */
+/* 'options' is used to pass a transceiver override or full-duplex flag
+   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1;                 /* The debug level */
+
+/* A list of all installed Speedo devices, for removing the driver module. */
+static struct device *root_speedo_dev = NULL;
+#endif
+
+int eepro100_init(struct device *dev)
+{
+       int cards_found = 0;
+
+       if (pcibios_present()) {
+               int pci_index;
+               for (pci_index = 0; pci_index < 8; pci_index++) {
+                       unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+                       int pci_ioaddr;
+#else
+                       long pci_ioaddr;
+#endif
+                       unsigned short pci_command;
+
+                       if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
+                                                                       PCI_DEVICE_ID_INTEL_82557,
+                                                                       pci_index, &pci_bus,
+                                                                       &pci_device_fn))
+                         break;
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                        PCI_INTERRUPT_LINE, &pci_irq_line);
+                       /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
+                       pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                         PCI_BASE_ADDRESS_1, &pci_ioaddr);
+                       /* Remove I/O space marker in bit 0. */
+                       pci_ioaddr &= ~3;
+                       if (speedo_debug > 2)
+                               printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
+                                          (int)pci_ioaddr, pci_irq_line);
+
+                       /* Get and check the bus-master and latency values. */
+                       pcibios_read_config_word(pci_bus, pci_device_fn,
+                                                                        PCI_COMMAND, &pci_command);
+                       if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                               printk("  PCI Master Bit has not been set! Setting...\n");
+                               pci_command |= PCI_COMMAND_MASTER;
+                               pcibios_write_config_word(pci_bus, pci_device_fn,
+                                                                                 PCI_COMMAND, pci_command);
+                       }
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                                PCI_LATENCY_TIMER, &pci_latency);
+                       if (pci_latency < 10) {
+                               printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+                                          "  Setting to 255 clocks.\n", pci_latency);
+                               pcibios_write_config_byte(pci_bus, pci_device_fn,
+                                                                                 PCI_LATENCY_TIMER, 255);
+                       } else if (speedo_debug > 1)
+                               printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+#ifdef MODULE
+                       speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+#else
+                       speedo_found1(dev, pci_ioaddr, pci_irq_line,
+                                                 dev ? dev->mem_start : 0);
+#endif
+                       cards_found++;
+               }
+       }
+
+       return cards_found;
+}
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
+{
+       static int did_version = 0;                     /* Already printed version info. */
+       struct speedo_private *sp;
+       int i;
+       u16 eeprom[0x40];
+
+       if (speedo_debug > 0  &&  did_version++ == 0)
+               printk(version);
+
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+       dev = init_etherdev(dev, sizeof(struct speedo_private));
+#else
+       dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
+#endif
+
+       /* Read the station address EEPROM before doing the reset.
+          Perhaps this should even be done before accepting the device,
+          then we wouldn't have a device name with which to report the error. */
+       {
+               u16 sum = 0;
+               int j;
+               for (j = 0, i = 0; i < 0x40; i++) {
+                       unsigned short value = read_eeprom(ioaddr, i);
+                       eeprom[i] = value;
+                       sum += value;
+                       if (i < 3) {
+                               dev->dev_addr[j++] = value;
+                               dev->dev_addr[j++] = value >> 8;
+                       }
+               }
+               if (sum != 0xBABA)
+                       printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
+                                  "check settings before activating this device!\n",
+                                  dev->name, sum);
+               /* Don't  unregister_netdev(dev);  as the EEPro may actually be
+                  usable, especially if the MAC address is set later. */
+       }
+
+       /* Reset the chip: stop Tx and Rx processes and clear counters.
+          This takes less than 10usec and will easily finish before the next
+          action. */
+       outl(0, ioaddr + SCBPort);
+
+       printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
+                  dev->name, ioaddr);
+       for (i = 0; i < 5; i++)
+               printk("%2.2X:", dev->dev_addr[i]);
+       printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifndef kernel_bloat
+       /* OK, this is pure kernel bloat.  I don't like it when other drivers
+          waste non-pageable kernel space to emit similar messages, but I need
+          them for bug reports. */
+       {
+               const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
+               /* The self-test results must be paragraph aligned. */
+               int str[6], *volatile self_test_results;
+               int boguscnt = 16000;   /* Timeout for set-test. */
+               if (eeprom[3] & 0x03)
+                       printk(KERN_INFO "  Receiver lock-up bug exists -- enabling"
+                                  " work-around.\n");
+               printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical"
+                          " connectors present:",
+                          eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
+               for (i = 0; i < 4; i++)
+                       if (eeprom[5] & (1<<i))
+                               printk(connectors[i]);
+               printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n",
+                          phys[(eeprom[6]>>8)&7], eeprom[6] & 0x1f);
+               if (eeprom[7] & 0x0700)
+                       printk(KERN_INFO "    Secondary interface chip %s.\n",
+                                  phys[(eeprom[7]>>8)&7]);
+#if defined(notdef)
+               /* ToDo: Read and set PHY registers through MDIO port. */
+               for (i = 0; i < 2; i++)
+                       printk("  MDIO register %d is %4.4x.\n",
+                                  i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+               for (i = 5; i < 7; i++)
+                       printk("  MDIO register %d is %4.4x.\n",
+                                  i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+               printk("  MDIO register %d is %4.4x.\n",
+                          25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
+#endif
+               if (((eeprom[6]>>8) & 0x3f) == DP83840) {
+                       int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
+                       if (congenb)
+                         mdi_reg23 |= 0x0100;
+                       printk("  DP83840 specific setup, setting register 23 to %4.4x.\n",
+                                  mdi_reg23);
+                       mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
+               }
+               if ((options >= 0) && (options & 0x60)) {
+                       printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
+                                  (options & 0x20 ? 100 : 10),
+                                  (options & 0x10 ? "full" : "half"));
+                       mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
+                                          ((options & 0x20) ? 0x2000 : 0) |    /* 100mbps? */
+                                          ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+               }
+
+               /* Perform a system self-test. */
+               self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+               self_test_results[0] = 0;
+               self_test_results[1] = -1;
+               outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+               do {
+#ifdef _LINUX_DELAY_H
+                       udelay(10);
+#else
+                       SLOW_DOWN_IO;
+#endif
+               } while (self_test_results[1] == -1  &&  --boguscnt >= 0);
+
+               if (boguscnt < 0) {             /* Test optimized out. */
+                       printk(KERN_ERR "Self test failed, status %8.8x:\n"
+                                  KERN_ERR " Failure to initialize the i82557.\n"
+                                  KERN_ERR " Verify that the card is a bus-master"
+                                  " capable slot.\n",
+                                  self_test_results[1]);
+               } else 
+                       printk(KERN_INFO "  General self-test: %s.\n"
+                                  KERN_INFO "  Serial sub-system self-test: %s.\n"
+                                  KERN_INFO "  Internal registers self-test: %s.\n"
+                                  KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n",
+                                  self_test_results[1] & 0x1000 ? "failed" : "passed",
+                                  self_test_results[1] & 0x0020 ? "failed" : "passed",
+                                  self_test_results[1] & 0x0008 ? "failed" : "passed",
+                                  self_test_results[1] & 0x0004 ? "failed" : "passed",
+                                  self_test_results[0]);
+       }
+#endif  /* kernel_bloat */
+
+       /* We do a request_region() only to register /proc/ioports info. */
+       request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+
+       if (dev->priv == NULL)
+               dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
+       sp = dev->priv;
+       memset(sp, 0, sizeof(*sp));
+#ifdef MODULE
+       sp->next_module = root_speedo_dev;
+       root_speedo_dev = dev;
+#endif
+
+       sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+       sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+
+       sp->phy[0] = eeprom[6];
+       sp->phy[1] = eeprom[7];
+       sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+
+       printk(KERN_INFO "  Operating in %s duplex mode.\n",
+                  sp->full_duplex ? "full" : "half");
+       if (sp->rx_bug)
+         printk(KERN_INFO "  Reciever lock-up workaround activated.\n");
+
+       /* The Speedo-specific entries in the device structure. */
+       dev->open = &speedo_open;
+       dev->hard_start_xmit = &speedo_start_xmit;
+       dev->stop = &speedo_close;
+       dev->get_stats = &speedo_get_stats;
+#ifdef NEW_MULTICAST
+       dev->set_multicast_list = &set_rx_mode;
+#endif
+
+       return;
+}
+\f
+/* Serial EEPROM section.
+   A "bit" grungy, but we work our way through bit-by-bit :->. */
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK   0x01    /* EEPROM shift clock. */
+#define EE_CS                  0x02    /* EEPROM chip select. */
+#define EE_DATA_WRITE  0x04    /* EEPROM chip data in. */
+#define EE_WRITE_0             0x01
+#define EE_WRITE_1             0x05
+#define EE_DATA_READ   0x08    /* EEPROM chip data out. */
+#define EE_ENB                 (0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   This is a "nasty" timing loop, but PC compatible machines are defined
+   to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */
+#define eeprom_delay(nanosec)  do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD   (5 << 6)
+#define EE_READ_CMD            (6 << 6)
+#define EE_ERASE_CMD   (7 << 6)
+
+static int read_eeprom(int ioaddr, int location)
+{
+       int i;
+       unsigned short retval = 0;
+       int ee_addr = ioaddr + SCBeeprom;
+       int read_cmd = location | EE_READ_CMD;
+       
+       outw(EE_ENB & ~EE_CS, ee_addr);
+       outw(EE_ENB, ee_addr);
+       
+       /* Shift the read command bits out. */
+       for (i = 10; i >= 0; i--) {
+               short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+               outw(EE_ENB | dataval, ee_addr);
+               eeprom_delay(100);
+               outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+               eeprom_delay(150);
+               outw(EE_ENB | dataval, ee_addr);        /* Finish EEPROM a clock tick. */
+               eeprom_delay(250);
+       }
+       outw(EE_ENB, ee_addr);
+       
+       for (i = 15; i >= 0; i--) {
+               outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
+               eeprom_delay(100);
+               retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+               outw(EE_ENB, ee_addr);
+               eeprom_delay(100);
+       }
+
+       /* Terminate the EEPROM access. */
+       outw(EE_ENB & ~EE_CS, ee_addr);
+       return retval;
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+       int val, boguscnt = 64*4;               /* <64 usec. to complete, typ 27 ticks */
+       outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+       do {
+#ifdef _LINUX_DELAY_H
+               udelay(16);
+#else
+               SLOW_DOWN_IO;
+#endif
+               val = inl(ioaddr + SCBCtrlMDI);
+               if (--boguscnt < 0) {
+                       printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
+               }
+       } while (! (val & 0x10000000));
+       return val & 0xffff;
+}
+
+static int mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+       int val, boguscnt = 64*4;               /* <64 usec. to complete, typ 27 ticks */
+       outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+                ioaddr + SCBCtrlMDI);
+       do {
+#ifdef _LINUX_DELAY_H
+               udelay(16);
+#else
+               SLOW_DOWN_IO;
+#endif
+               val = inl(ioaddr + SCBCtrlMDI);
+               if (--boguscnt < 0) {
+                       printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
+               }
+       } while (! (val & 0x10000000));
+       return val & 0xffff;
+}
+
+\f
+static int
+speedo_open(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+
+#ifdef notdef
+       /* We could reset the chip, but should not need to. */
+       outl(0, ioaddr + SCBPort);
+       for (i = 40; i >= 0; i--)
+               SLOW_DOWN_IO;                   /* At least 250ns */
+#endif
+
+#ifdef SA_SHIRQ
+       if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
+                                       "Intel EtherExpress Pro 10/100 Ethernet", dev)) {
+               return -EAGAIN;
+       }
+#else
+#ifdef USE_SHARED_IRQ
+       if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
+                                                  "Intel EtherExpress Pro 10/100 Ethernet"))
+               return -EAGAIN;
+#else
+       if (dev->irq < 2  ||  dev->irq > 15  ||  irq2dev_map[dev->irq] != NULL)
+               return -EAGAIN;
+       irq2dev_map[dev->irq] = dev;
+       if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
+               irq2dev_map[dev->irq] = NULL;
+               return -EAGAIN;
+       }
+#endif
+#endif
+
+       if (speedo_debug > 1)
+               printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+
+       MOD_INC_USE_COUNT;
+
+       /* Load the statistics block address. */
+       outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+       outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
+       synchronize_irq();
+       sp->lstats.done_marker = 0;
+
+       speedo_init_rx_ring(dev);
+       outl(0, ioaddr + SCBPointer);
+       outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+       synchronize_irq();      
+
+       /* Todo: verify that we must wait for previous command completion. */
+       wait_for_cmd_done(ioaddr + SCBCmd);
+       outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
+       outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+       synchronize_irq();
+
+       /* Fill the first command with our physical address. */
+       { 
+               unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+               unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
+
+               /* Avoid a bug(?!) here by marking the command already completed. */
+               sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
+               sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
+               *setup_frm++ = eaddrs[0];
+               *setup_frm++ = eaddrs[1];
+               *setup_frm++ = eaddrs[2];
+       }
+       sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
+       sp->cur_tx = 1;
+       sp->dirty_tx = 0;
+       sp->tx_full = 0;
+
+       outl(0, ioaddr + SCBPointer);
+       outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+       synchronize_irq();
+
+       dev->if_port = sp->default_port;
+
+       dev->tbusy = 0;
+       dev->interrupt = 0;
+       dev->start = 1;
+
+       /* Start the chip's Tx process and unmask interrupts. */
+       /* Todo: verify that we must wait for previous command completion. */
+       wait_for_cmd_done(ioaddr + SCBCmd);
+       outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
+       outw(CU_START, ioaddr + SCBCmd);
+
+       /* Setup the chip and configure the multicast list. */
+       sp->mc_setup_frm = NULL;
+       sp->mc_setup_frm_len = 0;
+       sp->rx_mode = -1;                       /* Invalid -> always reset the mode. */
+       set_rx_mode(dev);
+
+       if (speedo_debug > 2) {
+               printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
+                          dev->name, inw(ioaddr + SCBStatus));
+       }
+       /* Set the timer.  The timer serves a dual purpose:
+          1) to monitor the media interface (e.g. link beat) and perhaps switch
+          to an alternate media type
+          2) to monitor Rx activity, and restart the Rx process if the receiver
+          hangs. */
+       init_timer(&sp->timer);
+       sp->timer.expires = RUN_AT((24*HZ)/10);                         /* 2.4 sec. */
+       sp->timer.data = (unsigned long)dev;
+       sp->timer.function = &speedo_timer;                                     /* timer handler */
+       add_timer(&sp->timer);
+
+       outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+       return 0;
+}
+
+/* Media monitoring and control. */
+static void speedo_timer(unsigned long data)
+{
+       struct device *dev = (struct device *)data;
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int tickssofar = jiffies - sp->last_rx_time;
+
+       if (speedo_debug > 3) {
+               int ioaddr = dev->base_addr;
+               printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
+                          dev->name, inw(ioaddr + SCBStatus));
+       }
+       if (sp->rx_bug) {
+               if (tickssofar > 2*HZ  || sp->rx_mode < 0) {
+                       /* We haven't received a packet in a Long Time.  We might have been
+                          bitten by the receiver hang bug.  This can be cleared by sending
+                          a set multicast list command. */
+                       set_rx_mode(dev);
+               }
+               /* We must continue to monitor the media. */
+               sp->timer.expires = RUN_AT(2*HZ);                       /* 2.0 sec. */
+               add_timer(&sp->timer);
+       }
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+speedo_init_rx_ring(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       struct RxFD *rxf, *last_rxf = NULL;
+       int i;
+
+       sp->cur_rx = 0;
+       sp->dirty_rx = RX_RING_SIZE - 1;
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb;
+#ifndef KERNEL_1_2
+               skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#else
+               skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#endif
+               sp->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;                  /* Bad news!  */
+               skb->dev = dev;                 /* Mark as being used by this device. */
+
+#if LINUX_VERSION_CODE >= 0x10300
+               rxf = (struct RxFD *)skb->tail;
+               skb_reserve(skb, sizeof(struct RxFD));
+#else
+               /* Save the data in the header region -- it's restored later. */
+               rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
+               memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
+#endif
+               sp->rx_ringp[i] = rxf;
+               if (last_rxf)
+                       last_rxf->link = virt_to_bus(rxf);
+               last_rxf = rxf;
+               rxf->status = 0x00000001;                       /* '1' is flag value only. */
+               rxf->link = 0;                                          /* None yet. */
+#if LINUX_VERSION_CODE < 0x10300
+               /* This field unused by i82557, we use it as a consistency check. */
+               rxf->rx_buf_addr = virt_to_bus(skb->data);
+#else
+               rxf->rx_buf_addr = virt_to_bus(skb->tail);
+#endif
+               rxf->count = 0;
+               rxf->size = PKT_BUF_SZ;
+       }
+       /* Mark the last entry as end-of-list. */
+       last_rxf->status = 0xC0000002;                  /* '2' is flag value only. */
+       sp->last_rxf = last_rxf;
+}
+
+static void speedo_tx_timeout(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int i;
+
+       printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
+                  "command %4.4x.\n",
+                  dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+#ifndef final_version
+       printk("%s:  Tx timeout  fill index %d  scavenge index %d.\n",
+                  dev->name, sp->cur_tx, sp->dirty_tx);
+       printk("    Tx queue ");
+       for (i = 0; i < TX_RING_SIZE; i++)
+         printk(" %8.8x", (int)sp->tx_ring[i].status);
+       printk(".\n    Rx ring ");
+       for (i = 0; i < RX_RING_SIZE; i++)
+         printk(" %8.8x", (int)sp->rx_ringp[i]->status);
+       printk(".\n");
+               
+#else
+       dev->if_port ^= 1;
+       printk("  (Media type switching not yet implemented.)\n");
+       /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
+#endif
+       if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
+         printk("%s: Trying to restart the transmitter...\n", dev->name);
+         outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
+                  ioaddr + SCBPointer);
+         outw(CU_START, ioaddr + SCBCmd);
+       } else {
+         outw(DRVR_INT, ioaddr + SCBCmd);
+       }
+       sp->stats.tx_errors++;
+       dev->trans_start = jiffies;
+       return;
+}
+
+static int
+speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int entry;
+
+       if (skb == NULL || skb->len <= 0) {
+               printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+                          dev->name);
+               dev_tint(dev);
+               return 0;
+       }
+
+       /* Block a timer-based transmit from overlapping.  This could better be
+          done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+          If this ever occurs the queue layer is doing something evil! */
+       if (set_bit(0, (void*)&dev->tbusy) != 0) {
+               int tickssofar = jiffies - dev->trans_start;
+               if (tickssofar < TX_TIMEOUT - 2)
+                       return 1;
+               if (tickssofar < TX_TIMEOUT) {
+                       /* Reap sent packets from the full Tx queue. */
+                       outw(DRVR_INT, ioaddr + SCBCmd);
+                       return 1;
+               }
+               speedo_tx_timeout(dev);
+               return 0;
+       }
+
+       /* Caution: the write order is important here, set the base address
+          with the "ownership" bits last. */
+
+       {       /* Prevent interrupts from changing the Tx ring from underneath us. */
+               unsigned long flags;
+
+               save_flags(flags);
+               cli();
+               /* Calculate the Tx descriptor entry. */
+               entry = sp->cur_tx++ % TX_RING_SIZE;
+
+               sp->tx_skbuff[entry] = skb;
+               /* Todo: be a little more clever about setting the interrupt bit. */
+               sp->tx_ring[entry].status =
+                       (CmdSuspend | CmdTx | CmdTxFlex) << 16;
+               sp->tx_ring[entry].link =
+                 virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+               sp->tx_ring[entry].tx_desc_addr =
+                 virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
+               /* The data region is always in one buffer descriptor, Tx FIFO
+                  threshold of 256. */
+               sp->tx_ring[entry].count = 0x01208000;
+               sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
+               sp->tx_ring[entry].tx_buf_size = skb->len;
+               /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
+                  than half full.  Argument against: we should be receiving packets
+                  and scavenging the queue.  Argument for: if so, it shouldn't
+                  matter. */
+               sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
+               sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+               /* Trigger the command unit resume. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               restore_flags(flags);
+       }
+
+       /* Leave room for set_rx_mode() to fill two entries. */
+       if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
+               sp->tx_full = 1;
+       else
+               dev->tbusy = 0;
+
+       dev->trans_start = jiffies;
+
+       return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+#ifdef SA_SHIRQ
+       struct device *dev = (struct device *)dev_instance;
+#else
+#ifdef USE_SHARED_IRQ
+       struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+#endif
+       struct speedo_private *sp;
+       int ioaddr, boguscnt = INTR_WORK;
+       unsigned short status;
+
+#ifndef final_version
+       if (dev == NULL) {
+               printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
+#endif
+
+       ioaddr = dev->base_addr;
+       sp = (struct speedo_private *)dev->priv;
+#ifndef final_version
+       if (dev->interrupt) {
+               printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+               return;
+       }
+       dev->interrupt = 1;
+#endif
+
+       do {
+               status = inw(ioaddr + SCBStatus);
+               /* Acknowledge all of the current interrupt sources ASAP. */
+               outw(status & 0xfc00, ioaddr + SCBStatus);
+
+               if (speedo_debug > 4)
+                       printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
+                                  dev->name, status);
+
+               if ((status & 0xfc00) == 0)
+                       break;
+
+               if (status & 0x4000)     /* Packet received. */
+                       speedo_rx(dev);
+
+               if (status & 0x1000) {
+#ifdef notdef
+                 int i;
+                 printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
+                                " state -- %4.4x!  Index %d (%d).\n", dev->name, status,
+                                sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
+                 printk("   Rx ring:\n ");
+                 for (i = 0; i < RX_RING_SIZE; i++)
+                       printk("   %d %8.8x %8.8x %8.8x %d %d.\n",
+                                  i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
+                                  sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
+                                  sp->rx_ringp[i]->size);
+#endif
+
+                 if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
+                       outw(RX_RESUMENR, ioaddr + SCBCmd);
+                 else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
+                       /* No idea of what went wrong.  Restart the receiver. */
+                       outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+                                ioaddr + SCBPointer);
+                       outw(RX_START, ioaddr + SCBCmd);
+                 }
+                 sp->stats.rx_errors++;
+               }
+
+               /* User interrupt, Command/Tx unit interrupt or CU not active. */
+               if (status & 0xA400) {
+                       unsigned int dirty_tx = sp->dirty_tx;
+
+                       while (sp->cur_tx - dirty_tx > 0) {
+                               int entry = dirty_tx % TX_RING_SIZE;
+                               int status = sp->tx_ring[entry].status;
+
+                               if (speedo_debug > 5)
+                                       printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n",
+                                                  entry, status);
+                               if ((status & 0x8000) == 0)
+                                       break;                  /* It still hasn't been processed. */
+                               /* Free the original skb. */
+                               if (sp->tx_skbuff[entry]) {
+                                       sp->stats.tx_packets++; /* Count only user packets. */
+                                       dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
+                                       sp->tx_skbuff[entry] = 0;
+                               }
+                               dirty_tx++;
+                       }
+
+#ifndef final_version
+                       if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
+                               printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+                                          dirty_tx, sp->cur_tx, sp->tx_full);
+                               dirty_tx += TX_RING_SIZE;
+                       }
+#endif
+
+                       if (sp->tx_full && dev->tbusy
+                               && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
+                               /* The ring is no longer full, clear tbusy. */
+                               sp->tx_full = 0;
+                               dev->tbusy = 0;
+                               mark_bh(NET_BH);
+                       }
+
+                       sp->dirty_tx = dirty_tx;
+               }
+
+               if (--boguscnt < 0) {
+                       printk("%s: Too much work at interrupt, status=0x%4.4x.\n",
+                                  dev->name, status);
+                       /* Clear all interrupt sources. */
+                       outl(0xfc00, ioaddr + SCBStatus);
+                       break;
+               }
+       } while (1);
+
+       if (speedo_debug > 3)
+               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+                          dev->name, inw(ioaddr + SCBStatus));
+
+#ifndef final_version
+       /* Special code for testing *only*. */
+       {
+               static int stopit = 100;
+               if (dev->start == 0  &&  --stopit < 0) {
+                       printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
+                                  dev->name);
+#ifdef SA_SHIRQ
+                       free_irq(irq, dev);
+#else
+                       free_irq(irq);
+#endif
+               }
+       }
+#endif
+
+       dev->interrupt = 0;
+       return;
+}
+
+static int
+speedo_rx(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int entry = sp->cur_rx % RX_RING_SIZE;
+       int status;
+               
+       if (speedo_debug > 4)
+               printk(KERN_DEBUG " In speedo_rx().\n");
+       /* If we own the next entry, it's a new packet. Send it up. */
+       while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
+
+               if (speedo_debug > 4)
+                       printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
+                                  sp->rx_ringp[entry]->count & 0x3fff);
+               if (status & 0x0200) {
+                       printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n",
+                                  dev->name, status);
+               } else if ( ! (status & 0x2000)) {
+                       /* There was a fatal error.  This *should* be impossible. */
+                       sp->stats.rx_errors++;
+                       printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+                                  dev->name, status);
+               } else {
+                       /* Malloc up new buffer, compatible with net-2e. */
+                       short pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+                       struct sk_buff *skb;
+                       int rx_in_place = 0;
+
+                       /* Check if the packet is long enough to just accept without
+                          copying to a properly sized skbuff. */
+                       if (pkt_len > SKBUFF_RX_COPYBREAK) {
+                               struct sk_buff *newskb;
+                               char *temp;
+
+                               /* Pass up the skb already on the Rx ring. */
+                               skb = sp->rx_skbuff[entry];
+#ifdef KERNEL_1_2
+                               temp = skb->data;
+                               if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                                  " in speedo_rx: %p vs. %p / %p.\n", dev->name,
+                                                  bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+                                                  temp, skb->data);
+                               /* Get a fresh skbuff to replace the filled one. */
+                               newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#else
+                               temp = skb_put(skb, pkt_len);
+                               if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                                  " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
+                                                  sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
+                               /* Get a fresh skbuff to replace the filled one. */
+                               newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#endif
+                               if (newskb) {
+                                       struct RxFD *rxf;
+                                       rx_in_place = 1;
+                                       sp->rx_skbuff[entry] = newskb;
+                                       newskb->dev = dev;
+#ifdef KERNEL_1_2
+                                       /* Restore the data in the old header region. */
+                                       memcpy(skb->data - sizeof(struct RxFD),
+                                                  &sp->saved_skhead[entry], sizeof(struct RxFD));
+                                       /* Save the data in this header region. */
+                                       rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
+                                       sp->rx_ringp[entry] = rxf;
+                                       memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
+                                       rxf->rx_buf_addr = virt_to_bus(newskb->data);
+#else
+                                       rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
+                                       skb_reserve(newskb, sizeof(struct RxFD));
+                                       /* Unused by i82557, consistency check only. */
+                                       rxf->rx_buf_addr = virt_to_bus(newskb->tail);
+#endif
+                                       rxf->status = 0x00000001;
+                               } else                  /* No memory, drop the packet. */
+                                 skb = 0;
+                       } else
+#ifdef KERNEL_1_2
+                               skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+                               skb = dev_alloc_skb(pkt_len + 2);
+#endif
+                       if (skb == NULL) {
+                               int i;
+                               printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+                               /* Check that at least two ring entries are free.
+                                  If not, free one and mark stats->rx_dropped++. */
+                               /* ToDo: This is not correct!!!!  We should count the number
+                                  of linked-in Rx buffer to very that we have at least two
+                                  remaining. */
+                               for (i = 0; i < RX_RING_SIZE; i++)
+                                       if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
+                                                  & RX_COMPLETE))
+                                               break;
+
+                               if (i > RX_RING_SIZE -2) {
+                                       sp->stats.rx_dropped++;
+                                       sp->rx_ringp[entry]->status = 0;
+                                       sp->cur_rx++;
+                               }
+                               break;
+                       }
+                       skb->dev = dev;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+                       if (! rx_in_place) {
+                               skb_reserve(skb, 2);    /* 16 byte align the data fields */
+                               memcpy(skb_put(skb, pkt_len),
+                                          bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+#else
+#ifdef KERNEL_1_3
+#warning This code has only been tested with later 1.3.* kernels.
+                       skb->len = pkt_len;
+                       memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+                                  pkt_len);
+                       /* Needed for 1.3.*. */
+                       skb->protocol = eth_type_trans(skb, dev);
+#else  /* KERNEL_1_2 */
+                       skb->len = pkt_len;
+                       if (! rx_in_place) {
+                               memcpy(skb->data,
+                                          bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+                       }
+#endif
+#endif
+                       netif_rx(skb);
+                       sp->stats.rx_packets++;
+               }
+
+               /*      ToDo: This is better than before, but should be checked. */
+               { 
+                       struct RxFD *rxf = sp->rx_ringp[entry];
+                       rxf->status = 0xC0000003;               /* '3' for verification only */
+                       rxf->link = 0;                  /* None yet. */
+                       rxf->count = 0;
+                       rxf->size = PKT_BUF_SZ;
+                       sp->last_rxf->link = virt_to_bus(rxf);
+                       sp->last_rxf->status &= ~0xC0000000;
+                       sp->last_rxf = rxf;
+                       entry = (++sp->cur_rx) % RX_RING_SIZE;
+               }
+       }
+
+       sp->last_rx_time = jiffies;
+       return 0;
+}
+
+static int
+speedo_close(struct device *dev)
+{
+       int ioaddr = dev->base_addr;
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int i;
+
+       dev->start = 0;
+       dev->tbusy = 1;
+
+       if (speedo_debug > 1)
+               printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+                          dev->name, inw(ioaddr + SCBStatus));
+
+       /* Shut off the media monitoring timer. */
+       del_timer(&sp->timer);
+
+       /* Disable interrupts, and stop the chip's Rx process. */
+       outw(INT_MASK, ioaddr + SCBCmd);
+       outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+       synchronize_irq();
+
+#ifdef SA_SHIRQ
+       free_irq(dev->irq, dev);
+#else
+       free_irq(dev->irq);
+       irq2dev_map[dev->irq] = 0;
+#endif
+
+       /* Free all the skbuffs in the Rx and Tx queues. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = sp->rx_skbuff[i];
+               sp->rx_skbuff[i] = 0;
+               /* Clear the Rx descriptors. */
+               if (skb)
+                       dev_kfree_skb(skb, FREE_WRITE);
+       }
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               struct sk_buff *skb = sp->tx_skbuff[i];
+               sp->tx_skbuff[i] = 0;
+               /* Clear the Tx descriptors. */
+               if (skb)
+                       dev_kfree_skb(skb, FREE_WRITE);
+       }
+       if (sp->mc_setup_frm) {
+               kfree(sp->mc_setup_frm);
+               sp->mc_setup_frm_len = 0;
+       }
+
+       /* Print a few items for debugging. */
+       if (speedo_debug > 3) {
+               printk("%s:Printing Rx ring (next to receive into %d).\n",
+                          dev->name, sp->cur_rx);
+
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk("  Rx ring entry %d  %8.8x.\n",
+                                  i, (int)sp->rx_ringp[i]->status);
+
+               for (i = 0; i < 5; i++)
+                       printk("  PHY index %d register %d is %4.4x.\n",
+                                  1, i, mdio_read(ioaddr, 1, i));
+               for (i = 21; i < 26; i++)
+                       printk("  PHY index %d register %d is %4.4x.\n",
+                                  1, i, mdio_read(ioaddr, 1, i));
+       }
+       MOD_DEC_USE_COUNT;
+
+       return 0;
+}
+
+/* The Speedo-3 has an especially awkward and unusable method of getting
+   statistics out of the chip.  It takes an unpredictable length of time
+   for the dump-stats command to complete.  To avoid a busy-wait loop we
+   update the stats with the previous dump results, and then trigger a
+   new dump.
+
+   These problems are mitigated by the current /proc implementation, which
+   calls this routine first to judge the output length, and then to emit the
+   output.
+
+   Oh, and incoming frames are dropped while executing dump-stats!
+   */
+static struct enet_statistics *
+speedo_get_stats(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+
+       if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */
+               sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
+               sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
+               sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
+               sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
+               /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
+               sp->stats.collisions += sp->lstats.tx_total_colls;
+               sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
+               sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
+               sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
+               sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
+               sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
+               sp->lstats.done_marker = 0x0000;
+               if (dev->start)
+                       outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+       }
+       return &sp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   This is very ugly with Intel chips -- we usually have to execute an
+   entire configuration command, plus process a multicast command.
+   This is complicated.  We must put a large configuration command and
+   an arbitrarily-sized multicast command in the transmit list.
+   To minimize the disruption -- the previous command might have already
+   loaded the link -- we convert the current command block, normally a Tx
+   command, into a no-op and link it to the new command.
+*/
+static void
+set_rx_mode(struct device *dev)
+{
+       struct speedo_private *sp = (struct speedo_private *)dev->priv;
+       int ioaddr = dev->base_addr;
+       char new_rx_mode;
+       unsigned long flags;
+       int entry, i;
+
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               new_rx_mode = 3;
+       } else if (dev->flags & IFF_ALLMULTI) {
+               new_rx_mode = 1;
+       } else
+               new_rx_mode = 0;
+
+       if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
+         /* The Tx ring is full -- don't add anything!  Presumably the new mode
+            is in config_cmd_data and will be added anyway. */
+               sp->rx_mode = -1;
+               return;
+       }
+
+       if (new_rx_mode != sp->rx_mode) {
+               /* We must change the configuration. Construct a CmdConfig frame. */
+               memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
+               sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
+               sp->config_cmd_data[4] = rxdmacount;
+               sp->config_cmd_data[5] = txdmacount + 0x80;
+               sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
+               sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
+               sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
+               if (sp->phy[0] & 0x8000) {                      /* Use the AUI port instead. */
+                 sp->config_cmd_data[15] |= 0x80;
+                 sp->config_cmd_data[8] = 0;
+               }
+               save_flags(flags);
+               cli();
+               /* Fill the "real" tx_ring frame with a no-op and point it to us. */
+               entry = sp->cur_tx++ % TX_RING_SIZE;
+               sp->tx_skbuff[entry] = 0;       /* Nothing to free. */
+               sp->tx_ring[entry].status = CmdNOp << 16;
+               sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
+               sp->config_cmd.status = 0;
+               sp->config_cmd.command = CmdSuspend | CmdConfigure;
+               sp->config_cmd.link =
+                 virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+               sp->last_cmd->command &= ~CmdSuspend;
+               /* Immediately trigger the command unit resume. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               sp->last_cmd = &sp->config_cmd;
+               restore_flags(flags);
+               if (speedo_debug > 5) {
+                       int i;
+                       printk(" CmdConfig frame in entry %d.\n", entry);
+                       for(i = 0; i < 32; i++)
+                               printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
+                       printk(".\n");
+               }
+       }
+
+       if (new_rx_mode == 0  &&  dev->mc_count < 3) {
+               /* The simple case of 0-2 multicast list entries occurs often, and
+                  fits within one tx_ring[] entry. */
+               u16 *setup_params;
+               unsigned short *eaddrs;
+               struct dev_mc_list *mclist;
+
+               save_flags(flags);
+               cli();
+               entry = sp->cur_tx++ % TX_RING_SIZE;
+               sp->tx_skbuff[entry] = 0;
+               sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
+               sp->tx_ring[entry].link =
+                 virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+               sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
+               setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr;
+               *setup_params++ = dev->mc_count*6;
+               /* Fill in the multicast addresses. */
+               for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       eaddrs = (unsigned short *)mclist->dmi_addr;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+               }
+
+               sp->last_cmd->command &= ~CmdSuspend;
+               /* Immediately trigger the command unit resume. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+               restore_flags(flags);
+       } else if (new_rx_mode == 0) {
+               /* This does not work correctly, but why not? */
+               struct dev_mc_list *mclist;
+               unsigned short *eaddrs;
+               struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+               u16 *setup_params = (short *)mc_setup_frm->params;
+               int i;
+
+               if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
+                       || sp->mc_setup_frm == NULL) {
+                       /* Allocate a new frame, 10bytes + addrs, with a few
+                          extra entries for growth. */
+                       if (sp->mc_setup_frm)
+                               kfree(sp->mc_setup_frm);
+                       sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
+                       printk("%s: Allocating a setup frame of size %d.\n",
+                                  dev->name, sp->mc_setup_frm_len);
+                       sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
+                                                                          intr_count ? GFP_ATOMIC : GFP_KERNEL);
+                       if (sp->mc_setup_frm == NULL) {
+                         printk("%s: Failed to allocate a setup frame.\n", dev->name);
+                               sp->rx_mode = -1; /* We failed, try again. */
+                               return;
+                       }
+               }
+               mc_setup_frm = sp->mc_setup_frm;
+               /* Construct the new setup frame. */
+               printk("%s: Constructing a setup frame at %p, %d bytes.\n",
+                          dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+               mc_setup_frm->status = 0;
+               mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
+               /* Link set below. */
+               setup_params = (short *)mc_setup_frm->params;
+               *setup_params++ = dev->mc_count*6;
+               /* Fill in the multicast addresses. */
+               for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       eaddrs = (unsigned short *)mclist->dmi_addr;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+                       *setup_params++ = *eaddrs++;
+               }
+               
+               /* Disable interrupts while playing with the Tx Cmd list. */
+               save_flags(flags);
+               cli();
+               entry = sp->cur_tx++ % TX_RING_SIZE;
+
+               if (speedo_debug > 5)
+                       printk(" CmdMCSetup frame length %d in entry %d.\n",
+                                  dev->mc_count, entry);
+
+               /* Change the command to a NoOp, pointing to the CmdMulti command. */
+               sp->tx_skbuff[entry] = 0;
+               sp->tx_ring[entry].status = CmdNOp << 16;
+               sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
+
+               /* Set the link in the setup frame. */
+               mc_setup_frm->link =
+                 virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+
+               sp->last_cmd->command &= ~CmdSuspend;
+               /* Immediately trigger the command unit resume. */
+               outw(CU_RESUME, ioaddr + SCBCmd);
+               sp->last_cmd = mc_setup_frm;
+               restore_flags(flags);
+               printk("%s: Last command at %p is %4.4x.\n",
+                          dev->name, sp->last_cmd, sp->last_cmd->command);
+       }
+
+       sp->rx_mode = new_rx_mode;
+}
+\f
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,38))     /* 1.3.38 and later */
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+int
+init_module(void)
+{
+       int cards_found;
+
+       if (debug >= 0)
+               speedo_debug = debug;
+       if (speedo_debug)
+               printk(KERN_INFO "%s", version);
+
+       root_speedo_dev = NULL;
+       cards_found = eepro100_init(NULL);
+       return cards_found < 0 ? cards_found : 0;
+}
+
+void
+cleanup_module(void)
+{
+       struct device *next_dev;
+
+       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       while (root_speedo_dev) {
+               next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
+               unregister_netdev(root_speedo_dev);
+               release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
+               kfree(root_speedo_dev);
+               root_speedo_dev = next_dev;
+       }
+}
+#else   /* not MODULE */
+int eepro100_probe(struct device *dev)
+{
+       int cards_found = 0;
+
+       cards_found = eepro100_init(dev);
+
+       if (speedo_debug > 0  &&  cards_found)
+               printk(version);
+
+       return cards_found ? 0 : -ENODEV;
+}
+#endif  /* MODULE */
+\f
+/*
+ * Local variables:
+ *  compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
index aebbca8a4015363e4ea1c00a540fb37dcb2c2cb2..9d8e30761cae5e5e3e1b8c6a06514e76e878f5b3 100644 (file)
@@ -318,7 +318,7 @@ int register_netdev(struct device *dev)
        save_flags(flags);
        cli();
 
-       if (dev && dev->init) {
+       if (dev) {
                if (dev->name &&
                        ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
                        for (i = 0; i < MAX_ETH_CARDS; ++i)
@@ -330,13 +330,15 @@ int register_netdev(struct device *dev)
                                }
                }
 
-               sti();  /* device probes assume interrupts enabled */
-               if (dev->init(dev) != 0) {
+               if (dev->init) {
+                 sti();        /* device probes assume interrupts enabled */
+                 if (dev->init(dev) != 0) {
                    if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
                        restore_flags(flags);
                        return -EIO;
+                 }
+                 cli();
                }
-               cli();
 
                /* Add device to end of chain */
                if (dev_base) {
index 0a997845b8d950955a31ff70ae83d1d67cb87e97..fee809b1ea1c4dd8c0723c5fdec718878252e92e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
+/* $Id: plip.c,v 1.1.1.1.2.6 1997/03/26 17:52:20 phil Exp $ */
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
  *
  *             Modularization and ifreq/ifmap support by Alan Cox.
  *             Rewritten by Niibe Yutaka.
+ *              parport-sharing awareness code by Philip Blundell.
  *
  * Fixes:
- *             9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- *               - only claim 3 bytes of I/O space for port at 0x3bc
- *               - treat NULL return from register_netdev() as success in
- *                 init_module()
- *               - added message if driver loaded as a module but no
- *                 interfaces present.
- *               - release claimed I/O ports if malloc() fails during init.
- *
  *             Niibe Yutaka
  *               - Module initialization.  You can specify I/O addr and IRQ:
  *                     # insmod plip.o io=0x3bc irq=7
@@ -52,7 +45,7 @@
  *     To use with DOS box, please do (Turn on ARP switch):
  *     # ifconfig plip[0-2] arp
  */
-static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n";
 
 /*
   Sources:
@@ -114,6 +107,11 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 
+#include <linux/parport.h>
+
+/* Maximum number of devices to support. */
+#define PLIP_MAX  8
+
 /* Use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
 #define NET_DEBUG 1
@@ -150,6 +148,8 @@ static int plip_close(struct device *dev);
 static struct net_device_stats *plip_get_stats(struct device *dev);
 static int plip_config(struct device *dev, struct ifmap *map);
 static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int plip_preempt(void *handle);
+static int plip_wakeup(void *handle);
 \f
 enum plip_connection_state {
        PLIP_CN_NONE=0,
@@ -203,61 +203,47 @@ struct net_local {
        struct tq_struct deferred;
        struct plip_local snd_data;
        struct plip_local rcv_data;
+       struct ppd *pardev;
        unsigned long  trigger;
        unsigned long  nibble;
        enum plip_connection_state connection;
        unsigned short timeout_count;
-       char is_deferred;
+       int is_deferred;
+       int port_owner;
+       int should_relinquish;
        int (*orig_rebuild_header)(struct sk_buff *skb);
 };
 \f
 /* Entry point of PLIP driver.
-   Probe the hardware, and register/initialize the driver. */
+   Probe the hardware, and register/initialize the driver.
+
+   PLIP is rather weird, because of the way it interacts with the parport
+   system.  It is _not_ initialised from Space.c.  Instead, plip_init()
+   is called, and that function makes up a "struct device" for each port, and
+   then calls us here.
 
-int plip_init(struct device *dev)
+   */
+int
+plip_init_dev(struct device *dev, struct parport *pb)
 {
        struct net_local *nl;
-       int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
+       struct ppd *pardev;
 
-       /* Check region before the probe */
-       if (check_region(PAR_DATA(dev), iosize) < 0)
-               return -ENODEV;
+       dev->irq = pb->irq;
+       dev->base_addr = pb->base;
 
-       /* Check that there is something at base_addr. */
-       outb(0, PAR_DATA(dev));
-       udelay(1000);
-       if (inb(PAR_DATA(dev)) != 0)
+       if (pb->irq == -1) {
+               printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
                return -ENODEV;
-
-       printk(version);
-       printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
-       if (dev->irq) {
-               printk("using assigned IRQ %d.\n", dev->irq);
-       } else {
-               int irq = 0;
-#ifdef MODULE
-               /* dev->irq==0 means autoprobe, but we don't try to do so
-                  with module.  We can change it by ifconfig */
-#else
-               unsigned int irqs = probe_irq_on();
-
-               outb(0x00, PAR_CONTROL(dev));
-               udelay(1000);
-               outb(PAR_INTR_OFF, PAR_CONTROL(dev));
-               outb(PAR_INTR_ON, PAR_CONTROL(dev));
-               outb(PAR_INTR_OFF, PAR_CONTROL(dev));
-               udelay(1000);
-               irq = probe_irq_off(irqs);
-#endif
-               if (irq > 0) {
-                       dev->irq = irq;
-                       printk("using probed IRQ %d.\n", dev->irq);
-               } else
-                       printk("failed to detect IRQ(%d) --"
-                              " Please set IRQ by ifconfig.\n", irq);
        }
+       
+       pardev = parport_register_device(pb, dev->name, plip_preempt,
+                                       plip_wakeup,
+                                       plip_interrupt, PARPORT_DEV_LURK, dev);
 
-       request_region(PAR_DATA(dev), iosize, dev->name);
+       printk(version);
+       printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
+              dev->base_addr, dev->irq);
 
        /* Fill in the generic fields of the device structure. */
        ether_setup(dev);
@@ -276,7 +262,7 @@ int plip_init(struct device *dev)
        dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
        if (dev->priv == NULL) {
                printk(KERN_ERR "%s: out of memory\n", dev->name);
-               release_region(PAR_DATA(dev), iosize);
+               parport_unregister_device(pardev);
                return -ENOMEM;
        }
        memset(dev->priv, 0, sizeof(struct net_local));
@@ -284,6 +270,9 @@ int plip_init(struct device *dev)
 
        nl->orig_rebuild_header = dev->rebuild_header;
        dev->rebuild_header     = plip_rebuild_header;
+       nl->pardev = pardev; 
+
+       nl->port_owner = 0;
 
        /* Initialize constants */
        nl->trigger     = PLIP_TRIGGER_WAIT;
@@ -306,8 +295,8 @@ int plip_init(struct device *dev)
 /* Bottom half handler for the delayed request.
    This routine is kicked by do_timer().
    Request `plip_bh' to be invoked. */
-   
-static void plip_kick_bh(struct device *dev)
+static void
+plip_kick_bh(struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
 
@@ -350,7 +339,8 @@ static plip_func connection_state_table[] =
 };
 
 /* Bottom half handler of PLIP. */
-static void plip_bh(struct device *dev)
+static void
+plip_bh(struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
        struct plip_local *snd = &nl->snd_data;
@@ -367,7 +357,8 @@ static void plip_bh(struct device *dev)
        }
 }
 
-static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+static int
+plip_bh_timeout_error(struct device *dev, struct net_local *nl,
                      struct plip_local *snd, struct plip_local *rcv,
                      int error)
 {
@@ -429,7 +420,8 @@ static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
        return TIMEOUT;
 }
 \f
-static int plip_none(struct device *dev, struct net_local *nl,
+static int
+plip_none(struct device *dev, struct net_local *nl,
          struct plip_local *snd, struct plip_local *rcv)
 {
        return OK;
@@ -437,7 +429,8 @@ static int plip_none(struct device *dev, struct net_local *nl,
 
 /* PLIP_RECEIVE --- receive a byte(two nibbles)
    Returns OK on success, TIMEOUT on timeout */
-extern inline int plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
+inline static int
+plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
             enum plip_nibble_state *ns_p, unsigned char *data_p)
 {
        unsigned char c0, c1;
@@ -486,7 +479,8 @@ extern inline int plip_receive(unsigned short nibble_timeout, unsigned short sta
 }
 
 /* PLIP_RECEIVE_PACKET --- receive a packet */
-static int plip_receive_packet(struct device *dev, struct net_local *nl,
+static int
+plip_receive_packet(struct device *dev, struct net_local *nl,
                    struct plip_local *snd, struct plip_local *rcv)
 {
        unsigned short status_addr = PAR_STATUS(dev);
@@ -602,7 +596,8 @@ static int plip_receive_packet(struct device *dev, struct net_local *nl,
 
 /* PLIP_SEND --- send a byte (two nibbles)
    Returns OK on success, TIMEOUT when timeout    */
-extern inline int plip_send(unsigned short nibble_timeout, unsigned short data_addr,
+inline static int
+plip_send(unsigned short nibble_timeout, unsigned short data_addr,
          enum plip_nibble_state *ns_p, unsigned char data)
 {
        unsigned char c0;
@@ -648,7 +643,8 @@ extern inline int plip_send(unsigned short nibble_timeout, unsigned short data_a
 }
 
 /* PLIP_SEND_PACKET --- send a packet */
-static int plip_send_packet(struct device *dev, struct net_local *nl,
+static int
+plip_send_packet(struct device *dev, struct net_local *nl,
                 struct plip_local *snd, struct plip_local *rcv)
 {
        unsigned short data_addr = PAR_DATA(dev);
@@ -752,7 +748,8 @@ static int plip_send_packet(struct device *dev, struct net_local *nl,
        return OK;
 }
 
-static int plip_connection_close(struct device *dev, struct net_local *nl,
+static int
+plip_connection_close(struct device *dev, struct net_local *nl,
                      struct plip_local *snd, struct plip_local *rcv)
 {
        cli();
@@ -762,11 +759,16 @@ static int plip_connection_close(struct device *dev, struct net_local *nl,
                mark_bh(NET_BH);
        }
        sti();
+       if (nl->should_relinquish) {
+               nl->should_relinquish = nl->port_owner = 0;
+               parport_release(nl->pardev);
+       }
        return OK;
 }
 
 /* PLIP_ERROR --- wait till other end settled */
-static int plip_error(struct device *dev, struct net_local *nl,
+static int
+plip_error(struct device *dev, struct net_local *nl,
           struct plip_local *snd, struct plip_local *rcv)
 {
        unsigned char status;
@@ -776,6 +778,7 @@ static int plip_error(struct device *dev, struct net_local *nl,
                if (net_debug > 2)
                        printk("%s: reset interface.\n", dev->name);
                nl->connection = PLIP_CN_NONE;
+               nl->should_relinquish = 0;
                dev->tbusy = 0;
                dev->interrupt = 0;
                outb(PAR_INTR_ON, PAR_CONTROL(dev));
@@ -790,11 +793,12 @@ static int plip_error(struct device *dev, struct net_local *nl,
 }
 \f
 /* Handle the parallel port interrupts. */
-static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static void
+plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
        struct device *dev = (struct device *) irq2dev_map[irq];
-       struct net_local *nl = (struct net_local *)dev->priv;
-       struct plip_local *rcv = &nl->rcv_data;
+       struct net_local *nl;
+       struct plip_local *rcv;
        unsigned char c0;
 
        if (dev == NULL) {
@@ -802,6 +806,9 @@ static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
                return;
        }
 
+       nl = (struct net_local *)dev->priv;
+       rcv = &nl->rcv_data;
+
        if (dev->interrupt)
                return;
 
@@ -843,7 +850,8 @@ static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 }
 \f
 /* We don't need to send arp, for plip is point-to-point. */
-static int plip_rebuild_header(struct sk_buff *skb)
+static int
+plip_rebuild_header(struct sk_buff *skb)
 {
        struct device *dev = skb->dev;
        struct net_local *nl = (struct net_local *)dev->priv;
@@ -877,7 +885,8 @@ static int plip_rebuild_header(struct sk_buff *skb)
        return 0;
 }
 
-static int plip_tx_packet(struct sk_buff *skb, struct device *dev)
+static int
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
        struct plip_local *snd = &nl->snd_data;
@@ -885,6 +894,21 @@ static int plip_tx_packet(struct sk_buff *skb, struct device *dev)
        if (dev->tbusy)
                return 1;
 
+       /* We may need to grab the bus */
+       if (!nl->port_owner) {
+               if (parport_claim(nl->pardev))
+                       return 1;
+               nl->port_owner = 1;
+       }
+
+       /* If some higher layer thinks we've missed an tx-done interrupt
+          we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+          itself. */
+       if (skb == NULL) {
+               dev_tint(dev);
+               return 0;
+       }
+
        if (set_bit(0, (void*)&dev->tbusy) != 0) {
                printk("%s: Transmitter access conflict.\n", dev->name);
                return 1;
@@ -921,23 +945,19 @@ static int plip_tx_packet(struct sk_buff *skb, struct device *dev)
    This routine gets exclusive access to the parallel port by allocating
    its IRQ line.
  */
-static int plip_open(struct device *dev)
+static int
+plip_open(struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
        int i;
 
-       if (dev->irq == 0) {
-               printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
-               return -EAGAIN;
+       /* Grab the port */
+       if (!nl->port_owner) {
+               if (parport_claim(nl->pardev)) return -EAGAIN;
+               nl->port_owner = 1;
        }
-       cli();
-       if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) {
-               sti();
-               printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
-               return -EAGAIN;
-       }
-       irq2dev_map[dev->irq] = dev;
-       sti();
+
+       nl->should_relinquish = 0;
 
        /* Clear the data port. */
        outb (0x00, PAR_DATA(dev));
@@ -954,17 +974,21 @@ static int plip_open(struct device *dev)
        /* Fill in the MAC-level header. */
        for (i=0; i < ETH_ALEN - sizeof(u32); i++)
                dev->dev_addr[i] = 0xfc;
+       /* Ugh, this is like death. */
        *(u32 *)(dev->dev_addr+i) = dev->pa_addr;
 
        dev->interrupt = 0;
        dev->start = 1;
        dev->tbusy = 0;
+       irq2dev_map[dev->irq] = dev;
+
        MOD_INC_USE_COUNT;
        return 0;
 }
 
 /* The inverse routine to plip_open (). */
-static int plip_close(struct device *dev)
+static int
+plip_close(struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
        struct plip_local *snd = &nl->snd_data;
@@ -973,12 +997,17 @@ static int plip_close(struct device *dev)
        dev->tbusy = 1;
        dev->start = 0;
        cli();
-       free_irq(dev->irq, NULL);
        irq2dev_map[dev->irq] = NULL;
-       nl->is_deferred = 0;
-       nl->connection = PLIP_CN_NONE;
        sti();
+#ifdef NOTDEF
        outb(0x00, PAR_DATA(dev));
+#endif
+       nl->is_deferred = 0;
+       nl->connection = PLIP_CN_NONE;
+       if (nl->port_owner) {
+               parport_release(nl->pardev);
+               nl->port_owner = 0;
+       }
 
        snd->state = PLIP_PK_DONE;
        if (snd->skb) {
@@ -991,13 +1020,62 @@ static int plip_close(struct device *dev)
                rcv->skb = NULL;
        }
 
+#ifdef NOTDEF
        /* Reset. */
        outb(0x00, PAR_CONTROL(dev));
+#endif
        MOD_DEC_USE_COUNT;
        return 0;
 }
 
-static struct net_device_stats *plip_get_stats(struct device *dev)
+static int
+plip_preempt(void *handle)
+{
+       struct device *dev = (struct device *)handle;
+       struct net_local *nl = (struct net_local *)dev->priv;
+
+       /* Stand our ground if a datagram is on the wire */
+       if (nl->connection != PLIP_CN_NONE) {
+               nl->should_relinquish = 1;
+               return 1;
+       }
+
+       nl->port_owner = 0;     /* Remember that we released the bus */
+       return 0;
+}
+
+static int
+plip_wakeup(void *handle)
+{
+       struct device *dev = (struct device *)handle;
+       struct net_local *nl = (struct net_local *)dev->priv;
+
+       if (nl->port_owner) {
+               /* Why are we being woken up? */
+               printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name);
+               if (!parport_claim(nl->pardev))
+                       /* bus_owner is already set (but why?) */
+                       printk(KERN_DEBUG "%s: I'm broken.\n", dev->name);
+               else
+                       return 1;
+       }
+       
+       if (!(dev->flags & IFF_UP))
+               /* Don't need the port when the interface is down */
+               return 1;
+
+       if (!parport_claim(nl->pardev)) {
+               nl->port_owner = 1;
+               irq2dev_map[dev->irq] = dev;
+               /* Clear the data port. */
+               outb (0x00, PAR_DATA(dev));
+       }
+
+       return 0;
+}
+
+static struct net_device_stats *
+plip_get_stats(struct device *dev)
 {
        struct net_local *nl = (struct net_local *)dev->priv;
        struct net_device_stats *r = &nl->enet_stats;
@@ -1005,7 +1083,8 @@ static struct net_device_stats *plip_get_stats(struct device *dev)
        return r;
 }
 
-static int plip_config(struct device *dev, struct ifmap *map)
+static int
+plip_config(struct device *dev, struct ifmap *map)
 {
        if (dev->flags & IFF_UP)
                return -EBUSY;
@@ -1019,7 +1098,8 @@ static int plip_config(struct device *dev, struct ifmap *map)
        return 0;
 }
 
-static int plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int
+plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
        struct net_local *nl = (struct net_local *) dev->priv;
        struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
@@ -1039,89 +1119,133 @@ static int plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
        return 0;
 }
 \f
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-MODULE_PARM(io, "1-3i");
-MODULE_PARM(irq, "1-3i");
-
-static struct device dev_plip[] = {
-       {
-               "plip0",
-               0, 0, 0, 0,             /* memory */
-               0x3BC, 5,               /* base, irq */
-               0, 0, 0, NULL, plip_init
-       },
-       {
-               "plip1",
-               0, 0, 0, 0,             /* memory */
-               0x378, 7,               /* base, irq */
-               0, 0, 0, NULL, plip_init
-       },
-       {
-               "plip2",
-               0, 0, 0, 0,             /* memory */
-               0x278, 2,               /* base, irq */
-               0, 0, 0, NULL, plip_init
-       }
-};
+static int parport[PLIP_MAX] = { -1, };
+static int timid = 0;
 
-int init_module(void)
+MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
+MODULE_PARM(timid, "1i");
+
+static struct device *dev_plip[PLIP_MAX] = { NULL, };
+
+#ifdef MODULE
+void
+cleanup_module(void)
 {
-       int no_parameters=1;
-       int devices=0;
        int i;
 
-       /* When user feeds parameters, use them */
-       for (i=0; i < 3; i++) {
-               int specified=0;
-
-               if (io[i] != 0) {
-                       dev_plip[i].base_addr = io[i];
-                       specified++;
-               }
-               if (irq[i] != 0) {
-                       dev_plip[i].irq = irq[i];
-                       specified++;
-               }
-               if (specified) {
-                       if (register_netdev(&dev_plip[i]) != 0) {
-                               printk(KERN_INFO "plip%d: Not found\n", i);
-                               return -EIO;
-                       }
-                       no_parameters = 0;
+       for (i=0; i < PLIP_MAX; i++) {
+               if (dev_plip[i]) {
+                       struct net_local *nl =
+                               (struct net_local *)dev_plip[i]->priv;
+                       unregister_netdev(dev_plip[i]);
+                       if (nl->port_owner)
+                               parport_release(nl->pardev);
+                       parport_unregister_device(nl->pardev);
+                       kfree(dev_plip[i]->priv);
+                       kfree(dev_plip[i]->name);
+                       kfree(dev_plip[i]);
+                       dev_plip[i] = NULL;
                }
        }
-       if (!no_parameters)
-               return 0;
+}
+
+#define plip_init  init_module
+
+#else /* !MODULE */
+
+static int parport_ptr = 0;
 
-       /* No parameters.  Default action is probing all interfaces. */
-       for (i=0; i < 3; i++) {
-               if (register_netdev(&dev_plip[i]) == 0)
-                       devices++;
+void plip_setup(char *str, int *ints)
+{
+       /* Ugh. */
+       if (!strncmp(str, "parport", 7)) {
+               int n = simple_strtoul(str+7, NULL, 10);
+               if (parport_ptr < PLIP_MAX)
+                       parport[parport_ptr++] = n;
+               else
+                       printk(KERN_INFO "plip: too many ports, %s ignored.\n",
+                              str);
+       } else if (!strcmp(str, "timid")) {
+               timid = 1;
+       } else {
+               if (ints[0] == 0 || ints[1] == 0) {
+                       /* disable driver on "parport=" or "parport=0" */
+                       parport[0] = -2;
+               } else {
+                       printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n", 
+                              ints[1]);
+               }
        }
-       if (devices == 0) {
-               printk(KERN_INFO "plip: no interfaces found\n");
-               return -EIO;
+}
+
+#endif /* MODULE */
+
+static int inline 
+plip_searchfor(int list[], int a)
+{
+       int i;
+       for (i = 0; i < 3 && list[i] != -1; i++) {
+               if (list[i] == a) return 1;
        }
        return 0;
 }
 
-void cleanup_module(void)
+int
+plip_init(void)
 {
-       int i;
+       struct parport *pb = parport_enumerate();
+       int devices=0;
+       int i=0;
+
+       if (parport[0] == -2)
+               return 0;
+
+       if (parport[0] != -1 && timid) {
+               printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
+               timid = 0;
+       }
 
-       for (i=0; i < 3; i++) {
-               if (dev_plip[i].priv) {
-                       unregister_netdev(&dev_plip[i]);
-                       release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
-                       kfree_s(dev_plip[i].priv, sizeof(struct net_local));
-                       dev_plip[i].priv = NULL;
+       /* When user feeds parameters, use them */
+       while (pb) {
+               if ((parport[0] == -1 && (!timid || !pb->devices)) || 
+                   plip_searchfor(parport, i)) {
+                       if (i == PLIP_MAX) {
+                               printk(KERN_ERR "plip: too many devices\n");
+                               break;
+                       }
+                       dev_plip[i] = kmalloc(sizeof(struct device),
+                                             GFP_KERNEL);
+                       if (!dev_plip[i]) {
+                               printk(KERN_ERR "plip: memory squeeze\n");
+                               break;
+                       }
+                       memset(dev_plip[i], 0, sizeof(struct device));
+                       dev_plip[i]->name = 
+                               kmalloc(strlen("plipXXX"), GFP_KERNEL);
+                       if (!dev_plip[i]->name) {
+                               printk(KERN_ERR "plip: memory squeeze.\n");
+                               kfree(dev_plip[i]);
+                               break;
+                       }
+                       sprintf(dev_plip[i]->name, "plip%d", i);
+                       dev_plip[i]->priv = pb;
+                       if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) {
+                               kfree(dev_plip[i]->name);
+                               kfree(dev_plip[i]);
+                       } else {
+                               devices++;
+                       }
                }
+               i++;
+               pb = pb->next;
+       }
+
+       if (devices == 0) {
+               printk(KERN_INFO "plip: no devices registered\n");
+               return -EIO;
        }
+       return 0;
 }
-#endif /* MODULE */
 \f
 /*
  * Local variables:
index 634abf15210d2662dda3c230337d05b50a502ded..08e9b39c5af1580b82354061c7cad1d3f5347e25 100644 (file)
@@ -1,4 +1,4 @@
-#define RCS_ID "$Id: scc.c,v 1.66 1997/01/08 22:56:06 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.69 1997/04/06 19:22:45 jreuter Exp jreuter $"
 
 #define VERSION "3.0"
 #define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
@@ -16,7 +16,7 @@
 
    ********************************************************************
 
-       Copyright (c) 1993, 1996 Joerg Reuter DL1BKE
+       Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
 
        portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -86,7 +86,9 @@
                  You can use 'kissbridge' if you need a KISS TNC emulator.
 
    961213      - Fixed for Linux networking changes. (G4KLX)
-   960108      - Fixed the remaining problems.
+   970108      - Fixed the remaining problems.
+   970402      - Hopefully fixed the problems with the new *_timer()
+                 routines, added calibration code.
 
    Thanks to all who contributed to this driver with ideas and bug
    reports!
 #include <linux/if_arp.h>
 #include <linux/socket.h>
 
-#include <linux/config.h> /* for CONFIG_PROC_FS */
-
 #include <linux/scc.h>
 #include "z8530.h"
 
@@ -222,6 +222,9 @@ static unsigned char Driver_Initialized = 0;
 static int Nchips = 0;
 static io_port Vector_Latch = 0;
 
+MODULE_AUTHOR("Joerg Reuter <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("scc");
 
 /* ******************************************************************** */
 /* *                   Port Access Functions                         * */
@@ -511,7 +514,7 @@ extern __inline__ void scc_exint(struct scc_channel *scc)
                }
                
                or(scc,R10,ABUNDER);
-               scc_start_tx_timer(scc, t_txdelay, 1);  /* restart transmission */
+               scc_start_tx_timer(scc, t_txdelay, 0);  /* restart transmission */
        }
                
        scc->status = status;
@@ -799,10 +802,10 @@ extern __inline__ void init_brg(struct scc_channel *scc)
  
 static void init_channel(struct scc_channel *scc)
 {
+       del_timer(&scc->tx_t);
+       del_timer(&scc->tx_wdog);
+
        disable_irq(scc->irq);
-       
-       if (scc->tx_t.next)    del_timer(&scc->tx_t);
-       if (scc->tx_wdog.next) del_timer(&scc->tx_wdog);
 
        wr(scc,R4,X1CLK|SDLC);          /* *1 clock, SDLC mode */
        wr(scc,R1,0);                   /* no W/REQ operation */
@@ -982,11 +985,11 @@ static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned
 {
        unsigned long flags;
        
+       
        save_flags(flags);
        cli();
 
-       if (scc->tx_t.next) 
-               del_timer(&scc->tx_t);
+       del_timer(&scc->tx_t);
 
        if (when == 0)
        {
@@ -1000,7 +1003,7 @@ static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned
                add_timer(&scc->tx_t);
        }
        
-       restore_flags(flags);   
+       restore_flags(flags);
 }
 
 static void scc_start_defer(struct scc_channel *scc)
@@ -1010,8 +1013,7 @@ static void scc_start_defer(struct scc_channel *scc)
        save_flags(flags);
        cli();
 
-       if (scc->tx_wdog.next)
-               del_timer(&scc->tx_wdog);
+       del_timer(&scc->tx_wdog);
        
        if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
        {
@@ -1030,8 +1032,7 @@ static void scc_start_maxkeyup(struct scc_channel *scc)
        save_flags(flags);
        cli();
 
-       if (scc->tx_wdog.next)
-               del_timer(&scc->tx_wdog);
+       del_timer(&scc->tx_wdog);
        
        if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
        {
@@ -1184,9 +1185,7 @@ static void t_tail(unsigned long channel)
        save_flags(flags);
        cli();
  
-       if (scc->tx_wdog.next)
-               del_timer(&scc->tx_wdog);
-               
+       del_timer(&scc->tx_wdog);       
        scc_key_trx(scc, TX_OFF);
 
        restore_flags(flags);
@@ -1215,16 +1214,8 @@ static void t_tail(unsigned long channel)
 static void t_busy(unsigned long channel)
 {
        struct scc_channel *scc = (struct scc_channel *) channel;
-       unsigned long flags;
 
-       save_flags(flags);
-       cli();
-       
-       if (scc->tx_t.next)
-               del_timer(&scc->tx_t);
-
-       restore_flags(flags);
-       
+       del_timer(&scc->tx_t);
        scc_lock_dev(scc);
 
        scc_discard_buffers(scc);
@@ -1256,8 +1247,7 @@ static void t_maxkeyup(unsigned long channel)
        scc_lock_dev(scc);
        scc_discard_buffers(scc);
 
-       if (scc->tx_t.next)
-               del_timer(&scc->tx_t);
+       del_timer(&scc->tx_t);
 
        cl(scc, R1, TxINT_ENAB);        /* force an ABORT, but don't */
        cl(scc, R15, TxUIE);            /* count it. */
@@ -1280,16 +1270,9 @@ static void t_maxkeyup(unsigned long channel)
 static void t_idle(unsigned long channel)
 {
        struct scc_channel *scc = (struct scc_channel *) channel;
-       unsigned long flags;
        
-       save_flags(flags);
-       cli();
-       
-       if (scc->tx_wdog.next)
-               del_timer(&scc->tx_wdog);
+       del_timer(&scc->tx_wdog);
 
-       restore_flags(flags);
-       
        scc_key_trx(scc, TX_OFF);
 
        if (scc->kiss.mintime != TIMER_OFF)
@@ -1415,6 +1398,65 @@ static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
 #undef CAST
 #undef SVAL
 
+/* ******************************************************************* */
+/* *                   Send calibration pattern                     * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+       struct scc_channel *scc = (struct scc_channel *) channel;
+       unsigned long flags;
+       
+       save_flags(flags);
+       cli();
+
+       del_timer(&scc->tx_wdog);
+       scc_key_trx(scc, TX_OFF);
+       wr(scc, R6, 0);
+       wr(scc, R7, FLAG);
+       Outb(scc->ctrl,RES_EXT_INT);    /* reset ext/status interrupts */
+       Outb(scc->ctrl,RES_EXT_INT);
+
+       scc_unlock_dev(scc);
+       
+       restore_flags(flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+       unsigned long flags;
+       
+       save_flags(flags);
+       cli();
+
+       scc_lock_dev(scc);
+       scc_discard_buffers(scc);
+
+       del_timer(&scc->tx_wdog);
+
+       scc->tx_wdog.data = (unsigned long) scc;
+       scc->tx_wdog.function = scc_stop_calibrate;
+       scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+       add_timer(&scc->tx_wdog);
+       
+       wr(scc, R6, 0);
+       wr(scc, R7, pattern);
+
+       /* 
+        * Don't know if this works. 
+        * Damn, where is my Z8530 programming manual...? 
+        */
+
+       Outb(scc->ctrl,RES_EXT_INT);    /* reset ext/status interrupts */
+       Outb(scc->ctrl,RES_EXT_INT);
+
+       scc_key_trx(scc, TX_ON);
+       
+       restore_flags(flags);
+}
+
 /* ******************************************************************* */
 /* *           Init channel structures, special HW, etc...          * */
 /* ******************************************************************* */
@@ -1644,26 +1686,8 @@ static int scc_net_tx(struct sk_buff *skb, struct device *dev)
        struct scc_channel *scc = (struct scc_channel *) dev->priv;
        unsigned long flags;
        char kisscmd;
-       long ticks;
        
-       if (dev->tbusy)
-       {
-               ticks = (signed) (jiffies - dev->trans_start);
-
-               if (ticks < scc->kiss.maxdefer*HZ || scc == NULL)
-                       return 1;
-
-               /* 
-                * Throw away transmission queue. 
-                */
-
-               if (scc->tx_wdog.next)
-                       del_timer(&scc->tx_wdog);
-               t_busy((unsigned long) scc);
-                dev->trans_start = jiffies;
-        }
-        
-       if (scc == NULL || scc->magic != SCC_MAGIC)
+       if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy)
        {
                dev_kfree_skb(skb, FREE_WRITE);
                return 0;
@@ -1692,10 +1716,13 @@ static int scc_net_tx(struct sk_buff *skb, struct device *dev)
        save_flags(flags);
        cli();
        
+       if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
+       {
+               struct sk_buff *skb_del;
+               skb_del = __skb_dequeue(&scc->tx_queue);
+               dev_kfree_skb(skb_del, FREE_WRITE);
+       }
        __skb_queue_tail(&scc->tx_queue, skb);
-       
-       if (skb_queue_len(&scc->tx_queue) == MAXQUEUE)
-               scc_lock_dev(scc);
 
        dev->trans_start = jiffies;
 
@@ -1729,6 +1756,7 @@ static int scc_net_tx(struct sk_buff *skb, struct device *dev)
  * SIOCSCCGKISS                - get level 1 parameter arg: (struct scc_kiss_cmd *) arg
  * SIOCSCCSKISS                - set level 1 parameter arg: (struct scc_kiss_cmd *) arg
  * SIOCSCCGSTAT                - get driver status     arg: (struct scc_stat *) arg
+ * SIOCSCCCAL          - send calib. pattern   arg: (struct scc_calibrate *) arg
  */
 
 static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
@@ -1736,6 +1764,7 @@ static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
        struct scc_kiss_cmd kiss_cmd;
        struct scc_mem_config memcfg;
        struct scc_hw_config hwcfg;
+       struct scc_calibrate cal;
        int chan;
        unsigned char device_name[10];
        void *arg;
@@ -1942,6 +1971,14 @@ static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
                        if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
                                return -EINVAL;
                        return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
+               
+               case SIOCSCCCAL:
+                       if (!suser()) return -EPERM;
+                       if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
+                               return -EINVAL;
+
+                       scc_start_calibrate(scc, cal.time, cal.pattern);
+                       return 0;
 
                default:
                        return -ENOIOCTLCMD;
@@ -2164,7 +2201,7 @@ int init_module(void)
        result = scc_init();
 
        if (result == 0)
-               printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
+               printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
                
        return result;
 }
diff --git a/drivers/pnp/BUGS-parport b/drivers/pnp/BUGS-parport
new file mode 100644 (file)
index 0000000..db40b65
--- /dev/null
@@ -0,0 +1,15 @@
+Currently known (or at least suspected) bugs in parport:
+
+o /proc/parport is buggy under 2.0.29 (ls /proc/parport/0 gives no such
+  file or directory).  David has suggested a fix for this.  [/proc/parport
+  has been temporarily taken out]
+
+o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
+
+o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
+  the resulting kernel won't link.
+
+o IEEE1284 code does not do the terminating handshake after transfers, which
+  seems to upset some devices.
+
+o lp doesn't allow you to read status while printing is in progress.
diff --git a/drivers/pnp/Config.in b/drivers/pnp/Config.in
new file mode 100644 (file)
index 0000000..44bb52d
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Plug and Play configuration
+#
+
+mainmenu_option next_comment
+comment 'Plug and Play support'
+bool 'Plug and Play support' CONFIG_PNP
+
+if [ "$CONFIG_PNP" = "y" ]; then
+  if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+    bool '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT_AUTOPROBE
+  fi
+fi
+
+endmenu
diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile
new file mode 100644 (file)
index 0000000..c224c98
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Makefile for the kernel Plug-and-Play device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# Note 3! Plug and Play is the Borg.  We have assimilated some other
+# drivers in the `char', `net' and `scsi' directories, but left them
+# there to allay suspicion.
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := pnp.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PNP_PARPORT),y)
+  L_OBJS += parport_share.o parport_procfs.o
+  ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+    L_OBJS += parport_probe.o
+  endif
+  LX_OBJS += parport_init.o
+else
+  ifeq ($(CONFIG_PNP_PARPORT),m)
+    MI_OBJS += parport_share.o parport_procfs.o
+    ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+      MI_OBJS += parport_probe.o
+    endif
+    MIX_OBJS += parport_init.o
+    M_OBJS += parport.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+parport.o: $(MI_OBJS) $(MIX_OBJS)
+       $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff --git a/drivers/pnp/TODO-parport b/drivers/pnp/TODO-parport
new file mode 100644 (file)
index 0000000..cd99017
--- /dev/null
@@ -0,0 +1,43 @@
+Things to be done.
+
+0. Fix the bugs (see BUGS-parport).
+
+1. Write a /proc interface for parport.  As a starting point, we could
+   probably have something like this:
+
+   /proc/parport/N/hardware
+
+        Details of the port hardware - chipset, capabilities and so on.
+        Read-only.
+
+   /proc/parport/N/irq
+
+        IRQ number of the port.  Read-write.
+
+   /proc/parport/N/devices
+
+        List of devices connected to this bus, with the currently active
+        one marked in some way.  Probably you'd have the device in the
+        first column, and '*' (for the current device) or '-' (for a
+        lurker) in the second column.
+
+   NOTE: The directory structure has been coded -- but the files are
+         missing ...
+
+2. Proper documentation.
+
+3. Overhaul lp.c:
+
+   a) It's a mess, and there is a lot of code duplication.
+
+   b) ECP support would be nice.  This can only work if both the port and
+      the printer support it.
+
+   c) Errors could do with being handled better.  There's no point logging a
+      message every 10 seconds when the printer is out of paper. 
+
+   d) Handle status readback automatically.  IEEE1284 printers can post status
+      bits when they have something to say.  We should read out and deal 
+      with (maybe just log) whatever the printer wants to tell the world.
+
+4. Assimilate more drivers.
diff --git a/drivers/pnp/parport_init.c b/drivers/pnp/parport_init.c
new file mode 100644 (file)
index 0000000..a61d330
--- /dev/null
@@ -0,0 +1,815 @@
+/* $Id: parport_init.c,v 1.1.2.4 1997/04/01 18:19:10 phil Exp $
+ * Parallel-port initialisation code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *         Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static int io[PARPORT_MAX] = { 0, };
+static int irq[PARPORT_MAX] = { -1, };
+static int dma[PARPORT_MAX] = { -1, };
+
+/******************************************************
+ *  DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+       int tmp = 0;
+       int i,retv;
+       
+       for (i = 0; i < 8; i++) {
+               retv = request_dma(i, "probe");
+               if (retv)
+                       continue;
+               tmp |= 1 << i;
+
+               cli();
+               disable_dma(i);
+               clear_dma_ff(i);
+               set_dma_addr(i, virt_to_bus(buff));
+               set_dma_count(i, size);
+               set_dma_mode(i, DMA_MODE_READ);
+               sti();
+       }
+
+       return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+       int i;
+       
+       for (i = 0; i < 8; i++)
+               if (dma & (1 << i)) {
+               cli();
+               enable_dma(i);
+               sti();
+           }
+
+       return dma;
+}
+
+static int parport_detect_dma_transfer(int dma,int size)
+{
+       int i,n,retv;
+       int count=0;
+
+       retv = -1;
+       for (i = 0; i < 8; i++)
+               if (dma & (1 << i)) {
+                       disable_dma(i);
+                       clear_dma_ff(i);
+                       n = get_dma_residue(i);
+                       if (n != size) {
+                               retv = i;
+                               if (count > 0) {
+                                       retv = -1;      /* Multiple DMA's */
+                                       printk("Multiple DMA detected.\n");
+                               }
+                               count++;
+                       }
+                       free_dma(i);
+               }
+
+       return retv;    
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+       int dma;
+
+       w_ecr(pb,0xE0); /* Configuration MODE */
+       
+       dma = r_cnfgB(pb) & 0x07;
+
+       w_ecr(pb,pb->ecr);
+       
+       if( dma == 0 || dma == 4 ) /* Jumper selection */
+               return -1;
+       else
+               return dma;
+}
+
+/* Only called if port support ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of -1 is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ *    1Byte DMA transfer
+ * else // 4 < DMA < 8
+ *    2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+       int dma,retv;
+       int dsr,dsr_read;
+       char *buff;
+
+       retv = programmable_dma_support(pb);
+       if( retv != -1 )
+               return retv;
+       
+       buff = kmalloc(16, GFP_KERNEL | GFP_DMA);
+       if( !buff ){
+           printk("parport: memory squezze\n");
+           return -1;
+       }
+       
+       dsr = r_ctr(pb);
+       dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
+
+       w_ecr(pb, 0xc0);           /* ECP MODE */
+       w_ctr(pb, dsr_read );
+       dma=parport_prepare_dma(buff,8);
+       w_ecr(pb, 0xd8);           /* ECP FIFO + enable DMA */
+       parport_enable_dma(dma);
+       udelay(30);           /* Give some for DMA tranfer */
+       retv = parport_detect_dma_transfer(dma,8);
+       
+       /*
+        * National Semiconductors only supports DMA tranfers
+        * in ECP MODE
+        */
+       if( retv == -1 ){
+               w_ecr(pb, 0x60);           /* ECP MODE */
+               w_ctr(pb, dsr_read );
+               dma=parport_prepare_dma(buff,8);
+               w_ecr(pb, 0x68);           /* ECP FIFO + enable DMA */
+               parport_enable_dma(dma);
+               udelay(30);           /* Give some for DMA tranfer */
+               retv = parport_detect_dma_transfer(dma,8);
+       }
+       
+       kfree(buff);
+       
+       w_ctr(pb, pb->ctr);
+       w_ecr(pb, pb->ecr);
+
+       return retv;
+}
+/******************************************************
+ *  MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+       int r;
+
+    if( !(r_str(pb) & 0x01) )
+               return 1;
+
+       /* To clear timeout some chip requiere double read */
+       r_str(pb);
+       r = r_str(pb);
+       w_str(pb, r | 0x01); /* Some reset by writing 1 */
+       w_str(pb, r & 0xfe); /* Others by writing 0 */
+       r = r_str(pb);
+
+       return !(r & 0x01);
+}
+
+
+/*
+ * Checks for por existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+       int r,rr;
+       
+       /* Do a simple read-write test to make sure the port exists. */
+       w_dtr(pb, 0xaa);
+       r = r_dtr(pb);
+       
+       w_dtr(pb, 0x55);
+       rr = r_dtr(pb);
+       
+       if (r != 0xaa || rr != 0x55) {
+               return 0;
+       }
+       
+       return PARPORT_MODE_SPP;
+}
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+       int r;
+
+       if( pb->base == 0x3BC )
+               return 0;
+       
+       r= r_ctr(pb);   
+       if( (r_ecr(pb) & 0x03) == (r & 0x03) ){
+               w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+               r= r_ctr(pb);   
+               if( (r_ecr(pb) & 0x03) == (r & 0x03) )
+                       return 0; /* Sure that no ECR register exists */
+       }
+       
+       if( (r_ecr(pb) & 0x03 ) != 0x01 )
+               return 0;
+
+       w_ecr(pb,0x34);
+       if( r_ecr(pb) != 0x35 )
+               return 0;
+
+       w_ecr(pb,pb->ecr);
+       w_ctr(pb,pb->ctr);
+       
+       return PARPORT_MODE_ECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+       int i;
+       
+       if( !(pb->modes & PARPORT_MODE_ECR) )
+               return 0;
+       /*
+        * Usign LGS chipset it uses ECR register, but
+        * it doesn't support ECP or FIFO MODE
+        */
+       
+       w_ecr(pb,0xc0); /* TEST FIFO */
+       for( i=0 ; i < 1024 && (r_ecr(pb) & 0x01) ; i++ )
+               w_fifo(pb, 0xaa);
+
+       w_ecr(pb,pb->ecr);
+
+       if( i >= 1024 )
+               return 0;
+       
+       return PARPORT_MODE_ECP;
+}
+
+/* EPP mode detection
+ * Theory:
+ *     Bit 0 of STR is the EPP timeout bit, this bit is 0
+ *     when EPP is possible and is set high when an EPP timeout
+ *     occurs (EPP uses the HALT line to stop the CPU while it does
+ *     the byte transfer, an EPP timeout occurs if the attached
+ *     device fails to respond after 10 micro seconds).
+ *
+ *     This bit is cleared by either reading it (National Semi)
+ *     or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ *     This bit is always high in non EPP modes.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+       if( pb->base == 0x3BC )
+               return 0;
+
+       /* If EPP timeout bit clear then EPP available */
+       if( !epp_clear_timeout(pb) )
+               return 0;  /* No way to clear timeout */
+
+       w_ctr(pb, r_ctr(pb) | 0x20);
+       w_ctr(pb, r_ctr(pb) | 0x10);
+       epp_clear_timeout(pb);
+       
+       r_epp(pb);
+       udelay(30);  /* Wait for possible EPP timeout */
+       
+       if( r_str(pb) & 0x01 ){
+               epp_clear_timeout(pb);
+               return PARPORT_MODE_EPP;
+       }
+
+       return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+       int mode;
+
+       if( !(pb->modes & PARPORT_MODE_ECR) )
+               return 0;
+       
+       /* Search for SMC style EPP+ECP mode */
+       w_ecr(pb, 0x80);
+       
+       mode = parport_EPP_supported(pb);
+
+       w_ecr(pb,pb->ecr);
+       
+       if( mode )
+               return PARPORT_MODE_ECPEPP;
+       
+       return 0;
+}
+
+/* Detect LP_PS2 support
+ * Bit 5 (0x20) sets the PS/2 data direction, setting this high
+ * allows us to read data from the data lines, old style SPP ports
+ * will return 0xff.  This may not be reliable if there is a
+ * peripheral attached to the port. 
+ */
+static int parport_PS2_supported(struct parport *pb)
+{
+       int r,rr;
+
+       epp_clear_timeout(pb);
+
+       w_ctr(pb, pb->ctr | 0x20);      /* Tri-state the buffer */
+       
+       w_dtr(pb, 0xAA);
+       r = r_dtr(pb);
+
+       w_dtr(pb, 0x55);
+       rr = r_dtr(pb);
+       
+       w_ctr(pb, pb->ctr);     /* Reset CTR register */
+
+       if (r != 0xAA || rr != 0x55 )
+               return PARPORT_MODE_PS2;
+       
+       return 0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+       int mode;
+
+       if( !(pb->modes & PARPORT_MODE_ECR) )
+               return 0;
+       
+       w_ecr(pb, 0x20);
+       
+       mode = parport_PS2_supported(pb);
+
+       w_ecr(pb,pb->ecr);
+       
+       if (mode)
+               return PARPORT_MODE_ECPPS2;
+       
+       return 0;
+}
+
+/******************************************************
+ *  IRQ detection section:
+ */
+/*
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ */
+static int intr_vote[16];
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+       intr_vote[irq]++;
+       return;
+}
+
+static long open_intr_election(void)
+{
+       long tmp = 0;
+       int i;
+
+       /* We ignore the timer - irq 0 */
+       for (i = 1; i < 16; i++) {
+               intr_vote[i] = 0;
+               if (request_irq(i, parport_vote_intr_func,
+                      SA_INTERRUPT, "probe", intr_vote) == 0)
+                       tmp |= 1 << i;
+       }
+       return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+       long max_vote = 0;
+       int irq = -1;
+       int i;
+
+       /* We ignore the timer - irq 0 */
+       for (i = 1; i < 16; i++)
+               if (tmp & (1 << i)) {
+                       if (intr_vote[i] > max_vote) {
+                               if (max_vote)
+                                       return -1;
+                               max_vote = intr_vote[i];
+                               irq = i;
+                       }
+                       free_irq(i, intr_vote);
+               }
+       return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+       int irq;
+
+       w_ecr(pb,0xE0); /* Configuration MODE */
+       
+       irq = (r_cnfgB(pb) >> 3) & 0x07;
+
+       switch(irq){
+         case 2:
+                 irq = 9;
+                 break;
+         case 7:
+                 irq = 5;
+                 break;
+         case 0:
+                 irq = -1;
+                 break;
+         default:
+                 irq += 7;
+       }
+                 
+       w_ecr(pb,pb->ecr);
+       
+       return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+       int irqs,i;
+               
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = open_intr_election();
+               
+       w_ecr(pb, 0x00);            /* Reset FIFO */
+       w_ctr(pb, pb->ctr );    /* Force direction = 0 */
+       w_ecr(pb, 0xd0);            /* TEST FIFO + nErrIntrEn */
+
+       /* If Full FIFO sure that WriteIntrThresold is generated */
+       for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
+               w_fifo(pb, 0xaa);
+       }
+               
+       pb->irq = close_intr_election(irqs);
+       if (pb->irq == 0)
+               pb->irq = -1;   /* No interrupt detected */
+       
+       w_ecr(pb, pb->ecr);
+
+       return pb->irq;
+}
+
+/*
+ * It's called only if supports EPP on National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond 
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+       int irqs;
+
+#ifndef ADVANCED_DETECT
+       return -1;
+#endif
+       
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = open_intr_election();
+
+       if( pb->modes & PARPORT_MODE_ECR )
+               w_ecr(pb, r_ecr(pb) | 0x10 );
+       
+       epp_clear_timeout(pb);
+       w_ctr(pb, r_ctr(pb) | 0x20);
+       w_ctr(pb, r_ctr(pb) | 0x10);
+       epp_clear_timeout(pb);
+
+       /*  Device isn't expecting an EPP read
+        * and generates an IRQ.
+        */
+       r_epp(pb);
+       udelay(20);
+
+       pb->irq = close_intr_election(irqs);
+       if (pb->irq == 0)
+               pb->irq = -1;   /* No interrupt detected */
+       
+       w_ctr(pb,pb->ctr);
+       
+       return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+       int irqs;
+
+#ifndef ADVANCED_DETECT
+       return -1;
+#endif
+
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = probe_irq_on();
+
+       if( pb->modes & PARPORT_MODE_ECR )
+               w_ecr(pb, 0x10 );
+
+       w_dtr(pb,0x00);
+       w_ctr(pb,0x00);
+       w_ctr(pb,0x0c);
+       udelay(5);
+       w_ctr(pb,0x0d);
+       udelay(5);
+       w_ctr(pb,0x0c);
+       udelay(25);
+       w_ctr(pb,0x08);
+       udelay(25);
+       w_ctr(pb,0x0c);
+       udelay(50);
+
+       pb->irq = probe_irq_off(irqs);
+       if (pb->irq <= 0)
+               pb->irq = -1;   /* No interrupt detected */
+       
+       w_ctr(pb,pb->ctr);
+       
+       return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When LP_ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+       if( pb->modes & PARPORT_MODE_ECR )
+               pb->irq = programmable_irq_support(pb);
+
+       if( pb->modes & PARPORT_MODE_ECP )
+               pb->irq = irq_probe_ECP(pb);
+                       
+       if( pb->irq == -1 && (pb->modes & PARPORT_MODE_ECPEPP)){
+               w_ecr(pb,0x80);
+               pb->irq = irq_probe_EPP(pb);
+               w_ecr(pb,pb->ecr);
+       }
+
+       epp_clear_timeout(pb);
+
+       if( pb->irq == -1 && (pb->modes & PARPORT_MODE_EPP))
+               pb->irq = irq_probe_EPP(pb);
+
+       epp_clear_timeout(pb);
+
+       if( pb->irq == -1 )
+               pb->irq = irq_probe_SPP(pb);
+
+       return pb->irq;
+}
+
+
+int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
+{
+       /* Check some parameters */
+       if (dma < -2) {
+               printk("parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
+               return 0;
+       }
+
+       if (irq < -2) {
+               printk("parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
+               return 0;
+       }
+       
+       /* Init our structure */
+       memset(pb, 0, sizeof(struct parport));
+       pb->base = base;
+       pb->irq = irq;
+       pb->dma = dma;
+       pb->modes = 0;
+       pb->next = NULL;
+       pb->devices = pb->cad = pb->lurker = NULL;
+       pb->flags = 0;
+
+       /* Before we start, set the control registers to something sensible. */
+       pb->ecr = 0xc;
+       pb->ctr = 0xc;
+
+       pb->name = kmalloc(15, GFP_KERNEL);
+       if (!pb->name) {
+               printk("parport: memory squeeze\n");
+               return 0;
+       }
+       sprintf(pb->name, "parport%d", count);
+
+       if (!parport_SPP_supported(pb)) {
+               epp_clear_timeout(pb);
+               if (!parport_SPP_supported(pb)) {
+                       kfree(pb->name);
+                       return 0;
+               }
+       }
+
+       pb->modes |= PARPORT_MODE_SPP;  /* All ports support SPP mode. */
+       pb->modes |= parport_ECR_present(pb);   
+       pb->modes |= parport_ECP_supported(pb);
+       pb->modes |= parport_PS2_supported(pb);
+       pb->modes |= parport_ECPPS2_supported(pb);
+       pb->modes |= parport_EPP_supported(pb);
+       pb->modes |= parport_ECPEPP_supported(pb);
+
+       /* Now register regions */
+       if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) && 
+           (check_region(pb->base, 8))) {
+               printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
+               pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
+       }
+       pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
+
+       request_region(pb->base, pb->size, pb->name);
+       if (pb->modes & PARPORT_MODE_ECR)
+               request_region(pb->base+0x400, 3, pb->name);
+
+       /* DMA check */
+       if (pb->modes & PARPORT_MODE_ECP) {
+               if (pb->dma == -1)
+                       pb->dma = parport_dma_probe(pb);
+               else if (pb->dma == -2)
+                       pb->dma = -1;
+       }
+
+       /* IRQ check */
+       if (pb->irq == -1)
+               pb->irq = parport_irq_probe(pb);
+       else if (pb->irq == -2)
+               pb->irq = -1;
+
+       return 1;
+}
+
+#ifndef MODULE
+static int parport_setup_ptr = 0;
+
+void parport_setup(char *str, int *ints)
+{
+       if (ints[0] == 0 || ints[1] == 0) {
+               /* Disable parport if "parport=" or "parport=0" in cmdline */
+               io[0] = -2; 
+               return;
+       }
+       if (parport_setup_ptr < PARPORT_MAX) {
+               io[parport_setup_ptr] = ints[1];
+               if (ints[0]>1) {
+                       irq[parport_setup_ptr] = ints[2];
+                       if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+               }
+               parport_setup_ptr++;
+       } else {
+               printk(KERN_ERR "parport=0x%x", ints[1]);
+               if (ints[0]>1) {
+                       printk(",%d", ints[2]);
+                       if (ints[0]>2) printk(",%d", ints[3]);
+               }
+               printk(" ignored, too many ports.\n");
+       }
+}
+#endif
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+extern void parport_probe_one(struct parport *port);
+#endif
+
+#ifdef MODULE
+MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+
+int init_module(void)
+#else
+int pnp_parport_init(void)
+#endif                         /* MODULE */
+{
+       struct parport *pb;
+
+       printk(KERN_INFO "Parallel port sharing: %s\n",
+              "$Revision: 1.1.2.4 $");
+
+       if (io[0] == -2) return 1; 
+
+       /* Register /proc/parport */
+       parport_proc_register(NULL);
+
+       /* Run probes to ensure parport does exist */
+#define PORT(a,b,c) \
+               if ((pb = parport_register_port((a), (b), (c))))  \
+                       parport_destroy(pb); 
+       if (io[0]) {
+               /* If the user specified any ports, use them */
+               int i;
+               for (i = 0; io[i] && i < PARPORT_MAX; i++) {
+                       PORT(io[i], irq[i], dma[i]);
+               }
+       } else {
+               /* Go for the standard ports. */
+               PORT(0x378, -1, -1);
+               PORT(0x278, -1, -1);
+               PORT(0x3bc, -1, -1);
+#undef PORT
+       }
+
+       for (pb = parport_enumerate(); pb; pb = pb->next)
+               parport_probe_one(pb);
+
+       return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       struct parport *port, *next;
+   
+       for (port = parport_enumerate(); port; port = next) {
+               next = port->next;
+               parport_destroy(port);
+               parport_proc_unregister(port);
+               kfree(port->name);
+               kfree(port);
+       }
+       
+       parport_proc_unregister(NULL);
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_destroy);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+EXPORT_SYMBOL(parport_probe);
+EXPORT_SYMBOL(parport_probe_one);
+#endif
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+       MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+#endif
+}
diff --git a/drivers/pnp/parport_ll_io.h b/drivers/pnp/parport_ll_io.h
new file mode 100644 (file)
index 0000000..c2592af
--- /dev/null
@@ -0,0 +1,21 @@
+/* $Id: parport_ll_io.h,v 1.1.2.1 1997/03/26 13:01:09 phil Exp $ 
+ * David Campbell's "favourite IO routines" for parallel ports
+ */
+
+#define r_dtr(x)       inb((x)->base)
+#define r_str(x)       inb((x)->base+1)
+#define r_ctr(x)       inb((x)->base+2)
+#define r_epp(x)       inb((x)->base+4)
+#define r_fifo(x)      inb((x)->base+0x400)
+#define r_ecr(x)       inb((x)->base+0x402)
+#define r_cnfgA(x)     inb((x)->base+0x400)
+#define r_cnfgB(x)     inb((x)->base+0x401)
+
+#define w_dtr(x,y)     outb((y), (x)->base)
+#define w_str(x,y)     outb((y), (x)->base+1)
+#define w_ctr(x,y)     outb((y), (x)->base+2)
+#define w_epp(x,y)     outb((y), (x)->base+4)
+#define w_fifo(x,y)    outb((y), (x)->base+0x400)
+#define w_ecr(x,y)     outb((y), (x)->base+0x402)
+#define w_cnfgA(x,y)   outb((y), (x)->base+0x400)
+#define w_cnfgB(x,y)   outb((y), (x)->base+0x401)
diff --git a/drivers/pnp/parport_probe.c b/drivers/pnp/parport_probe.c
new file mode 100644 (file)
index 0000000..e180b3d
--- /dev/null
@@ -0,0 +1,266 @@
+/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $ 
+ * Parallel port device probing code
+ * 
+ * Authors:    Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ *             Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+
+#include <linux/lp.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG_PROBE
+
+static inline int read_nibble(struct parport *port) 
+{
+       unsigned char i;
+       i = parport_r_status(port)>>3;
+       i&=~8;
+       if ( ( i & 0x10) == 0) i|=8;
+       return(i & 0x0f);
+}
+
+static void read_terminate(struct parport *port) {
+       parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+       /* SelectIN high, AutoFeed low */
+       if (parport_wait_peripheral(port, 0x80, 0)) 
+               /* timeout, SelectIN high, Autofeed low */
+               return;
+       parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+       /* AutoFeed high */
+       parport_wait_peripheral(port, 0x80, 0x80);
+       /* no timeout possible, Autofeed low, SelectIN high */
+       parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+       return;
+}
+
+static long read_polled(struct parport *port, char *buf, 
+                          unsigned long length)
+{
+       int i;
+       char *temp=buf;
+       int count = 0;
+       unsigned char z=0;
+       unsigned char Byte=0;
+
+       for (i=0; ; i++) {
+               parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+               if (parport_wait_peripheral(port, 0x40, 0)) {
+                       printk("%s: read1 timeout.\n", port->name);
+                       parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+                       break;
+               }
+               z = read_nibble(port);
+               parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+               if (parport_wait_peripheral(port, 0x40, 0x40)) {
+                       printk("%s: read2 timeout.\n", port->name);
+                       break;
+               }
+               if (( i & 1) != 0) {
+                       Byte= (Byte | z<<4);
+                       if (temp) 
+                               *(temp++) = Byte; 
+                       if (count++ == length)
+                               temp = NULL;
+                       /* Does the error line indicate end of data? */
+                       if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP) 
+                               break;
+               } else Byte=z;
+       }
+       read_terminate(port);
+       return count; 
+}
+
+static struct wait_queue *wait_q = NULL;
+
+static int wakeup(void *ref)
+{
+       struct ppd **dev = (struct ppd **)ref;
+       
+       if (!wait_q || parport_claim(*dev))
+               return 1;
+
+       wake_up(&wait_q);
+       return 0;
+}
+
+int parport_probe(struct parport *port, char *buffer, int len)
+{
+       struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
+                                                 NULL, wakeup, NULL,
+                                                 PARPORT_DEV_TRAN, &dev);
+
+       int result = 0;
+
+       if (!dev) {
+               printk("%s: unable to register for probe.\n", port->name);
+               return -EINVAL;
+       }
+
+       if (parport_claim(dev)) {
+               sleep_on(&wait_q);
+               wait_q = NULL;
+       }
+
+       switch (parport_ieee1284_nibble_mode_ok(port, 4)) {
+       case 1:
+               current->state=TASK_INTERRUPTIBLE;
+               current->timeout=jiffies+1;
+               schedule();     /* HACK: wait 10ms because printer seems to
+                                * ack wrong */
+               result = read_polled(port, buffer, len);
+               break;
+       case 0:
+               result = -EIO;
+               break;
+       }
+
+       parport_release(dev);
+       parport_unregister_device(dev);
+
+       return result;
+}
+
+static struct {
+       char *token;
+       char *descr;
+} classes[] = {
+       { "",        "Legacy device" },
+       { "PRINTER", "Printer" }, 
+       { "MODEM",   "Modem" },
+       { "NET",     "Network device" },
+       { "HDC",     "Hard disk" },
+       { "PCMCIA",  "PCMCIA" },
+       { "MEDIA",   "Multimedia device" },
+       { "FDC",     "Floppy disk" },
+       { "PORTS",   "Ports" },
+       { "SCANNER", "Scanner" },
+       { "DIGICAM", "Digital camera" },
+       { "",        "Unknown device" },
+       { "",        "Unspecified" }, 
+       { NULL,      NULL }
+};
+
+static char *strdup(char *str)
+{
+       int n = strlen(str)+1;
+       char *s = kmalloc(n, GFP_KERNEL);
+       if (!s) return NULL;
+       return strcpy(s, str);
+}
+
+static void parse_data(struct parport *port, char *str)
+{
+       char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+       char *p = txt, *q; 
+       int guessed_class = PARPORT_CLASS_UNSPEC;
+
+       if (!txt) {
+               printk("%s probe: memory squeeze\n", port->name);
+               return;
+       }
+       strcpy(txt, str);
+       while (p) {
+               char *sep; 
+               q = strchr(p, ';');
+               if (q) *q = 0;
+               sep = strchr(p, ':');
+               if (sep) {
+                       char *u = p;
+                       *(sep++) = 0;
+                       while (*u) {
+                               *u = toupper(*u);
+                               u++;
+                       }
+                       if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+                               port->probe_info.mfr = strdup(sep);
+                       } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+                               port->probe_info.model = strdup(sep);
+                       } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+                               int i;
+                               for (u = sep; *u; u++)
+                                       *u = toupper(*u);
+                               for (i = 0; classes[i].token; i++) {
+                                       if (!strcmp(classes[i].token, sep)) {
+                                               port->probe_info.class = i;
+                                               goto rock_on;
+                                       }
+                               }
+                               printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+                               port->probe_info.class = PARPORT_CLASS_OTHER;
+                       } else if (!strcmp(p, "CMD") || !strcmp(p, "COMMAND SET")) {
+                               /* if it speaks printer language, it's
+                                  probably a printer */
+                               if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+                                       guessed_class = PARPORT_CLASS_PRINTER;
+                       } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+                               port->probe_info.description = strdup(sep);
+                       }
+               }
+       rock_on:
+               if (q) p = q+1; else p=NULL;
+       }
+
+       /* If the device didn't tell us its class, maybe we have managed to
+          guess one from the things it did say. */
+       if (port->probe_info.class == PARPORT_CLASS_UNSPEC)
+               port->probe_info.class = guessed_class;
+
+       kfree(txt);
+}
+
+static void pretty_print(struct parport *port)
+{
+       printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr);
+       if (port->probe_info.class) {
+               printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr);
+       }
+       printk("\n");
+}
+
+void parport_probe_one(struct parport *port)
+{
+       char *buffer = kmalloc(2048, GFP_KERNEL);
+       int r;
+
+       port->probe_info.model = "Unknown device";
+       port->probe_info.mfr = "Unknown vendor";
+       port->probe_info.description = NULL;
+       port->probe_info.class = PARPORT_CLASS_UNSPEC;
+
+       if (!buffer) {
+               printk(KERN_ERR "%s probe: Memory squeeze.\n", port->name);
+               return;
+       }
+
+       r = parport_probe(port, buffer, 2047);
+
+       if (r < 0) {
+               printk(KERN_INFO "%s: no IEEE-1284 device present.\n",
+                      port->name);
+               port->probe_info.class = PARPORT_CLASS_LEGACY;
+       } else if (r == 0) {
+               printk(KERN_INFO "%s: no ID data returned by device.\n",
+                      port->name);
+       } else {
+               buffer[r] = 0; 
+#ifdef DEBUG_PROBE
+               printk("%s id: %s\n", port->name, buffer+2);
+#endif
+               parse_data(port, buffer+2); 
+               pretty_print(port);
+       }
+       kfree(buffer);
+}
diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c
new file mode 100644 (file)
index 0000000..952060f
--- /dev/null
@@ -0,0 +1,126 @@
+/* $Id: parport_procfs.c,v 1.1.2.2 1997/03/26 17:50:36 phil Exp $
+ * Parallel port /proc interface code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <linux/parport.h>
+
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+
+/************************************************/
+static long proc_readparport(struct inode * inode, struct file * file,
+                                                        char * buf, unsigned long count)
+{
+       printk("proc_readparport\n");
+       return 0;
+}
+
+static long proc_writeparport(struct inode * inode, struct file * file,
+                                                         const char * buf, unsigned long count)
+{
+       printk("proc_writeparport\n");
+       
+       return 0;
+}
+
+static long long proc_parportlseek(struct inode * inode, struct file * file, 
+                               long long offset, int orig)
+{
+    switch (orig) {
+    case 0:
+       file->f_pos = offset;
+       return(file->f_pos);
+    case 1:
+       file->f_pos += offset;
+       return(file->f_pos);
+    case 2:
+       return(-EINVAL);
+    default:
+       return(-EINVAL);
+    }
+}
+
+static struct file_operations proc_dir_operations = {
+    proc_parportlseek, /* lseek   */
+    proc_readparport,  /* read    */
+    proc_writeparport, /* write   */
+    proc_readdir,          /* readdir */
+    NULL,              /* poll    */
+    NULL,              /* ioctl   */
+    NULL,              /* mmap    */
+    NULL,              /* no special open code    */
+    NULL,              /* no special release code */
+    NULL               /* can't fsync */
+};
+
+/************************************************/
+static struct inode_operations parport_proc_dir_inode_operations = {
+       &proc_dir_operations,   /* default net directory file-ops */
+       NULL,                   /* create */
+       proc_lookup,    /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL                    /* permission */
+};
+
+static struct proc_dir_entry proc_root_parport = {
+       PROC_PARPORT, 7, "parport",
+       S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+       0, &parport_proc_dir_inode_operations,
+       NULL, NULL,
+       NULL, &proc_root, NULL,
+       NULL, NULL
+};
+#endif
+
+int parport_proc_register(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+       return proc_register(&proc_root, &proc_root_parport);
+#else
+       return 0;
+#endif
+}
+
+void parport_proc_unregister(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+       if( pp ){
+               proc_unregister(&proc_root_parport, pp->proc_dir->low_ino);
+               kfree(pp->proc_dir);
+       }else{
+               proc_unregister(&proc_root, proc_root_parport.low_ino);
+       }
+#endif
+}
diff --git a/drivers/pnp/parport_share.c b/drivers/pnp/parport_share.c
new file mode 100644 (file)
index 0000000..a09f76f
--- /dev/null
@@ -0,0 +1,461 @@
+/* $Id: parport_share.c,v 1.1.2.4 1997/04/01 18:19:11 phil Exp $
+ * Parallel-port resource manager code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *         Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* from parport_init.c */
+extern int initialize_parport(struct parport *, unsigned long base, 
+                             int irq, int dma, int count);
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+       return portlist;
+}
+
+static void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+       /* NULL function - Does nothing */
+       return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma)
+{
+       struct parport new, *tmp;
+
+       /* Check for a previously registered port.
+        * NOTE: we will ignore irq and dma if we find a previously
+        * registered device.
+        */
+       for (tmp = portlist; tmp; tmp = tmp->next) {
+               if (tmp->base == base)
+                       return tmp;
+       }
+
+       /* Has someone grabbed the address yet? */
+       if (check_region(base, 3))
+               return NULL;
+
+       if (!initialize_parport(&new,base,irq,dma,portcount))
+               return NULL;
+                                          
+       if (new.dma >= 0) {
+               if (request_dma(new.dma, new.name)) {
+                       printk(KERN_INFO "%s: unable to claim DMA %d\n", 
+                              new.name, new..dma);
+                       release_region(new.base, new.size);
+                       if( new.modes & PARPORT_MODE_ECR )
+                               release_region(new.base+0x400, 3);
+                       kfree(new.name);
+                       return NULL;
+               }
+       }
+
+       tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+       if (!tmp) {
+               printk(KERN_WARNING "parport: memory squeeze\n");
+               release_region(new.base, new.size);
+               if( new.modes & PARPORT_MODE_ECR )
+                       release_region(new.base+0x400, 3);
+               kfree(new.name);
+               return NULL;
+       }
+       memcpy(tmp, &new, sizeof(struct parport));
+
+       if (new.irq != -1) {
+               if (request_irq(new.irq, parport_null_intr_func,
+                         SA_INTERRUPT, new.name, tmp) != 0) {
+                       printk(KERN_INFO "%s: unable to claim IRQ %d\n", 
+                              new.name, new.irq);
+                       kfree(tmp);
+                       release_region(new.base, new.size);
+                       if( new.modes & PARPORT_MODE_ECR )
+                               release_region(new.base+0x400, 3);
+                       kfree(new.name);
+                       return NULL;
+               }
+       }
+
+       /* Here we chain the entry to our list. */
+       if (portlist_tail)
+               portlist_tail->next = tmp;
+       portlist_tail = tmp;
+       if (!portlist)
+               portlist = tmp;
+
+       printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
+       if (tmp->irq >= 0)
+               printk(", irq %d", tmp->irq);
+       if (tmp->dma >= 0)
+               printk(", dma %d", tmp->dma);
+       printk(" [");
+       {
+               /* Ugh! */
+#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+               int f = 0;
+               printmode(SPP);
+               printmode(PS2);
+               printmode(EPP);
+               printmode(ECP);
+               printmode(ECPEPP);
+               printmode(ECPPS2);
+#undef printmode
+       }
+       printk("]\n");
+       portcount++;
+
+       /* Restore device back to default conditions */
+       if (tmp->modes & PARPORT_MODE_ECR)
+               w_ecr(tmp, tmp->ecr);
+       w_ctr(tmp, tmp->ctr);
+
+       tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
+       return tmp;
+}
+
+void parport_destroy(struct parport *port)
+{
+       /* Dangerous to try destroying a port if its friends are nearby. */
+       if (port->devices) {
+               printk("%s: attempt to release active port\n", port->name);
+               return;         /* Devices still present */
+       }
+
+       /* No point in further destroying a port that already lies in ruins. */
+       if (port->flags & PARPORT_FLAG_COMA) 
+               return;
+
+       /* Now clean out the port entry */
+       if (port->irq >= 0)
+               free_irq(port->irq, port);
+       if (port->dma >= 0)
+               free_dma(port->dma);
+       release_region(port->base, port->size);
+       if( port->modes & PARPORT_MODE_ECR )
+               release_region(port->base+0x400, 3);
+       port->flags |= PARPORT_FLAG_COMA; 
+}
+
+struct ppd *parport_register_device(struct parport *port, const char *name,
+                                  callback_func pf, callback_func kf,
+                                  irq_handler_func irq_func, int flags,
+                                  void *handle)
+{
+       struct ppd *tmp;
+
+       /* We only allow one lurker device (eg PLIP) */
+       if (flags & PARPORT_DEV_LURK) {
+               if (port->lurker) {
+                       printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+                                  port->name, name);
+                       return NULL;
+               }
+               if (!pf || !kf) {
+                       printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
+                                  ,port->name, name);
+                       return NULL;
+               }
+       }
+
+       /* We may need to claw back the port hardware. */
+       if (port->flags & PARPORT_FLAG_COMA) {
+               if (check_region(port->base, 3)) {
+                       return NULL;
+               }
+               request_region(port->base, port->size, port->name);
+               if( port->modes & PARPORT_MODE_ECR )
+                       request_region(port->base+0x400, 3,port->name);
+                       
+               if (port->dma >= 0) {
+                       if (request_dma(port->dma, port->name)) {
+                               release_region(port->base, port->size);
+                               if( port->modes & PARPORT_MODE_ECR )
+                                       release_region(port->base+0x400, 3);
+                               return NULL;
+                       }
+               }
+               if (port->irq != -1) {
+                       if (request_irq(port->irq, 
+                                                       parport_null_intr_func,
+                                                       SA_INTERRUPT, port->name,
+                                                       port) != 0) {
+                               release_region(port->base, port->size);
+                               if( port->modes & PARPORT_MODE_ECR )
+                                       release_region(port->base+0x400, 3);
+                               if (port->dma >= 0)
+                                       free_dma(port->dma);
+                               return NULL;
+                       }
+               }
+               port->flags &= ~PARPORT_FLAG_COMA;
+       }
+
+
+       tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
+       tmp->name = (char *) name;
+       tmp->port = port;
+       tmp->preempt = pf;
+       tmp->wakeup = kf;
+       tmp->private = handle;
+       tmp->irq_func = irq_func;
+       tmp->ctr = port->ctr;
+       tmp->ecr = port->ecr;
+
+       /* Chain this onto the list */
+       tmp->prev = NULL;
+       tmp->next = port->devices;
+       if (port->devices)
+               port->devices->prev = tmp;
+       port->devices = tmp;
+
+       if (flags & PARPORT_DEV_LURK)
+               port->lurker = tmp;
+
+       inc_parport_count();
+
+       return tmp;
+}
+
+void parport_unregister_device(struct ppd *dev)
+{
+       struct parport *port;
+
+       if (!dev) {
+               printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+               return;
+       }
+
+       port = dev->port;
+
+       if (port->cad == dev) {
+               printk(KERN_INFO "%s: refused to unregister currently active device %s\n",
+                          port->name, dev->name);
+               return;
+       }
+
+       if (port->lurker == dev)
+               port->lurker = NULL;
+
+       if (dev->next)
+               dev->next->prev = dev->prev;
+       if (dev->prev)
+               dev->prev->next = dev->next;
+       else
+               port->devices = dev->next;
+
+       kfree(dev);
+
+       dec_parport_count();
+
+       /* If there are no more devices, put the port to sleep. */
+       if (!port->devices)
+               parport_destroy(port);
+
+       return;
+}
+
+int parport_claim(struct ppd *dev)
+{
+       struct ppd *pd1;
+
+       if (dev->port->cad == dev) {
+               printk(KERN_INFO "%s: %s already owner\n",
+                          dev->port->name,dev->name);
+               return 0;
+       }
+
+       /* Preempt any current device */
+       pd1 = dev->port->cad;
+       if (dev->port->cad) {
+               if (dev->port->cad->preempt) {
+                       /* Now try to preempt */
+                       if (dev->port->cad->preempt(dev->port->cad->private))
+                               return -EAGAIN;
+
+                       /* Save control registers */
+                       if (dev->port->modes & PARPORT_MODE_ECR)
+                               dev->port->cad->ecr = dev->port->ecr = 
+                                       r_ecr(dev->port);
+                       dev->port->cad->ctr = dev->port->ctr =
+                               r_ctr(dev->port);
+               } else
+                       return -EAGAIN;
+       }
+
+       /* Watch out for bad things */
+       if (dev->port->cad != pd1) {
+               printk(KERN_WARNING "%s: death while preempting %s\n",
+                      dev->port->name, dev->name);
+               if (dev->port->cad)
+                       return -EAGAIN;
+       }
+
+       /* Now we do the change of devices */
+       dev->port->cad = dev;
+
+       if (dev->port->irq >= 0) {
+               free_irq(dev->port->irq, dev->port);
+               request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+                           parport_null_intr_func, SA_INTERRUPT, dev->name,
+                           dev->port);
+       }
+
+       /* Restore control registers */
+       if (dev->port->modes & PARPORT_MODE_ECR)
+               if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
+       if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
+
+       return 0;
+}
+
+void parport_release(struct ppd *dev)
+{
+       struct ppd *pd1;
+
+       /* Make sure that dev is the current device */
+       if (dev->port->cad != dev) {
+               printk(KERN_WARNING "%s: %s tried to release parport when not owner\n",
+                          dev->port->name, dev->name);
+               return;
+       }
+       dev->port->cad = NULL;
+
+       /* Save control registers */
+       if (dev->port->modes & PARPORT_MODE_ECR)
+               dev->ecr = dev->port->ecr = r_ecr(dev->port);
+       dev->ctr = dev->port->ctr = r_ctr(dev->port);
+       
+       if (dev->port->irq >= 0) {
+               free_irq(dev->port->irq, dev->port);
+               request_irq(dev->port->irq, parport_null_intr_func,
+                                       SA_INTERRUPT, dev->port->name, dev->port);
+       }
+
+       /* Walk the list, offering a wakeup callback to everybody other
+        * than the lurker and the device that called us.
+        */
+       for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+               if (!(pd1->flags & PARPORT_DEV_LURK)) {
+                       if (pd1->wakeup) {
+                               pd1->wakeup(pd1->private);
+                               if (dev->port->cad)
+                                       return;
+                       }
+               }
+       }
+
+       for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+               if (!(pd1->flags & PARPORT_DEV_LURK)) {
+                       if (pd1->wakeup) {
+                               pd1->wakeup(pd1->private);
+                               if (dev->port->cad)
+                                       return;
+                       }
+               }
+       }
+
+       /* Now give the lurker a chance.
+        * There should be a wakeup callback because we checked for it
+        * at registration.
+        */
+       if (dev->port->lurker && (dev->port->lurker != dev)) {
+               if (dev->port->lurker->wakeup) {
+                       dev->port->lurker->wakeup(dev->port->lurker->private);
+                       return;
+               }
+               printk(KERN_DEBUG
+                      "%s (%s): lurker's wakeup callback went away!\n",
+                      dev->port->name, dev->name);
+       }
+}
+
+/* The following read funktions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */ 
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask, 
+       unsigned char result)
+{
+       int counter=0;
+       unsigned char status; 
+       
+       do {
+               status = parport_r_status(port);
+               udelay(25);
+               counter++;
+               if (need_resched)
+                       schedule();
+       } while ( ((status & mask) != result) && (counter < 20) );
+       if ( (counter == 20) && ((status & mask) != result) ) { 
+               current->state=TASK_INTERRUPTIBLE;
+               current->timeout=jiffies+4;
+               schedule(); /* wait for 4 scheduler runs (40ms) */
+               status = parport_r_status(port);
+               if ((status & mask) != result) return 1; /* timeout */
+       }
+       return 0; /* okay right response from device */
+}              
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time).  There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
+{
+       parport_w_data(port, mode);
+       udelay(5);              
+       parport_w_ctrl(port, parport_r_ctrl(port) & ~8);  /* SelectIN low */
+       parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+       if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+               parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+               return 0; /* first stage of negotiation failed, 
+                           * no IEEE1284 compliant device on this port 
+                           */ 
+       }
+       parport_w_ctrl(port, parport_r_ctrl(port) | 1);      /* Strobe high */
+       udelay(5);                                   /* Strobe wait */
+       parport_w_ctrl(port, parport_r_ctrl(port) & ~1);     /* Strobe low */
+       udelay(5);
+       parport_w_ctrl(port, parport_r_ctrl(port) & ~2);     /* AutoFeed low */
+       return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}
index 57de3f7140813f04e3f3eecd35943356d1ea3811..ccf31d453879ecfc2b41cb0c374523eaf047cfde 100644 (file)
@@ -1,3 +1,37 @@
+Sat Apr 5  13:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18d
+       - Probe NCR pci device ids in reverse order if asked by user from 
+         the boot command line. Suggested by Richard Waltham.
+       - Make a separate function that prints out verbose information on 
+         severe error (assumed from hardware).
+       - Add the transfer period factor and the max commands per lun value 
+         to the proc info data. If debug flags are set or verbosity is 
+         greater than 1, debug flags and verbosity are returned in proc  
+         info data.
+       - Update the documentation.
+
+Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+       * revision 1.18c
+       - Add special features support for NCR53C885 and NCR53C896 chip.
+         Quite obvious, but untested, and based on the fact that:
+         The 885 supports same features as the 875.
+         The 896 is a 64 bits PCI version of the 895.
+       - Improve recovery from SCSI GROSS ERRORS.
+         I can get such errors by making the driver negotiate offset 8 with 
+         a disk and setting the ncr chip to a lower offset value.
+         I got bunches of errors that have been gracefully recovered by 
+         the driver.
+         The driver now uses its timer handler in order to wait 2 sec. for 
+         devices to settle after SCSI reset and so does not uselessly freeze 
+         the system with interrupt masked for seconds.
+       - Enable 'burst op code fetch' and 'read line' for 815 chips.
+       - Use a 2 commands queue depth instead of 1 for devices that does 
+          not support tagged command queuing.
+       - The ULTRA timing flag setting was based on the output resulting 
+         period factor of the ncr and not on the negotiated one.
+         This flag setting was wrong only for 24 ns negotiated period factor.
+       - Some other minor changes and cleanups.
+
 Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
        * ncr53c8xx.c ncr53c8xx.h revision 1.18b
        - 'On paper' support of the NCR53C895 Ultra-2 chip.
index aa38d41eff2a231efc1764e5e7c8752fca35c2ab..14ee759fa3fd7a6f407dd6308ce366eb1d3692fc 100644 (file)
@@ -71,9 +71,16 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
   fi
 fi
 if [ "$CONFIG_MCA" = "y" ]; then
+  bool 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA
+fi
+if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+  dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PNP_PARPORT
+  if [ "$CONFIG_SCSI_PPA" != "n" ]; then
+    int  '  Pedantic EPP-checking'   CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
+    int  '  EPP timeout'             CONFIG_SCSI_PPA_EPP_TIME 128
+  fi
   dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
 fi
-dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
index 13697dff2762b093dabe394f697595425b36ea98..53b690f7cdce27302a5399ff46787c5ce1d72451 100644 (file)
@@ -4,7 +4,7 @@ Written by Gerard Roudier <groudier@club-internet.fr>
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-27 February 1997
+6 April 1997
 ===============================================================================
 
 1.  Introduction
@@ -34,7 +34,11 @@ Written by Gerard Roudier <groudier@club-internet.fr>
 13. Control commands under linux-1.2.13
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
+      14.2 Device names change when another controller is added
 15. SCSI problem troubleshooting
+16. Synchonous transfer negotiation tables
+      16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+      16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
 
 ===============================================================================
 
@@ -56,6 +60,18 @@ Information about new chips is available at SYMBIOS web server:
 
           http://www.symbios.com/
 
+SCSI standard documentations are available at SYMBIOS ftp server:
+
+          ftp://ftp.symbios.com/
+
+Usefull SCSI tools written by Eric Youngdale are available at tsx-11:
+
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz
+
+These tools are not ALPHA but quite clean and work quite well.
+It is essential you have the 'scsiinfo' package.
+
 This short documentation only describes the features of the NCR53C8XX
 driver, configuration parameters and control commands available
 through the proc SCSI file system read / write operations.
@@ -186,6 +202,8 @@ General information:
   Chip NCR53C810, device id 0x1, revision id 0x2
   IO port address 0x6000, IRQ number 10
   Using memory mapped IO at virtual address 0x282c000
+  Synchronous transfer period 25, max commands per lun 4
+
 Profiling information:
   num_trans    = 18014
   num_kbytes   = 671314
@@ -554,6 +572,11 @@ IRQ mode
     irqm:1     same as initial settings (assumed BIOS settings)
     irqm:2     always totem pole
 
+Reverse probe
+    revprob:n   probe chip ids from the PCI configuration in this order:
+                810, 815, 820, 860, 875, 885, 895, 896
+    revprob:y   probe chip ids in the reverse order.
+
 Boot fail safe
     safe:y     load the following assumed fail safe initial setup
 
@@ -563,6 +586,7 @@ Boot fail safe
   special features             disabled                specf:n
   ultra scsi                   disabled                ultra:n
   force sync negotiation       disabled                fsn:n
+  reverse probe                        disabled                revprob:n
   verbosity level              2                       verb:2
   tagged command queuing       disabled                tags:0
   synchronous negotiation      disabled                sync:255
@@ -579,8 +603,8 @@ Boot fail safe
 If the driver has been configured with default options, the equivalent 
 boot setup is:
 
-    ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,verb:2,tags:0\
-              sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+             tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
 
 For an installation diskette or a safe but not fast system,
 boot setup can be:
@@ -592,8 +616,8 @@ boot setup can be:
 
 My personnal system works flawlessly with the following equivalent setup:
 
-    ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,verb:1,tags:8\
-              sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+             tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
 "ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
@@ -712,9 +736,9 @@ Driver and common files:
 
 You must untar the distribution with the following command:
 
-       tar zxvf ncrBsd2Linux-1.18b-src.tar.gz
+       tar zxvf ncrBsd2Linux-1.18d-src.tar.gz
 
-The sub-directory ncr53c8xx-1.18b will be created. Change to this directory.
+The sub-directory ncr53c8xx-1.18d will be created. Change to this directory.
 
 
 12.2 Installation procedure
@@ -770,6 +794,23 @@ other problem that may appear is timeouts. The only way to avoid
 timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
 current timeout values.
 
+14.2 Device names change when another controller is added.
+
+When you add a new NCR53C8XX chip based controller to a system that already 
+has one or more controllers of this family, it may happen that the order 
+the driver registers them to the kernel causes problems due to device 
+name changes.
+SDMS BIOS version 4 allows you to define the order the BIOS will scan the 
+scsi boards and stores this information for next reboots. Unfortunately, the 
+driver is not currently able to read this information and so may register 
+controllers in a different order.
+
+If you have such a problem, you can:
+
+- Ask the driver to probe chip ids in reverse order from the boot command
+  line: ncr53c8xx=revprob:y
+- Make appropriate changes in the fstab.
+- Use the 'scsidev' tool from Eric Youngdale.
 
 15. SCSI problem troubleshooting
 
@@ -819,6 +860,98 @@ Try to enable one feature at a time with control commands.  For example:
 Once you have found the device and the feature that cause problems, just 
 disable that feature for that device.
 
+
+16. Synchonous transfer negotiation tables
+
+Tables below have been created by calling the routine the driver uses
+for synchronisation negotiation timing calculation and chip setting.
+The first table corresponds to Ultra chips 53875 and 53C860 with 80 MHz 
+clock and 5 clock divisors.
+The second one has been calculated by setting the scsi clock to 40 Mhz 
+and using 4 clock divisors and so applies to all NCR53C8XX chips in fast 
+SCSI-2 mode.
+
+Periods are in nano-seconds and speeds are in Mega-transfers per second.
+1 Mega-transfers/second means 1 MB/s with 8 bits SCSI and 2 MB/s with 
+Wide16 SCSI.
+
+16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 12       50       20.000       50       20.000
+ 13       52       19.230       62       16.000
+ 14       56       17.857       62       16.000
+ 15       60       16.666       62       16.000
+ 16       64       15.625       75       13.333
+ 17       68       14.705       75       13.333
+ 18       72       13.888       75       13.333
+ 19       76       13.157       87       11.428
+ 20       80       12.500       87       11.428
+ 21       84       11.904       87       11.428
+ 22       88       11.363       93       10.666
+ 23       92       10.869       93       10.666
+ 24       96       10.416      100       10.000
+ 25      100       10.000      100       10.000
+ 26      104        9.615      112        8.888
+ 27      108        9.259      112        8.888
+ 28      112        8.928      112        8.888
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 25      100       10.000      100       10.000
+ 26      104        9.615      125        8.000
+ 27      108        9.259      125        8.000
+ 28      112        8.928      125        8.000
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
 ===============================================================================
 End of NCR53C8XX driver README file
 
index fc8ac124c13ddacfef48119b334feb25b42e5ba7..c1202f87722262d65c81328e574b45cf5818d5bb 100644 (file)
@@ -40,7 +40,7 @@
 */
 
 /*
-**     27 February 1997, version 1.18b
+**     6 April 1997, version 1.18d
 **
 **     Supported SCSI-II features:
 **         Synchronous negotiation
@@ -469,6 +469,7 @@ struct ncr_driver_setup {
        unsigned special_features : 1;
        unsigned ultra_scsi     : 2;
        unsigned force_sync_nego: 1;
+       unsigned reverse_probe: 1;
        u_char  verbose;
        u_char  default_tags;
        u_short default_sync;
@@ -476,7 +477,7 @@ struct ncr_driver_setup {
        u_char  burst_max;
        u_char  led_pin;
        u_char  max_wide;
-       u_char  settle_time;
+       u_char  settle_delay;
        u_char  diff_support;
        u_char  irqm;
 };
@@ -1344,8 +1345,8 @@ struct ncb {
        int     ncr_cache;              /* Cache test variable               */
        Scsi_Cmnd *waiting_list;        /* Waiting list header for commands  */
                                        /* that we can't put into the squeue */
+       u_long  settle_time;            /* Reset in progess                  */
        u_char  release_stage;          /* Synchronisation stage on release  */
-       u_char  resetting;              /* Reset in progess                  */
 
        /*-----------------------------------------------
        **      Added field to support differences
@@ -1691,6 +1692,7 @@ static    int     ncr_show_msg    (u_char * msg);
 static int     ncr_snooptest   (ncb_p np);
 static void    ncr_timeout     (ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
+static void    ncr_start_reset (ncb_p np, int settle_delay);
 
 #ifdef SCSI_NCR_USER_COMMAND
 static void    ncr_usercmd     (ncb_p np);
@@ -1718,16 +1720,6 @@ static void process_waiting_list(ncb_p np, int sts);
 **==========================================================
 */
 
-#if 0
-static char ident[] =
-       "\n$Id: ncr.c,v 1.67 1996/03/11 19:36:07 se Exp $\n";
-static u_long  ncr_version = NCR_VERSION       * 11
-       + (u_long) sizeof (struct ncb)  *  7
-       + (u_long) sizeof (struct ccb)  *  5
-       + (u_long) sizeof (struct lcb)  *  3
-       + (u_long) sizeof (struct tcb)  *  2;
-#endif
-
 #ifdef SCSI_NCR_DEBUG
 static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
 #endif
@@ -3255,7 +3247,7 @@ static    struct scripth scripth0 = {
 **     #define MAX_SCATTER parameter,
 **     it is filled in at runtime.
 **
-**     SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**     SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)),
 **             PADDR (no_data),
 **     SCR_COPY (sizeof (u_long)),
 **             KVAR(SCRIPT_KVAR_JIFFIES),
@@ -3596,7 +3588,7 @@ struct host_data {
 #define PRINT_LUN(np, target, lun) \
 printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
-static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
        struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
        ncb_p np                    = host_data->ncb;
@@ -3744,13 +3736,18 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
                np->clock_divn  = 5;
                break;
        case PCI_DEVICE_ID_NCR_53C875:
+       case PCI_DEVICE_ID_NCR_53C885:
                np->maxwide = 1;
                if (driver_setup.special_features)
                        np->maxoffs = 16;
                np->clock_divn  = 5;
-               ncr_getclock(np, revision_id >= 2 ? 2 : 1);
+               if (device_id == PCI_DEVICE_ID_NCR_53C875)
+                       ncr_getclock(np, revision_id >= 2 ? 2 : 1);
+               else
+                       ncr_getclock(np, 2);
                break;
        case PCI_DEVICE_ID_NCR_53C895:
+       case PCI_DEVICE_ID_NCR_53C896:
                np->maxwide = 1;
                if (driver_setup.special_features)
                        np->maxoffs = 31;
@@ -3767,7 +3764,9 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
                if (revision_id < 0x10)
                        break;
        case PCI_DEVICE_ID_NCR_53C875:
+       case PCI_DEVICE_ID_NCR_53C885:
        case PCI_DEVICE_ID_NCR_53C895:
+       case PCI_DEVICE_ID_NCR_53C896:
                if (driver_setup.special_features) {
                        OUTONB(nc_ctest2, 0x8);
                        np->paddr2 = INL(nc_scr0);
@@ -3794,7 +3793,7 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
                }
                else
                        if (bootverbose > 1)
-                               printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+                               printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
        }
 #endif /* !defined NCR_IOMAPPED */
 
@@ -3911,17 +3910,12 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
        **      After SCSI devices have been opened, we cannot
        **      reset the bus safely, so we do it here.
        **      Interrupt handler does the real work.
-       */
-
-       OUTB (nc_scntl1, CRST);
-       DELAY (1000);
-
-       /*
        **      Process the reset exception,
        **      if interrupts are not enabled yet.
        **      Then enable disconnects.
        */
        save_flags(flags); cli();
+       ncr_start_reset(np, driver_setup.settle_delay);
        ncr_exception (np);
        restore_flags(flags);
 
@@ -3930,12 +3924,13 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
        /*
        **      The middle-level SCSI driver does not
        **      wait devices to settle.
+       **      Wait synchronously if more than 2 seconds.
        */
-       if (driver_setup.settle_time > 2)
+       if (driver_setup.settle_delay > 2) {
                printf("%s: waiting %d seconds for scsi devices to settle...\n",
-                       ncr_name(np), driver_setup.settle_time);
-       if (driver_setup.settle_time)
-               DELAY(1000000UL * driver_setup.settle_time);
+                       ncr_name(np), driver_setup.settle_delay);
+               DELAY(1000000UL * driver_setup.settle_delay);
+       }
 
        /*
        **      Now let the generic SCSI driver
@@ -3945,8 +3940,8 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n",
        /*
        **      start the timeout daemon
        */
-       ncr_timeout (np);
        np->lasttime=0;
+       ncr_timeout (np);
 
        /*
        **  use SIMPLE TAG messages by default
@@ -4058,22 +4053,6 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        cmd->SCp.ptr       = NULL;
        cmd->SCp.buffer    = NULL;
 
-       /*---------------------------------------------
-       **
-       **   Reset SCSI bus
-       **
-       **      Interrupt handler does the real work.
-       **
-       **---------------------------------------------
-       */
-#if 0
-       if (flags & SCSI_RESET) {
-               OUTB (nc_scntl1, CRST);
-               DELAY (1000);
-               return(COMPLETE);
-       }
-#endif
-
        /*---------------------------------------------
        **
        **      Some shortcuts ...
@@ -4095,13 +4074,14 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        /*---------------------------------------------------
        **
        **      Assign a ccb / bind cmd
-       **      If no free ccb, insert cmd into the waiting list.
+       **      If resetting or no free ccb,
+       **      insert cmd into the waiting list.
        **
        **----------------------------------------------------
        */
        save_flags(flags); cli();
 
-        if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+        if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
                insert_into_waiting_list(np, cmd);
                restore_flags(flags);
                return(DID_OK);
@@ -4497,6 +4477,40 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
        return(DID_OK);
 }
 
+/*==========================================================
+**
+**
+**     Start reset process.
+**     If reset in progress do nothing.
+**     The interrupt handler will reinitialize the chip.
+**     The timeout handler will wait for settle_time before 
+**     clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+       u_long flags;
+
+       save_flags(flags); cli();
+
+       if (!np->settle_time) {
+               if (bootverbose > 1)
+                       printf("%s: resetting, command processing suspended for %d seconds\n",
+                               ncr_name(np), settle_delay);
+               np->settle_time = jiffies + settle_delay * HZ;
+               OUTB (nc_istat, SRST);
+               DELAY (1000);
+               OUTB (nc_istat, 0);
+               OUTW (nc_sien, RST);
+               OUTB (nc_scntl1, CRST);
+               DELAY (100);
+       }
+
+       restore_flags(flags);
+}
+
 /*==========================================================
 **
 **
@@ -4518,23 +4532,19 @@ int ncr_reset_bus (Scsi_Cmnd *cmd)
 
        save_flags(flags); cli();
 /*
- * Return immediately from recursive call
+ * Return immediately if reset is in progress.
  */
-       if (np->resetting) {
+       if (np->settle_time) {
                restore_flags(flags);
                return SCSI_RESET_PUNT;
        }
-       ++np->resetting;
 /*
- * Perform chip reset, SCSI reset, wait 2 seconds
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle 
+ * delay of 2 seconds will be completed.
  */
-       OUTB (nc_istat, SRST);
-       DELAY (1000);
-       OUTB (nc_istat, 0);
-       OUTB (nc_scntl1, CRST);
-       DELAY (100);
-       OUTB (nc_scntl1, 0);
-       DELAY (2000000);
+       ncr_start_reset(np, 2);
 /*
  * First, look in the wakeup list
  */
@@ -4554,25 +4564,23 @@ int ncr_reset_bus (Scsi_Cmnd *cmd)
        if (!found && retrieve_from_waiting_list(0, np, cmd))
                found = 1;
 /*
- * Reinitialise the NCR and wake-up pending commands
+ * Wake-up all awaiting commands with DID_RESET.
  */
-       ncr_init(np, "scsi bus reset", HS_RESET);
-       np->disc = 1;
+       reset_waiting_list(np);
 /*
- * Complete awaiting commands with DID_RESET
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
  */
-       reset_waiting_list(np);
+       ncr_wakeup(np, HS_RESET);
 /*
- * If the involved command was not in a driver queue, complete it 
- * with DID_RESET, in order to keep it alive.
+ * If the involved command was not in a driver queue, and is 
+ * not in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
  */
-       if (!found && cmd && cmd->scsi_done) {
+       if (!found && cmd && !remove_from_waiting_list(np, cmd)) {
                cmd->result = ScsiResult(DID_RESET, 0);
                cmd->scsi_done(cmd);
        }
 
-       --np->resetting;
-
        restore_flags(flags);
 
        return SCSI_RESET_SUCCESS;
@@ -4622,11 +4630,17 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
                        break;
                }
        }
+
        if (!found) {
                restore_flags(flags);
                return SCSI_ABORT_NOT_RUNNING;
        }
 
+       if (np->settle_time) {
+               restore_flags(flags);
+               return SCSI_ABORT_SNOOZE;
+       }
+
        /*
        **      Disable reselect.
        **      Remove it from startqueue.
@@ -5255,17 +5269,22 @@ void ncr_init (ncb_p np, char * msg, u_long code)
 /**    NCR53C810                       **/
        if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
                burst_max       = burst_max < 4 ? burst_max : 4;
+               if (driver_setup.special_features)
+                       np->rv_dmode    =  ERL;         /* read line */
        }
        else
 /**    NCR53C815                       **/
        if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
                burst_max       = burst_max < 4 ? burst_max : 4;
+               if (driver_setup.special_features)
+                       np->rv_dmode    = BOF | ERL;    /* burst opcode fetch, read line */
        }
        else
 /**    NCR53C825                       **/
        if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
                burst_max       = burst_max < 4 ? burst_max : 4;
-               np->rv_dmode    = 0x0a; /* burst opcode fetch */
+               if (driver_setup.special_features)
+                       np->rv_dmode    = BOF | ERL;    /* burst opcode fetch, read line */
        }
        else
 /**    NCR53C810A or NCR53C860         **/
@@ -5282,10 +5301,12 @@ void ncr_init (ncb_p np, char * msg, u_long code)
                }
        }
        else
-/**    NCR53C825A or NCR53C875 or NCR53C895            **/
+/**    NCR53C825A or NCR53C875 or NCR53C885 or NCR53C895 or NCR53C896  **/
        if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
            ChipDevice == PCI_DEVICE_ID_NCR_53C875 ||
-           ChipDevice == PCI_DEVICE_ID_NCR_53C895) {
+           ChipDevice == PCI_DEVICE_ID_NCR_53C885 ||
+           ChipDevice == PCI_DEVICE_ID_NCR_53C895 ||
+           ChipDevice == PCI_DEVICE_ID_NCR_53C896) {
                if (!driver_setup.special_features)
                        burst_max       = burst_max < 4 ? burst_max : 4;
                else {
@@ -5296,7 +5317,7 @@ void ncr_init (ncb_p np, char * msg, u_long code)
                        np->rv_dcntl    = PFEN | CLSE;
                                                /* prefetch, cache line size */
                        np->rv_ctest3   = WRIE; /* write and invalidate */
-                       np->rv_ctest5   = 0x20; /* large dma fifo (0x20) */
+                       np->rv_ctest5   = DFS;  /* large dma fifo (0x20) */
                }
        }
 /**    OTHERS                          **/
@@ -5305,14 +5326,6 @@ void ncr_init (ncb_p np, char * msg, u_long code)
        }
 #endif /* SCSI_NCR_TRUST_BIOS_SETTING */
 
-#if 0
-       /*
-        *      Do not enable read-multiple for 810A revision 0x11
-        */
-       if (np->device_id == PCI_DEVICE_ID_NCR_53C810 && np->revision_id == 0x11)
-               np->rv_dmode &= (~ERMP);
-#endif
-
        /*
         *      Prepare initial io register bits for burst length
         */
@@ -5612,7 +5625,7 @@ static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
        **      Compute and return sync parameters for the ncr
        */
        *fakp           = fak - 4;
-       *scntl3p        = ((idiv+1) << 4) + (per < 100 ? 0x80 : 0);
+       *scntl3p        = ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
 
 #ifdef DEBUG
 printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
@@ -5963,6 +5976,23 @@ static void ncr_timeout (ncb_p np)
 
        add_timer(&np->timer);
 
+       /*
+       **      If we are resetting the ncr, wait for settle_time before 
+       **      clearing it. Then command processing will be resumed.
+       */
+       if (np->settle_time) {
+               if (np->settle_time <= thistime) {
+                       if (bootverbose > 1)
+                               printf("%s: command processing resumed\n", ncr_name(np));
+                       save_flags(flags); cli();
+                       np->settle_time = 0;
+                       np->disc        = 1;
+                       requeue_waiting_list(np);
+                       restore_flags(flags);
+               }
+               return;
+       }
+
        /*
        **      Since the generic scsi driver only allows us 0.5 second 
        **      to perform abort of a command, we must look at ccbs about 
@@ -5997,18 +6027,6 @@ static void ncr_timeout (ncb_p np)
                t = (thistime - np->heartbeat) / HZ;
 
                if (t<2) np->latetime=0; else np->latetime++;
-#if 0
-               if (np->latetime>5) {
-                       /*
-                       **      If there are no requests, the script
-                       **      processor will sleep on SEL_WAIT_RESEL.
-                       **      But we have to check whether it died.
-                       **      Let's wake it up.
-                       */
-
-                       OUTB (nc_istat, SIGP);
-               }
-#endif
 
                /*----------------------------------------------------
                **
@@ -6085,6 +6103,75 @@ static void ncr_timeout (ncb_p np)
 #endif /* SCSI_NCR_BROKEN_INTR */
 }
 
+/*==========================================================
+**
+**     log message for real hard errors
+**
+**     "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+**     "             reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+**     exception register:
+**             ds:     dstat
+**             si:     sist
+**
+**     SCSI bus lines:
+**             so:     control lines as driver by NCR.
+**             si:     control lines as seen by NCR.
+**             sd:     scsi data lines as seen by NCR.
+**
+**     wide/fastmode:
+**             sxfer:  (see the manual)
+**             scntl3: (see the manual)
+**
+**     current script command:
+**             dsp:    script adress (relative to start of script).
+**             dbc:    first word of script command.
+**
+**     First 16 register of the chip:
+**             r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+       u_int32 dsp;
+       int     script_ofs;
+       char    *script_name;
+       u_char  *script_base;
+       int     i;
+
+       dsp     = INL (nc_dsp);
+
+       if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+               script_ofs      = dsp - np->p_script;
+               script_base     = (u_char *) np->script;
+               script_name     = "script";
+       }
+       else {
+               script_ofs      = dsp - np->p_scripth;
+               script_base     = (u_char *) np->scripth;
+               script_name     = "scripth";
+       }
+
+       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+               ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+               (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+               (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+               (unsigned)INL (nc_dbc));
+
+       if (((script_ofs & 3) == 0) &&
+           (unsigned)script_ofs < sizeof(struct script)) {
+               printf ("%s: script cmd = %08x\n", ncr_name(np),
+                       (int) *(ncrcmd *)(script_base + script_ofs));
+       }
+
+        printf ("%s: regdump:", ncr_name(np));
+        for (i=0; i<16;i++)
+            printf (" %02x", (unsigned)INB_OFF(i));
+        printf (".\n");
+}
+
 /*==========================================================
 **
 **
@@ -6098,8 +6185,6 @@ void ncr_exception (ncb_p np)
 {
        u_char  istat, dstat;
        u_short sist;
-       u_int32 dsp, dsa;
-       int     script_ofs;
        int     i;
 
        /*
@@ -6222,54 +6307,8 @@ void ncr_exception (ncb_p np)
        /*=========================================
        **      log message for real hard errors
        **=========================================
-
-       "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)."
-       "             reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
-
-       exception register:
-               ds:     dstat
-               si:     sist
-
-       SCSI bus lines:
-               so:     control lines as driver by NCR.
-               si:     control lines as seen by NCR.
-               sd:     scsi data lines as seen by NCR.
-
-       wide/fastmode:
-               sxfer:  (see the manual)
-               scntl3: (see the manual)
-
-       current script command:
-               dsp:    script adress (relative to start of script).
-               dbc:    first word of script command.
-
-       First 16 register of the chip:
-               r0..rf
-
-       =============================================
        */
-
-       dsp = (unsigned) INL (nc_dsp);
-       dsa = (unsigned) INL (nc_dsa);
-
-       script_ofs = dsp - np->p_script;
-
-       printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%x:%08x).\n",
-               ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
-               (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
-               (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_ofs,
-               (unsigned) INL (nc_dbc));
-
-       if (((script_ofs & 3) == 0) &&
-           (unsigned)script_ofs < sizeof(struct script)) {
-               printf ("%s: script cmd = %08x\n", ncr_name(np),
-                       (int) *(ncrcmd *)((char*)np->script +script_ofs));
-       }
-
-        printf ("%s: reg:", ncr_name(np));
-        for (i=0; i<16;i++)
-            printf (" %02x", (unsigned)INB_OFF(i));
-        printf (".\n");
+       ncr_log_hard_error(np, sist, dstat);
 
        /*----------------------------------------
        **      clean up the dma fifo
@@ -6418,8 +6457,7 @@ void ncr_exception (ncb_p np)
        **      sorry, have to kill ALL jobs ...
        */
 
-       ncr_init (np, "fatal error", HS_FAIL);
-       np->disc = 1;
+       ncr_start_reset(np, 2);
 }
 
 /*==========================================================
@@ -6521,8 +6559,8 @@ static void ncr_int_ma (ncb_p np)
                u_short delta;
 
                if (!(INB(nc_dstat) & DFE)) {
-                       ctest5 = (np->rv_ctest5 & 0x20) ? INB (nc_ctest5) : 0;
-                       if (ctest5 & 0x20)
+                       ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+                       if (ctest5 & DFS)
                                delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
                        else
                                delta=(INB (nc_dfifo) - rest) & 0x7f;
@@ -7699,7 +7737,7 @@ static    int     ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
                        segment = 1;
                }
        }
-       else if (use_sg < MAX_SCATTER) {
+       else if (use_sg <= MAX_SCATTER) {
                struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
 
                while (segment < use_sg) {
@@ -8245,6 +8283,8 @@ void ncr53c8xx_setup(char *str, int *ints)
                        driver_setup.ultra_scsi = val;
                else if (!strncmp(cur, "fsn:", 4))
                        driver_setup.force_sync_nego    = val;
+               else if (!strncmp(cur, "revprob:", 8))
+                       driver_setup.reverse_probe      = val;
                else if (!strncmp(cur, "tags:", 5)) {
                        if (val > SCSI_NCR_MAX_TAGS)
                                val = SCSI_NCR_MAX_TAGS;
@@ -8263,7 +8303,7 @@ void ncr53c8xx_setup(char *str, int *ints)
                else if (!strncmp(cur, "wide:", 5))
                        driver_setup.max_wide   = val? 1:0;
                else if (!strncmp(cur, "settle:", 7))
-                       driver_setup.settle_time= val;
+                       driver_setup.settle_delay= val;
                else if (!strncmp(cur, "diff:", 5))
                        driver_setup.diff_support= val;
                else if (!strncmp(cur, "irqm:", 5))
@@ -8318,7 +8358,7 @@ static struct {
 
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 {
-     int i;
+     int i, j;
      int count = 0;                    /* Number of boards detected */
      uchar pci_bus, pci_device_fn;
      short pci_index;  /* Device index to PCI BIOS calls */
@@ -8341,7 +8381,7 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
                        driver_setup.verbose,
                        driver_setup.debug,
                        YesNo(driver_setup.led_pin),
-                       driver_setup.settle_time,
+                       driver_setup.settle_delay,
                        driver_setup.irqm);
    }
 #undef YesNo
@@ -8358,7 +8398,8 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 #endif
 
      if (pcibios_present()) {
-         for (i = 0; i < NPCI_CHIP_IDS; ++i) 
+         for (j = 0; j < NPCI_CHIP_IDS; ++j) {
+              i = driver_setup.reverse_probe ? NPCI_CHIP_IDS-1 - j : j;
               for (pci_index = 0;
                    !pcibios_find_device(PCI_VENDOR_ID_NCR, 
                                         pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
@@ -8367,6 +8408,7 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
                    if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip, 
                              pci_bus, pci_device_fn, /* no options */ 0))
                    ++count;
+        }
      }
 
      return count;
@@ -8487,12 +8529,17 @@ static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_de
 
        for (device = devlist; device; device = device->next) {
                if (device->host == host) {
+#if SCSI_NCR_MAX_TAGS > 1
                        if (device->tagged_supported) {
                                device->queue_depth = SCSI_NCR_MAX_TAGS;
                        }
                        else {
-                               device->queue_depth = 1;
+                               device->queue_depth = 2;
                        }
+#else
+                       device->queue_depth = 1;
+#endif
+
 #ifdef DEBUG
 printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
        device->id, device->lun, device->queue_depth);
@@ -9014,6 +9061,13 @@ static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
                copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
                                  (u_long) np->reg);
 #endif
+       copy_info(&info, "  Synchronous period factor %d, ", (int) np->ns_sync);
+       copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+       if (driver_setup.debug || driver_setup.verbose > 1) {
+               copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
+               copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+       }
 
 #ifdef SCSI_NCR_PROFILE
        copy_info(&info, "Profiling information:\n");
index 8ce538b7d426b2e5b10acadc2aae1b6d175a64bb..28f36400f36e8fdabbe990cb63689ee31fe1b703 100644 (file)
@@ -45,7 +45,7 @@
 /*
 **     Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME           "ncr53c8xx - revision 1.18b"
+#define SCSI_NCR_DRIVER_NAME           "ncr53c8xx - revision 1.18d"
  
 /*
 **     If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined,
 */
 
 #define SCSI_NCR_ALWAYS_SIMPLE_TAG
-#define SCSI_NCR_MAX_SCATTER   (128)
+#define SCSI_NCR_MAX_SCATTER   (127)
 #define SCSI_NCR_MAX_TARGET    (16)
 #define SCSI_NCR_MAX_HOST      (2)
 #define SCSI_NCR_TIMEOUT_ALERT (3*HZ)
 
 #define SCSI_NCR_CAN_QUEUE     (7*SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_CMD_PER_LUN   (SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE  (SCSI_NCR_MAX_SCATTER-1)
+#define SCSI_NCR_SG_TABLESIZE  (SCSI_NCR_MAX_SCATTER)
 
 #define SCSI_NCR_TIMER_INTERVAL        ((HZ+5-1)/5)
 
        SCSI_NCR_SETUP_SPECIAL_FEATURES,        \
        SCSI_NCR_SETUP_ULTRA_SCSI,              \
        SCSI_NCR_SETUP_FORCE_SYNC_NEGO,         \
+       0,                                      \
        1,                                      \
        SCSI_NCR_SETUP_DEFAULT_TAGS,            \
        SCSI_NCR_SETUP_DEFAULT_SYNC,            \
        0,                                      \
        0,                                      \
        0,                                      \
+       0,                                      \
        2,                                      \
        0,                                      \
        255,                                    \
@@ -552,6 +554,7 @@ struct ncr_reg {
 
 /*03*/  u_char    nc_scntl3;    /* cnf system clock dependent       */
        #define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
+       #define   ULTRA   0x80  /* cmd: ULTRA enable                */
 
 /*04*/  u_char    nc_scid;     /* cnf host adapter scsi address    */
        #define   RRE     0x40  /* r/w:e enable response to resel.  */
@@ -643,6 +646,7 @@ struct ncr_reg {
        #define   MPEE    0x08  /* mod: master parity error enable  */
 
 /*22*/  u_char    nc_ctest5;
+       #define   DFS     0x20  /* mod: dma fifo size               */
 /*23*/  u_char    nc_ctest6;
 
 /*24*/  u_int32    nc_dbc;     /* ### Byte count and command       */
index b744599009dde0be91f46091e20f73e65d548a9c..28ae88a5711138289b2d942420ce3c935abde1f1 100644 (file)
-/*      ppa.c   --  low level driver for the IOMEGA PPA3 
                   parallel port SCSI host adapter.
-
       (The PPA3 is the embedded controller in the ZIP drive.)
-
       (c) 1995,1996 Grant R. Guenther, grant@torque.net,
                     under the terms of the GNU Public License.
-
-*/
+/* ppa.c   --  low level driver for the IOMEGA PPA3 
* parallel port SCSI host adapter.
+ * 
* (The PPA3 is the embedded controller in the ZIP drive.)
+ * 
* (c) 1995,1996 Grant R. Guenther, grant@torque.net,
* under the terms of the GNU Public License.
+ * 
+ */
 
 /*      This driver was developed without the benefit of any technical
-        specifications for the interface.  Instead, a modified version of
-        DOSemu was used to monitor the protocol used by the DOS driver
-        for this adapter.  I have no idea how my programming model relates
-        to IOMEGA's design.
-
-        IOMEGA's driver does not generate linked commands.  I've never
-        observed a SCSI message byte in the protocol transactions, so
-        I am assuming that as long as linked commands are not used
-        we won't see any.  
-
-        So far, this driver has been tested with the embedded PPA3 in the
-        ZIP drive, only.   It can detect and adapt to 4- and 8-bit parallel
-        ports, but there is currently no support for EPP or ECP ports, as
-        I have been unable to make the DOS drivers work in these modes on
-        my test rig.
-
-        For more information, see the file drivers/scsi/README.ppa.
-
-*/
-
-#define   PPA_VERSION   "0.26"
-
-/* Change these variables here or with insmod or with a LILO or LOADLIN
-   command line argument
-*/
-
-static int      ppa_base       = 0x378;  /* parallel port address    */
-static int      ppa_speed_high = 1;      /* port delay in data phase */
-static int      ppa_speed_low  = 6;      /* port delay otherwise     */
-static int      ppa_nybble     = 0;      /* don't force nybble mode  */
-
-
-#define   PPA_CAN_QUEUE         1       /* use "queueing" interface */
-#define   PPA_SELECT_TMO        5000    /* how long to wait for target ? */
-#define   PPA_SPIN_TMO          5000000 /* ppa_wait loop limiter */
-#define   PPA_SECTOR_SIZE       512     /* for a performance hack only */
-
-#include  <linux/stddef.h>
-#include  <linux/module.h>
-#include  <linux/kernel.h>
-#include  <linux/tqueue.h>
-#include  <linux/ioport.h>
-#include  <linux/delay.h>
-#include  <linux/blk.h>
-#include  <linux/proc_fs.h>
-#include  <linux/stat.h>
-#include  <asm/io.h>
-#include  "sd.h"
-#include  "hosts.h"
+ * specifications for the interface.  Instead, a modified version of
+ * DOSemu was used to monitor the protocol used by the DOS driver
+ * for this adapter.  I have no idea how my programming model relates
+ * to IOMEGA's design.
+ * 
+ * IOMEGA's driver does not generate linked commands.  I've never
+ * observed a SCSI message byte in the protocol transactions, so
+ * I am assuming that as long as linked commands are not used
+ * we won't see any.  
+ * 
+ * For more information, see the file drivers/scsi/README.ppa.
+ * 
+ */
+
+/* 
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather.                        [0.26-athena]
+ *
+ * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au)
+ * in response to this driver "mis-behaving" on his machine.
+ *      Fixed EPP to handle "software" changing of EPP port data direction.
+ *      Chased down EPP timeouts
+ *      Made this driver "kernel version friendly"           [0.28-athena]
+ *
+ * Really hacked it out of existance (David Campbell)
+ *      EPP timeouts no longer occur (yet better handling)
+ *      Probes known parallel ports
+ *      Autodetects EPP / ECP / PS2 / NIBBLE
+ *      Support for multiple devices (does anyone need this??)
+ *                                                           [0.29-Curtin]
+ * [ Stuff removed ]
+ *
+ *      Modified PEDANTIC for less PEDANTIC drivers as people
+ *      were complaining about speed (I received a report indicating
+ *      that PEDANTIC is necessary for WinBond chipsets.
+ *      Updated config_ppa and Makefile
+ *                                                           [0.36b-Curtin]
+ *
+ * First round of cleanups
+ *      Remove prior 1.3.34 kernel support
+ *      SMC support changed
+ *              ECP+EPP detection always invoked.
+ *              If compat mode => PS/2
+ *              else ecp_sync() called (ECP+EPP uses FIFO).
+ *      Added routine to detect interrupt channel for ECP (not used)
+ *      Changed version numbering
+ *              1       Major number
+ *              00      Minor revision number
+ *              ALPHA   Expected stability (alpha, beta, stable)
+ *                                              [Curtin-1-00-ALPHA]
+ * Second round of cleanups
+ *      Clean up timer queues
+ *      Fixed problem with non-detection of PS/2 ports
+ *      SMC ECP+EPP confirmed to work, remove option from config_ppa
+ *                                              [Curtin-1-01-ALPHA]
+ *
+ * Parport hits with a vengance!!
+ *      Several internal revisions have been made with huge amounts of
+ *      fixes including:
+ *              ioport_2_hostno removed (unique_id is quicker)
+ *              SMC compat option is history
+ *              Driver name / info hardwired
+ *              Played with inlines and saved 4k on module
+ *      Parport support
+ *              Using PnP Parport allows use of printer attached to
+ *              ZIP drive.
+ *              Numerous fixups for device registration and to allow
+ *              proper aborts.
+ *      Version jumps a few numbers here - considered BETA
+ *      Shipping Parport with monolithic driver :)
+ *                                              [Curtin-1-05-BETA]
+ *
+ * Fixed code to ensure SCSI abort will release the SCSI command
+ *      if the driver is STILL trying to claim the parport (PNP ver)
+ *      Now I have to fix the lp driver then there will NEVER be a
+ *      problem.
+ *      Got around to doing the ppa_queuecommand() clean up
+ *      Fixed bug relating to SMC EPP+ECP and monolithic driver
+ *                                              [Curtin-1-06-BETA]
+ *
+ * Where did the ppa_setup() code disappear to ??
+ *      Back in now...
+ *      Distribution of ppa now independent of parport (less work for me).
+ *      Also cleaned up the port detection to allow for variations on
+ *      IO aliasing (in an attempt to fix a few problems with some
+ *      machines...)
+ *                                              [Curtin-1-07-BETA]
+ *
+ * Rewrote detection code for monolithic driver and ported changes to
+ *      parport driver. Result is more stable detection of hardware and
+ *      better immunity to port aliasing (old XT cards).
+ *      Parport 0.16 (or better) is required for parport operation and
+ *      ECP+EPP modes, otherwise the latest parport edition is recommended.
+ *
+ *      When using EPP and writing to disk CPU usage > 40%, while reading <10%.
+ *      This is due to ZIP drive IO scheduling, the drive does a verify after
+ *      write to ensure data integrity (removable media is ALWAYS questionable
+ *      since you never know where it has been).
+ *      Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
+ *      over 10 sectors per jiffy.
+ *
+ *      Hmm... I think I know a way but it will send the driver into
+ *      ALPHA state again.
+ *                                              [Curtin-1-08-STABLE]
+ */
+
+/* The following #define is to avoid a clash with hosts.c */
+#define PPA_CODE 1
 #include  "ppa.h"
-
-struct proc_dir_entry proc_scsi_ppa = 
-                { PROC_SCSI_PPA, 3, "ppa", S_IFDIR|S_IRUGO|S_IXUGO, 2 };
-
-static int              ppa_abort_flag = 0;
-static int              ppa_error_code = DID_OK;
-static char             ppa_info_string[132];
-static Scsi_Cmnd        *ppa_current = 0;
-static void             (*ppa_done) (Scsi_Cmnd *);
-static int              ppa_port_delay;
-
-void    out_p( short port, char byte)
-
-{       outb(byte,ppa_base+port);
-        udelay(ppa_port_delay);
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate 
+ */
+#define   PPA_AUTODETECT        0      /* Autodetect mode                */
+#define   PPA_NIBBLE            1      /* work in standard 4 bit mode    */
+#define   PPA_PS2               2      /* PS/2 byte mode         */
+#define   PPA_EPP_8             3      /* EPP mode, 8 bit                */
+#define   PPA_EPP_16            4      /* EPP mode, 16 bit               */
+#define   PPA_EPP_32            5      /* EPP mode, 32 bit               */
+#define   PPA_UNKNOWN           6      /* Just in case...                */
+
+static char *PPA_MODE_STRING[] =
+{
+       "Autodetect",
+       "SPP",
+       "PS/2",
+       "EPP 8 bit",
+       "EPP 16 bit",
+       "EPP 32 bit",
+       "Unknown"};
+
+typedef struct {
+       struct ppd *dev;        /* Parport device entry          */
+       int speed;              /* General PPA delay constant   */
+       int speed_fast;         /* Const for nibble/byte modes  */
+       int epp_speed;          /* Reset time period            */
+       int mode;               /* Transfer mode                */
+       int timeout;            /* Number of timeouts           */
+       int host;               /* Host number (for proc)       */
+       int abort_flag;         /* Abort flag                   */
+       int error_code;         /* Error code                   */
+       int ppa_failed;         /* Failure flag                 */
+       Scsi_Cmnd *cur_cmd;     /* Current queued command       */
+       void (*done) (Scsi_Cmnd *);     /* Done func for queuecommand   */
+       struct tq_struct ppa_tq;        /* Polling interupt stuff       */
+       struct wait_queue *ppa_wait_q;  /* Used for PnP stuff           */
+} ppa_struct;
+
+static void ppa_interrupt(void *data);
+/* I know that this is a mess but it works!! */
+#define NO_HOSTS 4
+static ppa_struct ppa_hosts[NO_HOSTS] =
+{
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL}
+};
+
+/* This is a global option */
+static int ppa_speed = -1;     /* Set to >0 to act as a global value */
+static int ppa_speed_fast = -1;        /* ditto.. */
+static int ppa_sg = SG_ALL;    /* enable/disable scatter-gather. */
+
+/* other options */
+#define   PPA_CAN_QUEUE         1      /* use "queueing" interface */
+#define   PPA_BURST_SIZE        512    /* block size for bulk transfers */
+#define   PPA_SELECT_TMO        5000   /* how long to wait for target ? */
+#define   PPA_SPIN_TMO          500000 /* ppa_wait loop limiter */
+
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL  0
+
+#define PPA_BASE(x)    ppa_hosts[(x)].dev->port->base
+
+/* Port IO - Sorry Grant but I prefer the following symbols */
+#define r_dtr(x)        inb(PPA_BASE(x))
+#define r_str(x)        inb(PPA_BASE(x)+1)
+#define r_ctr(x)        inb(PPA_BASE(x)+2)
+#define r_epp(x)        inb(PPA_BASE(x)+4)
+#define r_fifo(x)       inb(PPA_BASE(x)+0x400)
+#define r_ecr(x)        inb(PPA_BASE(x)+0x402)
+
+#define w_dtr(x,y)      outb(y, PPA_BASE(x))
+#define w_str(x,y)      outb(y, PPA_BASE(x)+1)
+#define w_ctr(x,y)      outb(y, PPA_BASE(x)+2);\
+                       udelay( ppa_hosts[(x)].speed)
+#define w_epp(x,y)      outb(y, PPA_BASE(x)+4)
+#define w_fifo(x,y)     outb(y, PPA_BASE(x)+0x400)
+#define w_ecr(x,y)      outb(y, PPA_BASE(x)+0x402)
+
+int ppa_wakeup(void *ref)
+{
+       ppa_struct *ppa_dev = (ppa_struct *) ref;
+
+       if (!ppa_dev->ppa_wait_q)
+               return 1;       /* Wake up whom ? */
+
+       /* Claim the Parport */
+       if (parport_claim(ppa_dev->dev))
+               return 1;       /* Shouldn't happen */
+
+       wake_up(&ppa_dev->ppa_wait_q);
+       return 0;
 }
 
-char    in_p( short port)
+int ppa_release(struct Scsi_Host *host)
+{
+       int host_no = host->unique_id;
 
-{       return inb(ppa_base+port);
-        udelay(ppa_port_delay);
+       printk("Releasing ppa%i\n", host_no);
+       parport_unregister_device(ppa_hosts[host_no].dev);
+       return 0;
 }
 
-void    ppa_d_pulse( char b )
+static int ppa_pb_claim(int host_no)
+{
+       if (parport_claim(ppa_hosts[host_no].dev)) {
+               sleep_on(&ppa_hosts[host_no].ppa_wait_q);
+               ppa_hosts[host_no].ppa_wait_q = NULL;
+
+               /* Check to see if we were aborted or reset */
+               if (ppa_hosts[host_no].dev->port->cad !=
+                   ppa_hosts[host_no].dev) {
+                       printk("Abort detected on ppa%i\n", host_no);
+                       return 1;
+               }
+       }
+       return 0;
+}
 
-{       out_p(0,b);
-        out_p(2,0xc); out_p(2,0xe); out_p(2,0xc); out_p(2,0x4); out_p(2,0xc);
+static void ppa_pb_release(int host_no)
+{
+       parport_release(ppa_hosts[host_no].dev);
 }
 
-void    ppa_disconnect( void )
 
-{       ppa_d_pulse(0);
-        ppa_d_pulse(0x3c);
-        ppa_d_pulse(0x20);
-        ppa_d_pulse(0xf);
-}
+/* Placed here so everyone knows what ecp_sync does.. */
+static void ecp_sync(int host_no)
+{
+       int i, r;
 
-void    ppa_c_pulse( char b )
+       r = r_ecr(host_no);
+       if ((r & 0xe0) != 0x80)
+               return;
 
-{       out_p(0,b);
-        out_p(2,0x4); out_p(2,0x6); out_p(2,0x4); out_p(2,0xc);
-}
+       for (i = 0; i < 100; i++) {
+               r = r_ecr(host_no);
+               if (r & 0x01)
+                       return;
+               udelay(5);
+       }
 
-void    ppa_connect( void )
+       printk("ppa: ECP sync failed as data still present in FIFO.\n");
+}
 
-{       ppa_c_pulse(0);
-        ppa_c_pulse(0x3c);
-        ppa_c_pulse(0x20);
-        ppa_c_pulse(0x8f);
+static inline void ppa_d_pulse(int host_no, char b)
+{
+       w_dtr(host_no, b);
+       w_ctr(host_no, 0xc);
+       w_ctr(host_no, 0xe);
+       w_ctr(host_no, 0xc);
+       w_ctr(host_no, 0x4);
+       w_ctr(host_no, 0xc);
 }
 
-void    ppa_do_reset( void )
+static void ppa_disconnect(int host_no)
+{
+       ppa_d_pulse(host_no, 0);
+       ppa_d_pulse(host_no, 0x3c);
+       ppa_d_pulse(host_no, 0x20);
+       ppa_d_pulse(host_no, 0xf);
 
-{       out_p(2,0);             /* This is really just a guess */
-        udelay(100);
+       ppa_pb_release(host_no);
 }
 
-char    ppa_select( int  initiator, int target )
+static inline void ppa_c_pulse(int host_no, char b)
+{
+       w_dtr(host_no, b);
+       w_ctr(host_no, 0x4);
+       w_ctr(host_no, 0x6);
+       w_ctr(host_no, 0x4);
+       w_ctr(host_no, 0xc);
+}
 
-{       char    r;
-        int     k;
+static int ppa_connect(int host_no, int flag)
+{
+       int retv = ppa_pb_claim(host_no);
 
-        r = in_p(1);
-        out_p(0,(1<<target)); out_p(2,0xe); out_p(2,0xc);
-        out_p(0,(1<<initiator)); out_p(2,0x8);
+       ppa_c_pulse(host_no, 0);
+       ppa_c_pulse(host_no, 0x3c);
+       ppa_c_pulse(host_no, 0x20);
+       if ((flag == CONNECT_EPP_MAYBE) &&
+           IN_EPP_MODE(ppa_hosts[host_no].mode))
+               ppa_c_pulse(host_no, 0xcf);
+       else
+               ppa_c_pulse(host_no, 0x8f);
 
-        k = 0;
-        while ( !(r = (in_p(1) & 0xf0)) && (k++ < PPA_SELECT_TMO)) barrier();
-        return r;
+       return retv;
 }
 
-char    ppa_wait( void ) 
+static void ppa_do_reset(int host_no)
+{
+       /*
+        * SCSI reset taken from ppa_init and checked with
+        * Iomega document that Grant has (via email :(
+        */
+       ppa_pb_claim(host_no);
+       ppa_disconnect(host_no);
+
+       ppa_connect(host_no, CONNECT_NORMAL);
+
+       w_ctr(host_no, 0x6);
+       w_ctr(host_no, 0x4);
+       w_dtr(host_no, 0x40);
+       w_ctr(host_no, 0x8);
+       udelay(50);
+       w_ctr(host_no, 0xc);
+
+       ppa_disconnect(host_no);
+}
 
-/*      Wait for the high bit to be set.
+static char ppa_select(int host_no, int initiator, int target)
+{
+       char r;
+       int k;
 
-        In principle, this could be tied to an interrupt, but the adapter
-        doesn't appear to be designed to support interrupts.  We spin on
-        the 0x80 ready bit. 
-*/
+       r = r_str(host_no);     /* TODO */
 
-{       int     k;
-        char    r;
+       w_dtr(host_no, (1 << target));
+       w_ctr(host_no, 0xe);
+       w_ctr(host_no, 0xc);
+       w_dtr(host_no, (1 << initiator));
+       w_ctr(host_no, 0x8);
 
-        ppa_error_code = DID_OK;
-        k = 0;
-        while (!((r = in_p(1)) & 0x80) 
-                && (k++ < PPA_SPIN_TMO) && !ppa_abort_flag  ) barrier();
-                        
-        if (ppa_abort_flag) {
-                if (ppa_abort_flag == 1) ppa_error_code = DID_ABORT;
-                else {  ppa_do_reset();
-                        ppa_error_code = DID_RESET;
-                }
-                ppa_disconnect();
-                return 0;
-        }
-        if (k >= PPA_SPIN_TMO) { 
-                ppa_error_code = DID_TIME_OUT;
-                ppa_disconnect();
-                return 0;               /* command timed out */
-        }
-        return (r & 0xf0);
-}
+       k = 0;
+       while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
+               barrier();
 
-int     ppa_init( void )
+       if (k >= PPA_SELECT_TMO)
+               return 0;
 
-/* This is based on a trace of what the Iomega DOS 'guest' driver does.
-   I've tried several different kinds of parallel ports with guest and
-   coded this to react in the same ways that it does.
+       return r;
+}
 
-   The return value from this function is just a hint about where the
-   handshaking failed.
+static void ppa_fail(int host_no, int error_code)
+{
+       ppa_hosts[host_no].error_code = error_code;
+       ppa_hosts[host_no].ppa_failed = 1;
+       ppa_disconnect(host_no);
+}
 
-*/
+/*
+ * Wait for the high bit to be set.
+ * 
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts.  We spin on
+ * the 0x80 ready bit. 
+ */
+static char ppa_wait(int host_no)
+{
+       int k;
+       char r;
+
+       k = 0;
+       while (!((r = r_str(host_no)) & 0x80)
+              && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
+               barrier();
+
+       /* check if we were interrupted */
+       if (ppa_hosts[host_no].abort_flag) {
+               if (ppa_hosts[host_no].abort_flag == 1)
+                       ppa_fail(host_no, DID_ABORT);
+               else {
+                       ppa_do_reset(host_no);
+                       ppa_fail(host_no, DID_RESET);
+               }
+               return 0;
+       }
+       /* check if timed out */
+       if (k >= PPA_SPIN_TMO) {
+               ppa_fail(host_no, DID_TIME_OUT);
+               return 0;       /* command timed out */
+       }
+       /* 
+        * return some status information.
+        * Semantics: 0xc0 = ZIP wants more data
+        *            0xd0 = ZIP wants to send more data
+        *            0xf0 = end of transfer, ZIP is sending status
+        */
+       return (r & 0xf0);
+}
 
-{       char    r, s;
 
-        out_p(0,0xaa); 
-        if (in_p(0) != (char) 0xaa) return 1; 
-        ppa_disconnect();
-        ppa_connect();
-        out_p(2,0x6); 
-        if ((in_p(1) & 0xf0) != 0xf0) return 2; 
-        out_p(2,0x4);
-        if ((in_p(1) & 0xf0) != 0x80) return 3; 
-        ppa_disconnect();
-        s = in_p(2);
-        out_p(2,0xec);
-        out_p(0,0x55); 
-        r = in_p(0);    
-        if (r != (char) 0xff) { 
-                ppa_nybble = 1;
-                if (r != (char) 0x55) return 4; 
-                out_p(0,0xaa); if (in_p(0) != (char) 0xaa) return 5; 
-        }
-        out_p(2,s);
-        ppa_connect();
-        out_p(0,0x40); out_p(2,0x8); out_p(2,0xc);
-        ppa_disconnect();
-
-        return 0;
-}
-
-int     ppa_start( Scsi_Cmnd * cmd )
-
-{       int     k;
-
-        ppa_error_code = DID_OK;
-        ppa_abort_flag = 0;
-
-        if (cmd->target == PPA_INITIATOR) {
-                ppa_error_code = DID_BAD_TARGET;
-                return 0;
-        }
-        ppa_connect();
-        if (!ppa_select(PPA_INITIATOR,cmd->target)) {
-                ppa_disconnect();
-                ppa_error_code = DID_NO_CONNECT;
-                return 0;
-        }
-        out_p(2,0xc);
-
-        for (k=0; k < cmd->cmd_len; k++) {        /* send the command */
-                if (!ppa_wait()) return 0;
-                out_p(0,cmd->cmnd[k]);
-                out_p(2,0xe);
-                out_p(2,0xc);
-        }
-
-#ifdef PPA_DEBUG
-        printk("PPA: command out: ");
-        for (k=0; k < cmd->cmd_len; k++)
-                printk("%3x",(cmd->cmnd[k]) & 0xff );
-        printk("\n");
-#endif
+/* 
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ * 
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ * 
+ */
+static int ppa_init(int host_no)
+{
+       if (ppa_pb_claim(host_no))
+               return 1;
+       ppa_disconnect(host_no);
+
+       ppa_connect(host_no, CONNECT_NORMAL);
+
+       w_ctr(host_no, 0x6);
+       if ((r_str(host_no) & 0xf0) != 0xf0) {
+               ppa_pb_release(host_no);
+               return 2;
+       }
+       w_ctr(host_no, 0x4);
+       if ((r_str(host_no) & 0xf0) != 0x80) {
+               ppa_pb_release(host_no);
+               return 3;
+       }
+       /* This is a SCSI reset signal */
+       w_dtr(host_no, 0x40);
+       w_ctr(host_no, 0x8);
+       udelay(50);
+       w_ctr(host_no, 0xc);
+
+       ppa_disconnect(host_no);
+
+       return 0;
+}
 
-        return 1;
+/* 
+ * check the epp status. After a EPP transfer, it should be true that
+ * 1) the TIMEOUT bit (SPP_STR.0) is clear
+ * 2) the READY bit (SPP_STR.7) is set
+ */
+static int ppa_check_epp_status(int host_no)
+{
+       char r;
+       r = r_str(host_no);
+
+       if (r & 1) {
+               /* EPP timeout, according to the PC87332 manual */
+               /* Semantics of clearing EPP timeout bit.
+                * PC87332 - reading SPP_STR does it...
+                * SMC     - write 1 to EPP timeout bit
+                * Others  - (???) write 0 to EPP timeout bit
+                */
+               w_str(host_no, r);
+               w_str(host_no, r & 0xfe);
+               ppa_hosts[host_no].timeout++;
+               printk("PPA: EPP timeout on port 0x%04x\n",
+                      PPA_BASE(host_no));
+               ppa_fail(host_no, DID_BUS_BUSY);
+               return 0;
+       }
+       if (!(r & 0x80)) {
+               ppa_fail(host_no, DID_ERROR);
+               return 0;
+       }
+       return 1;
 }
 
-int     ppa_completion( Scsi_Cmnd * cmd )
+static inline int ppa_force_epp_byte(int host_no, char x)
+{
+/* This routine forces a byte down the EPP data port whether the
+ * device is ready or not...
+ */
+       char r;
+
+       w_epp(host_no, x);
+
+       r = r_str(host_no);
+       if (!(r & 1))
+               return 1;
+
+       if (ppa_hosts[host_no].epp_speed > 0) {
+               /* EPP timeout, according to the PC87332 manual */
+               /* Semantics of clearing EPP timeout bit.
+                * PC87332 - reading SPP_STR does it...
+                * SMC     - write 1 to EPP timeout bit
+                * Others  - (???) write 0 to EPP timeout bit
+                */
+               w_str(host_no, r);
+               w_str(host_no, r & 0xfe);
+
+               /* Take a deep breath, count to 10 and then... */
+               udelay(ppa_hosts[host_no].epp_speed);
+
+               /* Second time around */
+               w_epp(host_no, x);
+
+               r = r_str(host_no);
+       }
+       if (r & 1) {
+               w_str(host_no, r);
+               w_str(host_no, r & 0xfe);
+               ppa_hosts[host_no].timeout++;
+               printk("PPA: warning: EPP timeout\n");
+               ppa_fail(host_no, DID_BUS_BUSY);
+               return 0;
+       } else
+               return 1;
+}
 
-/* The bulk flag enables some optimisations in the data transfer loops,
-   it should be true for any command that transfers data in integral
-   numbers of sectors.
+static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
+       int k;
+
+       w_ctr(host_no, 0x4);
+       for (k = 0; k < cmd->cmd_len; k++) {    /* send the command */
+               if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
+                       return 0;
+       }
+       w_ctr(host_no, 0xc);
+       ecp_sync(host_no);
+       return 1;
+}
 
-   The driver appears to remain stable if we speed up the parallel port
-   i/o in this function, but not elsewhere.
-*/
+static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
+       int k;
+
+       w_ctr(host_no, 0xc);
+
+       for (k = 0; k < cmd->cmd_len; k++) {    /* send the command */
+               if (!ppa_wait(host_no))
+                       return 0;
+               w_dtr(host_no, cmd->cmnd[k]);
+               w_ctr(host_no, 0xe);
+               w_ctr(host_no, 0xc);
+       }
+       return 1;
+}
 
-{       char    r, l, h, v;
-        int     dir, cnt, blen, fast, bulk;
-        char    *buffer;
+static int ppa_start(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
+
+       /* 
+        * by default, the command failed,
+        * unless explicitly completed in ppa_completion().
+        */
+       ppa_hosts[host_no].error_code = DID_ERROR;
+       ppa_hosts[host_no].abort_flag = 0;
+       ppa_hosts[host_no].ppa_failed = 0;
+
+       if (cmd->target == PPA_INITIATOR) {
+               ppa_hosts[host_no].error_code = DID_BAD_TARGET;
+               ppa_hosts[host_no].ppa_failed = 1;
+               return 0;
+       }
+       if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
+               return 0;
+
+       if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
+               ppa_fail(host_no, DID_NO_CONNECT);
+               return 0;
+       }
+       if (IN_EPP_MODE(ppa_hosts[host_no].mode))
+               return ppa_send_command_epp(cmd);
+       else
+               return ppa_send_command_normal(cmd);
+}
 
-#ifdef PPA_DEBUG
-        int     k;
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol. 
+ */
+static inline int ppa_outs(int host_no, char *buffer, int len)
+{
+       int k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+       int r;
 #endif
 
-        if (!(r = ppa_wait())) return 0;
-        v = cmd->cmnd[0];
-        bulk = ((v==READ_6)||(v==READ_10)||(v==WRITE_6)||(v==WRITE_10));
-        buffer = cmd->request_buffer;
-        blen = cmd->request_bufflen;
-        cnt = 0;  dir = 0;
-        if (r == (char) 0xc0) dir = 1;  /* d0 = read c0 = write f0 = status */
-
-        ppa_port_delay = ppa_speed_high;
-
-        while (r != (char) 0xf0) {
-                if (((r & 0xc0) != 0xc0 ) || (cnt >= blen)) {
-                        ppa_disconnect();
-                        ppa_error_code = DID_ERROR;
-                        return 0;
-                }
-                fast = bulk && ((blen - cnt) >= PPA_SECTOR_SIZE);
-                if (dir) do {
-                        out_p(0,buffer[cnt++]);
-                        out_p(2,0xe); out_p(2,0xc);
-                        if (!fast) break;
-                } while (cnt % PPA_SECTOR_SIZE);
-                else {  
-                        if (ppa_nybble) do {
-                                out_p(2,0x4); h = in_p(1); 
-                                out_p(2,0x6); l = in_p(1);
-                                v = ((l >> 4) & 0x0f) + (h & 0xf0);
-                                buffer[cnt++] = v;
-                                if (!fast) break;
-                        } while (cnt % PPA_SECTOR_SIZE);
-                        else do {
-                                out_p(2,0x25); v = in_p(0); out_p(2,0x27);
-                                buffer[cnt++] = v;
-                                if (!fast) break;
-                        } while (cnt % PPA_SECTOR_SIZE);
-                        if (!ppa_nybble) {
-                                out_p(2,0x5); out_p(2,0x4);
-                        }
-                        out_p(2,0xc);
-                }
-                if (!(r = ppa_wait())) return 0;
-        }
-
-        ppa_port_delay = ppa_speed_low;
-
-        out_p(2,0x4);            /* now read status byte */
-        h = in_p(1);
-        out_p(2,0x6);
-        l = in_p(1);
-        out_p(2,0xc);
-        r = ((l >> 4) & 0x0f) + (h & 0xf0);
-
-        out_p(2,0xe); out_p(2,0xc);
-        ppa_disconnect();
-
-#ifdef PPA_DEBUG
-        printk("PPA: status: %x, data[%d]: ",r & STATUS_MASK,cnt);
-        if (cnt > 12) cnt = 12;
-        for (k=0; k < cnt; k++)
-           printk("%3x",buffer[k] & 0xff );
-        printk("\n");   
+       switch (ppa_hosts[host_no].mode) {
+       case PPA_NIBBLE:
+       case PPA_PS2:
+               /* 8 bit output, with a loop */
+               for (k = len; k; k--) {
+                       w_dtr(host_no, *buffer++);
+                       w_ctr(host_no, 0xe);
+                       w_ctr(host_no, 0xc);
+               }
+               return 1;       /* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+       case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+               w_ctr(host_no, 0x4);
+               for (k = len; k; k -= 4) {
+                       w_epp(host_no, *buffer++);
+                       w_epp(host_no, *buffer++);
+                       w_epp(host_no, *buffer++);
+                       w_epp(host_no, *buffer++);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return 1;
+#endif
+       case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+               w_ctr(host_no, 0x4);
+               for (k = len; k; k -= 2) {
+                       w_epp(host_no, *buffer++);
+                       w_epp(host_no, *buffer++);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return 1;
+#endif
+       case PPA_EPP_8:
+               w_ctr(host_no, 0x4);
+               for (k = len; k; k--) {
+                       w_epp(host_no, *buffer++);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return 1;
+#else
+       case PPA_EPP_32:
+       case PPA_EPP_16:
+       case PPA_EPP_8:
+               w_ctr(host_no, 0x4);
+               switch (ppa_hosts[host_no].mode) {
+               case PPA_EPP_8:
+                       outsb(PPA_BASE(host_no) + 0x04,
+                             buffer, len);
+                       break;
+               case PPA_EPP_16:
+                       outsw(PPA_BASE(host_no) + 0x04,
+                             buffer, len / 2);
+                       break;
+               case PPA_EPP_32:
+                       outsl(PPA_BASE(host_no) + 0x04,
+                             buffer, len / 4);
+                       break;
+               }
+               k = ppa_check_epp_status(host_no);
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return k;
 #endif
 
-        return (r & STATUS_MASK);
+       default:
+               printk("PPA: bug in ppa_outs()\n");
+       }
+       return 0;
 }
 
-/* deprecated synchronous interface */
-
-int     ppa_command( Scsi_Cmnd * cmd )
-
-{       int     s;
-
-        sti();
-        s = 0;
-        if (ppa_start(cmd))
-           if (ppa_wait()) 
-               s = ppa_completion(cmd);
-        return s + (ppa_error_code << 16);
+static inline int ppa_outb(int host_no, char d)
+{
+       int k;
+
+       switch (ppa_hosts[host_no].mode) {
+       case PPA_NIBBLE:
+       case PPA_PS2:
+               w_dtr(host_no, d);
+               w_ctr(host_no, 0xe);
+               w_ctr(host_no, 0xc);
+               return 1;       /* assume transfer went OK */
+
+       case PPA_EPP_8:
+       case PPA_EPP_16:
+       case PPA_EPP_32:
+               w_ctr(host_no, 0x4);
+               w_epp(host_no, d);
+               k = ppa_check_epp_status(host_no);
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return k;
+
+       default:
+               printk("PPA: bug in ppa_outb()\n");
+       }
+       return 0;
 }
 
-/* pseudo-interrupt queueing interface */
-
-/* Since the PPA itself doesn't generate interrupts, we use
-   the scheduler's task queue to generate a stream of call-backs and
-   complete the request when the drive is ready.
-*/
-
-static void ppa_interrupt( void *data);
-
-static struct tq_struct ppa_tq = {0,0,ppa_interrupt,NULL};
-
-static void ppa_interrupt( void *data)
+static inline int ppa_ins(int host_no, char *buffer, int len)
+{
+       int k, h, l;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+       int r;
+#endif
 
-{       Scsi_Cmnd *cmd;
-        void  (*done) (Scsi_Cmnd *);
+       switch (ppa_hosts[host_no].mode) {
+       case PPA_NIBBLE:
+               /* 4 bit input, with a loop */
+               for (k = len; k; k--) {
+                       w_ctr(host_no, 0x4);
+                       h = r_str(host_no);
+                       w_ctr(host_no, 0x6);
+                       l = r_str(host_no);
+                       *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
+               }
+               w_ctr(host_no, 0xc);
+               return 1;       /* assume transfer went OK */
+
+       case PPA_PS2:
+               /* 8 bit input, with a loop */
+               for (k = len; k; k--) {
+                       w_ctr(host_no, 0x25);
+                       *buffer++ = r_dtr(host_no);
+                       w_ctr(host_no, 0x27);
+               }
+               w_ctr(host_no, 0x5);
+               w_ctr(host_no, 0x4);
+               w_ctr(host_no, 0xc);
+               return 1;       /* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+       case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+               w_ctr(host_no, 0x24);
+               for (k = len; k; k -= 4) {
+                       *buffer++ = r_epp(host_no);
+                       *buffer++ = r_epp(host_no);
+                       *buffer++ = r_epp(host_no);
+                       *buffer++ = r_epp(host_no);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0x2c);
+               ecp_sync(host_no);
+               return 1;
+#endif
+       case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+               w_ctr(host_no, 0x24);
+               for (k = len; k; k -= 2) {
+                       *buffer++ = r_epp(host_no);
+                       *buffer++ = r_epp(host_no);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0x2c);
+               ecp_sync(host_no);
+               return 1;
+#endif
+       case PPA_EPP_8:
+               w_ctr(host_no, 0x24);
+               for (k = len; k; k--) {
+                       *buffer++ = r_epp(host_no);
+                       r = ppa_check_epp_status(host_no);
+                       if (!r)
+                               return r;
+               }
+               w_ctr(host_no, 0x2c);
+               ecp_sync(host_no);
+               return 1;
+               break;
+#else
+       case PPA_EPP_8:
+       case PPA_EPP_16:
+       case PPA_EPP_32:
+               w_ctr(host_no, 0x24);
+               switch (ppa_hosts[host_no].mode) {
+               case PPA_EPP_8:
+                       insb(PPA_BASE(host_no) + 0x04,
+                            buffer, len);
+                       break;
+               case PPA_EPP_16:
+                       insw(PPA_BASE(host_no) + 0x04,
+                            buffer, len / 2);
+                       break;
+               case PPA_EPP_32:
+                       insl(PPA_BASE(host_no) + 0x04,
+                            buffer, len / 4);
+                       break;
+               }
+               k = ppa_check_epp_status(host_no);
+               w_ctr(host_no, 0x2c);
+               ecp_sync(host_no);
+               return k;
+#endif
 
-        cmd = ppa_current;
-        done = ppa_done;
-        if (!cmd) return;
-        
-        if (ppa_abort_flag) {
-                ppa_disconnect();
-                if(ppa_abort_flag == 1) cmd->result = DID_ABORT << 16;
-                else { ppa_do_reset();
-                       cmd->result = DID_RESET << 16;
-                }
-                ppa_current = 0;
-                done(cmd);
-                return;
-        }
-        if (!( in_p(1) & 0x80)) {
-                queue_task(&ppa_tq,&tq_scheduler);
-                return;
-        }
-        cmd->result = ppa_completion(cmd) + (ppa_error_code << 16);
-        ppa_current = 0;
-        done(cmd);
-        return;
+       default:
+               printk("PPA: bug in ppa_ins()\n");
+       }
+       return 0;
 }
 
-int     ppa_queuecommand( Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-
-{       if (ppa_current) return 0;
-        sti();
-        ppa_current = cmd;
-        ppa_done = done;
-        if (!ppa_start(cmd)) {
-                cmd->result = ppa_error_code << 16;
-                ppa_current = 0;
-                done(cmd);
-                return 0;
-        }
-        queue_task(&ppa_tq,&tq_scheduler);
-        return 0;
+static int ppa_inb(int host_no, char *buffer)
+{
+       int h, l, k;
+
+       switch (ppa_hosts[host_no].mode) {
+       case PPA_NIBBLE:
+               /* 4 bit input */
+               w_ctr(host_no, 0x4);
+               h = r_str(host_no);
+               w_ctr(host_no, 0x6);
+               l = r_str(host_no);
+               *buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
+               w_ctr(host_no, 0xc);
+               return 1;       /* assume transfer went OK */
+
+       case PPA_PS2:
+               /* 8 bit input */
+               w_ctr(host_no, 0x25);
+               *buffer++ = r_dtr(host_no);
+               w_ctr(host_no, 0x27);
+               w_ctr(host_no, 0x5);
+               w_ctr(host_no, 0x4);
+               w_ctr(host_no, 0xc);
+               return 1;       /* assume transfer went OK */
+
+       case PPA_EPP_8:
+       case PPA_EPP_16:
+       case PPA_EPP_32:
+               w_ctr(host_no, 0x24);
+               *buffer = r_epp(host_no);
+               k = ppa_check_epp_status(host_no);
+               w_ctr(host_no, 0xc);
+               ecp_sync(host_no);
+               return k;
+
+       default:
+               printk("PPA: bug in ppa_inb()\n");
+       }
+       return 0;
 }
 
-int     ppa_detect( Scsi_Host_Template * host )
-
-{       struct  Scsi_Host       *hreg;
-        int     rs;
-
-        /* can we have the ports ? */
-
-        if (check_region(ppa_base,3)) {
-                printk("PPA: ports at 0x%3x are not available\n",ppa_base);
-                return 0;
-        }
-
-        /* attempt to initialise the controller */
-
-        ppa_port_delay = ppa_speed_low;
-
-        rs = ppa_init();
-        if (rs) {
-            printk("PPA: unable to initialise controller at 0x%x, error %d\n",
-                   ppa_base,rs);
-            return 0;
-        }
-
-        /* now the glue ... */
-
-        host->proc_dir = &proc_scsi_ppa;
-
-        request_region(ppa_base,3,"ppa");
-
-        host->can_queue = PPA_CAN_QUEUE;
-
-        hreg = scsi_register(host,0);
-        hreg->io_port = ppa_base;
-        hreg->n_io_port = 3;
-        hreg->dma_channel = -1;
-
-        sprintf(ppa_info_string,
-                "PPA driver version %s using %d-bit mode on port 0x%x.",
-                PPA_VERSION,8-ppa_nybble*4,ppa_base);
-        host->name = ppa_info_string;
-
-        return 1;       /* 1 host detected */
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ * 
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int ppa_completion(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
+
+       char r, l, h, v;
+       int dir, cnt, blen, fast, bulk, status;
+       char *buffer;
+       struct scatterlist *sl;
+       int current_segment, nsegment;
+
+       v = cmd->cmnd[0];
+       bulk = ((v == READ_6) ||
+               (v == READ_10) ||
+               (v == WRITE_6) ||
+               (v == WRITE_10));
+
+       /* code for scatter/gather: */
+       if (cmd->use_sg) {
+               /* if many buffers are available, start filling the first */
+               sl = (struct scatterlist *) cmd->request_buffer;
+               blen = sl->length;
+               buffer = sl->address;
+       } else {
+               /* else fill the only available buffer */
+               sl = NULL;
+               buffer = cmd->request_buffer;
+               blen = cmd->request_bufflen;
+       }
+       current_segment = 0;
+       nsegment = cmd->use_sg;
+
+       cnt = 0;
+
+       /* detect transfer direction */
+       dir = 0;
+       if (!(r = ppa_wait(host_no)))
+               return 0;
+       if (r == (char) 0xc0)
+               dir = 1;        /* d0 = read c0 = write f0 = status */
+
+       while (r != (char) 0xf0) {
+               if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
+                       ppa_fail(host_no, DID_ERROR);
+                       return 0;
+               }
+               /* determine if we should use burst I/O */
+               fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
+                       ((((long)buffer + cnt)) & 0x3) == 0);
+
+               if (fast) {
+                       if (dir)
+                               status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
+                       else
+                               status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
+                       cnt += PPA_BURST_SIZE;
+               } else {
+                       if (dir)
+                               status = ppa_outb(host_no, buffer[cnt]);
+                       else
+                               status = ppa_inb(host_no, &buffer[cnt]);
+                       cnt++;
+               }
+
+               if (!status || !(r = ppa_wait(host_no)))
+                       return 0;
+
+               if (sl && cnt == blen) {
+                       /* if scatter/gather, advance to the next segment */
+                       if (++current_segment < nsegment) {
+                               ++sl;
+                               blen = sl->length;
+                               buffer = sl->address;
+                               cnt = 0;
+                       }
+                       /*
+                        * the else case will be captured by the (cnt >= blen)
+                        * test above.
+                        */
+               }
+       }
+
+       /* read status and message bytes */
+       if (!ppa_inb(host_no, &l))      /* read status byte */
+               return 0;
+       if (!(ppa_wait(host_no)))
+               return 0;
+       if (!ppa_inb(host_no, &h))      /* read message byte */
+               return 0;
+
+       ppa_disconnect(host_no);
+
+       ppa_hosts[host_no].error_code = DID_OK;
+       return (h << 8) | (l & STATUS_MASK);
 }
 
-int     ppa_biosparam( Disk * disk, kdev_t dev, int ip[])
-
-/*  Apparently the the disk->capacity attribute is off by 1 sector 
-    for all disk drives.  We add the one here, but it should really
-    be done in sd.c.  Even if it gets fixed there, this will still
-    work.
-*/
+/* deprecated synchronous interface */
 
-{       ip[0] = 0x40;
-        ip[1] = 0x20;
-        ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
-        if (ip[2] > 1024) {
-                ip[0] = 0xff;
-                ip[1] = 0x3f;
-                ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
-                if (ip[2] > 1023)
-                        ip[2] = 1023;
-        }
-        return 0;
+int ppa_command(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
+       int s;
+
+       sti();
+       s = 0;
+       if (ppa_start(cmd))
+               if (ppa_wait(host_no))
+                       s = ppa_completion(cmd);
+       return s + (ppa_hosts[host_no].error_code << 16);
 }
 
-int     ppa_abort( Scsi_Cmnd * cmd )
-
-{       ppa_abort_flag = 1;
-        return SCSI_ABORT_SNOOZE;
+/* pseudo-interrupt queueing interface */
+/*
+ * Since the PPA itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void ppa_interrupt(void *data)
+{
+       ppa_struct *tmp = (ppa_struct *) data;
+       Scsi_Cmnd *cmd = tmp->cur_cmd;
+       void (*done) (Scsi_Cmnd *) = tmp->done;
+       int host_no = cmd->host->unique_id;
+
+       if (!cmd) {
+               printk("PPA: bug in ppa_interrupt\n");
+               return;
+       }
+       /* First check for any errors that may of occured
+        * Here we check for internal errors
+        */
+       if (tmp->ppa_failed) {
+               printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
+                      tmp->error_code);
+               cmd->result = DID_ERROR << 16;
+               tmp->cur_cmd = 0;
+               done(cmd);
+               return;
+       }
+       /* Occasionally the mid level driver will abort a SCSI
+        * command because we are taking to long, if this occurs
+        * we should abort the command.
+        */
+       if (tmp->abort_flag) {
+               ppa_disconnect(host_no);
+               if (tmp->abort_flag == 1)
+                       cmd->result = DID_ABORT << 16;
+               else {
+                       ppa_do_reset(host_no);
+                       cmd->result = DID_RESET << 16;
+               }
+               tmp->cur_cmd = 0;
+               done(cmd);
+               return;
+       }
+       /* Check to see if the device is now free, if not
+        * then throw this function onto the scheduler queue
+        * to be called back in a jiffy.
+        * (i386: 1 jiffy = 0.01 seconds)
+        */
+       if (!(r_str(host_no) & 0x80)) {
+               tmp->ppa_tq.data = (void *) tmp;
+               queue_task(&tmp->ppa_tq, &tq_scheduler);
+               return;
+       }
+       /* Device is now free and no errors have occured so
+        * it is safe to do the data phase
+        */
+       cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
+       tmp->cur_cmd = 0;
+       done(cmd);
+       return;
 }
 
-int     ppa_reset( Scsi_Cmnd * cmd, unsigned int ignored )
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+       int host_no = cmd->host->unique_id;
+
+       if (ppa_hosts[host_no].cur_cmd) {
+               printk("PPA: bug in ppa_queuecommand\n");
+               return 0;
+       }
+       sti();
+       ppa_hosts[host_no].cur_cmd = cmd;
+       ppa_hosts[host_no].done = done;
+
+       if (!ppa_start(cmd)) {
+               cmd->result = ppa_hosts[host_no].error_code << 16;
+               ppa_hosts[host_no].cur_cmd = 0;
+               done(cmd);
+               return 0;
+       }
+       ppa_interrupt(ppa_hosts + host_no);
+
+       return 0;
+}
 
-{       ppa_abort_flag = 2;
-        return SCSI_RESET_PUNT;
+/*
+ * Apparently the the disk->capacity attribute is off by 1 sector 
+ * for all disk drives.  We add the one here, but it should really
+ * be done in sd.c.  Even if it gets fixed there, this will still
+ * work.
+ */
+int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+       ip[0] = 0x40;
+       ip[1] = 0x20;
+       ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+       if (ip[2] > 1024) {
+               ip[0] = 0xff;
+               ip[1] = 0x3f;
+               ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+               if (ip[2] > 1023)
+                       ip[2] = 1023;
+       }
+       return 0;
 }
 
-const char      *ppa_info( struct Scsi_Host * host )
+int ppa_abort(Scsi_Cmnd * cmd)
+{
+       int host_no = cmd->host->unique_id;
 
-{       return ppa_info_string;
+       ppa_hosts[host_no].abort_flag = 1;
+       ppa_hosts[host_no].error_code = DID_ABORT;
+       if (ppa_hosts[host_no].ppa_wait_q)
+               wake_up(&ppa_hosts[host_no].ppa_wait_q);
+
+       return SCSI_ABORT_SNOOZE;
 }
 
-#ifndef MODULE
+int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
+{
+       int host_no = cmd->host->unique_id;
 
-/* Command line parameters (for built-in driver):
+       ppa_hosts[host_no].abort_flag = 2;
+       ppa_hosts[host_no].error_code = DID_RESET;
+       if (ppa_hosts[host_no].ppa_wait_q)
+               wake_up(&ppa_hosts[host_no].ppa_wait_q);
 
-   Syntax:  ppa=base[,speed_high[,speed_low[,nybble]]]
+       return SCSI_RESET_PUNT;
+}
 
-   For example:  ppa=0x378   or   ppa=0x378,0,3
 
-*/
 
-void    ppa_setup(char *str, int *ints)
+/***************************************************************************
+ *                   Parallel port probing routines                        *
+ ***************************************************************************/
 
-{       if (ints[0] > 0) ppa_base = ints[1];
-        if (ints[0] > 1) ppa_speed_high = ints[2];
-        if (ints[0] > 2) ppa_speed_low = ints[3];
-        if (ints[0] > 3) ppa_nybble = ints[4];
-        if (ints[0] > 4) ppa_nybble = ints[5];
-}
 
-#else
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
+#include  "scsi_module.c"
+#endif
 
-Scsi_Host_Template      driver_template = PPA;
+/*
+ * Start of Chipset kludges
+ */
+
+int ppa_detect(Scsi_Host_Template * host)
+{
+       struct Scsi_Host *hreg;
+       int rs;
+       int ports;
+       int i, nhosts;
+       struct parport *pb = parport_enumerate();
+
+       printk("PPA driver version: %s\n", PPA_VERSION);
+       nhosts = 0;
+
+       for (i = 0; pb; i++, pb=pb->next) {
+               int modes;
+
+               /* transfer global values here */
+               if (ppa_speed >= 0)
+                       ppa_hosts[i].speed = ppa_speed;
+               if (ppa_speed_fast >= 0)
+                       ppa_hosts[i].speed_fast = ppa_speed_fast;
+
+               ppa_hosts[i].dev =
+                   parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+                             NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
+
+               /* Claim the bus so it remembers what we do to the control
+                * registers. [ CTR and ECP ]
+                */
+               ppa_pb_claim(i);
+               w_ctr(i, 0x0c);
+               modes = ppa_hosts[i].dev->port->modes;
+
+               ppa_hosts[i].mode = PPA_NIBBLE;
+               if (modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) {
+                       ppa_hosts[i].mode = PPA_EPP_32;
+                       printk("PPA: Parport [ EPP ]\n");
+               } else if (modes & PARPORT_MODE_ECP) {
+                       w_ecr(i, 0x20);
+                       ppa_hosts[i].mode = PPA_PS2;
+                       printk("PPA: Parport [ ECP in PS2 submode ]\n");
+               } else if (modes & PARPORT_MODE_PS2) {
+                       ppa_hosts[i].mode = PPA_PS2;
+                       printk("PPA: Parport [ PS2 ]\n");
+               }
+               /* Done configuration */
+               ppa_pb_release(i);
+
+               rs = ppa_init(i);
+               if (rs) {
+                       parport_unregister_device(ppa_hosts[i].dev);
+                       continue;
+               }
+               /* now the glue ... */
+               switch (ppa_hosts[i].mode) {
+               case PPA_NIBBLE:
+               case PPA_PS2:
+                       ports = 3;
+                       break;
+               case PPA_EPP_8:
+               case PPA_EPP_16:
+               case PPA_EPP_32:
+                       ports = 8;
+                       break;
+               default:        /* Never gets here */
+                       continue;
+               }
+
+               host->can_queue = PPA_CAN_QUEUE;
+               host->sg_tablesize = ppa_sg;
+               hreg = scsi_register(host, 0);
+               hreg->io_port = pb->base;
+               hreg->n_io_port = ports;
+               hreg->dma_channel = -1;
+               hreg->unique_id = i;
+               ppa_hosts[i]..host = hreg->host_no;
+               nhosts++;
+       }
+       if (nhosts == 0)
+               return 0;
+       else
+               return 1;       /* return number of hosts detected */
+}
 
-#include  "scsi_module.c"
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static int ppa_strncmp(const char *a, const char *b, int len)
+{
+       int loop;
+       for (loop = 0; loop < len; loop++)
+               if (a[loop] != b[loop])
+                       return 1;
+
+       return 0;
+}
 
-#endif
+static int ppa_proc_write(int hostno, char *buffer, int length)
+{
+       unsigned long x;
+       const char *inv_num = "ppa /proc entry passed invalid number\n";
+
+       if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
+               x = simple_strtoul(buffer + 15, NULL, 0);
+               if (x <= ppa_hosts[hostno].speed)
+                       ppa_hosts[hostno].speed_fast = x;
+               else
+                       printk(inv_num);
+               return length;
+       }
+       if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
+               x = simple_strtoul(buffer + 10, NULL, 0);
+               if (x >= ppa_hosts[hostno].speed_fast)
+                       ppa_hosts[hostno].speed = x;
+               else
+                       printk(inv_num);
+               return length;
+       }
+       if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
+               x = simple_strtoul(buffer + 10, NULL, 0);
+               ppa_hosts[hostno].epp_speed = x;
+               return length;
+       }
+       if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
+               x = simple_strtoul(buffer + 12, NULL, 0);
+               ppa_hosts[hostno].timeout = x;
+               return length;
+       }
+       if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+               x = simple_strtoul(buffer + 5, NULL, 0);
+               ppa_hosts[hostno].mode = x;
+               return length;
+       }
+       printk("ppa /proc: invalid variable\n");
+       return (-EINVAL);
+}
 
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+                 int length, int hostno, int inout)
+{
+       int i;
+       int size, len = 0;
+       off_t begin = 0;
+       off_t pos = 0;
+
+       for (i = 0; i < 4; i++)
+               if (ppa_hosts[i].host == hostno)
+                       break;
+
+       if (inout)
+               return ppa_proc_write(i, buffer, length);
+
+       size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+       len += size;
+       pos = begin + len;
+       size = sprintf(buffer + len, "Parport  : %s\n",
+                      ppa_hosts[i].dev->port->name);
+       len += size;
+       pos = begin + len;
+
+       size = sprintf(buffer + len, "Mode    : %s\n",
+                      PPA_MODE_STRING[ppa_hosts[i].mode]);
+       len += size;
+       pos = begin + len;
+
+       size = sprintf(buffer + len, "\nTiming Parameters\n");
+       len += size;
+       pos = begin + len;
+
+       size = sprintf(buffer + len, "ppa_speed       %i\n",
+                      ppa_hosts[i].speed);
+       len += size;
+       pos = begin + len;
+
+       size = sprintf(buffer + len, "ppa_speed_fast  %i\n",
+                      ppa_hosts[i].speed_fast);
+       len += size;
+       pos = begin + len;
+
+       if (IN_EPP_MODE(ppa_hosts[i].mode)) {
+               size = sprintf(buffer + len, "epp_speed       %i\n",
+                              ppa_hosts[i].epp_speed);
+               len += size;
+               pos = begin + len;
+
+               size = sprintf(buffer + len, "\nInternal Counters\n");
+               len += size;
+               pos = begin + len;
+
+               size = sprintf(buffer + len, "epp_timeout     %i\n",
+                              ppa_hosts[i].timeout);
+               len += size;
+               pos = begin + len;
+       }
+       *start = buffer + (offset + begin);
+       len -= (offset - begin);
+       if (len > length)
+               len = length;
+       return len;
+}
 /* end of ppa.c */
index c00198031840605dc3cc8ecc232d1fb294b8cbdc..84fa2dec8e987cb09601fd76faad8f61ee7622ae 100644 (file)
@@ -1,44 +1,90 @@
+/*  Driver for the PPA3 parallel port SCSI HBA embedded in 
+ * the Iomega ZIP drive
+ * 
+ * (c) 1996     Grant R. Guenther  grant@torque.net
+ */
+
 #ifndef _PPA_H
 #define _PPA_H
 
-/*  Driver for the PPA3 parallel port SCSI HBA embedded in 
-    the Iomega ZIP drive
+#define   PPA_VERSION   "Curtin 1-08-BETA"
+
+/* This driver reqires a 1.3.37 kernel or higher!! */
+
+/* Use the following to enable certain chipset support
+ * Default is PEDANTIC = 3
+ */
+
+#include <linux/config.h>
+
+#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+#define CONFIG_SCSI_PPA_HAVE_PEDANTIC  3
+#endif
+#ifndef CONFIG_SCSI_PPA_EPP_TIME
+#define CONFIG_SCSI_PPA_EPP_TIME       64
+#endif
 
-       (c) 1996        Grant R. Guenther  grant@torque.net
-*/
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#include  <linux/stddef.h>
+#include  <linux/module.h>
+#include  <linux/kernel.h>
+#include  <linux/tqueue.h>
+#include  <linux/ioport.h>
+#include  <linux/delay.h>
+#include  <linux/proc_fs.h>
+#include  <linux/stat.h>
+#include  <linux/blk.h>
+
+#include  <asm/io.h>
+#include  "sd.h"
+#include  "hosts.h"
+#include  <linux/parport.h>
+/* batteries not included :-) */
 
 #define        PPA_INITIATOR   7
 
-int ppa_detect(Scsi_Host_Template * );
-const char * ppa_info(struct Scsi_Host *);
+int ppa_detect(Scsi_Host_Template *);
+const char *ppa_info(struct Scsi_Host *);
 int ppa_command(Scsi_Cmnd *);
-int ppa_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int ppa_abort(Scsi_Cmnd *);
 int ppa_reset(Scsi_Cmnd *, unsigned int);
-int ppa_biosparam(Disk *, kdev_t, int[]);
-
-#define PPA {                  \
-       0,                      \
-       0,                      \
-       0,                      \
-       0,                      \
-       0,                      \
-       ppa_detect,             \
-       0,                      \
-       ppa_info,               \
-       ppa_command,            \
-       ppa_queuecommand,       \
-       ppa_abort,              \
-       ppa_reset,              \
-       0,                      \
-       ppa_biosparam,          \
-       0,                      \
-       PPA_INITIATOR,          \
-       SG_NONE,                \
-       1,                      \
-       0,                      \
-       0,                      \
-       DISABLE_CLUSTERING      \
-}
+int ppa_proc_info(char *, char **, off_t, int, int, int);
+int ppa_biosparam(Disk *, kdev_t, int*);
+int ppa_release(struct Scsi_Host *);
 
-#endif /* _PPA_H */
+#ifndef        MODULE
+#ifdef PPA_CODE
+#define SKIP_PROC_DIR
+#endif
+#endif
+
+#ifndef SKIP_PROC_DIR
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#endif /* !PPA_CODE => hosts.c */
+
+#define PPA {  /* next */              0, \
+               /* usage_count */       0, \
+               /* proc_dir */          &proc_scsi_ppa, \
+               /* proc_info */         ppa_proc_info, \
+               /* name */              "Iomega ZIP/JAZ Traveller", \
+               /* detect */            ppa_detect, \
+               /* release */           ppa_release, \
+               /* info */              0, \
+               /* command */           ppa_command, \
+               /* queuecommand */      ppa_queuecommand, \
+               /* abort */             ppa_abort, \
+               /* reset */             ppa_reset, \
+               /* slave_attach */      0, \
+               /* bios_param */        ppa_biosparam, \
+               /* can_queue */         0, \
+               /* this_id */           PPA_INITIATOR, \
+               /* sg_tablesize */      SG_ALL, \
+               /* cmd_per_lun */       1, \
+               /* present */           0, \
+               /* unchecked_isa_dma */ 0, \
+               /* use_clustering */    ENABLE_CLUSTERING \
+}
+#endif                         /* _PPA_H */
index fa529fa98a815977383919924ffa639abbe94df0..fe279dc94277a8cd8bfc808b15f55ad3aaa682aa 100644 (file)
@@ -18,14 +18,25 @@ bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
   if [ "$CONFIG_NFS_FS" = "y" ]; then
-    define_bool CONFIG_SUNRPC y
-    define_bool CONFIG_LOCKD y
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
     if [ "$CONFIG_ROOT_NFS" = "y" ]; then
       bool '      BOOTP support' CONFIG_RNFS_BOOTP
       bool '      RARP support' CONFIG_RNFS_RARP
     fi
   fi
+  tristate 'NFS server support' CONFIG_NFSD
+  if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
+    define_bool CONFIG_SUNRPC y
+    define_bool CONFIG_LOCKD y
+  else
+    if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" ]; then
+      define_bool CONFIG_SUNRPC m
+      define_bool CONFIG_LOCKD m
+    else
+      define_bool CONFIG_SUNRPC n
+      define_bool CONFIG_LOCKD n
+    fi
+  fi
   tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
   if [ "$CONFIG_SMB_FS" != "n" ]; then
     bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
index 9324df239fe12892814d6e149590f700fba89425..2855bf7561b1ddb2d795777b0c47eeb2701c3161 100644 (file)
@@ -173,6 +173,7 @@ void clear_inode(struct inode * inode)
 {
        struct wait_queue * wait;
 
+       inode->i_count++;
        truncate_inode_pages(inode, 0);
        wait_on_inode(inode);
        if (IS_WRITABLE(inode)) {
@@ -182,6 +183,7 @@ void clear_inode(struct inode * inode)
        remove_inode_hash(inode);
        remove_inode_free(inode);
        wait = ((volatile struct inode *) inode)->i_wait;
+       inode->i_count--;
        if (inode->i_count)
                nr_free_inodes++;
        memset(inode,0,sizeof(*inode));
index f09db15807724e6b4f4826f87e2370e741a4a022..b14e09d7bf7a1a93eeb5850979a7e8fecef72bbf 100644 (file)
@@ -554,7 +554,7 @@ int isofs_bmap(struct inode * inode,int block)
            if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
              {
 
-               printk("_isofs_bmap: block>= EOF(%d, %d)", block, 
+               printk("_isofs_bmap: block>= EOF(%d, %d)\n", block, 
                       inode->i_size);
              }
            return 0;
index 29aa6ba2ff9c4b2c065a1a550d9b713f535f3752..1506a2ba632d8819f12e58dda260c8396fbcb78e 100644 (file)
@@ -322,7 +322,8 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
        int             status;
 
        if (!host->h_monitored && nsm_monitor(host) < 0) {
-               printk(KERN_NOTICE "lockd: failed to monitor %s", host->h_name);
+               printk(KERN_NOTICE "lockd: failed to monitor %s\n",
+                                       host->h_name);
                return -ENOLCK;
        }
 
@@ -496,10 +497,10 @@ nlmclnt_cancel_callback(struct rpc_task *task)
                /* Everything's good */
                break;
        case NLM_LCK_DENIED_NOLOCKS:
-               dprintk("lockd: CANCEL failed (server has no locks)");
+               dprintk("lockd: CANCEL failed (server has no locks)\n");
                goto retry_cancel;
        default:
-               printk(KERN_NOTICE "lockd: weird return %d for CANCEL call",
+               printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
                        req->a_res.status);
        }
 
index ffb6cbf20f975eaafcb61f86b837f8f8c849e6ac..a7c9fe768dfee34e2f1a9f5159570a4a9c3df448 100644 (file)
 #include <linux/locks.h>
 #include <linux/unistd.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
 #include <linux/nfs_fs.h>
 #include <linux/lockd/bind.h>
 
 #include <asm/system.h>
-# include <asm/uaccess.h>
+#include <asm/uaccess.h>
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -50,6 +51,7 @@ static struct super_operations nfs_sops = {
        NULL
 };
 
+struct rpc_stat                        nfs_rpcstat = { &nfs_program };
 
 /*
  * The "read_inode" function doesn't actually do anything:
@@ -438,7 +440,7 @@ int
 init_nfs_fs(void)
 {
 #ifdef CONFIG_PROC_FS
-       rpcstat_register(&nfs_rpcstat);
+       rpc_proc_register(&nfs_rpcstat);
 #endif
         return register_filesystem(&nfs_fs_type);
 }
@@ -462,7 +464,7 @@ void
 cleanup_module(void)
 {
 #ifdef CONFIG_PROC_FS
-       rpcstat_unregister(&nfs_rpcstat);
+       rpc_proc_unregister("nfs");
 #endif
        unregister_filesystem(&nfs_fs_type);
        nfs_free_dircache();
index aee9f720a8dc5ca11cfdf5a4d412f06bffe815d6..df962d4b4e6023f76a70878c09c555912f1a0734 100644 (file)
@@ -625,25 +625,3 @@ struct rpc_program nfs_program = {
        nfs_version,
        &nfs_rpcstat,
 };
-
-/*
- * RPC stats support
- */
-static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-       return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
-}
-
-static struct proc_dir_entry   proc_nfsclnt = {
-       0, 3, "nfs",
-       S_IFREG | S_IRUGO, 1, 0, 0,
-       6, &proc_net_inode_operations,
-       nfs_get_info
-};
-
-struct rpc_stat                        nfs_rpcstat = {
-       NULL,                   /* next */
-       &proc_nfsclnt,          /* /proc/net directory entry */
-       &nfs_program,           /* RPC program */
-};
index 170b6e5b088444582ec37f3f9f6856acde323047..ee76fcf6d84ca2dffa557a5d11d67d189504e297 100644 (file)
@@ -1,18 +1,16 @@
 /*
- * nfsstat.c   procfs-based user access to knfsd statistics
+ * linux/fs/nfsd/stats.c
+ *
+ * procfs-based user access to knfsd statistics
+ *
+ * /proc/net/rpc/nfsd
  *
- * /proc/net/nfssrv
  * Format:
- *     net <packets> <udp> <tcp> <tcpconn>
- *     rpc <packets> <badfmt> <badclnt>
- *     auth <flavor> <creds> <upcalls> <badauth> <badverf> <authrej>
- *     fh  <hits> <misses> <avg_util> <stale> <cksum> <badcksum>
  *     rc <hits> <misses> <nocache>
- *     proto <version> <nrprocs>
- *     <calls> <time_msec>
- *     ... (for each procedure and protocol version)
+ *                     Statistsics for the reply cache
+ *     plus generic RPC stats (see net/sunrpc/stats.c)
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/kernel.h>
 #include <linux/nfsd/stats.h>
 
 struct nfsd_stats      nfsdstats;
-
-static int             nfsd_get_info(char *, char **, off_t, int, int);
-
-#ifndef PROC_NET_NFSSRV
-# define PROC_NET_NFSSRV       0
-#endif
-
-static struct proc_dir_entry proc_nfssrv = {
-       PROC_NET_NFSSRV, 4, "nfsd",
-       S_IFREG | S_IRUGO, 1, 0, 0,
-       6, &proc_net_inode_operations,
-       nfsd_get_info
-};
-
-struct svc_stat                nfsd_svcstats = {
-       NULL, &proc_nfssrv, &nfsd_program,
-};
+struct svc_stat                nfsd_svcstats = { &nfsd_program, };
 
 static int
-nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
+                               int *eof, void *data)
 {
-       int                     len;
+       int     len;
 
        len = sprintf(buffer,
                "rc %d %d %d\n",
@@ -55,38 +38,45 @@ nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
                        nfsdstats.rcmisses,
                        nfsdstats.rcnocache);
 
+       /* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
+       *eof = 0;
+
        /*
-        * Append generic nfsd RPC statistics
+        * Append generic nfsd RPC statistics if there's room for it.
         */
-       if (offset >= len) {
-               offset -= len;
-               len = svcstat_get_info(&nfsd_svcstats, buffer, start,
-                                       offset, length);
-#if 0
-       } else if (len < length) {
-               len = svcstat_get_info(&nfsd_svcstats, buffer + len, start,
-                                       offset - len, length - len);
-#endif
+       if (len <= offset) {
+               len = svc_proc_read(buffer, start, offset - len, count,
+                               eof, data);
+               return len;
+       }
+
+       if (len < count) {
+               len += svc_proc_read(buffer + len, start, 0, count - len,
+                               eof, data);
        }
 
        if (offset >= len) {
                *start = buffer;
                return 0;
        }
+
        *start = buffer + offset;
-       if ((len -= offset) > length)
-               len = length;
+       if ((len -= offset) > count)
+               return count;
        return len;
 }
 
 void
 nfsd_stat_init(void)
 {
-       svcstat_register(&nfsd_svcstats);
+       struct proc_dir_entry   *ent;
+
+       if ((ent = svc_proc_register(&nfsd_svcstats)) != 0)
+               ent->read_proc = nfsd_proc_read;
 }
 
 void
 nfsd_stat_shutdown(void)
 {
-       svcstat_unregister(&nfsd_svcstats);
+       svc_proc_unregister("nfsd");
 }
index d56c05170e9d192e1845ae84b7105a7ba81ba59a..809a260842ff3769ecca0094b07c0f65f67076f0 100644 (file)
@@ -14,6 +14,8 @@ extern struct inode_operations proc_scsi_inode_operations;
 
 EXPORT_SYMBOL(proc_register);
 EXPORT_SYMBOL(proc_unregister);
+EXPORT_SYMBOL(create_proc_entry);
+EXPORT_SYMBOL(remove_proc_entry);
 EXPORT_SYMBOL(proc_root);
 EXPORT_SYMBOL(proc_get_inode);
 EXPORT_SYMBOL(in_group_p);
index 0fb03a1a175caf17a2a4f2e562101b6f2b496cb6..9d43d794829e9c315c4164d238edcdd1912f80e5 100644 (file)
@@ -156,6 +156,18 @@ SYMBOL_NAME_STR(x) ":\n\t" \
        "call "SYMBOL_NAME_STR(smp_##x)"\n\t" \
        "jmp ret_from_intr\n");
 
+#define BUILD_SMP_TIMER_INTERRUPT(x) \
+asmlinkage void x(struct pt_regs * regs); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(x) ":\n\t" \
+       SAVE_MOST \
+       "movl %esp,%eax\n\t" \
+       "pushl %eax\n\t" \
+       "call "SYMBOL_NAME_STR(smp_##x)"\n\t" \
+       "addl $4,%esp\n\t" \
+       RESTORE_MOST);
+
 #endif /* __SMP__ */
 
 #define BUILD_IRQ(chip,nr,mask) \
index 19a25ff751989bf15c0e5779c632413c7407682d..d8b03e17c177a861bfb000c27c7371b371d90287 100644 (file)
@@ -423,7 +423,7 @@ if (copy_from_user(to,from,n)) \
 
 #define __copy_from_user(to,from,n)                    \
        (__builtin_constant_p(n) ?                      \
-        __constant_copy_from_user_nockeck((to),(from),(n)) :   \
+        __constant_copy_from_user_nocheck((to),(from),(n)) :   \
         __generic_copy_from_user_nocheck((to),(from),(n)))
 
 
index 2a1282c8add2b0dcb69376a98a6222166daa43eb..18076c1afcc6707b7154444b5c4d842953c7a812 100644 (file)
 #define LP_TIMEOUT_INTERRUPT   (60 * HZ)
 #define LP_TIMEOUT_POLLED      (10 * HZ)
 
-#define LP_B(minor)    lp_table[(minor)].base          /* IO address */
 #define LP_F(minor)    lp_table[(minor)].flags         /* flags for busy, etc. */
-#define LP_S(minor)    inb_p(LP_B((minor)) + 1)        /* status port */
-#define LP_C(minor)    (lp_table[(minor)].base + 2)    /* control port */
 #define LP_CHAR(minor) lp_table[(minor)].chars         /* busy timeout */
 #define LP_TIME(minor) lp_table[(minor)].time          /* wait time */
 #define LP_WAIT(minor) lp_table[(minor)].wait          /* strobe wait */
-#define LP_IRQ(minor)  lp_table[(minor)].irq           /* interrupt # */
+#define LP_IRQ(minor)  lp_table[(minor)].dev->port->irq /* interrupt # */
                                                        /* 0 means polled */
 #define LP_STAT(minor) lp_table[(minor)].stats         /* statistics area */
-
 #define LP_BUFFER_SIZE 256
 
+#define LP_BASE(x)     lp_table[(x)].dev->port->base
+
+#define r_dtr(x)       inb(LP_BASE(x))
+#define r_str(x)       inb(LP_BASE(x)+1)
+#define r_ctr(x)       inb(LP_BASE(x)+2)
+#define r_epp(x)       inb(LP_BASE(x)+4)
+#define r_fifo(x)      inb(LP_BASE(x)+0x400)
+#define r_ecr(x)       inb(LP_BASE(x)+0x402)
+
+#define w_dtr(x,y)     outb((y), LP_BASE(x))
+#define w_str(x,y)     outb((y), LP_BASE(x)+1)
+#define w_ctr(x,y)     outb((y), LP_BASE(x)+2)
+#define w_epp(x,y)     outb((y), LP_BASE(x)+4)
+#define w_fifo(x,y)    outb((y), LP_BASE(x)+0x400)
+#define w_ecr(x,y)     outb((y), LP_BASE(x)+0x402)
+
 struct lp_stats {
        unsigned long chars;
        unsigned long sleeps;
@@ -101,8 +113,7 @@ struct lp_stats {
 };
 
 struct lp_struct {
-       int base;
-       unsigned int irq;
+       struct ppd *dev;
        int flags;
        unsigned int chars;
        unsigned int time;
diff --git a/include/linux/parport.h b/include/linux/parport.h
new file mode 100644 (file)
index 0000000..3c3c26c
--- /dev/null
@@ -0,0 +1,213 @@
+/* $Id: parport.h,v 1.1.2.5 1997/03/29 21:08:31 phil Exp $ */
+
+#ifndef _PARPORT_H_
+#define _PARPORT_H_
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+
+/* Maximum of 8 ports per machine */
+#define PARPORT_MAX  8 
+
+/* Type classes for Plug-and-Play probe */
+
+typedef enum {
+       PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
+       PARPORT_CLASS_PRINTER,
+       PARPORT_CLASS_MODEM,
+       PARPORT_CLASS_NET,
+       PARPORT_CLASS_HDC,              /* Hard disk controller */
+       PARPORT_CLASS_PCMCIA,
+       PARPORT_CLASS_MEDIA,            /* Multimedia device */
+       PARPORT_CLASS_FDC,              /* Floppy disk controller */
+       PARPORT_CLASS_PORTS,
+       PARPORT_CLASS_SCANNER,
+       PARPORT_CLASS_DIGCAM,
+       PARPORT_CLASS_OTHER,            /* Anything else */
+       PARPORT_CLASS_UNSPEC            /* No CLS field in ID */
+} parport_device_class;
+
+struct parport_device_info {
+       parport_device_class class;
+       char *mfr;
+       char *model;
+       char *cmdset;
+       char *description;
+};
+
+/* Definitions for parallel port sharing */
+
+/* Forward declare some stuff so we can use mutually circular structures */
+struct ppd;
+struct parport;
+
+/* Each device can have two callback functions:
+ *  1) a preemption function, called by the resource manager to request
+ *     that the driver relinquish control of the port.  The driver should
+ *     return zero if it agrees to release the port, and nonzero if it 
+ *     refuses.  Do not call parport_release() - the kernel will do this
+ *     implicitly.
+ *
+ *  2) a wake-up function, called by the resource manager to tell drivers
+ *     that the port is available to be claimed.  If a driver wants to use
+ *     the port, it should call parport_claim() here.  The return value from
+ *     this function is ignored.
+ */
+typedef int (*callback_func) (void *);
+
+/* This is an ordinary kernel IRQ handler routine.
+ * The dev_id field (void *) will point the the port structure
+ * associated with the interrupt request (to allow IRQ sharing)
+ * Please make code IRQ sharing as this function may be called
+ * when it isn't meant for you...
+ */
+typedef void (*irq_handler_func) (int, void *, struct pt_regs *);
+
+/* A parallel port device */
+struct ppd {
+       char *name;
+       struct parport *port;   /* The port this is associated with */
+       callback_func preempt;  /* preemption function */
+       callback_func wakeup;   /* kick function */
+       void *private;
+       irq_handler_func irq_func;
+       int flags;
+       unsigned char ctr;      /* SPP CTR register */
+       unsigned char ecr;      /* ECP ECR register */
+       struct ppd *next;
+       struct ppd *prev;
+};
+
+/* A parallel port */
+struct parport {
+       unsigned int base;      /* base address */
+       unsigned int size;      /* IO extent */
+       char *name;
+       int irq;                /* interrupt (or -1 for none) */
+       int dma;
+       unsigned int modes;
+       struct ppd *devices;
+       struct ppd *cad;        /* port owner */
+       struct ppd *lurker;
+       unsigned int ctr;       /* SPP CTR register */
+       unsigned int ecr;       /* ECP ECR register */
+       struct parport *next;
+        unsigned int flags; 
+       struct proc_dir_entry *proc_dir;
+       struct parport_device_info probe_info; 
+};
+
+/* parport_register_port registers a new parallel port at the given address (if
+ * one does not already exist) and returns a pointer to it.  This entails
+ * claiming the I/O region, IRQ and DMA.
+ * NULL is returned if initialisation fails. 
+ */
+struct parport *parport_register_port(unsigned long base, int irq, int dma);
+
+/* parport_in_use returns nonzero if there are devices attached to a port. */
+#define parport_in_use(x)  ((x)->devices != NULL)
+
+/* parport_destroy blows away a parallel port.  This fails if any devices are
+ * registered.
+ */
+void parport_destroy(struct parport *);
+
+/* parport_enumerate returns a pointer to the linked list of all the ports
+ * in this machine.
+ */
+struct parport *parport_enumerate(void);
+
+/* parport_register_device declares that a device is connected to a port, and 
+ * tells the kernel all it needs to know.  
+ * pf is the preemption function (may be NULL for a transient driver)
+ * kf is the wake-up function (may be NULL for a transient driver)
+ * irq_func is the interrupt handler (may be NULL for no interrupts)
+ * Only one lurking driver can be used on a given port. 
+ * handle is a user pointer that gets handed to callback functions. 
+ */
+struct ppd *parport_register_device(struct parport *port, const char *name,
+                                   callback_func pf, callback_func kf,
+                                   irq_handler_func irq_func, int flags,
+                                   void *handle);
+
+/* parport_deregister causes the kernel to forget about a device */
+void parport_unregister_device(struct ppd *dev);
+
+/* parport_claim tries to gain ownership of the port for a particular driver.
+ * This may fail (return non-zero) if another driver is busy.  If this
+ * driver has registered an interrupt handler, it will be enabled. 
+ */
+int parport_claim(struct ppd *dev);
+
+/* parport_release reverses a previous parport_claim.  This can never fail, 
+ * though the effects are undefined (except that they are bad) if you didn't
+ * previously own the port.  Once you have released the port you should make
+ * sure that neither your code nor the hardware on the port tries to initiate
+ * any communication without first re-claiming the port.
+ * If you mess with the port state (enabling ECP for example) you should
+ * clean up before releasing the port. 
+ */
+void parport_release(struct ppd *dev);
+
+/* The "modes" entry in parport is a bit field representing the following
+ * modes.
+ * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
+ * 100% compatible with EPP.
+ */
+#define PARPORT_MODE_SPP               0x0001
+#define PARPORT_MODE_PS2               0x0002
+#define PARPORT_MODE_EPP               0x0004
+#define PARPORT_MODE_ECP               0x0008
+#define PARPORT_MODE_ECPEPP            0x0010
+#define PARPORT_MODE_ECR               0x0020  /* ECR Register Exists */
+#define PARPORT_MODE_ECPPS2            0x0040
+
+/* Flags used to identify what a device does
+ */
+#define PARPORT_DEV_TRAN               0x0000
+#define PARPORT_DEV_LURK               0x0001
+
+#define PARPORT_FLAG_COMA              1
+
+extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
+extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
+                                  char);
+
+/* Prototypes from parport_procfs */
+extern int parport_proc_register(struct parport *pp);
+extern void parport_proc_unregister(struct parport *pp);
+
+/* Prototypes from parport_ksyms.c */
+extern void dec_parport_count(void);
+extern void inc_parport_count(void);
+
+extern int parport_probe(struct parport *port, char *buffer, int len);
+extern void parport_probe_one(struct parport *port);
+
+/* Primitive port access functions */
+extern inline void parport_w_ctrl(struct parport *port, int val) 
+{
+       outb(val, port->base+2);
+}
+
+extern inline int parport_r_ctrl(struct parport *port)
+{
+       return inb(port->base+2);
+}
+
+extern inline void parport_w_data(struct parport *port, int val)
+{
+       outb(val, port->base);
+}
+
+extern inline int parport_r_data(struct parport *port)
+{
+       return inb(port->base);
+}
+
+extern inline int parport_r_status(struct parport *port)
+{
+       return inb(port->base+1);
+}
+
+#endif /* _PARPORT_H_ */
index c396c39ce41d517b41a658e500e7d1e290b44907..b3dabb0aaacf3d14fbee78d28234c0b8e0001c51 100644 (file)
@@ -48,7 +48,8 @@ enum root_directory_inos {
        PROC_RTC,
        PROC_LOCKS,
        PROC_ZORRO,
-       PROC_SLABINFO
+       PROC_SLABINFO,
+       PROC_PARPORT
 };
 
 enum pid_directory_inos {
index 330143da4a67eeb66e94df42136c2cfe74fa5ebd..275cbbb771f2fedc72d9fad9ce118c1c5fad89ed 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */
 
 #ifndef        _SCC_H
 #define        _SCC_H
 
 /* DEV ioctl() commands */
 
-#define SIOCSCCRESERVED (SIOCDEVPRIVATE+0)
-#define SIOCSCCCFG     (SIOCDEVPRIVATE+1)
-#define SIOCSCCINI     (SIOCDEVPRIVATE+2)
-#define SIOCSCCCHANINI (SIOCDEVPRIVATE+3)
-#define SIOCSCCSMEM    (SIOCDEVPRIVATE+4)
-#define SIOCSCCGKISS   (SIOCDEVPRIVATE+5)
-#define SIOCSCCSKISS   (SIOCDEVPRIVATE+6)
-#define SIOCSCCGSTAT   (SIOCDEVPRIVATE+7)
+enum SCC_ioctl_cmds {
+       SIOCSCCRESERVED = SIOCDEVPRIVATE,
+       SIOCSCCCFG,
+       SIOCSCCINI,
+       SIOCSCCCHANINI,
+       SIOCSCCSMEM,
+       SIOCSCCGKISS,
+       SIOCSCCSKISS,
+       SIOCSCCGSTAT,
+       SIOCSCCCAL
+};
 
 /* magic number */
 
 #define SCC_MAGIC      0x8530          /* ;-) */
 
-/* KISS state machine */
-
-#define        KISS_IDLE       0
-#define KISS_DATA      1
-#define KISS_ESCAPE    2
-#define KISS_RXFRAME   3
-
 /* Device parameter control (from WAMPES) */
 
-#define        PARAM_TXDELAY   1
-#define        PARAM_PERSIST   2
-#define        PARAM_SLOTTIME  3
-#define        PARAM_TXTAIL    4
-#define        PARAM_FULLDUP   5
-#define PARAM_SOFTDCD  6       /* was: PARAM_HW */
-#define PARAM_MUTE     7       /* ??? */
-#define PARAM_DTR       8
-#define PARAM_RTS      9
-#define PARAM_SPEED     10
-#define PARAM_ENDDELAY 11      /* ??? */
-#define PARAM_GROUP     12
-#define PARAM_IDLE      13
-#define PARAM_MIN       14
-#define        PARAM_MAXKEY    15
-#define PARAM_WAIT      16
-#define PARAM_MAXDEFER 17
-#define PARAM_TX        18
-#define PARAM_HWEVENT  31
-#define PARAM_RETURN   255     /* reset kiss mode */
+enum L1_params {
+       PARAM_DATA,
+       PARAM_TXDELAY,
+       PARAM_PERSIST,
+       PARAM_SLOTTIME,
+       PARAM_TXTAIL,
+       PARAM_FULLDUP,
+       PARAM_SOFTDCD,          /* was: PARAM_HW */
+       PARAM_MUTE,             /* ??? */
+       PARAM_DTR,
+       PARAM_RTS,
+       PARAM_SPEED,
+       PARAM_ENDDELAY,         /* ??? */
+       PARAM_GROUP,
+       PARAM_IDLE,
+       PARAM_MIN,
+       PARAM_MAXKEY,
+       PARAM_WAIT,
+       PARAM_MAXDEFER,
+       PARAM_TX,
+       PARAM_HWEVENT = 31,
+       PARAM_RETURN = 255      /* reset kiss mode */
+};
 
 /* fulldup parameter */
 
-#define KISS_DUPLEX_HALF       0       /* normal CSMA operation */
-#define KISS_DUPLEX_FULL       1       /* fullduplex, key down trx after transmission */
-#define KISS_DUPLEX_LINK       2       /* fullduplex, key down trx after 'idletime' sec */
-#define KISS_DUPLEX_OPTIMA     3       /* fullduplex, let the protocol layer control the hw */
+enum FULLDUP_modes {
+       KISS_DUPLEX_HALF,       /* normal CSMA operation */
+       KISS_DUPLEX_FULL,       /* fullduplex, key down trx after transmission */
+       KISS_DUPLEX_LINK,       /* fullduplex, key down trx after 'idletime' sec */
+       KISS_DUPLEX_OPTIMA      /* fullduplex, let the protocol layer control the hw */
+};
 
 /* misc. parameters */
 
 
 /* HWEVENT parameter */
 
-#define HWEV_DCD_ON    0
-#define HWEV_DCD_OFF   1
-#define HWEV_ALL_SENT  2
+enum HWEVENT_opts {
+       HWEV_DCD_ON,
+       HWEV_DCD_OFF,
+       HWEV_ALL_SENT
+};
 
 /* channel grouping */
 
 
 /* Tx/Rx clock sources */
 
-#define CLK_DPLL       0       /* normal halfduplex operation */
-#define CLK_EXTERNAL   1       /* external clocking (G3RUH/DF9IC modems) */
-#define CLK_DIVIDER    2       /* Rx = DPLL, Tx = divider (fullduplex with */
-                               /* modems without clock regeneration */
+enum CLOCK_sources {
+       CLK_DPLL,       /* normal halfduplex operation */
+       CLK_EXTERNAL,   /* external clocking (G3RUH/DF9IC modems) */
+       CLK_DIVIDER,    /* Rx = DPLL, Tx = divider (fullduplex with */
+                       /* modems without clock regeneration */
+       CLK_BRG         /* experimental fullduplex mode with DPLL/BRG for */
+                       /* MODEMs without clock recovery */
+};
 
 /* Tx state */
 
-#define TXS_IDLE       0       /* Transmitter off, no data pending */
-#define TXS_BUSY       1       /* waiting for permission to send / tailtime */
-#define TXS_ACTIVE     2       /* Transmitter on, sending data */
-#define TXS_NEWFRAME   3       /* reset CRC and send (next) frame */
-#define TXS_IDLE2      4       /* Transmitter on, no data pending */
-#define TXS_WAIT       5       /* Waiting for Mintime to expire */
-#define TXS_TIMEOUT    6       /* We had a transmission timeout */
+enum TX_state {
+       TXS_IDLE,       /* Transmitter off, no data pending */
+       TXS_BUSY,       /* waiting for permission to send / tailtime */
+       TXS_ACTIVE,     /* Transmitter on, sending data */
+       TXS_NEWFRAME,   /* reset CRC and send (next) frame */
+       TXS_IDLE2,      /* Transmitter on, no data pending */
+       TXS_WAIT,       /* Waiting for Mintime to expire */
+       TXS_TIMEOUT     /* We had a transmission timeout */
+};
 
 typedef unsigned long io_port; /* type definition for an 'io port address' */
 
@@ -123,7 +132,6 @@ struct scc_stat {
        unsigned int bufsize;   /* used buffersize */
 };
 
-
 struct scc_modem {
        long speed;             /* Line speed, bps */
        char clocksrc;          /* 0 = DPLL, 1 = external, 2 = divider */
@@ -159,11 +167,14 @@ struct scc_mem_config {
        unsigned int bufsize;
 };
 
+struct scc_calibrate {
+       unsigned int time;
+       unsigned char pattern;
+};
 
 #ifdef __KERNEL__
 
-#define TX_ON          1       /* command for scc_key_trx() */
-#define TX_OFF         0       /* dto */
+enum {TX_OFF, TX_ON};  /* command for scc_key_trx() */
 
 /* Vector masks in RR2B */
 
@@ -173,7 +184,6 @@ struct scc_mem_config {
 #define RXINT          0x04
 #define SPINT          0x06
 
-
 #ifdef SCC_DELAY
 #define Inb(port)      inb_p(port)
 #define Outb(port, val)        outb_p(val, port)
@@ -210,7 +220,7 @@ struct scc_channel {
        int init;                       /* channel exists? */
 
        struct device *dev;             /* link to device control structure */
-       struct enet_statistics dev_stat;/* device statistics */
+       struct net_device_stats dev_stat;/* device statistics */
 
        char brand;                     /* manufacturer of the board */
        long clock;                     /* used clock */
index 7280e36fd5bdb0b50bce42983f27bdf10f5577d0..d0ced9fe4796204ad9cb0d6d3ebf3e389e542c72 100644 (file)
@@ -12,8 +12,6 @@
 #include <linux/proc_fs.h>
 
 struct rpc_stat {
-       struct rpc_stat *       next;
-       struct proc_dir_entry * entry;
        struct rpc_program *    program;
 
        unsigned int            netcnt,
@@ -28,8 +26,6 @@ struct rpc_stat {
 };
 
 struct svc_stat {
-       struct svc_stat *       next;
-       struct proc_dir_entry * entry;
        struct svc_program *    program;
 
        unsigned int            netcnt,
@@ -42,18 +38,18 @@ struct svc_stat {
                                rpcbadclnt;
 };
 
-void           rpcstat_init(void);
-void           rpcstat_exit(void);
-
-void           rpcstat_register(struct rpc_stat *);
-void           rpcstat_unregister(struct rpc_stat *);
-int            rpcstat_get_info(struct rpc_stat *, char *, char **,
-                                       off_t, int);
-void           rpcstat_zero_info(struct rpc_program *);
-void           svcstat_register(struct svc_stat *);
-void           svcstat_unregister(struct svc_stat *);
-int            svcstat_get_info(struct svc_stat *, char *, char **,
-                                       off_t, int);
-void           svcstat_zero_info(struct svc_program *);
+void                   rpc_proc_init(void);
+void                   rpc_proc_exit(void);
+
+struct proc_dir_entry *        rpc_proc_register(struct rpc_stat *);
+void                   rpc_proc_unregister(const char *);
+int                    rpc_proc_read(char *, char **, off_t, int,
+                                       int *, void *);
+void                   rpc_proc_zero(struct rpc_program *);
+struct proc_dir_entry *        svc_proc_register(struct svc_stat *);
+void                   svc_proc_unregister(const char *);
+int                    svc_proc_read(char *, char **, off_t, int,
+                                       int *, void *);
+void                   svc_proc_zero(struct svc_program *);
 
 #endif /* _LINUX_SUNRPC_STATS_H */
index c85aa6c248026c14c9fdb94531d63a389c305719..e71dcd067a81e28777609cad2a53a05855459f81 100644 (file)
@@ -31,17 +31,6 @@ typedef struct swap_control_v5
 typedef struct swap_control_v5 swap_control_t;
 extern swap_control_t swap_control;
 
-typedef struct kswapd_control_v1
-{
-       unsigned int    maxpages;
-       unsigned int    pages_buff;
-       unsigned int    pages_shm;
-       unsigned int    pages_mmap;
-       unsigned int    pages_swap;
-} kswapd_control_v1;
-typedef kswapd_control_v1 kswapd_control_t;
-extern kswapd_control_t kswapd_ctl;
-
 typedef struct swapstat_v1
 {
        unsigned int    wakeups;
index 06b3696432df0e9f33a41b3b886f7937383798aa..634e791d539b8d3f7f7bc9da364066aef6b9ae47 100644 (file)
@@ -79,7 +79,9 @@ extern void buff_setup(char *str, int *ints);
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
+#ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
+#endif
 extern void eth_setup(char *str, int *ints);
 extern void xd_setup(char *str, int *ints);
 #ifdef CONFIG_BLK_DEV_EZ
@@ -107,7 +109,9 @@ extern void ibmmca_scsi_setup(char *str, int *ints);
 extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void wd7000_setup(char *str, int *ints);
+#ifdef NOTDEF
 extern void ppa_setup(char *str, int *ints);
+#endif
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
 extern void reboot_setup(char *str, int *ints);
@@ -201,6 +205,12 @@ extern void sm_setup(char *str, int *ints);
 #ifdef CONFIG_WDT
 extern void wdt_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_PNP_PARPORT
+extern void parport_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PLIP
+extern void plip_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -380,7 +390,7 @@ struct {
 #ifdef CONFIG_SCSI_7000FASST
        { "wd7000=", wd7000_setup},
 #endif
-#ifdef CONFIG_SCSI_PPA
+#ifdef NOTDEF /* CONFIG_SCSI_PPA */
         { "ppa=", ppa_setup },
 #endif
 #ifdef CONFIG_SCSI_IBMMCA
@@ -481,6 +491,12 @@ struct {
 #endif
 #ifdef CONFIG_WDT
        { "wdt=", wdt_setup },
+#endif
+#ifdef CONFIG_PNP_PARPORT
+       { "parport=", parport_setup },
+#endif
+#ifdef CONFIG_PLIP
+       { "plip=", plip_setup },
 #endif
        { 0, 0 }
 };
index c21506d7de0bdecc628a99ec359c0667f736b567..85ea2b26f62a30a987fb670b50d36679b2fb1cbb 100644 (file)
@@ -577,9 +577,9 @@ static void exit_notify(void)
 
 NORET_TYPE void do_exit(long code)
 {
-       if (0 && intr_count) {
+       if (in_interrupt()) {
+               local_irq_count[smp_processor_id()] = 0;        /* Not really correct */
                printk("Aiee, killing interrupt handler\n");
-               intr_count = 0;
        }
 fake_volatile:
        acct_process(code);
index 2003952df5a1e8179eb3baf9ddc450bc2c0ddb37..c1c944b23a804cb736a0c4157c468da7e5168336 100644 (file)
@@ -134,9 +134,11 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
                tsk->cmin_flt = tsk->cmaj_flt = 0;
                tsk->nswap = tsk->cnswap = 0;
                if (new_page_tables(tsk))
-                       return -1;
+                       goto free_mm;
                if (dup_mmap(mm)) {
                        free_page_tables(mm);
+free_mm:
+                       kfree(mm);
                        return -1;
                }
                return 0;
index b2068878efcc1030bd2f8a4ea7df601069551a1d..0d5d619b0932f07a2767de6047f483f75c11105b 100644 (file)
@@ -176,6 +176,7 @@ asmlinkage int printk(const char *fmt, ...)
        va_list args;
        int i;
        char *msg, *p, *buf_end;
+       int line_feed;
        static signed char msg_level = -1;
        long flags;
 
@@ -202,6 +203,7 @@ asmlinkage int printk(const char *fmt, ...)
                                msg += 3;
                        msg_level = p[1] - '0';
                }
+               line_feed = 0;
                for (; p < buf_end; p++) {
                        log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p;
                        if (log_size < LOG_BUF_LEN)
@@ -211,18 +213,20 @@ asmlinkage int printk(const char *fmt, ...)
                                log_start &= LOG_BUF_LEN-1;
                        }
                        logged_chars++;
-                       if (*p == '\n')
+                       if (*p == '\n') {
+                               line_feed = 1;
                                break;
+                       }
                }
                if (msg_level < console_loglevel && console_drivers) {
                        struct console *c = console_drivers;
                        while(c) {
                                if (c->write)
-                                       c->write(msg, p - msg + 1);
+                                       c->write(msg, p - msg + line_feed);
                                c = c->next;
                        }
                }
-               if (*p == '\n')
+               if (line_feed)
                        msg_level = -1;
        }
        __restore_flags(flags);
index bd83b34d273260d7d20f830e8a36fdad90f30a53..2317f64b4a18458e448bbcd0eda1c7b61a17f7a0 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 #include <linux/ioport.h>
 
-#define IOTABLE_SIZE 64
+#define IOTABLE_SIZE 128
 
 typedef struct resource_entry_t {
        u_long from, num;
index 1ac6ff5a9ef1eac01077d9ba067222b141662b51..002aef5d98b5b38ad0f94677bd434063205520b4 100644 (file)
@@ -155,7 +155,7 @@ static inline void del_from_runqueue(struct task_struct * p)
                return;
        }
 #endif
-       if (p == &init_task) {
+       if (!p->pid) {
                static int nr = 0;
                if (nr < 5) {
                        nr++;
@@ -307,6 +307,10 @@ asmlinkage void schedule(void)
 
        need_resched = 0;
        this_cpu = smp_processor_id();
+       if (local_irq_count[this_cpu]) {
+               printk("Scheduling in interrupt\n");
+               *(char *)0 = 0;
+       }
        prev = current;
        release_kernel_lock(prev, this_cpu, lock_depth);
        if (bh_active & bh_mask)
@@ -317,8 +321,16 @@ asmlinkage void schedule(void)
 
        /* move an exhausted RR process to be last.. */
        if (!prev->counter && prev->policy == SCHED_RR) {
-               prev->counter = prev->priority;
-               move_last_runqueue(prev);
+               if (prev->pid) {
+                       prev->counter = prev->priority;
+                       move_last_runqueue(prev);
+               } else {
+                       static int count = 5;
+                       if (count) {
+                               count--;
+                               printk("Moving pid 0 last\n");
+                       }
+               }
        }
        timeout = 0;
        switch (prev->state) {
@@ -1240,6 +1252,27 @@ static void timer_bh(void)
        run_timer_list();
 }
 
+/*
+ * It is up to the platform where it does the profiling: in the
+ * global timer interrupt, or in a special interrupt handler.
+ *
+ * by default it's done in the global timer interrupt.
+ */
+
+static void default_do_profile (struct pt_regs * regs)
+{
+       if (prof_buffer && current->pid) {
+               extern int _stext;
+               unsigned long ip = instruction_pointer(regs);
+               ip -= (unsigned long) &_stext;
+               ip >>= prof_shift;
+               if (ip < prof_len)
+                       prof_buffer[ip]++;
+       }
+}
+
+void (*do_profile)(struct pt_regs *) = default_do_profile;
+
 void do_timer(struct pt_regs * regs)
 {
        (*(unsigned long *)&jiffies)++;
@@ -1247,14 +1280,8 @@ void do_timer(struct pt_regs * regs)
        mark_bh(TIMER_BH);
        if (!user_mode(regs)) {
                lost_ticks_system++;
-               if (prof_buffer && current->pid) {
-                       extern int _stext;
-                       unsigned long ip = instruction_pointer(regs);
-                       ip -= (unsigned long) &_stext;
-                       ip >>= prof_shift;
-                       if (ip < prof_len)
-                               prof_buffer[ip]++;
-               }
+               if (do_profile)
+                       do_profile(regs);
        }
        if (tq_timer)
                mark_bh(TQUEUE_BH);
index bb33bcf3afcd0219ea4bde59c1091e07344475b2..7bf33d64ef391b724b8d08144cbf6ab8fe9c333b 100644 (file)
@@ -698,10 +698,12 @@ asmlinkage long sys_times(struct tms * tbuf)
         */
        if (tbuf) 
        {
-               if(put_user(current->utime,&tbuf->tms_utime)||
-                  put_user(current->stime,&tbuf->tms_stime) ||
-                  put_user(current->cutime,&tbuf->tms_cutime) ||
-                  put_user(current->cstime,&tbuf->tms_cstime))
+               /* ?? use copy_to_user() */
+               if(!access_ok(VERIFY_READ, tbuf, sizeof(struct tms)) ||
+                  __put_user(current->utime,&tbuf->tms_utime)||
+                  __put_user(current->stime,&tbuf->tms_stime) ||
+                  __put_user(current->cutime,&tbuf->tms_cutime) ||
+                  __put_user(current->cstime,&tbuf->tms_cstime))
                        return -EFAULT;
        }
        return jiffies;
@@ -943,25 +945,19 @@ asmlinkage int sys_olduname(struct oldold_utsname * name)
        lock_kernel();
        if (!name)
                goto out;
-       error = copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
-       if (!error)
-               error = put_user(0,name->sysname+__OLD_UTS_LEN);
-       if (!error)
-               error = copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
-       if (!error)
-               error = put_user(0,name->nodename+__OLD_UTS_LEN);
-       if (!error)
-               error = copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
-       if (!error)
-               error = put_user(0,name->release+__OLD_UTS_LEN);
-       if (!error)
-               error = copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
-       if (!error)
-               error = put_user(0,name->version+__OLD_UTS_LEN);
-       if (!error)
-               error = copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
-       if (!error)
-               error = put_user(0,name->machine+__OLD_UTS_LEN);
+       if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+               goto out;
+  
+       error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
+       error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
+       error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
+       error -= __put_user(0,name->release+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
+       error -= __put_user(0,name->version+__OLD_UTS_LEN);
+       error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
+       error = __put_user(0,name->machine+__OLD_UTS_LEN);
        error = error ? -EFAULT : 0;
 out:
        unlock_kernel();
index ff713a64579828561d26dfe3d1b147fa2739f972..9e0bb0fd8aae7bb34de020c41de8141fd270b90a 100644 (file)
@@ -183,10 +183,6 @@ static ctl_table kern_table[] = {
 static ctl_table vm_table[] = {
        {VM_SWAPCTL, "swapctl", 
         &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
-       {VM_KSWAPD, "kswapd", 
-        &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
-       {VM_SWAPOUT, "kswapd-interval",
-        &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec},
        {VM_FREEPG, "freepages", 
         &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
        {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
index 19505533ca47ddaa0ec9c6d47049d25bb8aa25d1..e446437aaa08813892555298dcc791d8ab5af3a6 100644 (file)
@@ -5,6 +5,8 @@
  *
  *  Swap reorganised 29.12.95, Stephen Tweedie.
  *  kswapd added: 7.1.96  sct
+ *  Removed kswapd_ctl limits, and swap out as many pages as needed
+ *  to bring the system back to free_pages_high: 2.4.97, Rik van Riel.
  *  Version: $Id: vmscan.c,v 1.21 1997/01/06 06:54:03 davem Exp $
  */
 
@@ -49,12 +51,6 @@ static struct wait_queue * kswapd_wait = NULL;
  */
 static int kswapd_awake = 0;
 
-/*
- * sysctl-modifiable parameters to control the aggressiveness of the
- * page-searching within the kswapd page recovery daemon.
- */
-kswapd_control_t kswapd_ctl = {4, -1, -1, -1, -1};
-
 static void init_swap_timer(void);
 
 /*
@@ -409,8 +405,16 @@ int kswapd(void *unused)
                interruptible_sleep_on(&kswapd_wait);
                kswapd_awake = 1;
                swapstats.wakeups++;
-               /* Do the background pageout: */
-               for (i=0; i < kswapd_ctl.maxpages; i++)
+               /* Do the background pageout: 
+                * We now only swap out as many pages as needed.
+                * When we are truly low on memory, we swap out
+                * synchronously (WAIT == 1).  -- Rik.
+                */
+               while(nr_free_pages < min_free_pages)
+                       try_to_free_page(GFP_KERNEL, 0, 1);
+               while((nr_free_pages + nr_async_pages) < free_pages_low)
+                       try_to_free_page(GFP_KERNEL, 0, 1);
+               while((nr_free_pages + nr_async_pages) < free_pages_high)
                        try_to_free_page(GFP_KERNEL, 0, 0);
        }
 }
@@ -436,12 +440,16 @@ void swap_tick(void)
                want_wakeup = 1;
        }
 
-       if (want_wakeup) {
-               if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
+       if (want_wakeup) { 
+               if (!kswapd_awake) {
                        wake_up(&kswapd_wait);
                        need_resched = 1;
                }
-               next_swap_jiffies = jiffies + swapout_interval;
+               /* low on memory, we need to start swapping soon */
+               if(last_wakeup_low) 
+                       next_swap_jiffies = jiffies;
+               else  
+                       next_swap_jiffies = jiffies + swapout_interval;
        }
        timer_active |= (1<<SWAP_TIMER);
 }
diff --git a/mm/vmscan.c.lock~ b/mm/vmscan.c.lock~
deleted file mode 100644 (file)
index 860783b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-torvalds@penguin.transmeta.com
\ No newline at end of file
index 7aef3f6b6050e1bd150e687c0b4c4c4887442339..61ba367b573bf8bd15916f04ca2bca21e6f482b3 100644 (file)
@@ -85,6 +85,9 @@
 #ifdef CONFIG_NET_RADIO
 #include <linux/wireless.h>
 #endif /* CONFIG_NET_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
 
 /*
  *     The list of devices, that are able to output.
@@ -1627,6 +1630,9 @@ int net_dev_init(void)
 #endif
 #if defined(CONFIG_LAPBETHER)
        lapbeth_init();
+#endif
+#if defined(CONFIG_PLIP)
+       plip_init();
 #endif
        /*
         *      SLHC if present needs attaching so other people see it
index 18553a03d49b08cd36a9f5a05e628ffebc462561..4dff8134f5491953d571c5bb03c1f161cb14b4e6 100644 (file)
@@ -172,6 +172,11 @@ int afinet_get_info(char *buffer, char **start, off_t offset, int length, int du
                       raw_prot.inuse, raw_prot.highestinuse);
        len += sprintf(buffer+len,"PAC: inuse %d highest %d\n",
                       packet_prot.inuse, packet_prot.highestinuse);
+       if (offset >= len)
+       {
+               *start = buffer;
+               return 0;
+       }
        *start = buffer + offset;
        len -= offset;
        if (len > length)
index f6adb091bf3119723cbd330351d64a41dbb36cb2..0a83dd3f7de82ff126cbcd3056a175defac2e761 100644 (file)
@@ -654,7 +654,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
        int code = skb->h.icmph->code;
        struct sock *sk;
 
-       sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+       sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source);
 
        if (sk == NULL)
                return;
index 4bd4ea878ff2a619c72869418b8e64e410494a60..82ee8ecb19698853e6e93e0c7e50466dbd48eb10 100644 (file)
@@ -436,7 +436,7 @@ void udp_err(struct sk_buff *skb, unsigned char *dp)
        int code = skb->h.icmph->code;
        struct sock *sk;
 
-       sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+       sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source);
        if (sk == NULL)
                return; /* No socket for error */
 
index d632cbcab6d5ba6292b3fd0d057d000988172c24..ec74271a30191b0957d57cb170ac7bd677a39ca6 100644 (file)
@@ -516,7 +516,7 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
        int err;
        int opening;
 
-       sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
+       sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
 
        if (sk == NULL)
                return;
index eb86be321e2f26bf0d447711ea5985831deeab11..b7173133e107db1dd8e019998083f206a8a6cfcc 100644 (file)
@@ -379,7 +379,7 @@ void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
        
        uh = (struct udphdr *) buff;
 
-       sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
+       sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
    
        if (sk == NULL) {
                printk(KERN_DEBUG "icmp for unkown sock\n");
index f2208fd6b63700504d5ef246435ff05ceb1f6aa9..bcdb13610cf607c0393e1e012d9c2bb2e8495058 100644 (file)
@@ -746,7 +746,7 @@ init_module(void)
 #ifdef RPC_DEBUG
        rpc_register_sysctl();
 #endif
-       rpcstat_init();
+       rpc_proc_init();
        return 0;
 }
 
@@ -756,6 +756,6 @@ cleanup_module(void)
 #ifdef RPC_DEBUG
        rpc_unregister_sysctl();
 #endif
-       rpcstat_exit();
+       rpc_proc_exit();
 }
 #endif
index 3b23d940558b3c5345fd71e2d281093a55614706..90a23a232846552efa240c21c27d8f213f1bfdd6 100644 (file)
@@ -1,14 +1,15 @@
 /*
  * linux/net/sunrpc/stats.c
  *
- * procfs-based user access to RPC statistics
+ * procfs-based user access to generic RPC statistics. The stats files
+ * reside in /proc/net/rpc.
  *
- * Everything is complicated by the fact that procfs doesn't pass the
- * proc_dir_info struct in the call to get_info. We need our own 
- * inode_ops for /proc/net/rpc (we want to have a write op for zeroing
- * the current stats, anyway).
+ * The read routines assume that the buffer passed in is just big enough.
+ * If you implement an RPC service that has its own stats routine which
+ * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
+ * limit.
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/kernel.h>
 
 #define RPCDBG_FACILITY        RPCDBG_MISC
 
-/*
- * Generic stats object (same for clnt and svc stats).
- * Must agree with first two fields of either.
- */
-struct stats {
-       struct stats *          next;
-       struct proc_dir_entry * entry;
-};
-
-/* Code disabled until updated to new dynamic /proc code */
-#if 0
-
-static struct stats *          rpc_stats = NULL;
-static struct stats *          svc_stats = NULL;
-
-static struct proc_dir_entry   proc_rpc = {
-       0, 3, "rpc",
-       S_IFDIR | S_IRUGO | S_IXUGO, 1, 0, 0,
-       0, NULL,
-       NULL, NULL,
-       NULL,
-       NULL, NULL,
-};
-
-/*
- * Register/unregister a stats file
- */
-static void
-stats_register(struct stats **q, struct stats *p)
-{
-       dprintk("RPC: registering /proc/net/rpc/%s\n",
-                               p->entry->name);
-       /* return; */
-       if (p->entry->low_ino)
-               return;
-       p->next = *q;
-       *q = p;
-       proc_register_dynamic(&proc_rpc, p->entry);
-}
-
-static void
-stats_unregister(struct stats **q, struct stats *p)
-{
-       dprintk("RPC: unregistering /proc/net/rpc/%s\n",
-                               p->entry->name);
-       /* return; */
-       if (!p->entry->low_ino)
-               return;
-       while (*q) {
-               if (*q == p) {
-                       *q = p->next;
-                       proc_unregister(&proc_rpc, p->entry->low_ino);
-                       return;
-               }
-               q = &((*q)->next);
-       }
-}
+static struct proc_dir_entry   *proc_net_rpc = 0;
 
 /*
- * Client stats handling
+ * Get RPC client stats
  */
-void
-rpcstat_register(struct rpc_stat *statp)
-{
-       stats_register(&rpc_stats, (struct stats *) statp);
-}
-
-void
-rpcstat_unregister(struct rpc_stat *statp)
-{
-       stats_unregister(&rpc_stats, (struct stats *) statp);
-}
-
 int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
-                       char **start, off_t offset, int length)
+rpc_proc_read(char *buffer, char **start, off_t offset, int count,
+                               int *eof, void *data)
 {
+       struct rpc_stat *statp = (struct rpc_stat *) data;
        struct rpc_program *prog = statp->program;
        struct rpc_version *vers;
        int             len, i, j;
@@ -125,34 +59,24 @@ rpcstat_get_info(struct rpc_stat *statp, char *buffer,
 
        if (offset >= len) {
                *start = buffer;
+               *eof = 1;
                return 0;
        }
        *start = buffer + offset;
-       len -= offset;
-       if (len > length)
-               len = length;
+       if ((len -= offset) > count)
+               return count;
+       *eof = 1;
        return len;
 }
 
 /*
- * Server stats handling
+ * Get RPC server stats
  */
-void
-svcstat_register(struct svc_stat *statp)
-{
-       stats_register(&svc_stats, (struct stats *) statp);
-}
-
-void
-svcstat_unregister(struct svc_stat *statp)
-{
-       stats_unregister(&svc_stats, (struct stats *) statp);
-}
-
 int
-svcstat_get_info(struct svc_stat *statp, char *buffer,
-                       char **start, off_t offset, int length)
+svc_proc_read(char *buffer, char **start, off_t offset, int count,
+                               int *eof, void *data)
 {
+       struct svc_stat *statp  = (struct svc_stat *) data;
        struct svc_program *prog = statp->program;
        struct svc_procedure *proc;
        struct svc_version *vers;
@@ -183,85 +107,69 @@ svcstat_get_info(struct svc_stat *statp, char *buffer,
 
        if (offset >= len) {
                *start = buffer;
+               *eof = 1;
                return 0;
        }
        *start = buffer + offset;
-       if ((len -= offset) > length)
-               len = length;
+       if ((len -= offset) > count)
+               return count;
+       *eof = 1;
        return len;
 }
 
 /*
- * Register /proc/net/rpc
+ * Register/unregister RPC proc files
  */
-void
-rpcstat_init(void)
+static inline struct proc_dir_entry *
+do_register(const char *name, void *data, int issvc)
 {
-       dprintk("RPC: registering /proc/net/rpc\n");
-       proc_rpc.ops = proc_net.ops;    /* cheat */
-       proc_register_dynamic(&proc_net, &proc_rpc);
-}
-
-/*
- * Unregister /proc/net/rpc
- */
-void
-rpcstat_exit(void)
-{
-       while (rpc_stats)
-               stats_unregister(&rpc_stats, rpc_stats);
-       while (svc_stats)
-               stats_unregister(&svc_stats, svc_stats);
-       dprintk("RPC: unregistering /proc/net/rpc\n");
-       proc_unregister(&proc_net, proc_rpc.low_ino);
-}
-
-#else
+       struct proc_dir_entry   *ent;
 
-/* Various dummy functions */
+       dprintk("RPC: registering /proc/net/rpc/%s\n", name);
+       ent = create_proc_entry(name, 0, proc_net_rpc);
+       ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
+       ent->data = data;
 
-int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
-                       char **start, off_t offset, int length)
-{
-       return 0;
+       return ent;
 }
 
-int
-svcstat_get_info(struct svc_stat *statp, char *buffer,
-                       char **start, off_t offset, int length)
+struct proc_dir_entry *
+rpc_proc_register(struct rpc_stat *statp)
 {
-       return 0;
+       return do_register(statp->program->name, statp, 0);
 }
 
 void
-rpcstat_register(struct rpc_stat *statp)
+rpc_proc_unregister(const char *name)
 {
+       remove_proc_entry(name, proc_net_rpc);
 }
 
-void
-rpcstat_unregister(struct rpc_stat *statp)
+struct proc_dir_entry *
+svc_proc_register(struct svc_stat *statp)
 {
+       return do_register(statp->program->pg_name, statp, 1);
 }
 
 void
-svcstat_register(struct svc_stat *statp)
+svc_proc_unregister(const char *name)
 {
+       remove_proc_entry(name, proc_net_rpc);
 }
 
 void
-svcstat_unregister(struct svc_stat *statp)
-{
-}
-
-void
-rpcstat_init(void)
+rpc_proc_init(void)
 {
+       dprintk("RPC: registering /proc/net/rpc\n");
+       if (!proc_net_rpc)
+               proc_net_rpc = create_proc_entry("net/rpc", S_IFDIR, 0);
 }
 
 void
-rpcstat_exit(void)
+rpc_proc_exit(void)
 {
+       dprintk("RPC: unregistering /proc/net/rpc\n");
+       if (proc_net_rpc)
+               remove_proc_entry("net/rpc", 0);
+       proc_net_rpc = 0;
 }
-
-#endif
index c0651ac437e7092aac140d6e3bd09aee8c368d4e..3e1d57873dc6f3534e9894ed86a7bcdfd37c99fd 100644 (file)
@@ -10,8 +10,6 @@
 #include <linux/config.h>
 #include <linux/module.h>
 
-#ifdef CONFIG_MODULES
-
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
@@ -75,12 +73,12 @@ EXPORT_SYMBOL(svc_wake_up);
 
 /* RPC statistics */
 #ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(rpcstat_register);
-EXPORT_SYMBOL(rpcstat_unregister);
-EXPORT_SYMBOL(rpcstat_get_info);
-EXPORT_SYMBOL(svcstat_register);
-EXPORT_SYMBOL(svcstat_unregister);
-EXPORT_SYMBOL(svcstat_get_info);
+EXPORT_SYMBOL(rpc_proc_register);
+EXPORT_SYMBOL(rpc_proc_unregister);
+EXPORT_SYMBOL(rpc_proc_read);
+EXPORT_SYMBOL(svc_proc_register);
+EXPORT_SYMBOL(svc_proc_unregister);
+EXPORT_SYMBOL(svc_proc_read);
 #endif
 
 /* Generic XDR */
@@ -101,5 +99,3 @@ EXPORT_SYMBOL(rpc_debug);
 EXPORT_SYMBOL(nfs_debug);
 EXPORT_SYMBOL(nfsd_debug);
 EXPORT_SYMBOL(nlm_debug);
-
-#endif /* CONFIG_MODULES */
index dfd5b8d81622a65a72f1cb9a957c17e3509ba884..2bb477114db4717a7d3c47a926d25b3fa5b8201e 100644 (file)
@@ -46,6 +46,9 @@
 #
 # 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
 # expressions for GNU expr since version 1.15 and up use \? and \+.
+#
+# 300397 Phil Blundell (pjb27@cam.ac.uk) - added support for "limint", 
+# allow dep_tristate to take a list of dependencies rather than just one.
 
 #
 # Make sure we're really running bash.
@@ -224,22 +227,35 @@ function tristate () {
 
 #
 # dep_tristate processes a tristate argument that depends upon
-# another option.  If the option we depend upon is a module,
-# then the only allowable options are M or N.  If Y, then
+# another option or options.  If any of the options we depend upon is a
+# module, then the only allowable options are M or N.  If all are Y, then
 # this is a normal tristate.  This is used in cases where modules
 # are nested, and one module requires the presence of something
 # else in the kernel.
 #
-#      tristate question define default
+#      tristate question define default ...
 #
 function dep_tristate () {
        old=$(eval echo "\${$2}")
        def=${old:-'n'}
-       if [ "$3" = "n" ]; then
-               define_bool "$2" "n"
-       elif [ "$3" = "y" ]; then
-               tristate "$1" "$2"
-       else
+       ques=$1
+       var=$2
+       need_module=0
+       shift 2
+       while [ $# -gt 0 ]; do
+         case "$1" in
+           n)
+             define_bool "$var" "n"
+             return
+             ;;
+           m)
+             need_module=1
+             ;;
+         esac
+         shift
+       done
+
+       if [ $need_module = 1 ]; then
           if [ "$CONFIG_MODULES" = "y" ]; then
                case "$def" in
                 "y" | "m") defprompt="M/n/?"
@@ -249,11 +265,11 @@ function dep_tristate () {
                      ;;
                esac
                while :; do
-                 readln "$1 ($2) [$defprompt] " "$def" "$old"
+                 readln "$ques ($var) [$defprompt] " "$def" "$old"
                  case "$ans" in
-                     [nN] | [nN]o )  define_bool "$2" "n"
+                     [nN] | [nN]o )  define_bool "$var" "n"
                                      break ;;
-                     [mM] )          define_bool "$2" "m"
+                     [mM] )          define_bool "$var" "m"
                                      break ;;
                      [yY] | [yY]es ) echo 
    echo "  This answer is not allowed, because it is not consistent with"
@@ -263,11 +279,13 @@ function dep_tristate () {
    echo "  as a module as well (with M) or leave it out altogether (N)."
                                      echo
                                      ;;
-                     * )             help "$2"
+                     * )             help "$var"
                                      ;;
                  esac
                done
           fi
+       else
+          tristate "$ques" "$var"
        fi
 }
 
@@ -283,23 +301,34 @@ function define_int () {
 }
 
 #
-# int processes an integer argument
+# int processes an integer argument with optional limits
 #
-#      int question define default
+#      int question define default [min max]
 #
 function int () {
        old=$(eval echo "\${$2}")
        def=${old:-$3}
+       if [ $# -gt 3 ]; then
+         min=$4
+       else
+         min=1
+       fi
+       if [ $# -gt 4 ]; then
+         max=$5
+       else
+         max=10000000     # !!
+       fi
        while :; do
          readln "$1 ($2) [$def] " "$def" "$old"
-         if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
-           define_int "$2" "$ans"
+         if expr \( \( $ans + 0 \) \>= $min \) \& \( $ans \<= $max \) >/dev/null 2>&1 ; then
+            define_int "$2" "$ans"
            break
-         else
+          else
            help "$2"
-         fi
+          fi
        done
 }
+
 #
 # define_hex sets the value of a hexadecimal argument
 #
index 42ea49aafae797f02c3ea897f4214158d4f80551..c452e19215775c5537e0f6c1ee2c400655466758 100644 (file)
@@ -21,6 +21,9 @@
 #
 # Please send comments / questions / bug fixes to roadcapw@cfw.com
 #
+# 070497 Bernhard Kaindl (bkaindl@netway.at) - get default values for
+# new bool, tristate and dep_tristate parameters from the defconfig file.
+# new configuration parameters are marked with '(NEW)' as in make config.
 #----------------------------------------------------------------------------
 
 
@@ -40,9 +43,21 @@ single_menu_mode=
 #
 set -h +o posix
 
+#
+# Converts "# xxx is not..." to xxx=n
+#
+parse_config () {
+       sed -e 's/# \(.*\) is not.*/\1=n/'
+}
 
-
-
+#
+# Parses the defconfig file to set the default for a new parameter.
+#
+function get_def () {
+       parse_config < arch/$ARCH/defconfig | grep "^$1=" > /tmp/conf.$$
+       . /tmp/conf.$$
+       rm /tmp/conf.$$
+}
 
 #
 # Load the functions used by the config.in files.
@@ -55,6 +70,19 @@ set -h +o posix
 #
 load_functions () {
 
+#
+# Macro for setting the x and info varibles. get's default from defconfig
+# file if it's a new parameter.
+#
+function set_x () {
+       eval x=\$$1
+       if [ -z "$x" ]; then
+               get_def "$1"
+               eval x=\${$1:-'n'} INFO_$1="' (NEW)'"
+       fi
+       eval info="\$INFO_$1"
+}
+
 #
 # Additional comments
 #
@@ -75,14 +103,14 @@ function define_bool () {
 # which calls our local bool function.
 #
 function bool () {
-       eval $2=\${$2:-'n'}  x=\$$2
+       set_x "$2"
 
        case $x in
        y|m)    flag="*" ;;
        n)      flag=" " ;;
        esac
 
-       echo -ne "'$2' '[$flag] $1' " >>MCmenu
+       echo -ne "'$2' '[$flag] $1$info' " >>MCmenu
 
        echo -e "function $2 () { l_bool '$2' \"\$1\" ;}\n" >>MCradiolists
 }
@@ -98,7 +126,7 @@ function tristate () {
        then
                bool "$1" "$2"
        else
-               eval $2=\${$2:-'n'}  x=\$$2
+               set_x "$2"
        
                case $x in
                y) flag="*" ;;
@@ -106,7 +134,7 @@ function tristate () {
                *) flag=" " ;;
                esac
        
-               echo -ne "'$2' '<$flag> $1' " >>MCmenu
+               echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
        
                echo -e "
                function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists
@@ -323,14 +351,14 @@ function l_bool () {
 # Same as bool() except options are (Module/No)
 #
 function mod_bool () {
-       eval $2=\${$2:-'n'}  x=\$$2
+       set_x "$2"
 
        case $x in
        y|m) flag='M' ;;
        *)   flag=' ' ;;
        esac
 
-       echo -ne "'$2' '<$flag> $1' " >>MCmenu
+       echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
 
        echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
 }
@@ -881,6 +909,18 @@ save_configuration () {
        ${DIALOG} --backtitle "$backtitle" \
                  --infobox "Saving your kernel configuration..."  3 40
 
+       #
+       # Macro for setting the newval varible. get's default from defconfig
+       # file if it's a new parameter and it has not been shown yet.
+       #
+       function set_newval () {
+               eval newval=\$$1
+               if [ -z "$newval" ]; then
+                       get_def "$1"
+                       eval newval=\${$1:-'n'}
+               fi
+       }
+
        #
        # Now, let's redefine the configuration functions for final
        # output to the config files.
@@ -888,25 +928,27 @@ save_configuration () {
        # Nested function definitions, YIPEE!
        #
        function bool () {
-               eval define_bool "$2" "\${$2:-n}"
+               set_newval "$2"
+               eval define_bool "$2" "$newval"
        }
 
        function tristate () {
-               eval define_bool "$2" "\${$2:-n}"
+               set_newval "$2"
+               eval define_bool "$2" "$newval"
        }
 
        function dep_tristate () {
-               eval x=\${$2:-n}
+               set_newval "$2"
 
                if eval [ "_$3" = "_m" ]
                then
-                       if [ "$x" = "y" ]
+                       if [ "$newval" = "y" ]
                        then
-                               x="m"
+                               newval="m"
                        fi
                fi
 
-               define_bool "$2" "$x"
+               define_bool "$2" "$newval"
        }
 
        function int () {