diff -u --recursive --new-file v2.1.32/linux/CREDITS linux/CREDITS --- v2.1.32/linux/CREDITS Mon Apr 7 11:35:28 1997 +++ linux/CREDITS Wed Apr 9 21:30:30 1997 @@ -133,13 +133,12 @@ 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 @@ 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: 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 @@ 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 @@ 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 @@ -1520,6 +1523,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 diff -u --recursive --new-file v2.1.32/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.32/linux/Documentation/Configure.help Mon Apr 7 11:35:28 1997 +++ linux/Documentation/Configure.help Wed Apr 9 21:30:30 1997 @@ -835,6 +835,23 @@ 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 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/Documentation/devices.tex linux/Documentation/devices.tex --- v2.1.32/linux/Documentation/devices.tex Mon Mar 17 14:54:20 1997 +++ linux/Documentation/devices.tex Tue Apr 8 12:17:22 1997 @@ -46,7 +46,7 @@ % \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 @@ \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 @@ \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 @@ \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 @@ \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 @@ \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 @@ 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} @@ -1466,6 +1548,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 diff -u --recursive --new-file v2.1.32/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.1.32/linux/Documentation/devices.txt Mon Mar 17 14:54:20 1997 +++ linux/Documentation/devices.txt Tue Apr 8 12:17:22 1997 @@ -1,8 +1,7 @@ LINUX ALLOCATED DEVICES - Maintained by H. Peter Anvin - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 255 RESERVED - - - **** ADDITIONAL /dev DIRECTORY ENTRIES This section details additional entries that should or may exist in @@ -1053,6 +1109,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 diff -u --recursive --new-file v2.1.32/linux/Documentation/digiboard.txt linux/Documentation/digiboard.txt --- v2.1.32/linux/Documentation/digiboard.txt Mon Sep 16 03:05:28 1996 +++ linux/Documentation/digiboard.txt Wed Apr 9 21:27:16 1997 @@ -3,37 +3,184 @@ 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. + +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. + +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. -Limitations: ------------- -Currently the Driver does not do autoprobing! + Bernhard Kaindl (bkaindl@netway.at) 6. April 1997. -The preconfigured I/O address is 0200h and the default memory address -0D0000h, ALT-PIN feature on and 16 ports available. +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= \ + membase= \ + memsize= \ + numports= \ + altpin= \ + verbose= + +or, if several cards are installed + +modprobe pcxx io=,,... \ + membase=,,... \ + memsize=,,... \ + numports=,,... \ + altpin=,,... \ + verbose= + +where is the io address of the Nth card and 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. -Use them and you will not have to worry about configuring anything. -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. +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 @@ 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 @@ ;; 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 5, April 1996. - diff -u --recursive --new-file v2.1.32/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt --- v2.1.32/linux/Documentation/ioctl-number.txt Mon Apr 7 11:35:28 1997 +++ linux/Documentation/ioctl-number.txt Mon Apr 7 14:17:09 1997 @@ -1,5 +1,5 @@ Ioctl Numbers -12 Feb 1997 +5 Apr 1997 Michael Chastain @@ -89,6 +89,8 @@ '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: + 'm' all linux/mtio.h conflict! 'm' all linux/soundcard.h conflict! 'n' all linux/ncp_fs.h @@ -105,8 +107,10 @@ 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: + 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: + +0xA0 all Small Device Project in development: + diff -u --recursive --new-file v2.1.32/linux/Documentation/parport.txt linux/Documentation/parport.txt --- v2.1.32/linux/Documentation/parport.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/parport.txt Wed Apr 9 21:30:30 1997 @@ -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. diff -u --recursive --new-file v2.1.32/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.32/linux/MAINTAINERS Mon Apr 7 11:35:28 1997 +++ linux/MAINTAINERS Wed Apr 9 21:30:30 1997 @@ -407,6 +407,19 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/Makefile linux/Makefile --- v2.1.32/linux/Makefile Mon Apr 7 11:35:28 1997 +++ linux/Makefile Wed Apr 9 23:28:33 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 32 +SUBLEVEL = 33 ARCH = i386 @@ -14,7 +14,7 @@ SMP = 1 # # SMP profiling options -# SMP_PROF = 1 +SMP_PROF = 1 .EXPORT_ALL_VARIABLES: @@ -142,6 +142,10 @@ ifdef CONFIG_PCI DRIVERS := $(DRIVERS) drivers/pci/pci.a +endif + +ifdef CONFIG_PNP +DRIVERS := $(DRIVERS) drivers/pnp/pnp.a endif ifdef CONFIG_SBUS diff -u --recursive --new-file v2.1.32/linux/Rules.make linux/Rules.make --- v2.1.32/linux/Rules.make Fri Apr 4 08:52:16 1997 +++ linux/Rules.make Wed Apr 9 21:30:30 1997 @@ -22,11 +22,14 @@ 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 @@ 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 @@ # 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))" "" diff -u --recursive --new-file v2.1.32/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v2.1.32/linux/arch/alpha/config.in Mon Apr 7 11:35:28 1997 +++ linux/arch/alpha/config.in Wed Apr 9 21:30:30 1997 @@ -95,7 +95,13 @@ 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 diff -u --recursive --new-file v2.1.32/linux/arch/i386/boot/compressed/Makefile.debug linux/arch/i386/boot/compressed/Makefile.debug --- v2.1.32/linux/arch/i386/boot/compressed/Makefile.debug Wed Dec 13 22:16:52 1995 +++ linux/arch/i386/boot/compressed/Makefile.debug Wed Dec 31 16:00:00 1969 @@ -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 - - diff -u --recursive --new-file v2.1.32/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c --- v2.1.32/linux/arch/i386/boot/compressed/misc.c Fri Apr 4 08:52:16 1997 +++ linux/arch/i386/boot/compressed/misc.c Wed Apr 9 21:33:22 1997 @@ -9,8 +9,6 @@ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 */ -#include - #include #include @@ -97,7 +95,6 @@ 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 @@ 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 @@ for (i=0;i #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -26,6 +27,7 @@ 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); diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v2.1.32/linux/arch/i386/kernel/irq.c Mon Apr 7 11:35:29 1997 +++ linux/arch/i386/kernel/irq.c Mon Apr 7 14:02:43 1997 @@ -35,6 +35,10 @@ #include #include +#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_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 @@ 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> 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"); diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.1.32/linux/arch/i386/kernel/process.c Fri Apr 4 08:52:17 1997 +++ linux/arch/i386/kernel/process.c Mon Apr 7 14:00:35 1997 @@ -107,6 +107,7 @@ 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 @@ 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 @@ run_task_queue(&tq_scheduler); unlock_kernel(); } + /* endless idle loop with no priority at all */ + current->counter = -100; schedule(); } } @@ -474,7 +478,7 @@ 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); diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.1.32/linux/arch/i386/kernel/smp.c Mon Apr 7 11:35:29 1997 +++ linux/arch/i386/kernel/smp.c Mon Apr 7 14:02:43 1997 @@ -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 @@ -45,6 +46,7 @@ #include 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_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 @@ 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 @@ 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 @@ 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 (tt2esp; 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 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.32/linux/arch/i386/mm/fault.c Sun Jan 26 02:07:05 1997 +++ linux/arch/i386/mm/fault.c Wed Apr 9 21:41:52 1997 @@ -187,11 +187,10 @@ * * 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) { diff -u --recursive --new-file v2.1.32/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.1.32/linux/arch/i386/mm/init.c Sun Jan 26 02:07:05 1997 +++ linux/arch/i386/mm/init.c Wed Apr 9 21:42:43 1997 @@ -146,19 +146,19 @@ 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 @@ 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 @@ /* 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; } diff -u --recursive --new-file v2.1.32/linux/drivers/Makefile linux/drivers/Makefile --- v2.1.32/linux/drivers/Makefile Thu Dec 19 01:03:33 1996 +++ linux/drivers/Makefile Wed Apr 9 21:30:30 1997 @@ -9,10 +9,15 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.1.32/linux/drivers/block/genhd.c Mon Apr 7 11:35:29 1997 +++ linux/drivers/block/genhd.c Wed Apr 9 21:30:30 1997 @@ -765,9 +765,15 @@ 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(); diff -u --recursive --new-file v2.1.32/linux/drivers/block/xd.h linux/drivers/block/xd.h --- v2.1.32/linux/drivers/block/xd.h Mon Sep 30 06:52:47 1996 +++ linux/drivers/block/xd.h Mon Apr 7 13:51:23 1997 @@ -112,7 +112,7 @@ 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); diff -u --recursive --new-file v2.1.32/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.1.32/linux/drivers/char/Config.in Fri Apr 4 08:52:18 1997 +++ linux/drivers/char/Config.in Wed Apr 9 21:30:30 1997 @@ -19,7 +19,7 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.1.32/linux/drivers/char/console.c Fri Apr 4 08:52:19 1997 +++ linux/drivers/char/console.c Mon Apr 7 14:17:10 1997 @@ -884,10 +884,10 @@ * 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 @@ 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--; diff -u --recursive --new-file v2.1.32/linux/drivers/char/consolemap.c linux/drivers/char/consolemap.c --- v2.1.32/linux/drivers/char/consolemap.c Mon Oct 28 04:29:20 1996 +++ linux/drivers/char/consolemap.c Mon Apr 7 14:17:10 1997 @@ -235,7 +235,7 @@ for (i=0; iunicode); - 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 @@ { 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); } diff -u --recursive --new-file v2.1.32/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.1.32/linux/drivers/char/lp.c Mon Apr 7 11:35:29 1997 +++ linux/drivers/char/lp.c Wed Apr 9 21:30:30 1997 @@ -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 @@ -17,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +26,8 @@ #include #include #include +#include +#include /* 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 @@ -33,13 +35,20 @@ * 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 @@ (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 @@ 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 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ #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 @@ #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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ lp_release }; -static int lp_probe(int offset) -{ - int base, size; - unsigned int testvalue; +static int parport[LP_NO] = { -1, }; - 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; -} +#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; - } -#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++; + struct parport *pb; + + if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) { + printk("lp: unable to get major %d\n", LP_MAJOR); + return -EIO; + } + + 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"); + if (++count == LP_NO) + break; + pb = pb->next; + } - return 0; + /* 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 @@ { 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 diff -u --recursive --new-file v2.1.32/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c --- v2.1.32/linux/drivers/char/pcxx.c Sun Dec 22 05:07:11 1996 +++ linux/drivers/char/pcxx.c Wed Apr 9 21:27:16 1997 @@ -33,10 +33,14 @@ * 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 @@ -67,13 +71,7 @@ #include #ifndef MODULE - -/* is* routines not available in modules -** the need for this should go away when probing is done. :-) -** brian@ilinx.com -*/ - -#include +#include /* We only need it for parsing the "digi="-line */ #endif #include @@ -81,8 +79,7 @@ #include #include -#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 @@ #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 @@ /* 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 @@ 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 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 @@ } } -/* 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 @@ 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 @@ return; } - while (str && *str) { /* find the next comma or terminator */ @@ -985,11 +1006,6 @@ 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 @@ 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 @@ printk("PC/Xx: Invalid memory base %s\n", str); return; } -#endif board.membase = simple_strtoul(str, NULL, 16); last = i; @@ -1075,33 +1078,97 @@ /* 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;iport); 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 @@ 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 @@ bd->status = DISABLED; continue; } + if (verbose) + printk(" done.\n"); for(i=0; i < 16; i++) { memaddr[MISCGLOBAL+i] = 0; @@ -1273,19 +1362,36 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ if(n <= ncmds * (sizeof(short)*4)) break; + /* Seems not to be good here: schedule(); */ } } diff -u --recursive --new-file v2.1.32/linux/drivers/char/pcxx.h linux/drivers/char/pcxx.h --- v2.1.32/linux/drivers/char/pcxx.h Sat Apr 6 22:42:34 1996 +++ linux/drivers/char/pcxx.h Wed Apr 9 21:27:16 1997 @@ -49,6 +49,7 @@ ushort numports; ushort port; ulong membase; + ulong memsize; ushort first_minor; }; diff -u --recursive --new-file v2.1.32/linux/drivers/char/selection.c linux/drivers/char/selection.c --- v2.1.32/linux/drivers/char/selection.c Fri Apr 4 08:52:19 1997 +++ linux/drivers/char/selection.c Wed Apr 9 22:01:41 1997 @@ -118,8 +118,9 @@ 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 @@ if (!bp || !c) return 0; - do_unblank_screen(); + poke_blanked_console(); add_wait_queue(&vt->paste_wait, &wait); do { current->state = TASK_INTERRUPTIBLE; diff -u --recursive --new-file v2.1.32/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.1.32/linux/drivers/char/tty_io.c Fri Apr 4 08:52:19 1997 +++ linux/drivers/char/tty_io.c Mon Apr 7 14:02:43 1997 @@ -44,6 +44,10 @@ * * 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 , March 97 */ #include diff -u --recursive --new-file v2.1.32/linux/drivers/char/vt.c linux/drivers/char/vt.c --- v2.1.32/linux/drivers/char/vt.c Fri Apr 4 08:52:19 1997 +++ linux/drivers/char/vt.c Mon Apr 7 14:29:26 1997 @@ -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 @@ -189,7 +190,271 @@ } 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 @@ perm = 0; if (current->tty == tty || suser()) perm = 1; - + kbd = kbd_table + console; switch (cmd) { case KIOCSOUND: @@ -247,10 +512,8 @@ /* * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ #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 @@ { 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; diff -u --recursive --new-file v2.1.32/linux/drivers/net/3c505.c linux/drivers/net/3c505.c --- v2.1.32/linux/drivers/net/3c505.c Sun Feb 2 05:18:35 1997 +++ linux/drivers/net/3c505.c Mon Apr 7 14:17:09 1997 @@ -621,6 +621,7 @@ 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 @@ } 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.1.32/linux/drivers/net/Config.in Fri Apr 4 08:52:20 1997 +++ linux/drivers/net/Config.in Wed Apr 9 21:30:30 1997 @@ -79,6 +79,7 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.1.32/linux/drivers/net/Makefile Fri Apr 4 08:52:20 1997 +++ linux/drivers/net/Makefile Mon Apr 7 15:20:13 1997 @@ -371,6 +371,14 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.1.32/linux/drivers/net/Space.c Fri Apr 4 08:52:20 1997 +++ linux/drivers/net/Space.c Wed Apr 9 21:30:30 1997 @@ -50,6 +50,7 @@ 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 @@ #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 @@ -326,18 +330,6 @@ # undef NEXT_DEV # define NEXT_DEV (ð0_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 diff -u --recursive --new-file v2.1.32/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- v2.1.32/linux/drivers/net/eepro100.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/eepro100.c Mon Apr 7 16:43:59 1997 @@ -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 + +*/ + +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 +#include +#ifdef MODULE +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +#include +#include +#include + +/* 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 +#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: + 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); + + + +#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<>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; +} + +/* 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; +} + + +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; +} + +#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 */ + +/* + * 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: + */ diff -u --recursive --new-file v2.1.32/linux/drivers/net/net_init.c linux/drivers/net/net_init.c --- v2.1.32/linux/drivers/net/net_init.c Sun Feb 2 05:18:40 1997 +++ linux/drivers/net/net_init.c Wed Apr 9 21:30:30 1997 @@ -318,7 +318,7 @@ 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 @@ } } - 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) { diff -u --recursive --new-file v2.1.32/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v2.1.32/linux/drivers/net/plip.c Fri Apr 4 08:52:21 1997 +++ linux/drivers/net/plip.c Wed Apr 9 21:30:30 1997 @@ -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). */ /* @@ -11,16 +11,9 @@ * * 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 - * - 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 @@ #include #include +#include + +/* 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 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); enum plip_connection_state { PLIP_CN_NONE=0, @@ -203,61 +203,47 @@ 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); }; /* Entry point of PLIP driver. - Probe the hardware, and register/initialize the driver. */ + Probe the hardware, and register/initialize the driver. -int plip_init(struct device *dev) + 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_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 @@ 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 @@ 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 @@ /* 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 @@ }; /* 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 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 @@ return TIMEOUT; } -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 @@ /* 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 @@ } /* 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 @@ /* 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 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ } /* 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 @@ return; } + nl = (struct net_local *)dev->priv; + rcv = &nl->rcv_data; + if (dev->interrupt) return; @@ -843,7 +850,8 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ return 0; } -#ifdef MODULE -static int io[] = {0, 0, 0}; -static int irq[] = {0, 0, 0}; +static int parport[PLIP_MAX] = { -1, }; +static int timid = 0; -MODULE_PARM(io, "1-3i"); -MODULE_PARM(irq, "1-3i"); +MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i"); +MODULE_PARM(timid, "1i"); -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 struct device *dev_plip[PLIP_MAX] = { NULL, }; -int init_module(void) +#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 - /* No parameters. Default action is probing all interfaces. */ - for (i=0; i < 3; i++) { - if (register_netdev(&dev_plip[i]) == 0) - devices++; +#else /* !MODULE */ + +static int parport_ptr = 0; + +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 */ /* * Local variables: diff -u --recursive --new-file v2.1.32/linux/drivers/net/scc.c linux/drivers/net/scc.c --- v2.1.32/linux/drivers/net/scc.c Sun Feb 2 05:18:41 1997 +++ linux/drivers/net/scc.c Tue Apr 8 11:37:47 1997 @@ -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! @@ -151,8 +153,6 @@ #include #include -#include /* for CONFIG_PROC_FS */ - #include #include "z8530.h" @@ -222,6 +222,9 @@ static int Nchips = 0; static io_port Vector_Latch = 0; +MODULE_AUTHOR("Joerg Reuter "); +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 @@ } 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 @@ 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 @@ { 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 @@ add_timer(&scc->tx_t); } - restore_flags(flags); + restore_flags(flags); } static void scc_start_defer(struct scc_channel *scc) @@ -1010,8 +1013,7 @@ 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 @@ 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 @@ 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_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 @@ 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_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) @@ -1416,6 +1399,65 @@ #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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/BUGS-parport linux/drivers/pnp/BUGS-parport --- v2.1.32/linux/drivers/pnp/BUGS-parport Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/BUGS-parport Wed Apr 9 21:30:30 1997 @@ -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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/Config.in linux/drivers/pnp/Config.in --- v2.1.32/linux/drivers/pnp/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/Config.in Wed Apr 9 21:30:30 1997 @@ -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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/Makefile linux/drivers/pnp/Makefile --- v2.1.32/linux/drivers/pnp/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/Makefile Wed Apr 9 21:30:30 1997 @@ -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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/TODO-parport linux/drivers/pnp/TODO-parport --- v2.1.32/linux/drivers/pnp/TODO-parport Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/TODO-parport Wed Apr 9 21:30:30 1997 @@ -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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_init.c linux/drivers/pnp/parport_init.c --- v2.1.32/linux/drivers/pnp/parport_init.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/parport_init.c Wed Apr 9 21:30:30 1997 @@ -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 + * Tim Waugh + * Jose Renau + * + * based on work by Grant Guenther + * and Philip Blundell + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_ll_io.h linux/drivers/pnp/parport_ll_io.h --- v2.1.32/linux/drivers/pnp/parport_ll_io.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/parport_ll_io.h Wed Apr 9 21:30:30 1997 @@ -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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c --- v2.1.32/linux/drivers/pnp/parport_probe.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/parport_probe.c Wed Apr 9 21:30:31 1997 @@ -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 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_procfs.c linux/drivers/pnp/parport_procfs.c --- v2.1.32/linux/drivers/pnp/parport_procfs.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/parport_procfs.c Wed Apr 9 21:30:31 1997 @@ -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 + * Tim Waugh + * + * based on work by Grant Guenther + * and Philip Blundell + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include +#endif + +#include + +#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 -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_share.c linux/drivers/pnp/parport_share.c --- v2.1.32/linux/drivers/pnp/parport_share.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/pnp/parport_share.c Wed Apr 9 21:30:31 1997 @@ -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 + * Tim Waugh + * Jose Renau + * + * based on work by Grant Guenther + * and Philip Blundell + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.1.32/linux/drivers/scsi/ChangeLog.ncr53c8xx Mon Mar 17 14:54:29 1997 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Tue Apr 8 13:12:55 1997 @@ -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. diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.32/linux/drivers/scsi/Config.in Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/Config.in Wed Apr 9 21:30:31 1997 @@ -71,9 +71,16 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx --- v2.1.32/linux/drivers/scsi/README.ncr53c8xx Mon Mar 17 14:54:29 1997 +++ linux/drivers/scsi/README.ncr53c8xx Tue Apr 8 13:12:55 1997 @@ -4,7 +4,7 @@ 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -27 February 1997 +6 April 1997 =============================================================================== 1. Introduction @@ -34,7 +34,11 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c --- v2.1.32/linux/drivers/scsi/ncr53c8xx.c Mon Mar 17 14:54:30 1997 +++ linux/drivers/scsi/ncr53c8xx.c Tue Apr 8 13:12:55 1997 @@ -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 @@ 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 @@ 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 @@ 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_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 @@ **========================================================== */ -#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 @@ ** #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 @@ #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 @@ 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 @@ 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 @@ } 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 @@ ** 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 @@ /* ** 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 @@ /* ** start the timeout daemon */ - ncr_timeout (np); np->lasttime=0; + ncr_timeout (np); /* ** use SIMPLE TAG messages by default @@ -4060,22 +4055,6 @@ /*--------------------------------------------- ** - ** 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 @@ /*--------------------------------------------------- ** ** 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); @@ -4500,6 +4480,40 @@ /*========================================================== ** ** +** 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); +} + +/*========================================================== +** +** ** Reset the SCSI BUS. ** This is called from the generic SCSI driver. ** @@ -4518,23 +4532,19 @@ 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 @@ 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 @@ 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 @@ /** 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 @@ } } 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 @@ 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 @@ } #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 @@ ** 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); @@ -5964,6 +5977,23 @@ 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 ** every 0.25 second. @@ -5997,18 +6027,6 @@ 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 /*---------------------------------------------------- ** @@ -6087,6 +6105,75 @@ /*========================================================== ** +** 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"); +} + +/*========================================================== +** ** ** ncr chip exception handler. ** @@ -6098,8 +6185,6 @@ { u_char istat, dstat; u_short sist; - u_int32 dsp, dsa; - int script_ofs; int i; /* @@ -6222,54 +6307,8 @@ /*========================================= ** 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 @@ ** 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ #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 @@ 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 @@ 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 @@ 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"); diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h --- v2.1.32/linux/drivers/scsi/ncr53c8xx.h Mon Mar 17 14:54:30 1997 +++ linux/drivers/scsi/ncr53c8xx.h Wed Apr 9 23:32:37 1997 @@ -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, @@ -273,14 +273,14 @@ */ #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) @@ -302,6 +302,7 @@ 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, \ @@ -327,6 +328,7 @@ 0, \ 0, \ 0, \ + 0, \ 2, \ 0, \ 255, \ @@ -552,6 +554,7 @@ /*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 @@ #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 */ diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c --- v2.1.32/linux/drivers/scsi/ppa.c Fri Dec 27 02:03:23 1996 +++ linux/drivers/scsi/ppa.c Wed Apr 9 21:30:31 1997 @@ -1,504 +1,1326 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sd.h" -#include "hosts.h" -#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); -} - -char in_p( short port) - -{ return inb(ppa_base+port); - udelay(ppa_port_delay); -} - -void ppa_d_pulse( char b ) - -{ out_p(0,b); - out_p(2,0xc); out_p(2,0xe); out_p(2,0xc); out_p(2,0x4); out_p(2,0xc); -} - -void ppa_disconnect( void ) - -{ ppa_d_pulse(0); - ppa_d_pulse(0x3c); - ppa_d_pulse(0x20); - ppa_d_pulse(0xf); -} - -void ppa_c_pulse( char b ) - -{ out_p(0,b); - out_p(2,0x4); out_p(2,0x6); out_p(2,0x4); out_p(2,0xc); -} - -void ppa_connect( void ) - -{ ppa_c_pulse(0); - ppa_c_pulse(0x3c); - ppa_c_pulse(0x20); - ppa_c_pulse(0x8f); -} - -void ppa_do_reset( void ) - -{ out_p(2,0); /* This is really just a guess */ - udelay(100); -} - -char ppa_select( int initiator, int target ) - -{ char r; - int k; - - r = in_p(1); - out_p(0,(1< 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] + */ - k = 0; - while ( !(r = (in_p(1) & 0xf0)) && (k++ < PPA_SELECT_TMO)) barrier(); - return r; -} - -char ppa_wait( void ) - -/* 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. -*/ - -{ int k; - char r; - - 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); -} - -int ppa_init( void ) - -/* 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. - -*/ - -{ 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); - } +/* The following #define is to avoid a clash with hosts.c */ +#define PPA_CODE 1 +#include "ppa.h" +/* batteries not included :-) */ -#ifdef PPA_DEBUG - printk("PPA: command out: "); - for (k=0; k < cmd->cmd_len; k++) - printk("%3x",(cmd->cmnd[k]) & 0xff ); - printk("\n"); +/* + * 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; +} + +int ppa_release(struct Scsi_Host *host) +{ + int host_no = host->unique_id; + + printk("Releasing ppa%i\n", host_no); + parport_unregister_device(ppa_hosts[host_no].dev); + return 0; +} + +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; +} + +static void ppa_pb_release(int host_no) +{ + parport_release(ppa_hosts[host_no].dev); +} + + +/* Placed here so everyone knows what ecp_sync does.. */ +static void ecp_sync(int host_no) +{ + int i, r; + + r = r_ecr(host_no); + if ((r & 0xe0) != 0x80) + return; + + for (i = 0; i < 100; i++) { + r = r_ecr(host_no); + if (r & 0x01) + return; + udelay(5); + } + + printk("ppa: ECP sync failed as data still present in FIFO.\n"); +} + +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); +} + +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); + + ppa_pb_release(host_no); +} + +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); +} + +static int ppa_connect(int host_no, int flag) +{ + int retv = ppa_pb_claim(host_no); + + 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); + + return retv; +} + +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); +} + +static char ppa_select(int host_no, int initiator, int target) +{ + char r; + int k; + + r = r_str(host_no); /* TODO */ + + 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); + + k = 0; + while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO)) + barrier(); + + if (k >= PPA_SELECT_TMO) + return 0; + + return r; +} + +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); +} + + +/* + * 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; +} + +/* + * 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; +} + +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; +} + +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; +} + +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; +} + +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); +} + +/* + * 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 - return 1; -} - -int ppa_completion( Scsi_Cmnd * cmd ) - -/* 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. -*/ - -{ char r, l, h, v; - int dir, cnt, blen, fast, bulk; - char *buffer; - -#ifdef PPA_DEBUG - int k; + 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 - - 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"); + 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); -} - -/* 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); -} - -/* 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) - -{ Scsi_Cmnd *cmd; - void (*done) (Scsi_Cmnd *); - - 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; -} - -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; -} - -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 */ -} - -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. -*/ - -{ 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_abort( Scsi_Cmnd * cmd ) - -{ ppa_abort_flag = 1; - return SCSI_ABORT_SNOOZE; -} + default: + printk("PPA: bug in ppa_outs()\n"); + } + return 0; +} + +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; +} + +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 -int ppa_reset( Scsi_Cmnd * cmd, unsigned int ignored ) + 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 -{ ppa_abort_flag = 2; - return SCSI_RESET_PUNT; -} + default: + printk("PPA: bug in ppa_ins()\n"); + } + 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; +} + +/* + * 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; -const char *ppa_info( struct Scsi_Host * host ) + ppa_disconnect(host_no); -{ return ppa_info_string; + ppa_hosts[host_no].error_code = DID_OK; + return (h << 8) | (l & STATUS_MASK); } -#ifndef MODULE - -/* Command line parameters (for built-in driver): - - Syntax: ppa=base[,speed_high[,speed_low[,nybble]]] - - For example: ppa=0x378 or ppa=0x378,0,3 - -*/ - -void ppa_setup(char *str, int *ints) +/* deprecated synchronous interface */ -{ 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]; +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); } -#else +/* 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_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; +} + +/* + * 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; +} + +int ppa_abort(Scsi_Cmnd * cmd) +{ + int host_no = cmd->host->unique_id; + + 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; +} + +int ppa_reset(Scsi_Cmnd * cmd, unsigned int x) +{ + int host_no = cmd->host->unique_id; + + 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); + + return SCSI_RESET_PUNT; +} + + + +/*************************************************************************** + * Parallel port probing routines * + ***************************************************************************/ -Scsi_Host_Template driver_template = PPA; +#ifdef MODULE +Scsi_Host_Template driver_template = PPA; #include "scsi_module.c" - #endif +/* + * 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 */ +} + +/* 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; +} + +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 */ diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h --- v2.1.32/linux/drivers/scsi/ppa.h Fri Dec 27 02:03:23 1996 +++ linux/drivers/scsi/ppa.h Wed Apr 9 23:32:46 1997 @@ -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" - (c) 1996 Grant R. Guenther grant@torque.net -*/ +/* This driver reqires a 1.3.37 kernel or higher!! */ + +/* Use the following to enable certain chipset support + * Default is PEDANTIC = 3 + */ + +#include + +#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 + +/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sd.h" +#include "hosts.h" +#include +/* 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 *); + +#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 */ +#endif /* _PPA_H */ diff -u --recursive --new-file v2.1.32/linux/fs/Config.in linux/fs/Config.in --- v2.1.32/linux/fs/Config.in Mon Apr 7 11:35:30 1997 +++ linux/fs/Config.in Mon Apr 7 18:43:43 1997 @@ -18,12 +18,23 @@ 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 diff -u --recursive --new-file v2.1.32/linux/fs/inode.c linux/fs/inode.c --- v2.1.32/linux/fs/inode.c Thu Mar 27 14:40:06 1997 +++ linux/fs/inode.c Mon Apr 7 14:17:10 1997 @@ -173,6 +173,7 @@ { struct wait_queue * wait; + inode->i_count++; truncate_inode_pages(inode, 0); wait_on_inode(inode); if (IS_WRITABLE(inode)) { @@ -182,6 +183,7 @@ 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)); diff -u --recursive --new-file v2.1.32/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.1.32/linux/fs/isofs/inode.c Mon Apr 7 11:35:30 1997 +++ linux/fs/isofs/inode.c Tue Apr 8 11:58:19 1997 @@ -554,7 +554,7 @@ 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; diff -u --recursive --new-file v2.1.32/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c --- v2.1.32/linux/fs/lockd/clntproc.c Mon Apr 7 11:35:30 1997 +++ linux/fs/lockd/clntproc.c Mon Apr 7 18:43:43 1997 @@ -322,7 +322,8 @@ 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 @@ /* 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); } diff -u --recursive --new-file v2.1.32/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.32/linux/fs/nfs/inode.c Mon Apr 7 11:35:30 1997 +++ linux/fs/nfs/inode.c Mon Apr 7 18:43:43 1997 @@ -25,11 +25,12 @@ #include #include #include +#include #include #include #include -# include +#include #define NFSDBG_FACILITY NFSDBG_VFS @@ -50,6 +51,7 @@ NULL }; +struct rpc_stat nfs_rpcstat = { &nfs_program }; /* * The "read_inode" function doesn't actually do anything: @@ -438,7 +440,7 @@ 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 @@ cleanup_module(void) { #ifdef CONFIG_PROC_FS - rpcstat_unregister(&nfs_rpcstat); + rpc_proc_unregister("nfs"); #endif unregister_filesystem(&nfs_fs_type); nfs_free_dircache(); diff -u --recursive --new-file v2.1.32/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c --- v2.1.32/linux/fs/nfs/nfs2xdr.c Mon Apr 7 11:35:30 1997 +++ linux/fs/nfs/nfs2xdr.c Mon Apr 7 18:43:43 1997 @@ -625,25 +625,3 @@ 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 */ -}; diff -u --recursive --new-file v2.1.32/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c --- v2.1.32/linux/fs/nfsd/stats.c Mon Apr 7 11:35:31 1997 +++ linux/fs/nfsd/stats.c Mon Apr 7 18:43:43 1997 @@ -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 - * rpc - * auth - * fh * rc - * proto - * - * ... (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 + * Copyright (C) 1995, 1996, 1997 Olaf Kirch */ #include @@ -26,28 +24,13 @@ #include 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 @@ 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"); } diff -u --recursive --new-file v2.1.32/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c --- v2.1.32/linux/fs/proc/procfs_syms.c Mon Mar 17 14:54:31 1997 +++ linux/fs/proc/procfs_syms.c Mon Apr 7 18:43:43 1997 @@ -14,6 +14,8 @@ 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); diff -u --recursive --new-file v2.1.32/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h --- v2.1.32/linux/include/asm-i386/irq.h Thu Mar 27 14:40:06 1997 +++ linux/include/asm-i386/irq.h Mon Apr 7 14:02:43 1997 @@ -156,6 +156,18 @@ "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) \ diff -u --recursive --new-file v2.1.32/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h --- v2.1.32/linux/include/asm-i386/uaccess.h Thu Feb 6 02:53:45 1997 +++ linux/include/asm-i386/uaccess.h Wed Apr 9 23:31:13 1997 @@ -423,7 +423,7 @@ #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))) diff -u --recursive --new-file v2.1.32/linux/include/linux/lp.h linux/include/linux/lp.h --- v2.1.32/linux/include/linux/lp.h Thu Nov 30 04:03:38 1995 +++ linux/include/linux/lp.h Wed Apr 9 21:30:31 1997 @@ -78,19 +78,31 @@ #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_struct { - int base; - unsigned int irq; + struct ppd *dev; int flags; unsigned int chars; unsigned int time; diff -u --recursive --new-file v2.1.32/linux/include/linux/parport.h linux/include/linux/parport.h --- v2.1.32/linux/include/linux/parport.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/parport.h Wed Apr 9 21:30:31 1997 @@ -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 +#include + +/* 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_ */ diff -u --recursive --new-file v2.1.32/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.1.32/linux/include/linux/proc_fs.h Thu Mar 27 14:40:10 1997 +++ linux/include/linux/proc_fs.h Wed Apr 9 23:31:26 1997 @@ -48,7 +48,8 @@ PROC_RTC, PROC_LOCKS, PROC_ZORRO, - PROC_SLABINFO + PROC_SLABINFO, + PROC_PARPORT }; enum pid_directory_inos { diff -u --recursive --new-file v2.1.32/linux/include/linux/scc.h linux/include/linux/scc.h --- v2.1.32/linux/include/linux/scc.h Wed Oct 30 22:05:23 1996 +++ linux/include/linux/scc.h Tue Apr 8 11:37:47 1997 @@ -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 @@ -14,55 +14,56 @@ /* 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 */ @@ -71,9 +72,11 @@ /* 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 */ @@ -82,20 +85,26 @@ /* 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 @@ 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 @@ 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 @@ #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 @@ 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 */ diff -u --recursive --new-file v2.1.32/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h --- v2.1.32/linux/include/linux/sunrpc/stats.h Mon Apr 7 11:35:32 1997 +++ linux/include/linux/sunrpc/stats.h Wed Apr 9 23:34:09 1997 @@ -12,8 +12,6 @@ #include struct rpc_stat { - struct rpc_stat * next; - struct proc_dir_entry * entry; struct rpc_program * program; unsigned int netcnt, @@ -28,8 +26,6 @@ }; struct svc_stat { - struct svc_stat * next; - struct proc_dir_entry * entry; struct svc_program * program; unsigned int netcnt, @@ -42,18 +38,18 @@ rpcbadclnt; }; -void rpcstat_init(void); -void rpcstat_exit(void); +void rpc_proc_init(void); +void rpc_proc_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 *); +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 */ diff -u --recursive --new-file v2.1.32/linux/include/linux/swapctl.h linux/include/linux/swapctl.h --- v2.1.32/linux/include/linux/swapctl.h Thu Feb 6 02:54:09 1997 +++ linux/include/linux/swapctl.h Wed Apr 9 23:31:26 1997 @@ -31,17 +31,6 @@ 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; diff -u --recursive --new-file v2.1.32/linux/init/main.c linux/init/main.c --- v2.1.32/linux/init/main.c Mon Apr 7 11:35:32 1997 +++ linux/init/main.c Wed Apr 9 21:30:31 1997 @@ -79,7 +79,9 @@ 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 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 @@ #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 @@ #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 @@ #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 } }; diff -u --recursive --new-file v2.1.32/linux/kernel/exit.c linux/kernel/exit.c --- v2.1.32/linux/kernel/exit.c Thu Mar 27 14:40:12 1997 +++ linux/kernel/exit.c Mon Apr 7 11:17:31 1997 @@ -577,9 +577,9 @@ 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); diff -u --recursive --new-file v2.1.32/linux/kernel/fork.c linux/kernel/fork.c --- v2.1.32/linux/kernel/fork.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/fork.c Wed Apr 9 21:25:44 1997 @@ -134,9 +134,11 @@ 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; diff -u --recursive --new-file v2.1.32/linux/kernel/printk.c linux/kernel/printk.c --- v2.1.32/linux/kernel/printk.c Mon Apr 7 11:35:32 1997 +++ linux/kernel/printk.c Tue Apr 8 09:41:34 1997 @@ -176,6 +176,7 @@ 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 @@ 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 @@ 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); diff -u --recursive --new-file v2.1.32/linux/kernel/resource.c linux/kernel/resource.c --- v2.1.32/linux/kernel/resource.c Thu Mar 27 14:40:12 1997 +++ linux/kernel/resource.c Mon Apr 7 13:48:08 1997 @@ -13,7 +13,7 @@ #include #include -#define IOTABLE_SIZE 64 +#define IOTABLE_SIZE 128 typedef struct resource_entry_t { u_long from, num; diff -u --recursive --new-file v2.1.32/linux/kernel/sched.c linux/kernel/sched.c --- v2.1.32/linux/kernel/sched.c Fri Apr 4 08:52:26 1997 +++ linux/kernel/sched.c Tue Apr 8 22:11:27 1997 @@ -155,7 +155,7 @@ return; } #endif - if (p == &init_task) { + if (!p->pid) { static int nr = 0; if (nr < 5) { nr++; @@ -307,6 +307,10 @@ 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 @@ /* 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 @@ 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 @@ 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); diff -u --recursive --new-file v2.1.32/linux/kernel/sys.c linux/kernel/sys.c --- v2.1.32/linux/kernel/sys.c Mon Apr 7 11:35:32 1997 +++ linux/kernel/sys.c Mon Apr 7 14:24:54 1997 @@ -698,10 +698,12 @@ */ 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 @@ 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(); diff -u --recursive --new-file v2.1.32/linux/kernel/sysctl.c linux/kernel/sysctl.c --- v2.1.32/linux/kernel/sysctl.c Mon Apr 7 11:35:32 1997 +++ linux/kernel/sysctl.c Tue Apr 8 09:43:01 1997 @@ -183,10 +183,6 @@ 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, diff -u --recursive --new-file v2.1.32/linux/mm/vmscan.c linux/mm/vmscan.c --- v2.1.32/linux/mm/vmscan.c Sun Jan 26 02:07:50 1997 +++ linux/mm/vmscan.c Tue Apr 8 09:39:27 1997 @@ -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 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 @@ 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 @@ 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< #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 @@ #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 diff -u --recursive --new-file v2.1.32/linux/net/ipv4/proc.c linux/net/ipv4/proc.c --- v2.1.32/linux/net/ipv4/proc.c Thu Mar 27 14:40:15 1997 +++ linux/net/ipv4/proc.c Wed Apr 9 21:30:31 1997 @@ -172,6 +172,11 @@ 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) diff -u --recursive --new-file v2.1.32/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c --- v2.1.32/linux/net/ipv4/tcp_ipv4.c Fri Apr 4 08:52:28 1997 +++ linux/net/ipv4/tcp_ipv4.c Tue Apr 8 21:16:24 1997 @@ -654,7 +654,7 @@ 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; diff -u --recursive --new-file v2.1.32/linux/net/ipv4/udp.c linux/net/ipv4/udp.c --- v2.1.32/linux/net/ipv4/udp.c Fri Apr 4 08:52:28 1997 +++ linux/net/ipv4/udp.c Tue Apr 8 21:16:24 1997 @@ -436,7 +436,7 @@ 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 */ diff -u --recursive --new-file v2.1.32/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c --- v2.1.32/linux/net/ipv6/tcp_ipv6.c Thu Mar 27 14:40:17 1997 +++ linux/net/ipv6/tcp_ipv6.c Tue Apr 8 21:16:25 1997 @@ -516,7 +516,7 @@ 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; diff -u --recursive --new-file v2.1.32/linux/net/ipv6/udp.c linux/net/ipv6/udp.c --- v2.1.32/linux/net/ipv6/udp.c Thu Mar 27 14:40:17 1997 +++ linux/net/ipv6/udp.c Tue Apr 8 21:16:25 1997 @@ -379,7 +379,7 @@ 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"); diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c --- v2.1.32/linux/net/sunrpc/clnt.c Mon Apr 7 11:35:33 1997 +++ linux/net/sunrpc/clnt.c Mon Apr 7 18:43:43 1997 @@ -746,7 +746,7 @@ #ifdef RPC_DEBUG rpc_register_sysctl(); #endif - rpcstat_init(); + rpc_proc_init(); return 0; } @@ -756,6 +756,6 @@ #ifdef RPC_DEBUG rpc_unregister_sysctl(); #endif - rpcstat_exit(); + rpc_proc_exit(); } #endif diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c --- v2.1.32/linux/net/sunrpc/stats.c Mon Apr 7 11:35:33 1997 +++ linux/net/sunrpc/stats.c Mon Apr 7 18:43:43 1997 @@ -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 + * Copyright (C) 1995, 1996, 1997 Olaf Kirch */ #include @@ -19,83 +20,16 @@ #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, -}; +static struct proc_dir_entry *proc_net_rpc = 0; /* - * Register/unregister a stats file + * Get RPC client stats */ -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); - } -} - -/* - * Client stats handling - */ -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 @@ 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 @@ 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 - */ -void -rpcstat_init(void) -{ - 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 + * Register/unregister RPC proc files */ -void -rpcstat_exit(void) +static inline struct proc_dir_entry * +do_register(const char *name, void *data, int issvc) { - 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); -} + struct proc_dir_entry *ent; -#else + 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; -/* Various dummy functions */ - -int -rpcstat_get_info(struct rpc_stat *statp, char *buffer, - char **start, off_t offset, int length) -{ - return 0; -} - -int -svcstat_get_info(struct svc_stat *statp, char *buffer, - char **start, off_t offset, int length) -{ - return 0; + return ent; } -void -rpcstat_register(struct rpc_stat *statp) +struct proc_dir_entry * +rpc_proc_register(struct rpc_stat *statp) { + return do_register(statp->program->name, statp, 0); } void -rpcstat_unregister(struct rpc_stat *statp) +rpc_proc_unregister(const char *name) { + remove_proc_entry(name, proc_net_rpc); } -void -svcstat_register(struct svc_stat *statp) +struct proc_dir_entry * +svc_proc_register(struct svc_stat *statp) { + return do_register(statp->program->pg_name, statp, 1); } void -svcstat_unregister(struct svc_stat *statp) +svc_proc_unregister(const char *name) { + remove_proc_entry(name, proc_net_rpc); } 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 diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c --- v2.1.32/linux/net/sunrpc/sunrpc_syms.c Mon Apr 7 11:35:33 1997 +++ linux/net/sunrpc/sunrpc_syms.c Mon Apr 7 18:43:44 1997 @@ -10,8 +10,6 @@ #include #include -#ifdef CONFIG_MODULES - #include #include #include @@ -75,12 +73,12 @@ /* 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(nfs_debug); EXPORT_SYMBOL(nfsd_debug); EXPORT_SYMBOL(nlm_debug); - -#endif /* CONFIG_MODULES */ diff -u --recursive --new-file v2.1.32/linux/scripts/Configure linux/scripts/Configure --- v2.1.32/linux/scripts/Configure Mon Mar 17 14:54:36 1997 +++ linux/scripts/Configure Wed Apr 9 23:29:44 1997 @@ -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 @@ # # 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 @@ ;; 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 @@ 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 @@ } # -# 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 # diff -u --recursive --new-file v2.1.32/linux/scripts/Menuconfig linux/scripts/Menuconfig --- v2.1.32/linux/scripts/Menuconfig Mon Mar 17 14:54:36 1997 +++ linux/scripts/Menuconfig Wed Apr 9 21:59:27 1997 @@ -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 @@ # 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. @@ -56,6 +71,19 @@ 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 # function comment () { @@ -75,14 +103,14 @@ # 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 @@ then bool "$1" "$2" else - eval $2=\${$2:-'n'} x=\$$2 + set_x "$2" case $x in y) flag="*" ;; @@ -106,7 +134,7 @@ *) 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 @@ # 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 } @@ -882,31 +910,45 @@ --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. # # 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 () {