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
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
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
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
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
S: 1098 VA Amsterdam
S: The Netherlands
+N: Tim Waugh
+E: tmw20@cyberelk.demon.co.uk
+D: Co-architect of the parallel-port sharing system
+S: 51 Frensham Road
+S: Southsea
+S: Hampshire, UK. PO4 8AE
+
N: Juergen Weigert
E: jnweiger@immd4.informatik.uni-erlangen.de
D: The Linux Support Team Erlangen
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
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
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
%
\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
\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}
\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}
\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}
\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}
\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}}
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}
case for the {\file tty} device and lower case for the {\file cu}
device.
+The names {\file /dev/ttyQ$\#$} and {\file /dev/cuq$\#$} are reserved
+for local use.
+
The alternate devices provide for kernel-based exclusion and somewhat
different defaults than the primary devices. Their main purpose is to
allow the use of serial ports with programs with no inherent or broken
LINUX ALLOCATED DEVICES
-
Maintained by H. Peter Anvin <hpa@zytor.com>
- Last revised: March 3, 1997
+ Last revised: April 7, 1997
This list is the successor to Rick Miller's Linux Device List, which
he stopped maintaining when he got busy with other things in 1993. It
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
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
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
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
255 RESERVED
-
-
-
**** ADDITIONAL /dev DIRECTORY ENTRIES
This section details additional entries that should or may exist in
expected that multiple letters will be used; all letters will be upper
case for the "tty" device and lower case for the "cu" device.
+The names /dev/ttyQ# and /dev/cuq# are reserved for local use.
+
The alternate devices provide for kernel-based exclusion and somewhat
different defaults than the primary devices. Their main purpose is to
allow the use of serial ports with programs with no inherent or broken
The Digiboard Driver for Linux supports the following boards:
- DigiBoard PC/Xe, PC/Xi, PC/Xeve
+ DigiBoard PC/Xi, PC/Xe, PC/Xeve(which is the newer, smaller Xe with
+ a 8K window which is also known as PC/Xe(8K) and has no memory/irq
+ switches) You can use up to 4 cards with this driver and should work
+ on other architectures than intel also.
-Limitations:
-------------
-Currently the Driver does not do autoprobing!
+In case you have problems with this version(1.6.1) of this driver, please
+email directly to me as I made the last update. It you have a report about
+runnning it on other architectures than intel, email me, so I can document
+it here.
-The preconfigured I/O address is 0200h and the default memory address
-0D0000h, ALT-PIN feature on and 16 ports available.
+An version of this driver has been taken by Digiboard to make a driver
+software package which supports also PC/Xem cards and newer PCI cards
+but it don't support the old PC/Xi cards and it isn't yet ported to
+linux-2.1.x and may not be useable on other architectures than intel now.
+It is available from ftp.digi.com/ftp.digiboard.com. You can write me if
+you need an patch for this driver.
-Use them and you will not have to worry about configuring anything.
+ Bernhard Kaindl (bkaindl@netway.at) 6. April 1997.
-You can configure the driver via lilo. Have a look at the end of this
-message. The default settings vanish as soon as you give a digi= commandline.
-You can give multiple digi= commandline parameters to define multiple
-boards.
+Configuring the Driver
+----------------------
+
+The driver can be build direct into the kernel or as module.
+The pcxx driver can be configured using the command line feature while
+loading the kernel with LILO or LOADLIN or, if built as a module,
+with arguments to insmod and modprobe or with parameters in
+/etc/conf.modules for modprobe and kerneld.
+
+After configuring the driver you need to create the device special files
+as described in "Device file creation:" below and set the appropriate
+permissions for your application.
+
+As Module
+---------
+
+modprobe pcxx io=<io> \
+ membase=<membase> \
+ memsize=<memsize> \
+ numports=<numports> \
+ altpin=<altpin> \
+ verbose=<verbose>
+
+or, if several cards are installed
+
+modprobe pcxx io=<io-1>,<io-2>,... \
+ membase=<membase-1>,<membase-2>,... \
+ memsize=<memsize-1>,<memsize-2>,... \
+ numports=<numports-1>,<numports-2>,... \
+ altpin=<altpin-1>,<altpin-2>,... \
+ verbose=<verbose>
+
+where <io-N> is the io address of the Nth card and <membase-N> is the
+memory base address of the Nth card, etc.
+
+The parameters can be specified in any order. For example, the numports
+parameter can precede the membase parameter, or vice versa. If several
+cards are installed the ordering within the comma separated parameter
+lists must be consistent, of course.
+
+io - I/O port address of that card.
+membase - Memory start address of that card.
+memsize - Memory size of that card, in kilobytes. If given, this value
+ is compared against the card to verify configuration and
+ hinder the driver to use a misconfigured card. If the parameter
+ does not match the board it is disabled with a memory size error.
+numports - Number of ports on this card. This is the number of devices to
+ assign to this card or reserve if disabled.
+altpin - 1: swap DCD and DSR for 8-pin RJ-45 with modems.
+ 0: don't swap DCD and DSR.
+ other values count as 1.
+verbose - 1: give nice verbose output during initialisation of the driver.
+ possibly helpful during board configuration.
+ 0: normal terse output.
+
+Only the parameters which differ from the defaults need to be specified.
+If the io= parameter is not given, the default config is used. This is
+
+ io=0x200 membase=0xD0000 numports=16 altpin=0
+
+Only parameters applicable need be specified. For example to configure
+2 boards, first one at 0x200 with 8 ports, rest defaults, second one at
+0x120, memory at 0xD80000, altpin enabled, rest defaults, you can do this
+by using these parameters:
+
+ modprobe pcxx io=0x200,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+To disable a temporary unuseable board without changing the mapping of the
+devices following that board, you can empty the io-value for that board:
+
+ modprobe pcxx io=,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+The remainig board still uses ttyD8-ttyD15 and cud8-cud15.
+
+Example line for /etc/conf.modules for use with kerneld and as default
+parameters for modprobe:
+
+options pcxx io=0x200 numports=8
+
+For kerneld to work you will likely need to add these two lines to your
+/etc/conf.modules:
+
+alias char-major-22 pcxx
+alias char-major-23 pcxx
+
+
+Boot-time configuration when linked into the kernel
+---------------------------------------------------
+
+Per Board to be configured, pass a digi= commandline parameter to the
+kernel using lilo or loadlin. It consists of a string of comma separated
+identifiers or integers. The 6 values in order are:
+
+Card status: Enable - use that board
+ Disable - don't actually use that board.
+
+Card type: PC/Xi - the old ones with 64/128/256/512K RAM.
+ PC/Xe - PC/Xe(old ones with 64k mem range).
+ PC/Xeve - PC/Xe(newers with 8k mem range).
+
+Note: This is for documentation only, the type is detected from the board.
+
+Altpin setting: Enable - swap DCD and DSR for 8-pin RJ-45 with modems.
+ Disable - don't swap DCD and DSR.
+
+Number of ports: 1 ... 16 - Number of ports on this card. This is the
+ number of devices to assign to this card.
+
+I/O port address: eg. 200 - I/O Port address where the card is configured.
+
+Memory base addr: eg. 80000 - Memory address where the board's memory starts.
+
+This is an example for a line which you can insert into you lilo.conf:
+
+ append="digi=Enable,PC/Xi,Disable,4,120,D0000"
+
+there is an alternate form, in which you must use decimal values only:
+
+ append="digi=1,0,0,16,512,851968"
+
+If you don't give a digi= commandline, the compiled-in defaults of
+board 1: io=0x200, membase=0xd0000, altpin=off and numports=16 are used.
+
+If you have the resources (io&mem) free for use, configure your board to
+these settings and you should be set up fine even if yours has not 16 ports.
+
+
+Sources of Information
+----------------------
+
+Webpage: http://private.fuller.edu/clameter/digi.html
+
+Mailing List: digiboard@list.fuller.edu
+
+(Write e-mail to that address to subscribe. Common ListServ commands work.
+Archive of messages available)
+
+Christoph Lameter (clameter@fuller.edu) 16. April 1996.
+
+Supporting Tools
+----------------
-Supporting Tools:
------------------
Some tools and more detailed information can be found at
ftp://ftp.fuller.edu/Linux/digi
-WARNING: Most of the stuff available right now uses the WRONG Major Device
-numbers and the wrong call out devices. Be careful and check them first.
-Better not use any of the software in that directory if you run a recent
-1.3.X Kernel or later!
-
The "ditty" tool described in the Digiboard Manuals for other Unixes
is also available.
+
+Device file creation
+--------------------
+
Currently the Linux MAKEDEV command does not support generating the Digiboard
-Devices. Use the following script to generate the devices:
+Devices.
+
+The /dev/cud devices behave like the /dev/cua devices
+and the ttyD devices are like the /dev/ttyS devices.
+
+Use the following script to generate the devices:
------------------ mkdigidev begin
#!/bin/sh
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
;;
par[0-2])
----- End Makedev patch
-
-The /dev/cud?? devices behave like the /dev/cua?? devices
-and the ttyD devices are like the /dev/ttyS?? devices.
-
-Sources of Information
-----------------------
-
-Webpage: http://private.fuller.edu/clameter/digi.html
-
-Mailing List: digiboard@list.fuller.edu
-
-(Write e-mail to that address to subscribe. Common ListServ commands work.
-Archive of messages available)
-
-Christoph Lameter (clameter@fuller.edu) 16. April 1996.
-
------------------------------------------------------------------------------
-
-Changes v1.5.5:
-
-The ability to use the kernel's command line to pass in the configuration for
-boards. Using LILO's APPEND command, a string of comma separated identifiers
-or integers can be used. The 6 values in order are:
-
- Enable/Disable this card,
- Type of card: PC/Xi(0), PC/Xe(1), PC/Xeve(2), PC/Xem(3)
- Enable/Disable alternate pin arrangement,
- Number of ports on this card,
- I/O Port where card is configured (in HEX if using string identifiers),
- Base of memory window (in HEX if using string identifiers),
-
-Samples:
- append="digi=E,PC/Xi,D,16,200,D0000"
- append="digi=1,0,0,16,512,(whatever D0000 is in base 10 :)
-
-Driver's minor device numbers are conserved. This means that instead of
-each board getting a block of 16 minors pre-assigned, it gets however
-many it should, with the next card following directly behind it. A
-system with 4 2-port PC/Xi boards will use minor numbers 0-7.
-This conserves some memory, and removes a few hard coded constants.
-
-NOTE!! NOTE!! NOTE!!
-The definition of PC/Xem as a valid board type is the BEGINNING of support
-for this device. The driver does not currently recognise the board, nor
-does it want to initialize it. At least not the EISA version.
-
-Mike McLagan <mike.mclagan@linux.org> 5, April 1996.
-
Ioctl Numbers
-12 Feb 1997
+5 Apr 1997
Michael Chastain
<mec@shout.net>
'a' all various, see http://lrcwww.epfl.ch/linux-atm/magic.html
'c' all linux/comstats.h
'f' all linux/ext2_fs.h
+'l' 00-3F linux/tcfs_fs.h in development:
+ <http://mikonos.dia.unisa.it/tcfs>
'm' all linux/mtio.h conflict!
'm' all linux/soundcard.h conflict!
'n' all linux/ncp_fs.h
0x89 E0-EF linux/sockios.h SIOCPROTOPRIVATE range
0x89 F0-FF linux/sockios.h SIOCDEVPRIVATE range
0x8B all linux/wireless.h
+0x8C 00-3F WiNRADiO driver in development:
+ <mailto:brian@proximity.com.au>
0x90 00 linux/sbpcd.h
-0x99 00-0F 537-Addinboard driver in development, e-mail contact:
- b.kohl@ipn-b.comlink.apc.org
-0xA0 all Small Device Project in development, e-mail contact:
- khollis@northwest.com
+0x99 00-0F 537-Addinboard driver in development:
+ <mailto:b.kohl@ipn-b.comlink.apc.org>
+0xA0 all Small Device Project in development:
+ <mailto:khollis@northwest.com>
--- /dev/null
+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.
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
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
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 32
+SUBLEVEL = 33
ARCH = i386
SMP = 1
#
# SMP profiling options
-# SMP_PROF = 1
+SMP_PROF = 1
.EXPORT_ALL_VARIABLES:
DRIVERS := $(DRIVERS) drivers/pci/pci.a
endif
+ifdef CONFIG_PNP
+DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
+endif
+
ifdef CONFIG_SBUS
DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
endif
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
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
#
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))" ""
tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
+fi
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
endmenu
+source drivers/pnp/Config.in
+
source drivers/block/Config.in
if [ "$CONFIG_NET" = "y" ]; then
+++ /dev/null
-#
-# 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
-
-
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
*/
-#include <string.h>
-
#include <asm/segment.h>
#include <asm/io.h>
static void gzip_mark(void **);
static void gzip_release(void **);
-#ifndef STANDALONE_DEBUG
static void puts(const char *);
extern int end;
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;
for (i=0;i<n;i++) ss[i] = c;
}
-__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
+void* memcpy(void* __dest, __const void* __src,
size_t __n)
{
int i;
for (i=0;i<__n;i++) d[i] = s[i];
}
-#endif
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
short b;
} stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS };
-#ifdef STANDALONE_DEBUG
-
-static void gzip_mark(void **ptr)
-{
-}
-
-static void gzip_release(void **ptr)
-{
-}
-
-char output_buffer[1024 * 800];
-
-int
-main(argc, argv)
- int argc;
- char **argv;
-{
- output_data = output_buffer;
-
- makecrc();
- puts("Uncompressing Linux...");
- gunzip();
- puts("done.\n");
- return 0;
-}
-
-#else
-
void setup_normal_output_buffer()
{
#ifdef STANDARD_MEMORY_BIOS_CALL
if (high_loaded) close_output_buffer_if_we_run_high(mv);
return high_loaded;
}
-#endif
-
-
-
Pentium CONFIG_M586 \
PPro CONFIG_M686" Pentium
bool 'Video mode selection support' CONFIG_VIDEO_SELECT
+
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
+
endmenu
+source drivers/pnp/Config.in
+
source drivers/block/Config.in
if [ "$CONFIG_NET" = "y" ]; then
# CONFIG_PCNET32 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
-CONFIG_DE4X5=y
+# CONFIG_DE4X5 is not set
# CONFIG_DEC_ELCP is not set
# CONFIG_DGRS is not set
+CONFIG_EEXPRESS_PRO100=y
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_DLCI is not set
# CONFIG_UMSDOS_FS is not set
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
-# CONFIG_ROOT_NFS is not set
# CONFIG_SMB_FS is not set
CONFIG_ISO9660_FS=y
# CONFIG_HPFS_FS is not set
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include <asm/io.h>
+#include <asm/hardirq.h>
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(elf_fpregset_t *);
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);
#include <asm/smp.h>
#include <asm/pgtable.h>
+#ifdef __SMP_PROF__
+extern volatile unsigned long smp_apic_timer_ticks[1+NR_CPUS];
+#endif
+
#define CR0_NE 32
static unsigned char cache_21 = 0xff;
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
/*
unsigned long sum_spins_syscall = 0;
unsigned long sum_spins_sys_idle = 0;
unsigned long sum_smp_idle_count = 0;
+ unsigned long sum_apic_timer_ticks = 0;
for (i=0;i<smp_num_cpus;i++) {
int cpunum = cpu_logical_map[i];
sum_spins_syscall+=smp_spins_syscall[cpunum];
sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
sum_smp_idle_count+=smp_idle_count[cpunum];
+ sum_apic_timer_ticks+=smp_apic_timer_ticks[cpunum];
}
len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
len +=sprintf(buf+len," idle ticks\n");
+ len+=sprintf(buf+len,"TICK %10lu",sum_apic_timer_ticks);
+ for (i=0;i<smp_num_cpus;i++)
+ len+=sprintf(buf+len," %10lu",smp_apic_timer_ticks[cpu_logical_map[i]]);
+
+ len +=sprintf(buf+len," local APIC timer ticks\n");
+
len+=sprintf(buf+len, "IPI: %10lu received\n",
ipi_count);
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
+
for (i = 0; i < 16 ; i++)
set_intr_gate(0x20+i,bad_interrupt[i]);
- /* This bit is a hack because we don't send timer messages to all processors yet */
- /* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */
+
+ /*
+ * This bit is a hack because we don't send timer messages to all
+ * processors yet. It has to be here .. it doesn't work if you put
+ * it down the bottom - assembler explodes 8)
+ */
+
#ifdef __SMP__
- set_intr_gate(0x20+i, reschedule_interrupt); /* IRQ '16' - IPI for rescheduling */
- set_intr_gate(0x21+i, invalidate_interrupt); /* IRQ '17' - IPI for invalidation */
- set_intr_gate(0x22+i, stop_cpu_interrupt); /* IRQ '18' - IPI for CPU halt */
+ /* IRQ '16' - IPI for rescheduling */
+ set_intr_gate(0x20+i, reschedule_interrupt);
+
+ /* IRQ '17' - IPI for invalidation */
+ set_intr_gate(0x21+i, invalidate_interrupt);
+
+ /* IRQ '18' - IPI for CPU halt */
+ set_intr_gate(0x22+i, stop_cpu_interrupt);
+
+#ifdef __SMP_PROF__
+ /* IRQ '19' - self generated IPI for local APIC timer */
+ set_intr_gate(0x23+i, apic_timer_interrupt);
+#endif
#endif
request_region(0x20,0x20,"pic1");
request_region(0xa0,0x20,"pic2");
if (current->pid != 0)
goto out;
/* endless idle loop with no priority at all */
+ current->priority = -100;
current->counter = -100;
for (;;)
{
int cpu_idle(void *unused)
{
+ current->priority = -100;
while(1)
{
if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
run_task_queue(&tq_scheduler);
unlock_kernel();
}
+ /* endless idle loop with no priority at all */
+ current->counter = -100;
schedule();
}
}
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);
* Michel Lespinasse : Changes for 2.1 kernel map.
* Michael Chastain : Change trampoline.S to gnu as.
* Alan Cox : Dumb bug: 'B' step PPro's are fine
- *
+ * Ingo Molnar : Added APIC timers, based on code
+ * from Jose Renau
*/
#include <linux/kernel.h>
#include <asm/io.h>
extern unsigned long start_kernel, _etext;
+void setup_APIC_clock (void);
/*
* Some notes on processor bugs:
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 */
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.
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.
*/
for(;;) __asm__("hlt");
for (;;) ;
}
+
+#ifdef __SMP_PROF__
+
+extern void (*do_profile)(struct pt_regs *);
+static void (*default_do_profile)(struct pt_regs *) = NULL;
+
+/*
+ * APIC timer interrupt
+ */
+void smp_apic_timer_interrupt(struct pt_regs * regs)
+{
+ int cpu = smp_processor_id();
+
+ if (!user_mode(regs)) {
+ unsigned long flags;
+
+ /*
+ * local timer interrupt is not NMI yet, so
+ * it's simple, we just aquire the global cli
+ * lock to mess around with profiling info.
+ *
+ * later, in the NMI version, we have to build
+ * our own 'current' pointer (as we could have
+ * interrupted code that just changes "current")
+ * and have to lock profiling info between NMI
+ * interrupts properly.
+ */
+ save_flags(flags);
+ cli();
+ default_do_profile(regs);
+ restore_flags(flags);
+ }
+
+ /*
+ * this is safe outside the lock.
+ */
+ smp_apic_timer_ticks[cpu]++;
+
+ apic_read (APIC_SPIV);
+ apic_write (APIC_EOI, 0);
+}
+
+/*
+ * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
+ * per second. We assume that the caller has already set up the local
+ * APIC at apic_addr.
+ *
+ * Later we might want to split HZ into two parts: introduce
+ * PROFILING_HZ and scheduling HZ. The APIC timer is not exactly
+ * sync with the external timer chip, it closely follows bus clocks.
+ */
+
+#define RTDSC(x) __asm__ __volatile__ ( ".byte 0x0f,0x31" \
+ :"=a" (((unsigned long*)&x)[0]), \
+ "=d" (((unsigned long*)&x)[1]))
+
+/*
+ * The timer chip is already set up at HZ interrupts per second here,
+ * but we do not accept timer interrupts yet. We only allow the BP
+ * to calibrate.
+ */
+unsigned int get_8254_timer_count (void)
+{
+ unsigned int count;
+
+ outb_p(0x00, 0x43);
+ count = inb_p(0x40);
+ count |= inb_p(0x40) << 8;
+
+ return count;
+}
+
+/*
+ * This function sets up the local APIC timer, with a timeout of
+ * 'clocks' APIC bus clock. During calibration we actually call
+ * this function twice, once with a bogus timeout value, second
+ * time for real. The other (noncalibrating) CPUs call this
+ * function only once, with the real value.
+ *
+ * We are strictly in irqs off mode here, as we do not want to
+ * get an APIC interrupt go off accidentally.
+ */
+
+#define APIC_DIVISOR 16
+
+/*
+ * We do reads before writes even if unnecessary, to get around the
+ * APIC double write 'bug'.
+ */
+
+void setup_APIC_timer (unsigned int clocks)
+{
+ unsigned long lvtt1_value;
+ unsigned int tmp_value;
+
+ /*
+ * Unfortunately the local APIC timer cannot be set up into NMI
+ * mode. With the IO APIC we can re-route the external timer
+ * interrupt and broadcast it as an NMI to all CPUs, so no pain.
+ *
+ * NOTE: this irq vector 19 and the gate in BUILD_SMP_TIMER_INTERRUPT
+ * should be the same ;)
+ */
+ tmp_value = apic_read(APIC_LVTT);
+ lvtt1_value = APIC_LVT_TIMER_PERIODIC | (0x20+19);
+ apic_write(APIC_LVTT , lvtt1_value);
+
+ /*
+ * Divide PICLK by 16
+ */
+ tmp_value = apic_read(APIC_TDCR);
+ apic_write(APIC_TDCR , (tmp_value & ~APIC_TDR_DIV_1 )
+ | APIC_TDR_DIV_16);
+
+ tmp_value = apic_read(APIC_TMICT);
+ apic_write(APIC_TMICT, clocks/APIC_DIVISOR);
+}
+
+void wait_8254_wraparound (void)
+{
+ unsigned int curr_count, prev_count=~0;
+ int delta;
+
+ curr_count = get_8254_timer_count();
+
+ do {
+ prev_count = curr_count;
+ curr_count = get_8254_timer_count();
+ delta = curr_count-prev_count;
+
+ /*
+ * This limit for delta seems arbitrary, but it isnt, it's
+ * slightly above the level of error a buggy Mercury/Neptune
+ * chipset timer can cause.
+ */
+
+ } while (delta<1000);
+}
+
+/*
+ * In this function we calibrate APIC bus clocks to the external
+ * timer here. Unfortunately we cannot use jiffies and
+ * the timer irq to calibrate, since some later bootup
+ * code depends on getting the first irq? Ugh.
+ *
+ * We want to do the calibration only once since we
+ * want to have local timer irqs syncron. CPUs connected
+ * by the same APIC bus have the very same bus frequency.
+ * And we want to have irqs off anyways, no accidental
+ * APIC irq that way.
+ */
+
+int calibrate_APIC_clock (void)
+{
+ unsigned long long t1,t2;
+ unsigned long tt1,tt2;
+ unsigned int prev_count, curr_count;
+ long calibration_result;
+
+ printk("calibrating APIC timer ... ");
+
+ /*
+ * Put whatever arbitrary (but long enough) timeout
+ * value into the APIC clock, we just want to get the
+ * counter running for calibration.
+ */
+ setup_APIC_timer(1000000000);
+
+ /*
+ * The timer chip counts down to zero. Lets wait
+ * for a wraparound to start exact measurement:
+ * (the current tick might have been already half done)
+ */
+
+ wait_8254_wraparound ();
+
+ /*
+ * We wrapped around just now, lets start:
+ */
+ RTDSC(t1);
+ tt1=apic_read(APIC_TMCCT);
+
+ /*
+ * lets wait until we get to the next wrapround:
+ */
+ wait_8254_wraparound ();
+
+ tt2=apic_read(APIC_TMCCT);
+ RTDSC(t2);
+
+ /*
+ * The APIC bus clock counter is 32 bits only, it
+ * might have overflown:
+ */
+ if (tt2<tt1) {
+ unsigned long tmp = tt2;
+ tt2=tt1;
+ tt1=tmp;
+ }
+
+ calibration_result = (tt2-tt1)*APIC_DIVISOR;
+
+ printk("\n..... %ld CPU clocks in 1 timer chip tick.\n",
+ (unsigned long)(t2-t1));
+
+ printk("..... %ld APIC bus clocks in 1 timer chip tick.\n",
+ calibration_result);
+
+
+ printk("..... CPU clock speed is %ld.%ld MHz.\n",
+ ((long)(t2-t1))/(1000000/HZ),
+ ((long)(t2-t1))%(1000000/HZ) );
+
+ printk("..... APIC bus clock speed is %ld.%ld MHz.\n",
+ calibration_result/(1000000/HZ),
+ calibration_result%(1000000/HZ) );
+
+ return calibration_result;
+}
+
+void setup_APIC_clock (void)
+{
+ unsigned long flags;
+
+ static volatile int calibration_lock;
+ static unsigned int calibration_result;
+
+ return;
+ save_flags(flags);
+ cli();
+
+ printk("setup_APIC_clock() called.\n");
+
+ /*
+ * We use a private profiling function. (This is preparation
+ * for NMI local timer interrupts.)
+ *
+ * [ setup_APIC_clock() is called from all CPUs, but we want
+ * to do this part of the setup only once ... and it fits
+ * here best ]
+ */
+ if (!set_bit(0,&calibration_lock)) {
+
+ default_do_profile = do_profile;
+ do_profile = NULL;
+
+ calibration_result=calibrate_APIC_clock();
+ /*
+ * Signal completion to the other CPU[s]:
+ */
+ calibration_lock = 3;
+
+ } else {
+ /*
+ * Other CPU is calibrating, wait for finish:
+ */
+ printk("waiting for other CPU calibrating APIC timer ... ");
+ while (calibration_lock == 1);
+ printk("done, continuing.\n");
+ }
+
+ setup_APIC_timer (calibration_result);
+
+ restore_flags(flags);
+}
+
+#undef APIC_DIVISOR
+#undef RTDSC
+
+#endif
+
#define VMALLOC_OFFSET (8*1024*1024)
#define MODULE_RANGE (8*1024*1024)
-static void show_regs(struct pt_regs *regs)
+static void show_registers(struct pt_regs *regs)
{
int i;
unsigned long esp;
esp = (unsigned long) ®s->esp;
ss = KERNEL_DS;
- if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
- return;
if (regs->xcs & 3) {
esp = regs->esp;
ss = regs->xss & 0xffff;
/*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);
}
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
*
* 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) {
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"
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);
/* 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;
}
SUB_DIRS := block char net #streams
MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp
ifdef CONFIG_PCI
SUB_DIRS += pci
endif
+ifdef CONFIG_PNP
+SUB_DIRS += pnp
+MOD_SUB_DIRS += pnp
+endif
+
ifdef CONFIG_SBUS
SUB_DIRS += sbus
endif
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();
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);
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
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
* 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;
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--;
for (i=0; i<E_TABSZ ; i++) {
unsigned char uc;
- get_user(uc, arg+i);
+ __get_user(uc, arg+i);
p[i] = UNI_DIRECT_BASE | uc;
}
for (i=0; i<E_TABSZ ; i++)
{
ch = conv_uni_to_pc(p[i]);
- put_user((ch & ~0xff) ? 0 : ch, arg+i);
+ __put_user((ch & ~0xff) ? 0 : ch, arg+i);
}
return 0;
}
for (i=0; i<E_TABSZ ; i++) {
unsigned short us;
- get_user(us, arg+i);
+ __get_user(us, arg+i);
p[i] = us;
}
return i;
for (i=0; i<E_TABSZ ; i++)
- put_user(p[i], arg+i);
+ __put_user(p[i], arg+i);
return 0;
}
while( ct-- )
{
unsigned short unicode, fontpos;
- get_user(unicode, &list->unicode);
- get_user(fontpos, &list->fontpos);
+ __get_user(unicode, &list->unicode);
+ __get_user(fontpos, &list->fontpos);
if ( (err1 = con_insert_unipair(unicode,fontpos)) != 0 )
err = err1;
list++;
{
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);
}
* LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
* Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
* "lp=" command line parameters added by Grant Guenther, grant@torque.net
- * lp_read (Status readback) support added by Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * lp_read (Status readback) support added by Carsten Gross,
+ * carsten@sol.wohnheim.uni-ulm.de
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
-#include <linux/lp.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
+#include <linux/parport.h>
+#include <linux/lp.h>
/* the BIOS manuals say there can be up to 4 lpt devices
* but I have not seen a board where the 4th address is listed
* please let me know if you have different equipment
* if you have more than 3 printers, remember to increase LP_NO
*/
-struct lp_struct lp_table[] = {
- { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
- { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
- { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
+struct lp_struct lp_table[] =
+{
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+ {0}}
};
#define LP_NO 3
+/* Device name */
+static char *dev_name = "lp";
+
/* Test if printer is ready (and optionally has no error conditions) */
#define LP_READY(minor, status) \
((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
(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;
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;
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;
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);
} 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)
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;
} 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);
} 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;
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,
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 */
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);
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)
#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)
#endif
if (current->signal & ~current->blocked) {
lp_select_in_high(minor);
+ parport_release(lp_table[minor].dev);
if (temp !=buf)
return temp-buf;
else
} 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;
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;
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;
}
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;
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));
if (retval)
return retval;
else {
- int status = LP_S(minor);
+ int status = r_str(minor);
copy_to_user((int *) arg, &status, sizeof(int));
}
break;
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 */
lp_release
};
-static int lp_probe(int offset)
-{
- int base, size;
- unsigned int testvalue;
-
- base = LP_B(offset);
- if (base == 0)
- return -1; /* de-configured by command line */
- if (LP_IRQ(offset) > 15)
- return -1; /* bogus interrupt value */
- size = (base == 0x3bc)? 3 : 8;
- if (check_region(base, size) < 0)
- return -1;
- /* write to port & read back to check */
- outb_p(LP_DUMMY, base);
- udelay(LP_DELAY);
- testvalue = inb_p(base);
- if (testvalue == LP_DUMMY) {
- LP_F(offset) |= LP_EXIST;
- lp_reset(offset);
- printk(KERN_INFO "lp%d at 0x%04x, ", offset, base);
- request_region(base, size, "lp");
- if (LP_IRQ(offset))
- printk("(irq = %d)\n", LP_IRQ(offset));
- else
- printk("(polling)\n");
- return 1;
- } else
- return 0;
-}
+static int parport[LP_NO] = { -1, };
+
+#ifdef MODULE
+#define lp_init init_module
+MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
-/* Command line parameters:
+#else
- When the lp driver is built in to the kernel, you may use the
- LILO/LOADLIN command line to set the port addresses and interrupts
- that the driver will use.
+static int parport_ptr = 0;
- Syntax: lp=port0[,irq0[,port1[,irq1[,port2[,irq2]]]]]
+void lp_setup(char *str, int *ints)
+{
+ /* Ugh. */
+ if (!strncmp(str, "parport", 7)) {
+ int n = simple_strtoul(str+7, NULL, 10);
+ if (parport_ptr < LP_NO)
+ parport[parport_ptr++] = n;
+ else
+ printk(KERN_INFO "lp: too many ports, %s ignored.\n",
+ str);
+ } else if (!strcmp(str, "auto")) {
+ parport[0] = -3;
+ } else {
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* disable driver on "parport=" or "parport=0" */
+ parport[0] = -2;
+ } else {
+ printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
+ }
+ }
+}
- For example: lp=0x378,0 or lp=0x278,5,0x378,7
+#endif
- Note that if this feature is used, you must specify *all* the ports
- you want considered, there are no defaults. You can disable a
- built-in driver with lp=0 .
+int lp_wakeup(void *ref)
+{
+ struct lp_struct *lp_dev = (struct lp_struct *) ref;
-*/
+ if (!lp_dev->lp_wait_q)
+ return 1; /* Wake up whom? */
-void lp_setup(char *str, int *ints)
+ /* Claim the Parport */
+ if (parport_claim(lp_dev->dev))
+ return 1; /* Shouldn't happen */
-{
- LP_B(0) = ((ints[0] > 0) ? ints[1] : 0 );
- LP_IRQ(0) = ((ints[0] > 1) ? ints[2] : 0 );
- LP_B(1) = ((ints[0] > 2) ? ints[3] : 0 );
- LP_IRQ(1) = ((ints[0] > 3) ? ints[4] : 0 );
- LP_B(2) = ((ints[0] > 4) ? ints[5] : 0 );
- LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : 0 );
+ wake_up(&lp_dev->lp_wait_q);
+ return 0;
}
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-#define lp_init init_module
-#endif
+static int inline lp_searchfor(int list[], int a)
+{
+ int i;
+ for (i = 0; i < LP_NO && list[i] != -1; i++) {
+ if (list[i] == a) return 1;
+ }
+ return 0;
+}
int lp_init(void)
{
- int offset = 0;
int count = 0;
-#ifdef MODULE
- int failed = 0;
-#endif
-
- if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- printk("lp: unable to get major %d\n", LP_MAJOR);
- return -EIO;
+ struct parport *pb;
+
+ if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
+ printk("lp: unable to get major %d\n", LP_MAJOR);
+ return -EIO;
}
-#ifdef MODULE
- /* When user feeds parameters, use them */
- for (offset=0; offset < LP_NO; offset++) {
- int specified=0;
- if (io[offset] != 0) {
- LP_B(offset) = io[offset];
- specified++;
- }
- if (irq[offset] != 0) {
- LP_IRQ(offset) = irq[offset];
- specified++;
- }
- if (specified) {
- if (lp_probe(offset) <= 0) {
- printk(KERN_INFO "lp%d: Not found\n", offset);
- failed++;
- } else
- count++;
+ if (parport[0] == -2) return 0;
+
+ pb = parport_enumerate();
+
+ while (pb) {
+ if (parport[0] == -1 || lp_searchfor(parport, count) ||
+ (parport[0] == -3 &&
+ pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
+ lp_table[count].dev =
+ parport_register_device(pb, dev_name, NULL,
+ lp_wakeup,
+ lp_interrupt, PARPORT_DEV_TRAN,
+ (void *) &lp_table[count]);
+ lp_table[count].flags |= LP_EXIST;
+ printk(KERN_INFO "lp%d: using %s at 0x%x, ", count,
+ pb->name, pb->base);
+ if (pb->irq == -1)
+ printk("polling.\n");
+ else
+ printk("irq %d.\n", pb->irq);
}
- }
- /* Successful specified devices increase count
- * Unsuccessful specified devices increase failed
- */
- if (count)
- return 0;
- if (failed) {
- printk(KERN_INFO "lp: No override devices found.\n");
- unregister_chrdev(LP_MAJOR,"lp");
- return -EIO;
- }
- /* Only get here if there were no specified devices. To continue
- * would be silly since the above code has scribbled all over the
- * probe list.
- */
-#endif
- /* take on all known port values */
- for (offset = 0; offset < LP_NO; offset++) {
- int ret = lp_probe(offset);
- if (ret < 0)
- continue;
- count += ret;
- }
- if (count == 0)
- printk("lp: Driver configured but no interfaces found.\n");
-
- return 0;
+ if (++count == LP_NO)
+ break;
+ pb = pb->next;
+ }
+
+ /* Successful specified devices increase count
+ * Unsuccessful specified devices increase failed
+ */
+ if (count)
+ return 0;
+
+ printk(KERN_INFO "lp: driver loaded but no devices found\n");
+ return 1;
}
#ifdef MODULE
{
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
* 1.5.7 July 22, 1996 Martin Mares: CLOCAL fix, pcxe_table clearing.
* David Nugent: Bug in pcxe_open.
* Brian J. Murrell: Modem Control fixes, Majors correctly assigned
+ * 1.6.1 April 6, 1997 Bernhard Kaindl: fixed virtual memory access for 2.1
+ * i386-kernels and use on other archtitectures, Allowing use
+ * as module, added module parameters, added switch to enable
+ * verbose messages to assist user during card configuration.
+ * Currently only tested on a PC/Xi card, but should work on Xe
+ * and Xeve also.
*
*/
-#undef MODULE
-/* Module code is broken right now. Don't enable this unless you want to fix it */
#undef SPEED_HACK
/* If you define SPEED_HACK then you get the following Baudrate translation
#include <linux/string.h>
#ifndef MODULE
-
-/* is* routines not available in modules
-** the need for this should go away when probing is done. :-)
-** brian@ilinx.com
-*/
-
-#include <linux/ctype.h>
+#include <linux/ctype.h> /* We only need it for parsing the "digi="-line */
#endif
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
-#define VERSION "1.5.7"
-static char *banner = "Digiboard PC/X{i,e,eve} driver v1.5.7";
+#define VERSION "1.6.1"
#include "digi.h"
#include "fep.h"
#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;
/* 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);
-}
/*****************************************************************************/
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();
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);
}
}
}
-/* 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)
{
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));
return;
}
-
while (str && *str)
{
/* find the next comma or terminator */
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++;
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++;
printk("PC/Xx: Invalid memory base %s\n", str);
return;
}
-#endif
board.membase = simple_strtoul(str, NULL, 16);
last = i;
/* yeha! string parameter was successful! */
numcards++;
}
+#endif
-
+/*
+ * function to initialize the driver with the given parameters, which are either
+ * the default values from this file or the parameters given at boot.
+ */
int pcxe_init(void)
{
- ulong flags, memory_seg=0, memory_size;
- int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
+ ulong memory_seg=0, memory_size=0;
+ int lowwater, enabled_cards=0, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
unchar *fepos, *memaddr, *bios, v;
volatile struct global_data *gd;
- struct board_info *bd;
volatile struct board_chan *bc;
+ struct board_info *bd;
struct channel *ch;
- printk("%s\n", banner);
+ printk(KERN_NOTICE "Digiboard PC/X{i,e,eve} driver v%s\n", VERSION);
+
+#ifdef MODULE
+ for (i = 0; i < 4; i++) {
+ if (io[i]) {
+ numcards = 0;
+ break;
+ }
+ }
+ if (numcards == 0) {
+ int first_minor = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (io[i] == 0) {
+ boards[i].port = 0;
+ boards[i].status = DISABLED;
+ }
+ else {
+ boards[i].port = (ushort)io[i];
+ boards[i].status = ENABLED;
+ boards[i].first_minor = first_minor;
+ numcards=i+1;
+ }
+ if (membase[i])
+ boards[i].membase = (ulong)membase[i];
+ else
+ boards[i].membase = 0xD0000;
+
+ if (memsize[i])
+ boards[i].memsize = (ulong)(memsize[i] * 1024);
+ else
+ boards[i].memsize = 0;
+
+ if (altpin[i])
+ boards[i].altpin = ON;
+ else
+ boards[i].altpin = OFF;
+
+ if (numports[i])
+ boards[i].numports = (ushort)numports[i];
+ else
+ boards[i].numports = 16;
+
+ first_minor += boards[i].numports;
+ }
+ }
+#endif
if (numcards <= 0)
{
- printk("PC/Xx: No cards configured, exiting.\n");
- return(0);
+ printk("PC/Xx: No cards configured, driver not active.\n");
+ return -EIO;
}
+#if 1
+ if (debug)
+ for (i = 0; i < numcards; i++) {
+ printk("Card %d:status=%d, port=0x%x, membase=0x%lx, memsize=0x%lx, altpin=%d, numports=%d, first_minor=%d\n",
+ i+1,
+ boards[i].status,
+ boards[i].port,
+ boards[i].membase,
+ boards[i].memsize,
+ boards[i].altpin,
+ boards[i].numports,
+ boards[i].first_minor);
+ }
+#endif
for (i=0;i<numcards;i++)
nbdevs += boards[i].numports;
if (nbdevs <= 0)
{
- printk("PC/Xx: No devices activated, exiting.\n");
- return(0);
+ printk("PC/Xx: No devices activated, driver not active.\n");
+ return -EIO;
}
/*
pcxe_callout.subtype = SERIAL_TYPE_CALLOUT;
pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- save_flags(flags);
- cli();
-
for(crd=0; crd < numcards; crd++) {
bd = &boards[crd];
outb(FEPRST, bd->port);
pcxxdelay(1);
for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) {
- if(i > 1000) {
+ if(i > 100) {
printk("PC/Xx: Board not found at port 0x%x! Check switch settings.\n",
bd->port);
bd->status = DISABLED;
break;
}
- pcxxdelay(1);
+#ifdef MODULE
+ schedule();
+#endif
+ pcxxdelay(10);
}
if(bd->status == DISABLED)
continue;
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)
bd->status = DISABLED;
continue;
}
+ if (verbose)
+ printk(" done.\n");
for(i=0; i < 16; i++) {
memaddr[MISCGLOBAL+i] = 0;
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;
}
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;
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;
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;
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");
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);
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))
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;
}
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];
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)) {
if(n <= ncmds * (sizeof(short)*4))
break;
+ /* Seems not to be good here: schedule(); */
}
}
ushort numports;
ushort port;
ulong membase;
+ ulong memsize;
ushort first_minor;
};
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;
if (!bp || !c)
return 0;
- do_unblank_screen();
+ poke_blanked_console();
add_wait_queue(&vt->paste_wait, &wait);
do {
current->state = TASK_INTERRUPTIBLE;
*
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Move console and virtual terminal code to more apropriate files,
+ * implement CONFIG_VT and generalize console device interface.
+ * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
*/
#include <linux/config.h>
* Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
* Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
* Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
+ * Some code moved for less code duplication - Andi Kleen - Mar 1997
*/
#include <linux/types.h>
}
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.
perm = 0;
if (current->tty == tty || suser())
perm = 1;
-
+
kbd = kbd_table + console;
switch (cmd) {
case KIOCSOUND:
/*
* 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__
/*
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:
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 */
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:
{
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;
}
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)
/* 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)
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;
}
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
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,
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);
}
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;
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;
/* 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:
{
#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;
{ 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;
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;
} 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);
}
}
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
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
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
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
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 *);
#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
# 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
routines to the device structures. */
--- /dev/null
+/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
+/*
+ NOTICE: this version tested with kernels 1.3.72 and later only!
+ Written 1996 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the Intel EtherExpress Pro 100B boards.
+ It should work with other i82557 boards (if any others exist).
+ To use a built-in driver, install as drivers/net/eepro100.c.
+ To use as a module, use the compile-command at the end of the file.
+
+ The author may be reached as becker@CESDIS.usra.edu, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
+ For updates see
+ <base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
+*/
+
+static const char *version =
+"eepro100.c:v0.31 3/29/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+
+/* A few user-configurable values that apply to all boards.
+ First set are undocumented and spelled per Intel recommendations. */
+
+static int congenb = 0; /* Enable congestion control in the DP83840. */
+static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
+
+/* If defined use the copy-only-tiny-buffer scheme for higher performance.
+ The value sets the copy breakpoint. Lower uses more memory, but is
+ faster. */
+#define SKBUFF_RX_COPYBREAK 256
+
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+ LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP /* Warning: Bogus! This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#endif
+
+#endif
+/* This should be in a header file. */
+#if (LINUX_VERSION_CODE < VERSION(1,3,44))
+struct device *init_etherdev(struct device *dev, int sizeof_priv,
+ unsigned long *mem_startp);
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x) /* What to put in timer->expires. */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr) ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+/* The total I/O port extent of the board. Nominally 0x18, but rounded up
+ for PCI allocation. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry eepro100_drv =
+{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef SPEEDO3_DEBUG
+int speedo_debug = SPEEDO3_DEBUG;
+#else
+int speedo_debug = 3;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
+single-chip fast ethernet controller for PCI, as used on the Intel
+EtherExpress Pro 100 adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+possible to share PCI interrupt lines, it negatively impacts performance and
+only recent kernels support it.
+
+III. Driver operation
+
+IIIA. General
+The Speedo3 is very similar to other Intel network chips, that is to say
+"apparently designed on a different planet". This chips retains the complex
+Rx and Tx descriptors and multiple buffers pointers as previous chips, but
+also has simplified Tx and Rx buffer modes. This driver uses the "flexible"
+Tx mode, but in a simplified lower-overhead manner: it associates only a
+single buffer descriptor with each frame descriptor.
+
+Despite the extra space overhead in each recieve skbuff, the driver must use
+the simplified Rx buffer mode to assure that only a single data buffer is
+associated with each RxFD. The driver implements this by reserving space
+for the Rx descriptor at the head of each Rx skbuff
+
+The Speedo-3 has receive and command unit base addresses that are added to
+almost all descriptor pointers. The driver sets these to zero, so that all
+pointer fields are absolute addresses.
+
+The System Control Block (SCB) of some previous Intel chips exists on the
+chip in both PCI I/O and memory space. This driver uses the I/O space
+registers, but might switch to memory mapped mode to better support non-x86
+processors.
+
+IIIB. Transmit structure
+
+The driver must use the complex Tx command+descriptor mode in order to
+have a indirect pointer to the skbuff data section. Each Tx command block
+(TxCB) is associated with a single, immediately appended Tx buffer descriptor
+(TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the
+speedo_private data structure for each adapter instance.
+
+This ring structure is used for all normal transmit packets, but the
+transmit packet descriptors aren't long enough for most non-Tx commands such
+as CmdConfigure. This is complicated by the possibility that the chip has
+already loaded the link address in the previous descriptor. So for these
+commands we convert the next free descriptor on the ring to a NoOp, and point
+that descriptor's link to the complex command.
+
+An additional complexity of these non-transmit commands are that they may be
+added asynchronous to the normal transmit queue, so we disable interrupts
+whenever the Tx descriptor ring is manipulated.
+
+A notable aspect of the these special configure commands is that they do
+work with the normal Tx ring entry scavenge method. The Tx ring scavenge
+is done at interrupt time using the 'dirty_tx' index, and checking for the
+command-complete bit. While the setup frames may have the NoOp command on the
+Tx ring marked as complete, but not have completed the setup command, this
+is not a problem. The tx_ring entry can be still safely reused, as the
+tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
+
+Commands may have bits set e.g. CmdSuspend in the command word to either
+suspend or stop the transmit/command unit. This driver always flags the last
+command with CmdSuspend, erases the CmdSuspend in the previous command, and
+then issues a CU_RESUME.
+Note: Watch out for the potential race condition here: imagine
+ erasing the previous suspend
+ the chip processes the previous command
+ the chip processes the final command, and suspends
+ doing the CU_RESUME
+ the chip processes the next-yet-valid post-final-command.
+So blindly sending a CU_RESUME is only safe if we do it immediately after
+after erasing the previous CmdSuspend, without the possibility of an
+intervening delay. Thus the resume command is always within the
+interrupts-disabled region. This is a timing dependence, but handling this
+condition in a timing-independent way would considerably complicate the code.
+
+Note: In previous generation Intel chips, restarting the command unit was a
+notoriously slow process. This is presumably no longer true.
+
+IIIC. Receive structure
+
+Because of the bus-master support on the Speedo3 this driver uses the new
+SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
+This scheme allocates full-sized skbuffs as receive buffers. The value
+SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
+trade-off the memory wasted by passing the full-sized skbuff to the queue
+layer for all frames vs. the copying cost of copying a frame to a
+correctly-sized skbuff.
+
+For small frames the copying cost is negligible (esp. considering that we
+are pre-loading the cache with immediately useful header information), so we
+allocate a new, minimally-sized skbuff. For large frames the copying cost
+is non-trivial, and the larger copy might flush the cache of useful data, so
+we pass up the skbuff the packet was received into.
+
+IIID. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'sp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
+that stated that I could disclose the information. But I still resent
+having to sign an Intel NDA when I'm helping Intel sell their own product!
+
+*/
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE 16 /* Effectively 2 entries fewer. */
+#define RX_RING_SIZE 16
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ 1536
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK 16
+
+/* How to wait for the command unit to accept a command.
+ Typically this takes 0 ticks. */
+static inline void wait_for_cmd_done(int cmd_ioaddr)
+{
+ short wait = 100;
+ do ;
+ while(inb(cmd_ioaddr) && --wait >= 0);
+}
+
+/* Operational parameter that usually are not changed. */
+
+#ifndef PCI_VENDOR_ID_INTEL /* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_INTEL 0x8086 /* Hmmmm, how did they pick that? */
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82557
+#define PCI_DEVICE_ID_INTEL_82557 0x1229
+#endif
+
+/* The rest of these values should never change. */
+
+/* Offsets to the various registers.
+ All accesses need not be longword aligned. */
+enum speedo_offsets {
+ SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
+ SCBPointer = 4, /* General purpose pointer. */
+ SCBPort = 8, /* Misc. commands and operands. */
+ SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+ SCBCtrlMDI = 16, /* MDI interface control. */
+ SCBEarlyRx = 20, /* Early receive byte count. */
+};
+/* Commands that can be put in a command list entry. */
+enum commands {
+ CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
+ CmdSuspend = 0x4000, /* Suspend after completion. */
+ CmdIntr = 0x2000, /* Interrupt after completion. */
+ CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
+};
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define CU_START 0x0010
+#define CU_RESUME 0x0020
+#define CU_STATSADDR 0x0040
+#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
+#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */
+#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
+
+#define RX_START 0x0001
+#define RX_RESUME 0x0002
+#define RX_ABORT 0x0004
+#define RX_ADDR_LOAD 0x0006
+#define RX_RESUMENR 0x0007
+#define INT_MASK 0x0100
+#define DRVR_INT 0x0200 /* Driver generated interrupt. */
+
+/* The Speedo3 Rx and Tx frame/buffer descriptors. */
+struct descriptor { /* A generic descriptor. */
+ s16 status; /* Offset 0. */
+ s16 command; /* Offset 2. */
+ u32 link; /* struct descriptor * */
+ unsigned char params[0];
+};
+
+/* The Speedo3 Rx and Tx buffer descriptors. */
+struct RxFD { /* Receive frame descriptor. */
+ s32 status;
+ u32 link; /* struct RxFD * */
+ u32 rx_buf_addr; /* void * */
+ u16 count;
+ u16 size;
+};
+
+/* Elements of the RxFD.status word. */
+#define RX_COMPLETE 0x8000
+
+struct TxFD { /* Transmit frame descriptor set. */
+ s32 status;
+ u32 link; /* void * */
+ u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
+ s32 count; /* # of TBD (=1), Tx start thresh., etc. */
+ /* This constitutes a single "TBD" entry -- we only use one. */
+ u32 tx_buf_addr; /* void *, frame to be transmitted. */
+ s32 tx_buf_size; /* Length of Tx frame. */
+};
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+struct speedo_stats {
+ u32 tx_good_frames;
+ u32 tx_coll16_errs;
+ u32 tx_late_colls;
+ u32 tx_underruns;
+ u32 tx_lost_carrier;
+ u32 tx_deferred;
+ u32 tx_one_colls;
+ u32 tx_multi_colls;
+ u32 tx_total_colls;
+ u32 rx_good_frames;
+ u32 rx_crc_errs;
+ u32 rx_align_errs;
+ u32 rx_resource_errs;
+ u32 rx_overrun_errs;
+ u32 rx_colls_errs;
+ u32 rx_runt_errs;
+ u32 done_marker;
+};
+
+struct speedo_private {
+ char devname[8]; /* Used only for kernel debugging. */
+ const char *product_name;
+ struct device *next_module;
+ struct TxFD tx_ring[TX_RING_SIZE]; /* Commands (usually CmdTxPacket). */
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct descriptor *last_cmd; /* Last command sent. */
+ /* Rx descriptor ring & addresses of receive-in-place skbuffs. */
+ struct RxFD *rx_ringp[RX_RING_SIZE];
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+#if (LINUX_VERSION_CODE < 0x10300) /* Kernel v1.2.*. */
+ struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */
+#endif
+ struct RxFD *last_rxf; /* Last command sent. */
+ struct enet_statistics stats;
+ struct speedo_stats lstats;
+ struct timer_list timer; /* Media selection timer. */
+ long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct descriptor config_cmd; /* A configure command, with header... */
+ u8 config_cmd_data[22]; /* .. and setup parameters. */
+ int mc_setup_frm_len; /* The length of an allocated.. */
+ struct descriptor *mc_setup_frm; /* ..multicast setup frame. */
+ char rx_mode; /* Current PROMISC/ALLMULTI setting. */
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int default_port:1; /* Last dev->if_port value. */
+ unsigned int rx_bug:1; /* Work around receiver hang errata. */
+ unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */
+ unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */
+ unsigned short phy[2]; /* PHY media interfaces available. */
+};
+
+/* The parameters for a CmdConfigure operation.
+ There are so many options that it would be difficult to document each bit.
+ We mostly use the default or recommended settings. */
+const char basic_config_cmd[22] = {
+ 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
+ 0, 0x2E, 0, 0x60, 0,
+ 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
+ 0x3f, 0x05, };
+
+/* PHY media interface chips. */
+static const char *phys[] = { "None", "i82553-A/B", "i82553-C", "i82503",
+ "DP83840", "80c240", "80c24", "unknown" };
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+ S80C24, PhyUndefined, };
+static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options);
+
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static int mdio_write(int ioaddr, int phy_id, int location, int value);
+static int speedo_open(struct device *dev);
+static void speedo_timer(unsigned long data);
+static void speedo_init_rx_ring(struct device *dev);
+static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
+static int speedo_rx(struct device *dev);
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs);
+#endif
+static int speedo_close(struct device *dev);
+static struct enet_statistics *speedo_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+
+\f
+
+#ifdef MODULE
+/* The parameters that may be passed in... */
+/* 'options' is used to pass a transceiver override or full-duplex flag
+ e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1; /* The debug level */
+
+/* A list of all installed Speedo devices, for removing the driver module. */
+static struct device *root_speedo_dev = NULL;
+#endif
+
+int eepro100_init(struct device *dev)
+{
+ int cards_found = 0;
+
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+ int pci_ioaddr;
+#else
+ long pci_ioaddr;
+#endif
+ unsigned short pci_command;
+
+ if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82557,
+ pci_index, &pci_bus,
+ &pci_device_fn))
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_1, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ if (speedo_debug > 2)
+ printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
+ (int)pci_ioaddr, pci_irq_line);
+
+ /* Get and check the bus-master and latency values. */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk(" PCI Master Bit has not been set! Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 10) {
+ printk(" PCI latency timer (CFLT) is unreasonably low at %d."
+ " Setting to 255 clocks.\n", pci_latency);
+ pcibios_write_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, 255);
+ } else if (speedo_debug > 1)
+ printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+#ifdef MODULE
+ speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+#else
+ speedo_found1(dev, pci_ioaddr, pci_irq_line,
+ dev ? dev->mem_start : 0);
+#endif
+ cards_found++;
+ }
+ }
+
+ return cards_found;
+}
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
+{
+ static int did_version = 0; /* Already printed version info. */
+ struct speedo_private *sp;
+ int i;
+ u16 eeprom[0x40];
+
+ if (speedo_debug > 0 && did_version++ == 0)
+ printk(version);
+
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+ dev = init_etherdev(dev, sizeof(struct speedo_private));
+#else
+ dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
+#endif
+
+ /* Read the station address EEPROM before doing the reset.
+ Perhaps this should even be done before accepting the device,
+ then we wouldn't have a device name with which to report the error. */
+ {
+ u16 sum = 0;
+ int j;
+ for (j = 0, i = 0; i < 0x40; i++) {
+ unsigned short value = read_eeprom(ioaddr, i);
+ eeprom[i] = value;
+ sum += value;
+ if (i < 3) {
+ dev->dev_addr[j++] = value;
+ dev->dev_addr[j++] = value >> 8;
+ }
+ }
+ if (sum != 0xBABA)
+ printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
+ "check settings before activating this device!\n",
+ dev->name, sum);
+ /* Don't unregister_netdev(dev); as the EEPro may actually be
+ usable, especially if the MAC address is set later. */
+ }
+
+ /* Reset the chip: stop Tx and Rx processes and clear counters.
+ This takes less than 10usec and will easily finish before the next
+ action. */
+ outl(0, ioaddr + SCBPort);
+
+ printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
+ dev->name, ioaddr);
+ for (i = 0; i < 5; i++)
+ printk("%2.2X:", dev->dev_addr[i]);
+ printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifndef kernel_bloat
+ /* OK, this is pure kernel bloat. I don't like it when other drivers
+ waste non-pageable kernel space to emit similar messages, but I need
+ them for bug reports. */
+ {
+ const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
+ /* The self-test results must be paragraph aligned. */
+ int str[6], *volatile self_test_results;
+ int boguscnt = 16000; /* Timeout for set-test. */
+ if (eeprom[3] & 0x03)
+ printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
+ " work-around.\n");
+ printk(KERN_INFO " Board assembly %4.4x%2.2x-%3.3d, Physical"
+ " connectors present:",
+ eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
+ for (i = 0; i < 4; i++)
+ if (eeprom[5] & (1<<i))
+ printk(connectors[i]);
+ printk("\n"KERN_INFO" Primary interface chip %s PHY #%d.\n",
+ phys[(eeprom[6]>>8)&7], eeprom[6] & 0x1f);
+ if (eeprom[7] & 0x0700)
+ printk(KERN_INFO " Secondary interface chip %s.\n",
+ phys[(eeprom[7]>>8)&7]);
+#if defined(notdef)
+ /* ToDo: Read and set PHY registers through MDIO port. */
+ for (i = 0; i < 2; i++)
+ printk(" MDIO register %d is %4.4x.\n",
+ i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+ for (i = 5; i < 7; i++)
+ printk(" MDIO register %d is %4.4x.\n",
+ i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+ printk(" MDIO register %d is %4.4x.\n",
+ 25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
+#endif
+ if (((eeprom[6]>>8) & 0x3f) == DP83840) {
+ int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
+ if (congenb)
+ mdi_reg23 |= 0x0100;
+ printk(" DP83840 specific setup, setting register 23 to %4.4x.\n",
+ mdi_reg23);
+ mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
+ }
+ if ((options >= 0) && (options & 0x60)) {
+ printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n",
+ (options & 0x20 ? 100 : 10),
+ (options & 0x10 ? "full" : "half"));
+ mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
+ ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */
+ ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ }
+
+ /* Perform a system self-test. */
+ self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+ self_test_results[0] = 0;
+ self_test_results[1] = -1;
+ outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+ do {
+#ifdef _LINUX_DELAY_H
+ udelay(10);
+#else
+ SLOW_DOWN_IO;
+#endif
+ } while (self_test_results[1] == -1 && --boguscnt >= 0);
+
+ if (boguscnt < 0) { /* Test optimized out. */
+ printk(KERN_ERR "Self test failed, status %8.8x:\n"
+ KERN_ERR " Failure to initialize the i82557.\n"
+ KERN_ERR " Verify that the card is a bus-master"
+ " capable slot.\n",
+ self_test_results[1]);
+ } else
+ printk(KERN_INFO " General self-test: %s.\n"
+ KERN_INFO " Serial sub-system self-test: %s.\n"
+ KERN_INFO " Internal registers self-test: %s.\n"
+ KERN_INFO " ROM checksum self-test: %s (%#8.8x).\n",
+ self_test_results[1] & 0x1000 ? "failed" : "passed",
+ self_test_results[1] & 0x0020 ? "failed" : "passed",
+ self_test_results[1] & 0x0008 ? "failed" : "passed",
+ self_test_results[1] & 0x0004 ? "failed" : "passed",
+ self_test_results[0]);
+ }
+#endif /* kernel_bloat */
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ if (dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
+ sp = dev->priv;
+ memset(sp, 0, sizeof(*sp));
+#ifdef MODULE
+ sp->next_module = root_speedo_dev;
+ root_speedo_dev = dev;
+#endif
+
+ sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+ sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+
+ sp->phy[0] = eeprom[6];
+ sp->phy[1] = eeprom[7];
+ sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+
+ printk(KERN_INFO " Operating in %s duplex mode.\n",
+ sp->full_duplex ? "full" : "half");
+ if (sp->rx_bug)
+ printk(KERN_INFO " Reciever lock-up workaround activated.\n");
+
+ /* The Speedo-specific entries in the device structure. */
+ dev->open = &speedo_open;
+ dev->hard_start_xmit = &speedo_start_xmit;
+ dev->stop = &speedo_close;
+ dev->get_stats = &speedo_get_stats;
+#ifdef NEW_MULTICAST
+ dev->set_multicast_list = &set_rx_mode;
+#endif
+
+ return;
+}
+\f
+/* Serial EEPROM section.
+ A "bit" grungy, but we work our way through bit-by-bit :->. */
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ This is a "nasty" timing loop, but PC compatible machines are defined
+ to delay an ISA compatible period for the SLOW_DOWN_IO macro. */
+#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+static int read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ int ee_addr = ioaddr + SCBeeprom;
+ int read_cmd = location | EE_READ_CMD;
+
+ outw(EE_ENB & ~EE_CS, ee_addr);
+ outw(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outw(EE_ENB | dataval, ee_addr);
+ eeprom_delay(100);
+ outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay(150);
+ outw(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay(250);
+ }
+ outw(EE_ENB, ee_addr);
+
+ for (i = 15; i >= 0; i--) {
+ outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay(100);
+ retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outw(EE_ENB, ee_addr);
+ eeprom_delay(100);
+ }
+
+ /* Terminate the EEPROM access. */
+ outw(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+ do {
+#ifdef _LINUX_DELAY_H
+ udelay(16);
+#else
+ SLOW_DOWN_IO;
+#endif
+ val = inl(ioaddr + SCBCtrlMDI);
+ if (--boguscnt < 0) {
+ printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+static int mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+ ioaddr + SCBCtrlMDI);
+ do {
+#ifdef _LINUX_DELAY_H
+ udelay(16);
+#else
+ SLOW_DOWN_IO;
+#endif
+ val = inl(ioaddr + SCBCtrlMDI);
+ if (--boguscnt < 0) {
+ printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+\f
+static int
+speedo_open(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+#ifdef notdef
+ /* We could reset the chip, but should not need to. */
+ outl(0, ioaddr + SCBPort);
+ for (i = 40; i >= 0; i--)
+ SLOW_DOWN_IO; /* At least 250ns */
+#endif
+
+#ifdef SA_SHIRQ
+ if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
+ "Intel EtherExpress Pro 10/100 Ethernet", dev)) {
+ return -EAGAIN;
+ }
+#else
+#ifdef USE_SHARED_IRQ
+ if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
+ "Intel EtherExpress Pro 10/100 Ethernet"))
+ return -EAGAIN;
+#else
+ if (dev->irq < 2 || dev->irq > 15 || irq2dev_map[dev->irq] != NULL)
+ return -EAGAIN;
+ irq2dev_map[dev->irq] = dev;
+ if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+#endif
+#endif
+
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+
+ MOD_INC_USE_COUNT;
+
+ /* Load the statistics block address. */
+ outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+ outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
+ synchronize_irq();
+ sp->lstats.done_marker = 0;
+
+ speedo_init_rx_ring(dev);
+ outl(0, ioaddr + SCBPointer);
+ outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+ synchronize_irq();
+
+ /* Todo: verify that we must wait for previous command completion. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
+ outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+ synchronize_irq();
+
+ /* Fill the first command with our physical address. */
+ {
+ unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+ unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
+
+ /* Avoid a bug(?!) here by marking the command already completed. */
+ sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
+ sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ }
+ sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
+ sp->cur_tx = 1;
+ sp->dirty_tx = 0;
+ sp->tx_full = 0;
+
+ outl(0, ioaddr + SCBPointer);
+ outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+ synchronize_irq();
+
+ dev->if_port = sp->default_port;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* Start the chip's Tx process and unmask interrupts. */
+ /* Todo: verify that we must wait for previous command completion. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
+ outw(CU_START, ioaddr + SCBCmd);
+
+ /* Setup the chip and configure the multicast list. */
+ sp->mc_setup_frm = NULL;
+ sp->mc_setup_frm_len = 0;
+ sp->rx_mode = -1; /* Invalid -> always reset the mode. */
+ set_rx_mode(dev);
+
+ if (speedo_debug > 2) {
+ printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
+ dev->name, inw(ioaddr + SCBStatus));
+ }
+ /* Set the timer. The timer serves a dual purpose:
+ 1) to monitor the media interface (e.g. link beat) and perhaps switch
+ to an alternate media type
+ 2) to monitor Rx activity, and restart the Rx process if the receiver
+ hangs. */
+ init_timer(&sp->timer);
+ sp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
+ sp->timer.data = (unsigned long)dev;
+ sp->timer.function = &speedo_timer; /* timer handler */
+ add_timer(&sp->timer);
+
+ outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+ return 0;
+}
+
+/* Media monitoring and control. */
+static void speedo_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int tickssofar = jiffies - sp->last_rx_time;
+
+ if (speedo_debug > 3) {
+ int ioaddr = dev->base_addr;
+ printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
+ dev->name, inw(ioaddr + SCBStatus));
+ }
+ if (sp->rx_bug) {
+ if (tickssofar > 2*HZ || sp->rx_mode < 0) {
+ /* We haven't received a packet in a Long Time. We might have been
+ bitten by the receiver hang bug. This can be cleared by sending
+ a set multicast list command. */
+ set_rx_mode(dev);
+ }
+ /* We must continue to monitor the media. */
+ sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */
+ add_timer(&sp->timer);
+ }
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+speedo_init_rx_ring(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ struct RxFD *rxf, *last_rxf = NULL;
+ int i;
+
+ sp->cur_rx = 0;
+ sp->dirty_rx = RX_RING_SIZE - 1;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb;
+#ifndef KERNEL_1_2
+ skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#else
+ skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#endif
+ sp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break; /* Bad news! */
+ skb->dev = dev; /* Mark as being used by this device. */
+
+#if LINUX_VERSION_CODE >= 0x10300
+ rxf = (struct RxFD *)skb->tail;
+ skb_reserve(skb, sizeof(struct RxFD));
+#else
+ /* Save the data in the header region -- it's restored later. */
+ rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
+ memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
+#endif
+ sp->rx_ringp[i] = rxf;
+ if (last_rxf)
+ last_rxf->link = virt_to_bus(rxf);
+ last_rxf = rxf;
+ rxf->status = 0x00000001; /* '1' is flag value only. */
+ rxf->link = 0; /* None yet. */
+#if LINUX_VERSION_CODE < 0x10300
+ /* This field unused by i82557, we use it as a consistency check. */
+ rxf->rx_buf_addr = virt_to_bus(skb->data);
+#else
+ rxf->rx_buf_addr = virt_to_bus(skb->tail);
+#endif
+ rxf->count = 0;
+ rxf->size = PKT_BUF_SZ;
+ }
+ /* Mark the last entry as end-of-list. */
+ last_rxf->status = 0xC0000002; /* '2' is flag value only. */
+ sp->last_rxf = last_rxf;
+}
+
+static void speedo_tx_timeout(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
+ "command %4.4x.\n",
+ dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+#ifndef final_version
+ printk("%s: Tx timeout fill index %d scavenge index %d.\n",
+ dev->name, sp->cur_tx, sp->dirty_tx);
+ printk(" Tx queue ");
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %8.8x", (int)sp->tx_ring[i].status);
+ printk(".\n Rx ring ");
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (int)sp->rx_ringp[i]->status);
+ printk(".\n");
+
+#else
+ dev->if_port ^= 1;
+ printk(" (Media type switching not yet implemented.)\n");
+ /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
+#endif
+ if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
+ printk("%s: Trying to restart the transmitter...\n", dev->name);
+ outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
+ ioaddr + SCBPointer);
+ outw(CU_START, ioaddr + SCBCmd);
+ } else {
+ outw(DRVR_INT, ioaddr + SCBCmd);
+ }
+ sp->stats.tx_errors++;
+ dev->trans_start = jiffies;
+ return;
+}
+
+static int
+speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+
+ if (skb == NULL || skb->len <= 0) {
+ printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT - 2)
+ return 1;
+ if (tickssofar < TX_TIMEOUT) {
+ /* Reap sent packets from the full Tx queue. */
+ outw(DRVR_INT, ioaddr + SCBCmd);
+ return 1;
+ }
+ speedo_tx_timeout(dev);
+ return 0;
+ }
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ { /* Prevent interrupts from changing the Tx ring from underneath us. */
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Calculate the Tx descriptor entry. */
+ entry = sp->cur_tx++ % TX_RING_SIZE;
+
+ sp->tx_skbuff[entry] = skb;
+ /* Todo: be a little more clever about setting the interrupt bit. */
+ sp->tx_ring[entry].status =
+ (CmdSuspend | CmdTx | CmdTxFlex) << 16;
+ sp->tx_ring[entry].link =
+ virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+ sp->tx_ring[entry].tx_desc_addr =
+ virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
+ /* The data region is always in one buffer descriptor, Tx FIFO
+ threshold of 256. */
+ sp->tx_ring[entry].count = 0x01208000;
+ sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
+ sp->tx_ring[entry].tx_buf_size = skb->len;
+ /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
+ than half full. Argument against: we should be receiving packets
+ and scavenging the queue. Argument for: if so, it shouldn't
+ matter. */
+ sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
+ sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+ /* Trigger the command unit resume. */
+ outw(CU_RESUME, ioaddr + SCBCmd);
+ restore_flags(flags);
+ }
+
+ /* Leave room for set_rx_mode() to fill two entries. */
+ if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
+ sp->tx_full = 1;
+ else
+ dev->tbusy = 0;
+
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+#ifdef SA_SHIRQ
+ struct device *dev = (struct device *)dev_instance;
+#else
+#ifdef USE_SHARED_IRQ
+ struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+#endif
+ struct speedo_private *sp;
+ int ioaddr, boguscnt = INTR_WORK;
+ unsigned short status;
+
+#ifndef final_version
+ if (dev == NULL) {
+ printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+#endif
+
+ ioaddr = dev->base_addr;
+ sp = (struct speedo_private *)dev->priv;
+#ifndef final_version
+ if (dev->interrupt) {
+ printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+#endif
+
+ do {
+ status = inw(ioaddr + SCBStatus);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(status & 0xfc00, ioaddr + SCBStatus);
+
+ if (speedo_debug > 4)
+ printk(KERN_DEBUG "%s: interrupt status=%#4.4x.\n",
+ dev->name, status);
+
+ if ((status & 0xfc00) == 0)
+ break;
+
+ if (status & 0x4000) /* Packet received. */
+ speedo_rx(dev);
+
+ if (status & 0x1000) {
+#ifdef notdef
+ int i;
+ printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
+ " state -- %4.4x! Index %d (%d).\n", dev->name, status,
+ sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
+ printk(" Rx ring:\n ");
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %d %8.8x %8.8x %8.8x %d %d.\n",
+ i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
+ sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
+ sp->rx_ringp[i]->size);
+#endif
+
+ if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
+ outw(RX_RESUMENR, ioaddr + SCBCmd);
+ else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
+ /* No idea of what went wrong. Restart the receiver. */
+ outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+ ioaddr + SCBPointer);
+ outw(RX_START, ioaddr + SCBCmd);
+ }
+ sp->stats.rx_errors++;
+ }
+
+ /* User interrupt, Command/Tx unit interrupt or CU not active. */
+ if (status & 0xA400) {
+ unsigned int dirty_tx = sp->dirty_tx;
+
+ while (sp->cur_tx - dirty_tx > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = sp->tx_ring[entry].status;
+
+ if (speedo_debug > 5)
+ printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n",
+ entry, status);
+ if ((status & 0x8000) == 0)
+ break; /* It still hasn't been processed. */
+ /* Free the original skb. */
+ if (sp->tx_skbuff[entry]) {
+ sp->stats.tx_packets++; /* Count only user packets. */
+ dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
+ sp->tx_skbuff[entry] = 0;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, sp->cur_tx, sp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (sp->tx_full && dev->tbusy
+ && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ sp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ sp->dirty_tx = dirty_tx;
+ }
+
+ if (--boguscnt < 0) {
+ printk("%s: Too much work at interrupt, status=0x%4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupt sources. */
+ outl(0xfc00, ioaddr + SCBStatus);
+ break;
+ }
+ } while (1);
+
+ if (speedo_debug > 3)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ dev->name, inw(ioaddr + SCBStatus));
+
+#ifndef final_version
+ /* Special code for testing *only*. */
+ {
+ static int stopit = 100;
+ if (dev->start == 0 && --stopit < 0) {
+ printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
+ dev->name);
+#ifdef SA_SHIRQ
+ free_irq(irq, dev);
+#else
+ free_irq(irq);
+#endif
+ }
+ }
+#endif
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+speedo_rx(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int entry = sp->cur_rx % RX_RING_SIZE;
+ int status;
+
+ if (speedo_debug > 4)
+ printk(KERN_DEBUG " In speedo_rx().\n");
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
+
+ if (speedo_debug > 4)
+ printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status,
+ sp->rx_ringp[entry]->count & 0x3fff);
+ if (status & 0x0200) {
+ printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n",
+ dev->name, status);
+ } else if ( ! (status & 0x2000)) {
+ /* There was a fatal error. This *should* be impossible. */
+ sp->stats.rx_errors++;
+ printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+ dev->name, status);
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+ struct sk_buff *skb;
+ int rx_in_place = 0;
+
+ /* Check if the packet is long enough to just accept without
+ copying to a properly sized skbuff. */
+ if (pkt_len > SKBUFF_RX_COPYBREAK) {
+ struct sk_buff *newskb;
+ char *temp;
+
+ /* Pass up the skb already on the Rx ring. */
+ skb = sp->rx_skbuff[entry];
+#ifdef KERNEL_1_2
+ temp = skb->data;
+ if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+ printk("%s: Warning -- the skbuff addresses do not match"
+ " in speedo_rx: %p vs. %p / %p.\n", dev->name,
+ bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+ temp, skb->data);
+ /* Get a fresh skbuff to replace the filled one. */
+ newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#else
+ temp = skb_put(skb, pkt_len);
+ if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+ printk("%s: Warning -- the skbuff addresses do not match"
+ " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
+ sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
+ /* Get a fresh skbuff to replace the filled one. */
+ newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#endif
+ if (newskb) {
+ struct RxFD *rxf;
+ rx_in_place = 1;
+ sp->rx_skbuff[entry] = newskb;
+ newskb->dev = dev;
+#ifdef KERNEL_1_2
+ /* Restore the data in the old header region. */
+ memcpy(skb->data - sizeof(struct RxFD),
+ &sp->saved_skhead[entry], sizeof(struct RxFD));
+ /* Save the data in this header region. */
+ rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
+ sp->rx_ringp[entry] = rxf;
+ memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
+ rxf->rx_buf_addr = virt_to_bus(newskb->data);
+#else
+ rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
+ skb_reserve(newskb, sizeof(struct RxFD));
+ /* Unused by i82557, consistency check only. */
+ rxf->rx_buf_addr = virt_to_bus(newskb->tail);
+#endif
+ rxf->status = 0x00000001;
+ } else /* No memory, drop the packet. */
+ skb = 0;
+ } else
+#ifdef KERNEL_1_2
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+ skb = dev_alloc_skb(pkt_len + 2);
+#endif
+ if (skb == NULL) {
+ int i;
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ /* Check that at least two ring entries are free.
+ If not, free one and mark stats->rx_dropped++. */
+ /* ToDo: This is not correct!!!! We should count the number
+ of linked-in Rx buffer to very that we have at least two
+ remaining. */
+ for (i = 0; i < RX_RING_SIZE; i++)
+ if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
+ & RX_COMPLETE))
+ break;
+
+ if (i > RX_RING_SIZE -2) {
+ sp->stats.rx_dropped++;
+ sp->rx_ringp[entry]->status = 0;
+ sp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+ if (! rx_in_place) {
+ skb_reserve(skb, 2); /* 16 byte align the data fields */
+ memcpy(skb_put(skb, pkt_len),
+ bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+#else
+#ifdef KERNEL_1_3
+#warning This code has only been tested with later 1.3.* kernels.
+ skb->len = pkt_len;
+ memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+ pkt_len);
+ /* Needed for 1.3.*. */
+ skb->protocol = eth_type_trans(skb, dev);
+#else /* KERNEL_1_2 */
+ skb->len = pkt_len;
+ if (! rx_in_place) {
+ memcpy(skb->data,
+ bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+ }
+#endif
+#endif
+ netif_rx(skb);
+ sp->stats.rx_packets++;
+ }
+
+ /* ToDo: This is better than before, but should be checked. */
+ {
+ struct RxFD *rxf = sp->rx_ringp[entry];
+ rxf->status = 0xC0000003; /* '3' for verification only */
+ rxf->link = 0; /* None yet. */
+ rxf->count = 0;
+ rxf->size = PKT_BUF_SZ;
+ sp->last_rxf->link = virt_to_bus(rxf);
+ sp->last_rxf->status &= ~0xC0000000;
+ sp->last_rxf = rxf;
+ entry = (++sp->cur_rx) % RX_RING_SIZE;
+ }
+ }
+
+ sp->last_rx_time = jiffies;
+ return 0;
+}
+
+static int
+speedo_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int i;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, inw(ioaddr + SCBStatus));
+
+ /* Shut off the media monitoring timer. */
+ del_timer(&sp->timer);
+
+ /* Disable interrupts, and stop the chip's Rx process. */
+ outw(INT_MASK, ioaddr + SCBCmd);
+ outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+ synchronize_irq();
+
+#ifdef SA_SHIRQ
+ free_irq(dev->irq, dev);
+#else
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ /* Free all the skbuffs in the Rx and Tx queues. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = sp->rx_skbuff[i];
+ sp->rx_skbuff[i] = 0;
+ /* Clear the Rx descriptors. */
+ if (skb)
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ struct sk_buff *skb = sp->tx_skbuff[i];
+ sp->tx_skbuff[i] = 0;
+ /* Clear the Tx descriptors. */
+ if (skb)
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ if (sp->mc_setup_frm) {
+ kfree(sp->mc_setup_frm);
+ sp->mc_setup_frm_len = 0;
+ }
+
+ /* Print a few items for debugging. */
+ if (speedo_debug > 3) {
+ printk("%s:Printing Rx ring (next to receive into %d).\n",
+ dev->name, sp->cur_rx);
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" Rx ring entry %d %8.8x.\n",
+ i, (int)sp->rx_ringp[i]->status);
+
+ for (i = 0; i < 5; i++)
+ printk(" PHY index %d register %d is %4.4x.\n",
+ 1, i, mdio_read(ioaddr, 1, i));
+ for (i = 21; i < 26; i++)
+ printk(" PHY index %d register %d is %4.4x.\n",
+ 1, i, mdio_read(ioaddr, 1, i));
+ }
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/* The Speedo-3 has an especially awkward and unusable method of getting
+ statistics out of the chip. It takes an unpredictable length of time
+ for the dump-stats command to complete. To avoid a busy-wait loop we
+ update the stats with the previous dump results, and then trigger a
+ new dump.
+
+ These problems are mitigated by the current /proc implementation, which
+ calls this routine first to judge the output length, and then to emit the
+ output.
+
+ Oh, and incoming frames are dropped while executing dump-stats!
+ */
+static struct enet_statistics *
+speedo_get_stats(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */
+ sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
+ sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
+ sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
+ sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
+ /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
+ sp->stats.collisions += sp->lstats.tx_total_colls;
+ sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
+ sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
+ sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
+ sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
+ sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
+ sp->lstats.done_marker = 0x0000;
+ if (dev->start)
+ outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+ }
+ return &sp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ This is very ugly with Intel chips -- we usually have to execute an
+ entire configuration command, plus process a multicast command.
+ This is complicated. We must put a large configuration command and
+ an arbitrarily-sized multicast command in the transmit list.
+ To minimize the disruption -- the previous command might have already
+ loaded the link -- we convert the current command block, normally a Tx
+ command, into a no-op and link it to the new command.
+*/
+static void
+set_rx_mode(struct device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ char new_rx_mode;
+ unsigned long flags;
+ int entry, i;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ new_rx_mode = 3;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ new_rx_mode = 1;
+ } else
+ new_rx_mode = 0;
+
+ if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
+ /* The Tx ring is full -- don't add anything! Presumably the new mode
+ is in config_cmd_data and will be added anyway. */
+ sp->rx_mode = -1;
+ return;
+ }
+
+ if (new_rx_mode != sp->rx_mode) {
+ /* We must change the configuration. Construct a CmdConfig frame. */
+ memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
+ sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
+ sp->config_cmd_data[4] = rxdmacount;
+ sp->config_cmd_data[5] = txdmacount + 0x80;
+ sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
+ sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
+ sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
+ if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */
+ sp->config_cmd_data[15] |= 0x80;
+ sp->config_cmd_data[8] = 0;
+ }
+ save_flags(flags);
+ cli();
+ /* Fill the "real" tx_ring frame with a no-op and point it to us. */
+ entry = sp->cur_tx++ % TX_RING_SIZE;
+ sp->tx_skbuff[entry] = 0; /* Nothing to free. */
+ sp->tx_ring[entry].status = CmdNOp << 16;
+ sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
+ sp->config_cmd.status = 0;
+ sp->config_cmd.command = CmdSuspend | CmdConfigure;
+ sp->config_cmd.link =
+ virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+ sp->last_cmd->command &= ~CmdSuspend;
+ /* Immediately trigger the command unit resume. */
+ outw(CU_RESUME, ioaddr + SCBCmd);
+ sp->last_cmd = &sp->config_cmd;
+ restore_flags(flags);
+ if (speedo_debug > 5) {
+ int i;
+ printk(" CmdConfig frame in entry %d.\n", entry);
+ for(i = 0; i < 32; i++)
+ printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
+ printk(".\n");
+ }
+ }
+
+ if (new_rx_mode == 0 && dev->mc_count < 3) {
+ /* The simple case of 0-2 multicast list entries occurs often, and
+ fits within one tx_ring[] entry. */
+ u16 *setup_params;
+ unsigned short *eaddrs;
+ struct dev_mc_list *mclist;
+
+ save_flags(flags);
+ cli();
+ entry = sp->cur_tx++ % TX_RING_SIZE;
+ sp->tx_skbuff[entry] = 0;
+ sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
+ sp->tx_ring[entry].link =
+ virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+ sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
+ setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr;
+ *setup_params++ = dev->mc_count*6;
+ /* Fill in the multicast addresses. */
+ for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ eaddrs = (unsigned short *)mclist->dmi_addr;
+ *setup_params++ = *eaddrs++;
+ *setup_params++ = *eaddrs++;
+ *setup_params++ = *eaddrs++;
+ }
+
+ sp->last_cmd->command &= ~CmdSuspend;
+ /* Immediately trigger the command unit resume. */
+ outw(CU_RESUME, ioaddr + SCBCmd);
+ sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+ restore_flags(flags);
+ } else if (new_rx_mode == 0) {
+ /* This does not work correctly, but why not? */
+ struct dev_mc_list *mclist;
+ unsigned short *eaddrs;
+ struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+ u16 *setup_params = (short *)mc_setup_frm->params;
+ int i;
+
+ if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
+ || sp->mc_setup_frm == NULL) {
+ /* Allocate a new frame, 10bytes + addrs, with a few
+ extra entries for growth. */
+ if (sp->mc_setup_frm)
+ kfree(sp->mc_setup_frm);
+ sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
+ printk("%s: Allocating a setup frame of size %d.\n",
+ dev->name, sp->mc_setup_frm_len);
+ sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
+ intr_count ? GFP_ATOMIC : GFP_KERNEL);
+ if (sp->mc_setup_frm == NULL) {
+ printk("%s: Failed to allocate a setup frame.\n", dev->name);
+ sp->rx_mode = -1; /* We failed, try again. */
+ return;
+ }
+ }
+ mc_setup_frm = sp->mc_setup_frm;
+ /* Construct the new setup frame. */
+ printk("%s: Constructing a setup frame at %p, %d bytes.\n",
+ dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+ mc_setup_frm->status = 0;
+ mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
+ /* Link set below. */
+ setup_params = (short *)mc_setup_frm->params;
+ *setup_params++ = dev->mc_count*6;
+ /* Fill in the multicast addresses. */
+ for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ eaddrs = (unsigned short *)mclist->dmi_addr;
+ *setup_params++ = *eaddrs++;
+ *setup_params++ = *eaddrs++;
+ *setup_params++ = *eaddrs++;
+ }
+
+ /* Disable interrupts while playing with the Tx Cmd list. */
+ save_flags(flags);
+ cli();
+ entry = sp->cur_tx++ % TX_RING_SIZE;
+
+ if (speedo_debug > 5)
+ printk(" CmdMCSetup frame length %d in entry %d.\n",
+ dev->mc_count, entry);
+
+ /* Change the command to a NoOp, pointing to the CmdMulti command. */
+ sp->tx_skbuff[entry] = 0;
+ sp->tx_ring[entry].status = CmdNOp << 16;
+ sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
+
+ /* Set the link in the setup frame. */
+ mc_setup_frm->link =
+ virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+
+ sp->last_cmd->command &= ~CmdSuspend;
+ /* Immediately trigger the command unit resume. */
+ outw(CU_RESUME, ioaddr + SCBCmd);
+ sp->last_cmd = mc_setup_frm;
+ restore_flags(flags);
+ printk("%s: Last command at %p is %4.4x.\n",
+ dev->name, sp->last_cmd, sp->last_cmd->command);
+ }
+
+ sp->rx_mode = new_rx_mode;
+}
+\f
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,38)) /* 1.3.38 and later */
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+int
+init_module(void)
+{
+ int cards_found;
+
+ if (debug >= 0)
+ speedo_debug = debug;
+ if (speedo_debug)
+ printk(KERN_INFO "%s", version);
+
+ root_speedo_dev = NULL;
+ cards_found = eepro100_init(NULL);
+ return cards_found < 0 ? cards_found : 0;
+}
+
+void
+cleanup_module(void)
+{
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (root_speedo_dev) {
+ next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
+ unregister_netdev(root_speedo_dev);
+ release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
+ kfree(root_speedo_dev);
+ root_speedo_dev = next_dev;
+ }
+}
+#else /* not MODULE */
+int eepro100_probe(struct device *dev)
+{
+ int cards_found = 0;
+
+ cards_found = eepro100_init(dev);
+
+ if (speedo_debug > 0 && cards_found)
+ printk(version);
+
+ return cards_found ? 0 : -ENODEV;
+}
+#endif /* MODULE */
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
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)
}
}
- 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) {
-/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
+/* $Id: plip.c,v 1.1.1.1.2.6 1997/03/26 17:52:20 phil Exp $ */
/* PLIP: A parallel port "network" driver for Linux. */
/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
/*
*
* Modularization and ifreq/ifmap support by Alan Cox.
* Rewritten by Niibe Yutaka.
+ * parport-sharing awareness code by Philip Blundell.
*
* Fixes:
- * 9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- * - only claim 3 bytes of I/O space for port at 0x3bc
- * - treat NULL return from register_netdev() as success in
- * init_module()
- * - added message if driver loaded as a module but no
- * interfaces present.
- * - release claimed I/O ports if malloc() fails during init.
- *
* Niibe Yutaka
* - Module initialization. You can specify I/O addr and IRQ:
* # insmod plip.o io=0x3bc irq=7
* 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:
#include <asm/irq.h>
#include <asm/byteorder.h>
+#include <linux/parport.h>
+
+/* Maximum number of devices to support. */
+#define PLIP_MAX 8
+
/* Use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 1
static struct net_device_stats *plip_get_stats(struct device *dev);
static int plip_config(struct device *dev, struct ifmap *map);
static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int plip_preempt(void *handle);
+static int plip_wakeup(void *handle);
\f
enum plip_connection_state {
PLIP_CN_NONE=0,
struct tq_struct deferred;
struct plip_local snd_data;
struct plip_local rcv_data;
+ struct ppd *pardev;
unsigned long trigger;
unsigned long nibble;
enum plip_connection_state connection;
unsigned short timeout_count;
- char is_deferred;
+ int is_deferred;
+ int port_owner;
+ int should_relinquish;
int (*orig_rebuild_header)(struct sk_buff *skb);
};
\f
/* Entry point of PLIP driver.
- Probe the hardware, and register/initialize the driver. */
+ Probe the hardware, and register/initialize the driver.
+
+ PLIP is rather weird, because of the way it interacts with the parport
+ system. It is _not_ initialised from Space.c. Instead, plip_init()
+ is called, and that function makes up a "struct device" for each port, and
+ then calls us here.
-int plip_init(struct device *dev)
+ */
+int
+plip_init_dev(struct device *dev, struct parport *pb)
{
struct net_local *nl;
- int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
+ struct ppd *pardev;
- /* Check region before the probe */
- if (check_region(PAR_DATA(dev), iosize) < 0)
- return -ENODEV;
+ dev->irq = pb->irq;
+ dev->base_addr = pb->base;
- /* Check that there is something at base_addr. */
- outb(0, PAR_DATA(dev));
- udelay(1000);
- if (inb(PAR_DATA(dev)) != 0)
+ if (pb->irq == -1) {
+ printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
return -ENODEV;
-
- printk(version);
- printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
- if (dev->irq) {
- printk("using assigned IRQ %d.\n", dev->irq);
- } else {
- int irq = 0;
-#ifdef MODULE
- /* dev->irq==0 means autoprobe, but we don't try to do so
- with module. We can change it by ifconfig */
-#else
- unsigned int irqs = probe_irq_on();
-
- outb(0x00, PAR_CONTROL(dev));
- udelay(1000);
- outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- outb(PAR_INTR_ON, PAR_CONTROL(dev));
- outb(PAR_INTR_OFF, PAR_CONTROL(dev));
- udelay(1000);
- irq = probe_irq_off(irqs);
-#endif
- if (irq > 0) {
- dev->irq = irq;
- printk("using probed IRQ %d.\n", dev->irq);
- } else
- printk("failed to detect IRQ(%d) --"
- " Please set IRQ by ifconfig.\n", irq);
}
+
+ pardev = parport_register_device(pb, dev->name, plip_preempt,
+ plip_wakeup,
+ plip_interrupt, PARPORT_DEV_LURK, dev);
- request_region(PAR_DATA(dev), iosize, dev->name);
+ printk(version);
+ printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
+ dev->base_addr, dev->irq);
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
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));
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;
/* 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;
};
/* 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;
}
}
-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)
{
return TIMEOUT;
}
\f
-static int plip_none(struct device *dev, struct net_local *nl,
+static int
+plip_none(struct device *dev, struct net_local *nl,
struct plip_local *snd, struct plip_local *rcv)
{
return OK;
/* 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;
}
/* 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);
/* 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;
}
/* 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);
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();
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;
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));
}
\f
/* Handle the parallel port interrupts. */
-static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static void
+plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct device *dev = (struct device *) irq2dev_map[irq];
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
+ struct net_local *nl;
+ struct plip_local *rcv;
unsigned char c0;
if (dev == NULL) {
return;
}
+ nl = (struct net_local *)dev->priv;
+ rcv = &nl->rcv_data;
+
if (dev->interrupt)
return;
}
\f
/* We don't need to send arp, for plip is point-to-point. */
-static int plip_rebuild_header(struct sk_buff *skb)
+static int
+plip_rebuild_header(struct sk_buff *skb)
{
struct device *dev = skb->dev;
struct net_local *nl = (struct net_local *)dev->priv;
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;
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;
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));
/* 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;
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) {
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;
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;
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;
return 0;
}
\f
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-MODULE_PARM(io, "1-3i");
-MODULE_PARM(irq, "1-3i");
-
-static struct device dev_plip[] = {
- {
- "plip0",
- 0, 0, 0, 0, /* memory */
- 0x3BC, 5, /* base, irq */
- 0, 0, 0, NULL, plip_init
- },
- {
- "plip1",
- 0, 0, 0, 0, /* memory */
- 0x378, 7, /* base, irq */
- 0, 0, 0, NULL, plip_init
- },
- {
- "plip2",
- 0, 0, 0, 0, /* memory */
- 0x278, 2, /* base, irq */
- 0, 0, 0, NULL, plip_init
- }
-};
+static int parport[PLIP_MAX] = { -1, };
+static int timid = 0;
-int init_module(void)
+MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
+MODULE_PARM(timid, "1i");
+
+static struct device *dev_plip[PLIP_MAX] = { NULL, };
+
+#ifdef MODULE
+void
+cleanup_module(void)
{
- int no_parameters=1;
- int devices=0;
int i;
- /* When user feeds parameters, use them */
- for (i=0; i < 3; i++) {
- int specified=0;
-
- if (io[i] != 0) {
- dev_plip[i].base_addr = io[i];
- specified++;
- }
- if (irq[i] != 0) {
- dev_plip[i].irq = irq[i];
- specified++;
- }
- if (specified) {
- if (register_netdev(&dev_plip[i]) != 0) {
- printk(KERN_INFO "plip%d: Not found\n", i);
- return -EIO;
- }
- no_parameters = 0;
+ for (i=0; i < PLIP_MAX; i++) {
+ if (dev_plip[i]) {
+ struct net_local *nl =
+ (struct net_local *)dev_plip[i]->priv;
+ unregister_netdev(dev_plip[i]);
+ if (nl->port_owner)
+ parport_release(nl->pardev);
+ parport_unregister_device(nl->pardev);
+ kfree(dev_plip[i]->priv);
+ kfree(dev_plip[i]->name);
+ kfree(dev_plip[i]);
+ dev_plip[i] = NULL;
}
}
- if (!no_parameters)
- return 0;
+}
+
+#define plip_init init_module
+
+#else /* !MODULE */
+
+static int parport_ptr = 0;
- /* No parameters. Default action is probing all interfaces. */
- for (i=0; i < 3; i++) {
- if (register_netdev(&dev_plip[i]) == 0)
- devices++;
+void plip_setup(char *str, int *ints)
+{
+ /* Ugh. */
+ if (!strncmp(str, "parport", 7)) {
+ int n = simple_strtoul(str+7, NULL, 10);
+ if (parport_ptr < PLIP_MAX)
+ parport[parport_ptr++] = n;
+ else
+ printk(KERN_INFO "plip: too many ports, %s ignored.\n",
+ str);
+ } else if (!strcmp(str, "timid")) {
+ timid = 1;
+ } else {
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* disable driver on "parport=" or "parport=0" */
+ parport[0] = -2;
+ } else {
+ printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n",
+ ints[1]);
+ }
}
- if (devices == 0) {
- printk(KERN_INFO "plip: no interfaces found\n");
- return -EIO;
+}
+
+#endif /* MODULE */
+
+static int inline
+plip_searchfor(int list[], int a)
+{
+ int i;
+ for (i = 0; i < 3 && list[i] != -1; i++) {
+ if (list[i] == a) return 1;
}
return 0;
}
-void cleanup_module(void)
+int
+plip_init(void)
{
- int i;
+ struct parport *pb = parport_enumerate();
+ int devices=0;
+ int i=0;
+
+ if (parport[0] == -2)
+ return 0;
+
+ if (parport[0] != -1 && timid) {
+ printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
+ timid = 0;
+ }
- for (i=0; i < 3; i++) {
- if (dev_plip[i].priv) {
- unregister_netdev(&dev_plip[i]);
- release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
- kfree_s(dev_plip[i].priv, sizeof(struct net_local));
- dev_plip[i].priv = NULL;
+ /* When user feeds parameters, use them */
+ while (pb) {
+ if ((parport[0] == -1 && (!timid || !pb->devices)) ||
+ plip_searchfor(parport, i)) {
+ if (i == PLIP_MAX) {
+ printk(KERN_ERR "plip: too many devices\n");
+ break;
+ }
+ dev_plip[i] = kmalloc(sizeof(struct device),
+ GFP_KERNEL);
+ if (!dev_plip[i]) {
+ printk(KERN_ERR "plip: memory squeeze\n");
+ break;
+ }
+ memset(dev_plip[i], 0, sizeof(struct device));
+ dev_plip[i]->name =
+ kmalloc(strlen("plipXXX"), GFP_KERNEL);
+ if (!dev_plip[i]->name) {
+ printk(KERN_ERR "plip: memory squeeze.\n");
+ kfree(dev_plip[i]);
+ break;
+ }
+ sprintf(dev_plip[i]->name, "plip%d", i);
+ dev_plip[i]->priv = pb;
+ if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) {
+ kfree(dev_plip[i]->name);
+ kfree(dev_plip[i]);
+ } else {
+ devices++;
+ }
}
+ i++;
+ pb = pb->next;
+ }
+
+ if (devices == 0) {
+ printk(KERN_INFO "plip: no devices registered\n");
+ return -EIO;
}
+ return 0;
}
-#endif /* MODULE */
\f
/*
* Local variables:
-#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"
********************************************************************
- Copyright (c) 1993, 1996 Joerg Reuter DL1BKE
+ Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
portions (c) 1993 Guido ten Dolle PE1NNZ
You can use 'kissbridge' if you need a KISS TNC emulator.
961213 - Fixed for Linux networking changes. (G4KLX)
- 960108 - Fixed the remaining problems.
+ 970108 - Fixed the remaining problems.
+ 970402 - Hopefully fixed the problems with the new *_timer()
+ routines, added calibration code.
Thanks to all who contributed to this driver with ideas and bug
reports!
#include <linux/if_arp.h>
#include <linux/socket.h>
-#include <linux/config.h> /* for CONFIG_PROC_FS */
-
#include <linux/scc.h>
#include "z8530.h"
static int Nchips = 0;
static io_port Vector_Latch = 0;
+MODULE_AUTHOR("Joerg Reuter <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("scc");
/* ******************************************************************** */
/* * Port Access Functions * */
}
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;
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 */
{
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)
{
add_timer(&scc->tx_t);
}
- restore_flags(flags);
+ restore_flags(flags);
}
static void scc_start_defer(struct scc_channel *scc)
save_flags(flags);
cli();
- if (scc->tx_wdog.next)
- del_timer(&scc->tx_wdog);
+ del_timer(&scc->tx_wdog);
if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
{
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)
{
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);
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);
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. */
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)
#undef CAST
#undef SVAL
+/* ******************************************************************* */
+/* * Send calibration pattern * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+ struct scc_channel *scc = (struct scc_channel *) channel;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&scc->tx_wdog);
+ scc_key_trx(scc, TX_OFF);
+ wr(scc, R6, 0);
+ wr(scc, R7, FLAG);
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT);
+
+ scc_unlock_dev(scc);
+
+ restore_flags(flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ scc_lock_dev(scc);
+ scc_discard_buffers(scc);
+
+ del_timer(&scc->tx_wdog);
+
+ scc->tx_wdog.data = (unsigned long) scc;
+ scc->tx_wdog.function = scc_stop_calibrate;
+ scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+ add_timer(&scc->tx_wdog);
+
+ wr(scc, R6, 0);
+ wr(scc, R7, pattern);
+
+ /*
+ * Don't know if this works.
+ * Damn, where is my Z8530 programming manual...?
+ */
+
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT);
+
+ scc_key_trx(scc, TX_ON);
+
+ restore_flags(flags);
+}
+
/* ******************************************************************* */
/* * Init channel structures, special HW, etc... * */
/* ******************************************************************* */
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;
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;
* 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)
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;
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;
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;
}
--- /dev/null
+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.
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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)
--- /dev/null
+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.
--- /dev/null
+/* $Id: parport_init.c,v 1.1.2.4 1997/04/01 18:19:10 phil Exp $
+ * Parallel-port initialisation code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ * Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static int io[PARPORT_MAX] = { 0, };
+static int irq[PARPORT_MAX] = { -1, };
+static int dma[PARPORT_MAX] = { -1, };
+
+/******************************************************
+ * DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+ int tmp = 0;
+ int i,retv;
+
+ for (i = 0; i < 8; i++) {
+ retv = request_dma(i, "probe");
+ if (retv)
+ continue;
+ tmp |= 1 << i;
+
+ cli();
+ disable_dma(i);
+ clear_dma_ff(i);
+ set_dma_addr(i, virt_to_bus(buff));
+ set_dma_count(i, size);
+ set_dma_mode(i, DMA_MODE_READ);
+ sti();
+ }
+
+ return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (dma & (1 << i)) {
+ cli();
+ enable_dma(i);
+ sti();
+ }
+
+ return dma;
+}
+
+static int parport_detect_dma_transfer(int dma,int size)
+{
+ int i,n,retv;
+ int count=0;
+
+ retv = -1;
+ for (i = 0; i < 8; i++)
+ if (dma & (1 << i)) {
+ disable_dma(i);
+ clear_dma_ff(i);
+ n = get_dma_residue(i);
+ if (n != size) {
+ retv = i;
+ if (count > 0) {
+ retv = -1; /* Multiple DMA's */
+ printk("Multiple DMA detected.\n");
+ }
+ count++;
+ }
+ free_dma(i);
+ }
+
+ return retv;
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+ int dma;
+
+ w_ecr(pb,0xE0); /* Configuration MODE */
+
+ dma = r_cnfgB(pb) & 0x07;
+
+ w_ecr(pb,pb->ecr);
+
+ if( dma == 0 || dma == 4 ) /* Jumper selection */
+ return -1;
+ else
+ return dma;
+}
+
+/* Only called if port support ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of -1 is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ * 1Byte DMA transfer
+ * else // 4 < DMA < 8
+ * 2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+ int dma,retv;
+ int dsr,dsr_read;
+ char *buff;
+
+ retv = programmable_dma_support(pb);
+ if( retv != -1 )
+ return retv;
+
+ buff = kmalloc(16, GFP_KERNEL | GFP_DMA);
+ if( !buff ){
+ printk("parport: memory squezze\n");
+ return -1;
+ }
+
+ dsr = r_ctr(pb);
+ dsr_read = (dsr & ~(0x20)) | 0x04; /* Direction == read */
+
+ w_ecr(pb, 0xc0); /* ECP MODE */
+ w_ctr(pb, dsr_read );
+ dma=parport_prepare_dma(buff,8);
+ w_ecr(pb, 0xd8); /* ECP FIFO + enable DMA */
+ parport_enable_dma(dma);
+ udelay(30); /* Give some for DMA tranfer */
+ retv = parport_detect_dma_transfer(dma,8);
+
+ /*
+ * National Semiconductors only supports DMA tranfers
+ * in ECP MODE
+ */
+ if( retv == -1 ){
+ w_ecr(pb, 0x60); /* ECP MODE */
+ w_ctr(pb, dsr_read );
+ dma=parport_prepare_dma(buff,8);
+ w_ecr(pb, 0x68); /* ECP FIFO + enable DMA */
+ parport_enable_dma(dma);
+ udelay(30); /* Give some for DMA tranfer */
+ retv = parport_detect_dma_transfer(dma,8);
+ }
+
+ kfree(buff);
+
+ w_ctr(pb, pb->ctr);
+ w_ecr(pb, pb->ecr);
+
+ return retv;
+}
+/******************************************************
+ * MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+ int r;
+
+ if( !(r_str(pb) & 0x01) )
+ return 1;
+
+ /* To clear timeout some chip requiere double read */
+ r_str(pb);
+ r = r_str(pb);
+ w_str(pb, r | 0x01); /* Some reset by writing 1 */
+ w_str(pb, r & 0xfe); /* Others by writing 0 */
+ r = r_str(pb);
+
+ return !(r & 0x01);
+}
+
+
+/*
+ * Checks for por existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+ int r,rr;
+
+ /* Do a simple read-write test to make sure the port exists. */
+ w_dtr(pb, 0xaa);
+ r = r_dtr(pb);
+
+ w_dtr(pb, 0x55);
+ rr = r_dtr(pb);
+
+ if (r != 0xaa || rr != 0x55) {
+ return 0;
+ }
+
+ return PARPORT_MODE_SPP;
+}
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+ int r;
+
+ if( pb->base == 0x3BC )
+ return 0;
+
+ r= r_ctr(pb);
+ if( (r_ecr(pb) & 0x03) == (r & 0x03) ){
+ w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+ r= r_ctr(pb);
+ if( (r_ecr(pb) & 0x03) == (r & 0x03) )
+ return 0; /* Sure that no ECR register exists */
+ }
+
+ if( (r_ecr(pb) & 0x03 ) != 0x01 )
+ return 0;
+
+ w_ecr(pb,0x34);
+ if( r_ecr(pb) != 0x35 )
+ return 0;
+
+ w_ecr(pb,pb->ecr);
+ w_ctr(pb,pb->ctr);
+
+ return PARPORT_MODE_ECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+ int i;
+
+ if( !(pb->modes & PARPORT_MODE_ECR) )
+ return 0;
+ /*
+ * Usign LGS chipset it uses ECR register, but
+ * it doesn't support ECP or FIFO MODE
+ */
+
+ w_ecr(pb,0xc0); /* TEST FIFO */
+ for( i=0 ; i < 1024 && (r_ecr(pb) & 0x01) ; i++ )
+ w_fifo(pb, 0xaa);
+
+ w_ecr(pb,pb->ecr);
+
+ if( i >= 1024 )
+ return 0;
+
+ return PARPORT_MODE_ECP;
+}
+
+/* EPP mode detection
+ * Theory:
+ * Bit 0 of STR is the EPP timeout bit, this bit is 0
+ * when EPP is possible and is set high when an EPP timeout
+ * occurs (EPP uses the HALT line to stop the CPU while it does
+ * the byte transfer, an EPP timeout occurs if the attached
+ * device fails to respond after 10 micro seconds).
+ *
+ * This bit is cleared by either reading it (National Semi)
+ * or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ * This bit is always high in non EPP modes.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+ if( pb->base == 0x3BC )
+ return 0;
+
+ /* If EPP timeout bit clear then EPP available */
+ if( !epp_clear_timeout(pb) )
+ return 0; /* No way to clear timeout */
+
+ w_ctr(pb, r_ctr(pb) | 0x20);
+ w_ctr(pb, r_ctr(pb) | 0x10);
+ epp_clear_timeout(pb);
+
+ r_epp(pb);
+ udelay(30); /* Wait for possible EPP timeout */
+
+ if( r_str(pb) & 0x01 ){
+ epp_clear_timeout(pb);
+ return PARPORT_MODE_EPP;
+ }
+
+ return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+ int mode;
+
+ if( !(pb->modes & PARPORT_MODE_ECR) )
+ return 0;
+
+ /* Search for SMC style EPP+ECP mode */
+ w_ecr(pb, 0x80);
+
+ mode = parport_EPP_supported(pb);
+
+ w_ecr(pb,pb->ecr);
+
+ if( mode )
+ return PARPORT_MODE_ECPEPP;
+
+ return 0;
+}
+
+/* Detect LP_PS2 support
+ * Bit 5 (0x20) sets the PS/2 data direction, setting this high
+ * allows us to read data from the data lines, old style SPP ports
+ * will return 0xff. This may not be reliable if there is a
+ * peripheral attached to the port.
+ */
+static int parport_PS2_supported(struct parport *pb)
+{
+ int r,rr;
+
+ epp_clear_timeout(pb);
+
+ w_ctr(pb, pb->ctr | 0x20); /* Tri-state the buffer */
+
+ w_dtr(pb, 0xAA);
+ r = r_dtr(pb);
+
+ w_dtr(pb, 0x55);
+ rr = r_dtr(pb);
+
+ w_ctr(pb, pb->ctr); /* Reset CTR register */
+
+ if (r != 0xAA || rr != 0x55 )
+ return PARPORT_MODE_PS2;
+
+ return 0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+ int mode;
+
+ if( !(pb->modes & PARPORT_MODE_ECR) )
+ return 0;
+
+ w_ecr(pb, 0x20);
+
+ mode = parport_PS2_supported(pb);
+
+ w_ecr(pb,pb->ecr);
+
+ if (mode)
+ return PARPORT_MODE_ECPPS2;
+
+ return 0;
+}
+
+/******************************************************
+ * IRQ detection section:
+ */
+/*
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ */
+static int intr_vote[16];
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ intr_vote[irq]++;
+ return;
+}
+
+static long open_intr_election(void)
+{
+ long tmp = 0;
+ int i;
+
+ /* We ignore the timer - irq 0 */
+ for (i = 1; i < 16; i++) {
+ intr_vote[i] = 0;
+ if (request_irq(i, parport_vote_intr_func,
+ SA_INTERRUPT, "probe", intr_vote) == 0)
+ tmp |= 1 << i;
+ }
+ return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+ long max_vote = 0;
+ int irq = -1;
+ int i;
+
+ /* We ignore the timer - irq 0 */
+ for (i = 1; i < 16; i++)
+ if (tmp & (1 << i)) {
+ if (intr_vote[i] > max_vote) {
+ if (max_vote)
+ return -1;
+ max_vote = intr_vote[i];
+ irq = i;
+ }
+ free_irq(i, intr_vote);
+ }
+ return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+ int irq;
+
+ w_ecr(pb,0xE0); /* Configuration MODE */
+
+ irq = (r_cnfgB(pb) >> 3) & 0x07;
+
+ switch(irq){
+ case 2:
+ irq = 9;
+ break;
+ case 7:
+ irq = 5;
+ break;
+ case 0:
+ irq = -1;
+ break;
+ default:
+ irq += 7;
+ }
+
+ w_ecr(pb,pb->ecr);
+
+ return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+ int irqs,i;
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = open_intr_election();
+
+ w_ecr(pb, 0x00); /* Reset FIFO */
+ w_ctr(pb, pb->ctr ); /* Force direction = 0 */
+ w_ecr(pb, 0xd0); /* TEST FIFO + nErrIntrEn */
+
+ /* If Full FIFO sure that WriteIntrThresold is generated */
+ for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
+ w_fifo(pb, 0xaa);
+ }
+
+ pb->irq = close_intr_election(irqs);
+ if (pb->irq == 0)
+ pb->irq = -1; /* No interrupt detected */
+
+ w_ecr(pb, pb->ecr);
+
+ return pb->irq;
+}
+
+/*
+ * It's called only if supports EPP on National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+ int irqs;
+
+#ifndef ADVANCED_DETECT
+ return -1;
+#endif
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = open_intr_election();
+
+ if( pb->modes & PARPORT_MODE_ECR )
+ w_ecr(pb, r_ecr(pb) | 0x10 );
+
+ epp_clear_timeout(pb);
+ w_ctr(pb, r_ctr(pb) | 0x20);
+ w_ctr(pb, r_ctr(pb) | 0x10);
+ epp_clear_timeout(pb);
+
+ /* Device isn't expecting an EPP read
+ * and generates an IRQ.
+ */
+ r_epp(pb);
+ udelay(20);
+
+ pb->irq = close_intr_election(irqs);
+ if (pb->irq == 0)
+ pb->irq = -1; /* No interrupt detected */
+
+ w_ctr(pb,pb->ctr);
+
+ return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+ int irqs;
+
+#ifndef ADVANCED_DETECT
+ return -1;
+#endif
+
+ probe_irq_off(probe_irq_on()); /* Clear any interrupts */
+ irqs = probe_irq_on();
+
+ if( pb->modes & PARPORT_MODE_ECR )
+ w_ecr(pb, 0x10 );
+
+ w_dtr(pb,0x00);
+ w_ctr(pb,0x00);
+ w_ctr(pb,0x0c);
+ udelay(5);
+ w_ctr(pb,0x0d);
+ udelay(5);
+ w_ctr(pb,0x0c);
+ udelay(25);
+ w_ctr(pb,0x08);
+ udelay(25);
+ w_ctr(pb,0x0c);
+ udelay(50);
+
+ pb->irq = probe_irq_off(irqs);
+ if (pb->irq <= 0)
+ pb->irq = -1; /* No interrupt detected */
+
+ w_ctr(pb,pb->ctr);
+
+ return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When LP_ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+ if( pb->modes & PARPORT_MODE_ECR )
+ pb->irq = programmable_irq_support(pb);
+
+ if( pb->modes & PARPORT_MODE_ECP )
+ pb->irq = irq_probe_ECP(pb);
+
+ if( pb->irq == -1 && (pb->modes & PARPORT_MODE_ECPEPP)){
+ w_ecr(pb,0x80);
+ pb->irq = irq_probe_EPP(pb);
+ w_ecr(pb,pb->ecr);
+ }
+
+ epp_clear_timeout(pb);
+
+ if( pb->irq == -1 && (pb->modes & PARPORT_MODE_EPP))
+ pb->irq = irq_probe_EPP(pb);
+
+ epp_clear_timeout(pb);
+
+ if( pb->irq == -1 )
+ pb->irq = irq_probe_SPP(pb);
+
+ return pb->irq;
+}
+
+
+int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
+{
+ /* Check some parameters */
+ if (dma < -2) {
+ printk("parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
+ return 0;
+ }
+
+ if (irq < -2) {
+ printk("parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
+ return 0;
+ }
+
+ /* Init our structure */
+ memset(pb, 0, sizeof(struct parport));
+ pb->base = base;
+ pb->irq = irq;
+ pb->dma = dma;
+ pb->modes = 0;
+ pb->next = NULL;
+ pb->devices = pb->cad = pb->lurker = NULL;
+ pb->flags = 0;
+
+ /* Before we start, set the control registers to something sensible. */
+ pb->ecr = 0xc;
+ pb->ctr = 0xc;
+
+ pb->name = kmalloc(15, GFP_KERNEL);
+ if (!pb->name) {
+ printk("parport: memory squeeze\n");
+ return 0;
+ }
+ sprintf(pb->name, "parport%d", count);
+
+ if (!parport_SPP_supported(pb)) {
+ epp_clear_timeout(pb);
+ if (!parport_SPP_supported(pb)) {
+ kfree(pb->name);
+ return 0;
+ }
+ }
+
+ pb->modes |= PARPORT_MODE_SPP; /* All ports support SPP mode. */
+ pb->modes |= parport_ECR_present(pb);
+ pb->modes |= parport_ECP_supported(pb);
+ pb->modes |= parport_PS2_supported(pb);
+ pb->modes |= parport_ECPPS2_supported(pb);
+ pb->modes |= parport_EPP_supported(pb);
+ pb->modes |= parport_ECPEPP_supported(pb);
+
+ /* Now register regions */
+ if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) &&
+ (check_region(pb->base, 8))) {
+ printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
+ pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
+ }
+ pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
+
+ request_region(pb->base, pb->size, pb->name);
+ if (pb->modes & PARPORT_MODE_ECR)
+ request_region(pb->base+0x400, 3, pb->name);
+
+ /* DMA check */
+ if (pb->modes & PARPORT_MODE_ECP) {
+ if (pb->dma == -1)
+ pb->dma = parport_dma_probe(pb);
+ else if (pb->dma == -2)
+ pb->dma = -1;
+ }
+
+ /* IRQ check */
+ if (pb->irq == -1)
+ pb->irq = parport_irq_probe(pb);
+ else if (pb->irq == -2)
+ pb->irq = -1;
+
+ return 1;
+}
+
+#ifndef MODULE
+static int parport_setup_ptr = 0;
+
+void parport_setup(char *str, int *ints)
+{
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* Disable parport if "parport=" or "parport=0" in cmdline */
+ io[0] = -2;
+ return;
+ }
+ if (parport_setup_ptr < PARPORT_MAX) {
+ io[parport_setup_ptr] = ints[1];
+ if (ints[0]>1) {
+ irq[parport_setup_ptr] = ints[2];
+ if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+ }
+ parport_setup_ptr++;
+ } else {
+ printk(KERN_ERR "parport=0x%x", ints[1]);
+ if (ints[0]>1) {
+ printk(",%d", ints[2]);
+ if (ints[0]>2) printk(",%d", ints[3]);
+ }
+ printk(" ignored, too many ports.\n");
+ }
+}
+#endif
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+extern void parport_probe_one(struct parport *port);
+#endif
+
+#ifdef MODULE
+MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+
+int init_module(void)
+#else
+int pnp_parport_init(void)
+#endif /* MODULE */
+{
+ struct parport *pb;
+
+ printk(KERN_INFO "Parallel port sharing: %s\n",
+ "$Revision: 1.1.2.4 $");
+
+ if (io[0] == -2) return 1;
+
+ /* Register /proc/parport */
+ parport_proc_register(NULL);
+
+ /* Run probes to ensure parport does exist */
+#define PORT(a,b,c) \
+ if ((pb = parport_register_port((a), (b), (c)))) \
+ parport_destroy(pb);
+ if (io[0]) {
+ /* If the user specified any ports, use them */
+ int i;
+ for (i = 0; io[i] && i < PARPORT_MAX; i++) {
+ PORT(io[i], irq[i], dma[i]);
+ }
+ } else {
+ /* Go for the standard ports. */
+ PORT(0x378, -1, -1);
+ PORT(0x278, -1, -1);
+ PORT(0x3bc, -1, -1);
+#undef PORT
+ }
+
+ for (pb = parport_enumerate(); pb; pb = pb->next)
+ parport_probe_one(pb);
+
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ struct parport *port, *next;
+
+ for (port = parport_enumerate(); port; port = next) {
+ next = port->next;
+ parport_destroy(port);
+ parport_proc_unregister(port);
+ kfree(port->name);
+ kfree(port);
+ }
+
+ parport_proc_unregister(NULL);
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_destroy);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+EXPORT_SYMBOL(parport_probe);
+EXPORT_SYMBOL(parport_probe_one);
+#endif
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
--- /dev/null
+/* $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)
--- /dev/null
+/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $
+ * Parallel port device probing code
+ *
+ * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+
+#include <linux/lp.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG_PROBE
+
+static inline int read_nibble(struct parport *port)
+{
+ unsigned char i;
+ i = parport_r_status(port)>>3;
+ i&=~8;
+ if ( ( i & 0x10) == 0) i|=8;
+ return(i & 0x0f);
+}
+
+static void read_terminate(struct parport *port) {
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ /* SelectIN high, AutoFeed low */
+ if (parport_wait_peripheral(port, 0x80, 0))
+ /* timeout, SelectIN high, Autofeed low */
+ return;
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+ /* AutoFeed high */
+ parport_wait_peripheral(port, 0x80, 0x80);
+ /* no timeout possible, Autofeed low, SelectIN high */
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ return;
+}
+
+static long read_polled(struct parport *port, char *buf,
+ unsigned long length)
+{
+ int i;
+ char *temp=buf;
+ int count = 0;
+ unsigned char z=0;
+ unsigned char Byte=0;
+
+ for (i=0; ; i++) {
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+ if (parport_wait_peripheral(port, 0x40, 0)) {
+ printk("%s: read1 timeout.\n", port->name);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+ break;
+ }
+ z = read_nibble(port);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+ if (parport_wait_peripheral(port, 0x40, 0x40)) {
+ printk("%s: read2 timeout.\n", port->name);
+ break;
+ }
+ if (( i & 1) != 0) {
+ Byte= (Byte | z<<4);
+ if (temp)
+ *(temp++) = Byte;
+ if (count++ == length)
+ temp = NULL;
+ /* Does the error line indicate end of data? */
+ if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP)
+ break;
+ } else Byte=z;
+ }
+ read_terminate(port);
+ return count;
+}
+
+static struct wait_queue *wait_q = NULL;
+
+static int wakeup(void *ref)
+{
+ struct ppd **dev = (struct ppd **)ref;
+
+ if (!wait_q || parport_claim(*dev))
+ return 1;
+
+ wake_up(&wait_q);
+ return 0;
+}
+
+int parport_probe(struct parport *port, char *buffer, int len)
+{
+ struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
+ NULL, wakeup, NULL,
+ PARPORT_DEV_TRAN, &dev);
+
+ int result = 0;
+
+ if (!dev) {
+ printk("%s: unable to register for probe.\n", port->name);
+ return -EINVAL;
+ }
+
+ if (parport_claim(dev)) {
+ sleep_on(&wait_q);
+ wait_q = NULL;
+ }
+
+ switch (parport_ieee1284_nibble_mode_ok(port, 4)) {
+ case 1:
+ current->state=TASK_INTERRUPTIBLE;
+ current->timeout=jiffies+1;
+ schedule(); /* HACK: wait 10ms because printer seems to
+ * ack wrong */
+ result = read_polled(port, buffer, len);
+ break;
+ case 0:
+ result = -EIO;
+ break;
+ }
+
+ parport_release(dev);
+ parport_unregister_device(dev);
+
+ return result;
+}
+
+static struct {
+ char *token;
+ char *descr;
+} classes[] = {
+ { "", "Legacy device" },
+ { "PRINTER", "Printer" },
+ { "MODEM", "Modem" },
+ { "NET", "Network device" },
+ { "HDC", "Hard disk" },
+ { "PCMCIA", "PCMCIA" },
+ { "MEDIA", "Multimedia device" },
+ { "FDC", "Floppy disk" },
+ { "PORTS", "Ports" },
+ { "SCANNER", "Scanner" },
+ { "DIGICAM", "Digital camera" },
+ { "", "Unknown device" },
+ { "", "Unspecified" },
+ { NULL, NULL }
+};
+
+static char *strdup(char *str)
+{
+ int n = strlen(str)+1;
+ char *s = kmalloc(n, GFP_KERNEL);
+ if (!s) return NULL;
+ return strcpy(s, str);
+}
+
+static void parse_data(struct parport *port, char *str)
+{
+ char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+ char *p = txt, *q;
+ int guessed_class = PARPORT_CLASS_UNSPEC;
+
+ if (!txt) {
+ printk("%s probe: memory squeeze\n", port->name);
+ return;
+ }
+ strcpy(txt, str);
+ while (p) {
+ char *sep;
+ q = strchr(p, ';');
+ if (q) *q = 0;
+ sep = strchr(p, ':');
+ if (sep) {
+ char *u = p;
+ *(sep++) = 0;
+ while (*u) {
+ *u = toupper(*u);
+ u++;
+ }
+ if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+ port->probe_info.mfr = strdup(sep);
+ } else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+ port->probe_info.model = strdup(sep);
+ } else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+ int i;
+ for (u = sep; *u; u++)
+ *u = toupper(*u);
+ for (i = 0; classes[i].token; i++) {
+ if (!strcmp(classes[i].token, sep)) {
+ port->probe_info.class = i;
+ goto rock_on;
+ }
+ }
+ printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+ port->probe_info.class = PARPORT_CLASS_OTHER;
+ } else if (!strcmp(p, "CMD") || !strcmp(p, "COMMAND SET")) {
+ /* if it speaks printer language, it's
+ probably a printer */
+ if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+ guessed_class = PARPORT_CLASS_PRINTER;
+ } else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+ port->probe_info.description = strdup(sep);
+ }
+ }
+ rock_on:
+ if (q) p = q+1; else p=NULL;
+ }
+
+ /* If the device didn't tell us its class, maybe we have managed to
+ guess one from the things it did say. */
+ if (port->probe_info.class == PARPORT_CLASS_UNSPEC)
+ port->probe_info.class = guessed_class;
+
+ kfree(txt);
+}
+
+static void pretty_print(struct parport *port)
+{
+ printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr);
+ if (port->probe_info.class) {
+ printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr);
+ }
+ printk("\n");
+}
+
+void parport_probe_one(struct parport *port)
+{
+ char *buffer = kmalloc(2048, GFP_KERNEL);
+ int r;
+
+ port->probe_info.model = "Unknown device";
+ port->probe_info.mfr = "Unknown vendor";
+ port->probe_info.description = NULL;
+ port->probe_info.class = PARPORT_CLASS_UNSPEC;
+
+ if (!buffer) {
+ printk(KERN_ERR "%s probe: Memory squeeze.\n", port->name);
+ return;
+ }
+
+ r = parport_probe(port, buffer, 2047);
+
+ if (r < 0) {
+ printk(KERN_INFO "%s: no IEEE-1284 device present.\n",
+ port->name);
+ port->probe_info.class = PARPORT_CLASS_LEGACY;
+ } else if (r == 0) {
+ printk(KERN_INFO "%s: no ID data returned by device.\n",
+ port->name);
+ } else {
+ buffer[r] = 0;
+#ifdef DEBUG_PROBE
+ printk("%s id: %s\n", port->name, buffer+2);
+#endif
+ parse_data(port, buffer+2);
+ pretty_print(port);
+ }
+ kfree(buffer);
+}
--- /dev/null
+/* $Id: parport_procfs.c,v 1.1.2.2 1997/03/26 17:50:36 phil Exp $
+ * Parallel port /proc interface code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <linux/parport.h>
+
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+
+/************************************************/
+static long proc_readparport(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
+{
+ printk("proc_readparport\n");
+ return 0;
+}
+
+static long proc_writeparport(struct inode * inode, struct file * file,
+ const char * buf, unsigned long count)
+{
+ printk("proc_writeparport\n");
+
+ return 0;
+}
+
+static long long proc_parportlseek(struct inode * inode, struct file * file,
+ long long offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ return(file->f_pos);
+ case 1:
+ file->f_pos += offset;
+ return(file->f_pos);
+ case 2:
+ return(-EINVAL);
+ default:
+ return(-EINVAL);
+ }
+}
+
+static struct file_operations proc_dir_operations = {
+ proc_parportlseek, /* lseek */
+ proc_readparport, /* read */
+ proc_writeparport, /* write */
+ proc_readdir, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/************************************************/
+static struct inode_operations parport_proc_dir_inode_operations = {
+ &proc_dir_operations, /* default net directory file-ops */
+ NULL, /* create */
+ proc_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static struct proc_dir_entry proc_root_parport = {
+ PROC_PARPORT, 7, "parport",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &parport_proc_dir_inode_operations,
+ NULL, NULL,
+ NULL, &proc_root, NULL,
+ NULL, NULL
+};
+#endif
+
+int parport_proc_register(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+ return proc_register(&proc_root, &proc_root_parport);
+#else
+ return 0;
+#endif
+}
+
+void parport_proc_unregister(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+ if( pp ){
+ proc_unregister(&proc_root_parport, pp->proc_dir->low_ino);
+ kfree(pp->proc_dir);
+ }else{
+ proc_unregister(&proc_root, proc_root_parport.low_ino);
+ }
+#endif
+}
--- /dev/null
+/* $Id: parport_share.c,v 1.1.2.4 1997/04/01 18:19:11 phil Exp $
+ * Parallel-port resource manager code.
+ *
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Tim Waugh <tmw20@cam.ac.uk>
+ * Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ * and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* from parport_init.c */
+extern int initialize_parport(struct parport *, unsigned long base,
+ int irq, int dma, int count);
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+ return portlist;
+}
+
+static void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* NULL function - Does nothing */
+ return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma)
+{
+ struct parport new, *tmp;
+
+ /* Check for a previously registered port.
+ * NOTE: we will ignore irq and dma if we find a previously
+ * registered device.
+ */
+ for (tmp = portlist; tmp; tmp = tmp->next) {
+ if (tmp->base == base)
+ return tmp;
+ }
+
+ /* Has someone grabbed the address yet? */
+ if (check_region(base, 3))
+ return NULL;
+
+ if (!initialize_parport(&new,base,irq,dma,portcount))
+ return NULL;
+
+ if (new.dma >= 0) {
+ if (request_dma(new.dma, new.name)) {
+ printk(KERN_INFO "%s: unable to claim DMA %d\n",
+ new.name, new..dma);
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ }
+
+ tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+ if (!tmp) {
+ printk(KERN_WARNING "parport: memory squeeze\n");
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ memcpy(tmp, &new, sizeof(struct parport));
+
+ if (new.irq != -1) {
+ if (request_irq(new.irq, parport_null_intr_func,
+ SA_INTERRUPT, new.name, tmp) != 0) {
+ printk(KERN_INFO "%s: unable to claim IRQ %d\n",
+ new.name, new.irq);
+ kfree(tmp);
+ release_region(new.base, new.size);
+ if( new.modes & PARPORT_MODE_ECR )
+ release_region(new.base+0x400, 3);
+ kfree(new.name);
+ return NULL;
+ }
+ }
+
+ /* Here we chain the entry to our list. */
+ if (portlist_tail)
+ portlist_tail->next = tmp;
+ portlist_tail = tmp;
+ if (!portlist)
+ portlist = tmp;
+
+ printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
+ if (tmp->irq >= 0)
+ printk(", irq %d", tmp->irq);
+ if (tmp->dma >= 0)
+ printk(", dma %d", tmp->dma);
+ printk(" [");
+ {
+ /* Ugh! */
+#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+ int f = 0;
+ printmode(SPP);
+ printmode(PS2);
+ printmode(EPP);
+ printmode(ECP);
+ printmode(ECPEPP);
+ printmode(ECPPS2);
+#undef printmode
+ }
+ printk("]\n");
+ portcount++;
+
+ /* Restore device back to default conditions */
+ if (tmp->modes & PARPORT_MODE_ECR)
+ w_ecr(tmp, tmp->ecr);
+ w_ctr(tmp, tmp->ctr);
+
+ tmp->probe_info.class = PARPORT_CLASS_LEGACY; /* assume the worst */
+ return tmp;
+}
+
+void parport_destroy(struct parport *port)
+{
+ /* Dangerous to try destroying a port if its friends are nearby. */
+ if (port->devices) {
+ printk("%s: attempt to release active port\n", port->name);
+ return; /* Devices still present */
+ }
+
+ /* No point in further destroying a port that already lies in ruins. */
+ if (port->flags & PARPORT_FLAG_COMA)
+ return;
+
+ /* Now clean out the port entry */
+ if (port->irq >= 0)
+ free_irq(port->irq, port);
+ if (port->dma >= 0)
+ free_dma(port->dma);
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ port->flags |= PARPORT_FLAG_COMA;
+}
+
+struct ppd *parport_register_device(struct parport *port, const char *name,
+ callback_func pf, callback_func kf,
+ irq_handler_func irq_func, int flags,
+ void *handle)
+{
+ struct ppd *tmp;
+
+ /* We only allow one lurker device (eg PLIP) */
+ if (flags & PARPORT_DEV_LURK) {
+ if (port->lurker) {
+ printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+ port->name, name);
+ return NULL;
+ }
+ if (!pf || !kf) {
+ printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
+ ,port->name, name);
+ return NULL;
+ }
+ }
+
+ /* We may need to claw back the port hardware. */
+ if (port->flags & PARPORT_FLAG_COMA) {
+ if (check_region(port->base, 3)) {
+ return NULL;
+ }
+ request_region(port->base, port->size, port->name);
+ if( port->modes & PARPORT_MODE_ECR )
+ request_region(port->base+0x400, 3,port->name);
+
+ if (port->dma >= 0) {
+ if (request_dma(port->dma, port->name)) {
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ return NULL;
+ }
+ }
+ if (port->irq != -1) {
+ if (request_irq(port->irq,
+ parport_null_intr_func,
+ SA_INTERRUPT, port->name,
+ port) != 0) {
+ release_region(port->base, port->size);
+ if( port->modes & PARPORT_MODE_ECR )
+ release_region(port->base+0x400, 3);
+ if (port->dma >= 0)
+ free_dma(port->dma);
+ return NULL;
+ }
+ }
+ port->flags &= ~PARPORT_FLAG_COMA;
+ }
+
+
+ tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
+ tmp->name = (char *) name;
+ tmp->port = port;
+ tmp->preempt = pf;
+ tmp->wakeup = kf;
+ tmp->private = handle;
+ tmp->irq_func = irq_func;
+ tmp->ctr = port->ctr;
+ tmp->ecr = port->ecr;
+
+ /* Chain this onto the list */
+ tmp->prev = NULL;
+ tmp->next = port->devices;
+ if (port->devices)
+ port->devices->prev = tmp;
+ port->devices = tmp;
+
+ if (flags & PARPORT_DEV_LURK)
+ port->lurker = tmp;
+
+ inc_parport_count();
+
+ return tmp;
+}
+
+void parport_unregister_device(struct ppd *dev)
+{
+ struct parport *port;
+
+ if (!dev) {
+ printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+ return;
+ }
+
+ port = dev->port;
+
+ if (port->cad == dev) {
+ printk(KERN_INFO "%s: refused to unregister currently active device %s\n",
+ port->name, dev->name);
+ return;
+ }
+
+ if (port->lurker == dev)
+ port->lurker = NULL;
+
+ if (dev->next)
+ dev->next->prev = dev->prev;
+ if (dev->prev)
+ dev->prev->next = dev->next;
+ else
+ port->devices = dev->next;
+
+ kfree(dev);
+
+ dec_parport_count();
+
+ /* If there are no more devices, put the port to sleep. */
+ if (!port->devices)
+ parport_destroy(port);
+
+ return;
+}
+
+int parport_claim(struct ppd *dev)
+{
+ struct ppd *pd1;
+
+ if (dev->port->cad == dev) {
+ printk(KERN_INFO "%s: %s already owner\n",
+ dev->port->name,dev->name);
+ return 0;
+ }
+
+ /* Preempt any current device */
+ pd1 = dev->port->cad;
+ if (dev->port->cad) {
+ if (dev->port->cad->preempt) {
+ /* Now try to preempt */
+ if (dev->port->cad->preempt(dev->port->cad->private))
+ return -EAGAIN;
+
+ /* Save control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ dev->port->cad->ecr = dev->port->ecr =
+ r_ecr(dev->port);
+ dev->port->cad->ctr = dev->port->ctr =
+ r_ctr(dev->port);
+ } else
+ return -EAGAIN;
+ }
+
+ /* Watch out for bad things */
+ if (dev->port->cad != pd1) {
+ printk(KERN_WARNING "%s: death while preempting %s\n",
+ dev->port->name, dev->name);
+ if (dev->port->cad)
+ return -EAGAIN;
+ }
+
+ /* Now we do the change of devices */
+ dev->port->cad = dev;
+
+ if (dev->port->irq >= 0) {
+ free_irq(dev->port->irq, dev->port);
+ request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+ parport_null_intr_func, SA_INTERRUPT, dev->name,
+ dev->port);
+ }
+
+ /* Restore control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
+ if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
+
+ return 0;
+}
+
+void parport_release(struct ppd *dev)
+{
+ struct ppd *pd1;
+
+ /* Make sure that dev is the current device */
+ if (dev->port->cad != dev) {
+ printk(KERN_WARNING "%s: %s tried to release parport when not owner\n",
+ dev->port->name, dev->name);
+ return;
+ }
+ dev->port->cad = NULL;
+
+ /* Save control registers */
+ if (dev->port->modes & PARPORT_MODE_ECR)
+ dev->ecr = dev->port->ecr = r_ecr(dev->port);
+ dev->ctr = dev->port->ctr = r_ctr(dev->port);
+
+ if (dev->port->irq >= 0) {
+ free_irq(dev->port->irq, dev->port);
+ request_irq(dev->port->irq, parport_null_intr_func,
+ SA_INTERRUPT, dev->port->name, dev->port);
+ }
+
+ /* Walk the list, offering a wakeup callback to everybody other
+ * than the lurker and the device that called us.
+ */
+ for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+ if (!(pd1->flags & PARPORT_DEV_LURK)) {
+ if (pd1->wakeup) {
+ pd1->wakeup(pd1->private);
+ if (dev->port->cad)
+ return;
+ }
+ }
+ }
+
+ for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+ if (!(pd1->flags & PARPORT_DEV_LURK)) {
+ if (pd1->wakeup) {
+ pd1->wakeup(pd1->private);
+ if (dev->port->cad)
+ return;
+ }
+ }
+ }
+
+ /* Now give the lurker a chance.
+ * There should be a wakeup callback because we checked for it
+ * at registration.
+ */
+ if (dev->port->lurker && (dev->port->lurker != dev)) {
+ if (dev->port->lurker->wakeup) {
+ dev->port->lurker->wakeup(dev->port->lurker->private);
+ return;
+ }
+ printk(KERN_DEBUG
+ "%s (%s): lurker's wakeup callback went away!\n",
+ dev->port->name, dev->name);
+ }
+}
+
+/* The following read funktions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms. So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask,
+ unsigned char result)
+{
+ int counter=0;
+ unsigned char status;
+
+ do {
+ status = parport_r_status(port);
+ udelay(25);
+ counter++;
+ if (need_resched)
+ schedule();
+ } while ( ((status & mask) != result) && (counter < 20) );
+ if ( (counter == 20) && ((status & mask) != result) ) {
+ current->state=TASK_INTERRUPTIBLE;
+ current->timeout=jiffies+4;
+ schedule(); /* wait for 4 scheduler runs (40ms) */
+ status = parport_r_status(port);
+ if ((status & mask) != result) return 1; /* timeout */
+ }
+ return 0; /* okay right response from device */
+}
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time). There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode)
+{
+ parport_w_data(port, mode);
+ udelay(5);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~8); /* SelectIN low */
+ parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+ if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+ parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+ return 0; /* first stage of negotiation failed,
+ * no IEEE1284 compliant device on this port
+ */
+ }
+ parport_w_ctrl(port, parport_r_ctrl(port) | 1); /* Strobe high */
+ udelay(5); /* Strobe wait */
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~1); /* Strobe low */
+ udelay(5);
+ parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+ return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}
+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.
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
21 Rue Carnot
95170 DEUIL LA BARRE - FRANCE
-27 February 1997
+6 April 1997
===============================================================================
1. Introduction
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
===============================================================================
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.
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
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
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
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:
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"
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
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
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
*/
/*
-** 27 February 1997, version 1.18b
+** 6 April 1997, version 1.18d
**
** Supported SCSI-II features:
** Synchronous negotiation
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;
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;
};
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
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);
**==========================================================
*/
-#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
** #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),
#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;
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;
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);
}
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 */
** 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);
/*
** 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
/*
** start the timeout daemon
*/
- ncr_timeout (np);
np->lasttime=0;
+ ncr_timeout (np);
/*
** use SIMPLE TAG messages by default
cmd->SCp.ptr = NULL;
cmd->SCp.buffer = NULL;
- /*---------------------------------------------
- **
- ** Reset SCSI bus
- **
- ** Interrupt handler does the real work.
- **
- **---------------------------------------------
- */
-#if 0
- if (flags & SCSI_RESET) {
- OUTB (nc_scntl1, CRST);
- DELAY (1000);
- return(COMPLETE);
- }
-#endif
-
/*---------------------------------------------
**
** Some shortcuts ...
/*---------------------------------------------------
**
** 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);
return(DID_OK);
}
+/*==========================================================
+**
+**
+** Start reset process.
+** If reset in progress do nothing.
+** The interrupt handler will reinitialize the chip.
+** The timeout handler will wait for settle_time before
+** clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+ u_long flags;
+
+ save_flags(flags); cli();
+
+ if (!np->settle_time) {
+ if (bootverbose > 1)
+ printf("%s: resetting, command processing suspended for %d seconds\n",
+ ncr_name(np), settle_delay);
+ np->settle_time = jiffies + settle_delay * HZ;
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0);
+ OUTW (nc_sien, RST);
+ OUTB (nc_scntl1, CRST);
+ DELAY (100);
+ }
+
+ restore_flags(flags);
+}
+
/*==========================================================
**
**
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
*/
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;
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.
/** 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 **/
}
}
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 {
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 **/
}
#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
*/
** 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);
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
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
/*----------------------------------------------------
**
#endif /* SCSI_NCR_BROKEN_INTR */
}
+/*==========================================================
+**
+** log message for real hard errors
+**
+** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+** exception register:
+** ds: dstat
+** si: sist
+**
+** SCSI bus lines:
+** so: control lines as driver by NCR.
+** si: control lines as seen by NCR.
+** sd: scsi data lines as seen by NCR.
+**
+** wide/fastmode:
+** sxfer: (see the manual)
+** scntl3: (see the manual)
+**
+** current script command:
+** dsp: script adress (relative to start of script).
+** dbc: first word of script command.
+**
+** First 16 register of the chip:
+** r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+ u_int32 dsp;
+ int script_ofs;
+ char *script_name;
+ u_char *script_base;
+ int i;
+
+ dsp = INL (nc_dsp);
+
+ if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+ script_ofs = dsp - np->p_script;
+ script_base = (u_char *) np->script;
+ script_name = "script";
+ }
+ else {
+ script_ofs = dsp - np->p_scripth;
+ script_base = (u_char *) np->scripth;
+ script_name = "scripth";
+ }
+
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+ ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+ (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+ (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+ (unsigned)INL (nc_dbc));
+
+ if (((script_ofs & 3) == 0) &&
+ (unsigned)script_ofs < sizeof(struct script)) {
+ printf ("%s: script cmd = %08x\n", ncr_name(np),
+ (int) *(ncrcmd *)(script_base + script_ofs));
+ }
+
+ printf ("%s: regdump:", ncr_name(np));
+ for (i=0; i<16;i++)
+ printf (" %02x", (unsigned)INB_OFF(i));
+ printf (".\n");
+}
+
/*==========================================================
**
**
{
u_char istat, dstat;
u_short sist;
- u_int32 dsp, dsa;
- int script_ofs;
int i;
/*
/*=========================================
** 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
** sorry, have to kill ALL jobs ...
*/
- ncr_init (np, "fatal error", HS_FAIL);
- np->disc = 1;
+ ncr_start_reset(np, 2);
}
/*==========================================================
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;
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) {
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;
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))
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 */
driver_setup.verbose,
driver_setup.debug,
YesNo(driver_setup.led_pin),
- driver_setup.settle_time,
+ driver_setup.settle_delay,
driver_setup.irqm);
}
#undef YesNo
#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,
if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip,
pci_bus, pci_device_fn, /* no options */ 0))
++count;
+ }
}
return count;
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);
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");
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 1.18b"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 1.18d"
/*
** If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined,
*/
#define SCSI_NCR_ALWAYS_SIMPLE_TAG
-#define SCSI_NCR_MAX_SCATTER (128)
+#define SCSI_NCR_MAX_SCATTER (127)
#define SCSI_NCR_MAX_TARGET (16)
#define SCSI_NCR_MAX_HOST (2)
#define SCSI_NCR_TIMEOUT_ALERT (3*HZ)
#define SCSI_NCR_CAN_QUEUE (7*SCSI_NCR_MAX_TAGS)
#define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER-1)
+#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER)
#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5)
SCSI_NCR_SETUP_SPECIAL_FEATURES, \
SCSI_NCR_SETUP_ULTRA_SCSI, \
SCSI_NCR_SETUP_FORCE_SYNC_NEGO, \
+ 0, \
1, \
SCSI_NCR_SETUP_DEFAULT_TAGS, \
SCSI_NCR_SETUP_DEFAULT_SYNC, \
0, \
0, \
0, \
+ 0, \
2, \
0, \
255, \
/*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. */
#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 */
-/* ppa.c -- low level driver for the IOMEGA PPA3
- parallel port SCSI host adapter.
-
- (The PPA3 is the embedded controller in the ZIP drive.)
-
- (c) 1995,1996 Grant R. Guenther, grant@torque.net,
- under the terms of the GNU Public License.
-
-*/
+/* ppa.c -- low level driver for the IOMEGA PPA3
+ * parallel port SCSI host adapter.
+ *
+ * (The PPA3 is the embedded controller in the ZIP drive.)
+ *
+ * (c) 1995,1996 Grant R. Guenther, grant@torque.net,
+ * under the terms of the GNU Public License.
+ *
+ */
/* This driver was developed without the benefit of any technical
- specifications for the interface. Instead, a modified version of
- DOSemu was used to monitor the protocol used by the DOS driver
- for this adapter. I have no idea how my programming model relates
- to IOMEGA's design.
-
- IOMEGA's driver does not generate linked commands. I've never
- observed a SCSI message byte in the protocol transactions, so
- I am assuming that as long as linked commands are not used
- we won't see any.
-
- So far, this driver has been tested with the embedded PPA3 in the
- ZIP drive, only. It can detect and adapt to 4- and 8-bit parallel
- ports, but there is currently no support for EPP or ECP ports, as
- I have been unable to make the DOS drivers work in these modes on
- my test rig.
-
- For more information, see the file drivers/scsi/README.ppa.
-
-*/
-
-#define PPA_VERSION "0.26"
-
-/* Change these variables here or with insmod or with a LILO or LOADLIN
- command line argument
-*/
-
-static int ppa_base = 0x378; /* parallel port address */
-static int ppa_speed_high = 1; /* port delay in data phase */
-static int ppa_speed_low = 6; /* port delay otherwise */
-static int ppa_nybble = 0; /* don't force nybble mode */
-
-
-#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
-#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
-#define PPA_SPIN_TMO 5000000 /* ppa_wait loop limiter */
-#define PPA_SECTOR_SIZE 512 /* for a performance hack only */
-
-#include <linux/stddef.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/tqueue.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/blk.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <asm/io.h>
-#include "sd.h"
-#include "hosts.h"
+ * specifications for the interface. Instead, a modified version of
+ * DOSemu was used to monitor the protocol used by the DOS driver
+ * for this adapter. I have no idea how my programming model relates
+ * to IOMEGA's design.
+ *
+ * IOMEGA's driver does not generate linked commands. I've never
+ * observed a SCSI message byte in the protocol transactions, so
+ * I am assuming that as long as linked commands are not used
+ * we won't see any.
+ *
+ * For more information, see the file drivers/scsi/README.ppa.
+ *
+ */
+
+/*
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather. [0.26-athena]
+ *
+ * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au)
+ * in response to this driver "mis-behaving" on his machine.
+ * Fixed EPP to handle "software" changing of EPP port data direction.
+ * Chased down EPP timeouts
+ * Made this driver "kernel version friendly" [0.28-athena]
+ *
+ * Really hacked it out of existance (David Campbell)
+ * EPP timeouts no longer occur (yet better handling)
+ * Probes known parallel ports
+ * Autodetects EPP / ECP / PS2 / NIBBLE
+ * Support for multiple devices (does anyone need this??)
+ * [0.29-Curtin]
+ * [ Stuff removed ]
+ *
+ * Modified PEDANTIC for less PEDANTIC drivers as people
+ * were complaining about speed (I received a report indicating
+ * that PEDANTIC is necessary for WinBond chipsets.
+ * Updated config_ppa and Makefile
+ * [0.36b-Curtin]
+ *
+ * First round of cleanups
+ * Remove prior 1.3.34 kernel support
+ * SMC support changed
+ * ECP+EPP detection always invoked.
+ * If compat mode => PS/2
+ * else ecp_sync() called (ECP+EPP uses FIFO).
+ * Added routine to detect interrupt channel for ECP (not used)
+ * Changed version numbering
+ * 1 Major number
+ * 00 Minor revision number
+ * ALPHA Expected stability (alpha, beta, stable)
+ * [Curtin-1-00-ALPHA]
+ * Second round of cleanups
+ * Clean up timer queues
+ * Fixed problem with non-detection of PS/2 ports
+ * SMC ECP+EPP confirmed to work, remove option from config_ppa
+ * [Curtin-1-01-ALPHA]
+ *
+ * Parport hits with a vengance!!
+ * Several internal revisions have been made with huge amounts of
+ * fixes including:
+ * ioport_2_hostno removed (unique_id is quicker)
+ * SMC compat option is history
+ * Driver name / info hardwired
+ * Played with inlines and saved 4k on module
+ * Parport support
+ * Using PnP Parport allows use of printer attached to
+ * ZIP drive.
+ * Numerous fixups for device registration and to allow
+ * proper aborts.
+ * Version jumps a few numbers here - considered BETA
+ * Shipping Parport with monolithic driver :)
+ * [Curtin-1-05-BETA]
+ *
+ * Fixed code to ensure SCSI abort will release the SCSI command
+ * if the driver is STILL trying to claim the parport (PNP ver)
+ * Now I have to fix the lp driver then there will NEVER be a
+ * problem.
+ * Got around to doing the ppa_queuecommand() clean up
+ * Fixed bug relating to SMC EPP+ECP and monolithic driver
+ * [Curtin-1-06-BETA]
+ *
+ * Where did the ppa_setup() code disappear to ??
+ * Back in now...
+ * Distribution of ppa now independent of parport (less work for me).
+ * Also cleaned up the port detection to allow for variations on
+ * IO aliasing (in an attempt to fix a few problems with some
+ * machines...)
+ * [Curtin-1-07-BETA]
+ *
+ * Rewrote detection code for monolithic driver and ported changes to
+ * parport driver. Result is more stable detection of hardware and
+ * better immunity to port aliasing (old XT cards).
+ * Parport 0.16 (or better) is required for parport operation and
+ * ECP+EPP modes, otherwise the latest parport edition is recommended.
+ *
+ * When using EPP and writing to disk CPU usage > 40%, while reading <10%.
+ * This is due to ZIP drive IO scheduling, the drive does a verify after
+ * write to ensure data integrity (removable media is ALWAYS questionable
+ * since you never know where it has been).
+ * Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
+ * over 10 sectors per jiffy.
+ *
+ * Hmm... I think I know a way but it will send the driver into
+ * ALPHA state again.
+ * [Curtin-1-08-STABLE]
+ */
+
+/* The following #define is to avoid a clash with hosts.c */
+#define PPA_CODE 1
#include "ppa.h"
-
-struct proc_dir_entry proc_scsi_ppa =
- { PROC_SCSI_PPA, 3, "ppa", S_IFDIR|S_IRUGO|S_IXUGO, 2 };
-
-static int ppa_abort_flag = 0;
-static int ppa_error_code = DID_OK;
-static char ppa_info_string[132];
-static Scsi_Cmnd *ppa_current = 0;
-static void (*ppa_done) (Scsi_Cmnd *);
-static int ppa_port_delay;
-
-void out_p( short port, char byte)
-
-{ outb(byte,ppa_base+port);
- udelay(ppa_port_delay);
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate
+ */
+#define PPA_AUTODETECT 0 /* Autodetect mode */
+#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
+#define PPA_PS2 2 /* PS/2 byte mode */
+#define PPA_EPP_8 3 /* EPP mode, 8 bit */
+#define PPA_EPP_16 4 /* EPP mode, 16 bit */
+#define PPA_EPP_32 5 /* EPP mode, 32 bit */
+#define PPA_UNKNOWN 6 /* Just in case... */
+
+static char *PPA_MODE_STRING[] =
+{
+ "Autodetect",
+ "SPP",
+ "PS/2",
+ "EPP 8 bit",
+ "EPP 16 bit",
+ "EPP 32 bit",
+ "Unknown"};
+
+typedef struct {
+ struct ppd *dev; /* Parport device entry */
+ int speed; /* General PPA delay constant */
+ int speed_fast; /* Const for nibble/byte modes */
+ int epp_speed; /* Reset time period */
+ int mode; /* Transfer mode */
+ int timeout; /* Number of timeouts */
+ int host; /* Host number (for proc) */
+ int abort_flag; /* Abort flag */
+ int error_code; /* Error code */
+ int ppa_failed; /* Failure flag */
+ Scsi_Cmnd *cur_cmd; /* Current queued command */
+ void (*done) (Scsi_Cmnd *); /* Done func for queuecommand */
+ struct tq_struct ppa_tq; /* Polling interupt stuff */
+ struct wait_queue *ppa_wait_q; /* Used for PnP stuff */
+} ppa_struct;
+
+static void ppa_interrupt(void *data);
+/* I know that this is a mess but it works!! */
+#define NO_HOSTS 4
+static ppa_struct ppa_hosts[NO_HOSTS] =
+{
+ {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+ {0, 0, ppa_interrupt, NULL}, NULL},
+ {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+ {0, 0, ppa_interrupt, NULL}, NULL},
+ {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+ {0, 0, ppa_interrupt, NULL}, NULL},
+ {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+ {0, 0, ppa_interrupt, NULL}, NULL}
+};
+
+/* This is a global option */
+static int ppa_speed = -1; /* Set to >0 to act as a global value */
+static int ppa_speed_fast = -1; /* ditto.. */
+static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
+
+/* other options */
+#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
+#define PPA_BURST_SIZE 512 /* block size for bulk transfers */
+#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
+#define PPA_SPIN_TMO 500000 /* ppa_wait loop limiter */
+
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define PPA_BASE(x) ppa_hosts[(x)].dev->port->base
+
+/* Port IO - Sorry Grant but I prefer the following symbols */
+#define r_dtr(x) inb(PPA_BASE(x))
+#define r_str(x) inb(PPA_BASE(x)+1)
+#define r_ctr(x) inb(PPA_BASE(x)+2)
+#define r_epp(x) inb(PPA_BASE(x)+4)
+#define r_fifo(x) inb(PPA_BASE(x)+0x400)
+#define r_ecr(x) inb(PPA_BASE(x)+0x402)
+
+#define w_dtr(x,y) outb(y, PPA_BASE(x))
+#define w_str(x,y) outb(y, PPA_BASE(x)+1)
+#define w_ctr(x,y) outb(y, PPA_BASE(x)+2);\
+ udelay( ppa_hosts[(x)].speed)
+#define w_epp(x,y) outb(y, PPA_BASE(x)+4)
+#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400)
+#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402)
+
+int ppa_wakeup(void *ref)
+{
+ ppa_struct *ppa_dev = (ppa_struct *) ref;
+
+ if (!ppa_dev->ppa_wait_q)
+ return 1; /* Wake up whom ? */
+
+ /* Claim the Parport */
+ if (parport_claim(ppa_dev->dev))
+ return 1; /* Shouldn't happen */
+
+ wake_up(&ppa_dev->ppa_wait_q);
+ return 0;
}
-char in_p( short port)
+int ppa_release(struct Scsi_Host *host)
+{
+ int host_no = host->unique_id;
-{ return inb(ppa_base+port);
- udelay(ppa_port_delay);
+ printk("Releasing ppa%i\n", host_no);
+ parport_unregister_device(ppa_hosts[host_no].dev);
+ return 0;
}
-void ppa_d_pulse( char b )
+static int ppa_pb_claim(int host_no)
+{
+ if (parport_claim(ppa_hosts[host_no].dev)) {
+ sleep_on(&ppa_hosts[host_no].ppa_wait_q);
+ ppa_hosts[host_no].ppa_wait_q = NULL;
+
+ /* Check to see if we were aborted or reset */
+ if (ppa_hosts[host_no].dev->port->cad !=
+ ppa_hosts[host_no].dev) {
+ printk("Abort detected on ppa%i\n", host_no);
+ return 1;
+ }
+ }
+ return 0;
+}
-{ out_p(0,b);
- out_p(2,0xc); out_p(2,0xe); out_p(2,0xc); out_p(2,0x4); out_p(2,0xc);
+static void ppa_pb_release(int host_no)
+{
+ parport_release(ppa_hosts[host_no].dev);
}
-void ppa_disconnect( void )
-{ ppa_d_pulse(0);
- ppa_d_pulse(0x3c);
- ppa_d_pulse(0x20);
- ppa_d_pulse(0xf);
-}
+/* Placed here so everyone knows what ecp_sync does.. */
+static void ecp_sync(int host_no)
+{
+ int i, r;
-void ppa_c_pulse( char b )
+ r = r_ecr(host_no);
+ if ((r & 0xe0) != 0x80)
+ return;
-{ out_p(0,b);
- out_p(2,0x4); out_p(2,0x6); out_p(2,0x4); out_p(2,0xc);
-}
+ for (i = 0; i < 100; i++) {
+ r = r_ecr(host_no);
+ if (r & 0x01)
+ return;
+ udelay(5);
+ }
-void ppa_connect( void )
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
+}
-{ ppa_c_pulse(0);
- ppa_c_pulse(0x3c);
- ppa_c_pulse(0x20);
- ppa_c_pulse(0x8f);
+static inline void ppa_d_pulse(int host_no, char b)
+{
+ w_dtr(host_no, b);
+ w_ctr(host_no, 0xc);
+ w_ctr(host_no, 0xe);
+ w_ctr(host_no, 0xc);
+ w_ctr(host_no, 0x4);
+ w_ctr(host_no, 0xc);
}
-void ppa_do_reset( void )
+static void ppa_disconnect(int host_no)
+{
+ ppa_d_pulse(host_no, 0);
+ ppa_d_pulse(host_no, 0x3c);
+ ppa_d_pulse(host_no, 0x20);
+ ppa_d_pulse(host_no, 0xf);
-{ out_p(2,0); /* This is really just a guess */
- udelay(100);
+ ppa_pb_release(host_no);
}
-char ppa_select( int initiator, int target )
+static inline void ppa_c_pulse(int host_no, char b)
+{
+ w_dtr(host_no, b);
+ w_ctr(host_no, 0x4);
+ w_ctr(host_no, 0x6);
+ w_ctr(host_no, 0x4);
+ w_ctr(host_no, 0xc);
+}
-{ char r;
- int k;
+static int ppa_connect(int host_no, int flag)
+{
+ int retv = ppa_pb_claim(host_no);
- r = in_p(1);
- out_p(0,(1<<target)); out_p(2,0xe); out_p(2,0xc);
- out_p(0,(1<<initiator)); out_p(2,0x8);
+ ppa_c_pulse(host_no, 0);
+ ppa_c_pulse(host_no, 0x3c);
+ ppa_c_pulse(host_no, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) &&
+ IN_EPP_MODE(ppa_hosts[host_no].mode))
+ ppa_c_pulse(host_no, 0xcf);
+ else
+ ppa_c_pulse(host_no, 0x8f);
- k = 0;
- while ( !(r = (in_p(1) & 0xf0)) && (k++ < PPA_SELECT_TMO)) barrier();
- return r;
+ return retv;
}
-char ppa_wait( void )
+static void ppa_do_reset(int host_no)
+{
+ /*
+ * SCSI reset taken from ppa_init and checked with
+ * Iomega document that Grant has (via email :(
+ */
+ ppa_pb_claim(host_no);
+ ppa_disconnect(host_no);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+
+ w_ctr(host_no, 0x6);
+ w_ctr(host_no, 0x4);
+ w_dtr(host_no, 0x40);
+ w_ctr(host_no, 0x8);
+ udelay(50);
+ w_ctr(host_no, 0xc);
+
+ ppa_disconnect(host_no);
+}
-/* Wait for the high bit to be set.
+static char ppa_select(int host_no, int initiator, int target)
+{
+ char r;
+ int k;
- In principle, this could be tied to an interrupt, but the adapter
- doesn't appear to be designed to support interrupts. We spin on
- the 0x80 ready bit.
-*/
+ r = r_str(host_no); /* TODO */
-{ int k;
- char r;
+ w_dtr(host_no, (1 << target));
+ w_ctr(host_no, 0xe);
+ w_ctr(host_no, 0xc);
+ w_dtr(host_no, (1 << initiator));
+ w_ctr(host_no, 0x8);
- ppa_error_code = DID_OK;
- k = 0;
- while (!((r = in_p(1)) & 0x80)
- && (k++ < PPA_SPIN_TMO) && !ppa_abort_flag ) barrier();
-
- if (ppa_abort_flag) {
- if (ppa_abort_flag == 1) ppa_error_code = DID_ABORT;
- else { ppa_do_reset();
- ppa_error_code = DID_RESET;
- }
- ppa_disconnect();
- return 0;
- }
- if (k >= PPA_SPIN_TMO) {
- ppa_error_code = DID_TIME_OUT;
- ppa_disconnect();
- return 0; /* command timed out */
- }
- return (r & 0xf0);
-}
+ k = 0;
+ while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
+ barrier();
-int ppa_init( void )
+ if (k >= PPA_SELECT_TMO)
+ return 0;
-/* This is based on a trace of what the Iomega DOS 'guest' driver does.
- I've tried several different kinds of parallel ports with guest and
- coded this to react in the same ways that it does.
+ return r;
+}
- The return value from this function is just a hint about where the
- handshaking failed.
+static void ppa_fail(int host_no, int error_code)
+{
+ ppa_hosts[host_no].error_code = error_code;
+ ppa_hosts[host_no].ppa_failed = 1;
+ ppa_disconnect(host_no);
+}
-*/
+/*
+ * Wait for the high bit to be set.
+ *
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts. We spin on
+ * the 0x80 ready bit.
+ */
+static char ppa_wait(int host_no)
+{
+ int k;
+ char r;
+
+ k = 0;
+ while (!((r = r_str(host_no)) & 0x80)
+ && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
+ barrier();
+
+ /* check if we were interrupted */
+ if (ppa_hosts[host_no].abort_flag) {
+ if (ppa_hosts[host_no].abort_flag == 1)
+ ppa_fail(host_no, DID_ABORT);
+ else {
+ ppa_do_reset(host_no);
+ ppa_fail(host_no, DID_RESET);
+ }
+ return 0;
+ }
+ /* check if timed out */
+ if (k >= PPA_SPIN_TMO) {
+ ppa_fail(host_no, DID_TIME_OUT);
+ return 0; /* command timed out */
+ }
+ /*
+ * return some status information.
+ * Semantics: 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ return (r & 0xf0);
+}
-{ char r, s;
- out_p(0,0xaa);
- if (in_p(0) != (char) 0xaa) return 1;
- ppa_disconnect();
- ppa_connect();
- out_p(2,0x6);
- if ((in_p(1) & 0xf0) != 0xf0) return 2;
- out_p(2,0x4);
- if ((in_p(1) & 0xf0) != 0x80) return 3;
- ppa_disconnect();
- s = in_p(2);
- out_p(2,0xec);
- out_p(0,0x55);
- r = in_p(0);
- if (r != (char) 0xff) {
- ppa_nybble = 1;
- if (r != (char) 0x55) return 4;
- out_p(0,0xaa); if (in_p(0) != (char) 0xaa) return 5;
- }
- out_p(2,s);
- ppa_connect();
- out_p(0,0x40); out_p(2,0x8); out_p(2,0xc);
- ppa_disconnect();
-
- return 0;
-}
-
-int ppa_start( Scsi_Cmnd * cmd )
-
-{ int k;
-
- ppa_error_code = DID_OK;
- ppa_abort_flag = 0;
-
- if (cmd->target == PPA_INITIATOR) {
- ppa_error_code = DID_BAD_TARGET;
- return 0;
- }
- ppa_connect();
- if (!ppa_select(PPA_INITIATOR,cmd->target)) {
- ppa_disconnect();
- ppa_error_code = DID_NO_CONNECT;
- return 0;
- }
- out_p(2,0xc);
-
- for (k=0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_wait()) return 0;
- out_p(0,cmd->cmnd[k]);
- out_p(2,0xe);
- out_p(2,0xc);
- }
-
-#ifdef PPA_DEBUG
- printk("PPA: command out: ");
- for (k=0; k < cmd->cmd_len; k++)
- printk("%3x",(cmd->cmnd[k]) & 0xff );
- printk("\n");
-#endif
+/*
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ *
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ *
+ */
+static int ppa_init(int host_no)
+{
+ if (ppa_pb_claim(host_no))
+ return 1;
+ ppa_disconnect(host_no);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+
+ w_ctr(host_no, 0x6);
+ if ((r_str(host_no) & 0xf0) != 0xf0) {
+ ppa_pb_release(host_no);
+ return 2;
+ }
+ w_ctr(host_no, 0x4);
+ if ((r_str(host_no) & 0xf0) != 0x80) {
+ ppa_pb_release(host_no);
+ return 3;
+ }
+ /* This is a SCSI reset signal */
+ w_dtr(host_no, 0x40);
+ w_ctr(host_no, 0x8);
+ udelay(50);
+ w_ctr(host_no, 0xc);
+
+ ppa_disconnect(host_no);
+
+ return 0;
+}
- return 1;
+/*
+ * check the epp status. After a EPP transfer, it should be true that
+ * 1) the TIMEOUT bit (SPP_STR.0) is clear
+ * 2) the READY bit (SPP_STR.7) is set
+ */
+static int ppa_check_epp_status(int host_no)
+{
+ char r;
+ r = r_str(host_no);
+
+ if (r & 1) {
+ /* EPP timeout, according to the PC87332 manual */
+ /* Semantics of clearing EPP timeout bit.
+ * PC87332 - reading SPP_STR does it...
+ * SMC - write 1 to EPP timeout bit
+ * Others - (???) write 0 to EPP timeout bit
+ */
+ w_str(host_no, r);
+ w_str(host_no, r & 0xfe);
+ ppa_hosts[host_no].timeout++;
+ printk("PPA: EPP timeout on port 0x%04x\n",
+ PPA_BASE(host_no));
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ }
+ if (!(r & 0x80)) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ return 1;
}
-int ppa_completion( Scsi_Cmnd * cmd )
+static inline int ppa_force_epp_byte(int host_no, char x)
+{
+/* This routine forces a byte down the EPP data port whether the
+ * device is ready or not...
+ */
+ char r;
+
+ w_epp(host_no, x);
+
+ r = r_str(host_no);
+ if (!(r & 1))
+ return 1;
+
+ if (ppa_hosts[host_no].epp_speed > 0) {
+ /* EPP timeout, according to the PC87332 manual */
+ /* Semantics of clearing EPP timeout bit.
+ * PC87332 - reading SPP_STR does it...
+ * SMC - write 1 to EPP timeout bit
+ * Others - (???) write 0 to EPP timeout bit
+ */
+ w_str(host_no, r);
+ w_str(host_no, r & 0xfe);
+
+ /* Take a deep breath, count to 10 and then... */
+ udelay(ppa_hosts[host_no].epp_speed);
+
+ /* Second time around */
+ w_epp(host_no, x);
+
+ r = r_str(host_no);
+ }
+ if (r & 1) {
+ w_str(host_no, r);
+ w_str(host_no, r & 0xfe);
+ ppa_hosts[host_no].timeout++;
+ printk("PPA: warning: EPP timeout\n");
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ } else
+ return 1;
+}
-/* The bulk flag enables some optimisations in the data transfer loops,
- it should be true for any command that transfers data in integral
- numbers of sectors.
+static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ int k;
+
+ w_ctr(host_no, 0x4);
+ for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
+ if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
+ return 0;
+ }
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return 1;
+}
- The driver appears to remain stable if we speed up the parallel port
- i/o in this function, but not elsewhere.
-*/
+static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ int k;
+
+ w_ctr(host_no, 0xc);
+
+ for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
+ if (!ppa_wait(host_no))
+ return 0;
+ w_dtr(host_no, cmd->cmnd[k]);
+ w_ctr(host_no, 0xe);
+ w_ctr(host_no, 0xc);
+ }
+ return 1;
+}
-{ char r, l, h, v;
- int dir, cnt, blen, fast, bulk;
- char *buffer;
+static int ppa_start(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+
+ /*
+ * by default, the command failed,
+ * unless explicitly completed in ppa_completion().
+ */
+ ppa_hosts[host_no].error_code = DID_ERROR;
+ ppa_hosts[host_no].abort_flag = 0;
+ ppa_hosts[host_no].ppa_failed = 0;
+
+ if (cmd->target == PPA_INITIATOR) {
+ ppa_hosts[host_no].error_code = DID_BAD_TARGET;
+ ppa_hosts[host_no].ppa_failed = 1;
+ return 0;
+ }
+ if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
+ return 0;
+
+ if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
+ ppa_fail(host_no, DID_NO_CONNECT);
+ return 0;
+ }
+ if (IN_EPP_MODE(ppa_hosts[host_no].mode))
+ return ppa_send_command_epp(cmd);
+ else
+ return ppa_send_command_normal(cmd);
+}
-#ifdef PPA_DEBUG
- int k;
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol.
+ */
+static inline int ppa_outs(int host_no, char *buffer, int len)
+{
+ int k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+ int r;
#endif
- if (!(r = ppa_wait())) return 0;
- v = cmd->cmnd[0];
- bulk = ((v==READ_6)||(v==READ_10)||(v==WRITE_6)||(v==WRITE_10));
- buffer = cmd->request_buffer;
- blen = cmd->request_bufflen;
- cnt = 0; dir = 0;
- if (r == (char) 0xc0) dir = 1; /* d0 = read c0 = write f0 = status */
-
- ppa_port_delay = ppa_speed_high;
-
- while (r != (char) 0xf0) {
- if (((r & 0xc0) != 0xc0 ) || (cnt >= blen)) {
- ppa_disconnect();
- ppa_error_code = DID_ERROR;
- return 0;
- }
- fast = bulk && ((blen - cnt) >= PPA_SECTOR_SIZE);
- if (dir) do {
- out_p(0,buffer[cnt++]);
- out_p(2,0xe); out_p(2,0xc);
- if (!fast) break;
- } while (cnt % PPA_SECTOR_SIZE);
- else {
- if (ppa_nybble) do {
- out_p(2,0x4); h = in_p(1);
- out_p(2,0x6); l = in_p(1);
- v = ((l >> 4) & 0x0f) + (h & 0xf0);
- buffer[cnt++] = v;
- if (!fast) break;
- } while (cnt % PPA_SECTOR_SIZE);
- else do {
- out_p(2,0x25); v = in_p(0); out_p(2,0x27);
- buffer[cnt++] = v;
- if (!fast) break;
- } while (cnt % PPA_SECTOR_SIZE);
- if (!ppa_nybble) {
- out_p(2,0x5); out_p(2,0x4);
- }
- out_p(2,0xc);
- }
- if (!(r = ppa_wait())) return 0;
- }
-
- ppa_port_delay = ppa_speed_low;
-
- out_p(2,0x4); /* now read status byte */
- h = in_p(1);
- out_p(2,0x6);
- l = in_p(1);
- out_p(2,0xc);
- r = ((l >> 4) & 0x0f) + (h & 0xf0);
-
- out_p(2,0xe); out_p(2,0xc);
- ppa_disconnect();
-
-#ifdef PPA_DEBUG
- printk("PPA: status: %x, data[%d]: ",r & STATUS_MASK,cnt);
- if (cnt > 12) cnt = 12;
- for (k=0; k < cnt; k++)
- printk("%3x",buffer[k] & 0xff );
- printk("\n");
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ /* 8 bit output, with a loop */
+ for (k = len; k; k--) {
+ w_dtr(host_no, *buffer++);
+ w_ctr(host_no, 0xe);
+ w_ctr(host_no, 0xc);
+ }
+ return 1; /* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+ case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+ w_ctr(host_no, 0x4);
+ for (k = len; k; k -= 4) {
+ w_epp(host_no, *buffer++);
+ w_epp(host_no, *buffer++);
+ w_epp(host_no, *buffer++);
+ w_epp(host_no, *buffer++);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return 1;
+#endif
+ case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+ w_ctr(host_no, 0x4);
+ for (k = len; k; k -= 2) {
+ w_epp(host_no, *buffer++);
+ w_epp(host_no, *buffer++);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return 1;
+#endif
+ case PPA_EPP_8:
+ w_ctr(host_no, 0x4);
+ for (k = len; k; k--) {
+ w_epp(host_no, *buffer++);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return 1;
+#else
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ w_ctr(host_no, 0x4);
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_EPP_8:
+ outsb(PPA_BASE(host_no) + 0x04,
+ buffer, len);
+ break;
+ case PPA_EPP_16:
+ outsw(PPA_BASE(host_no) + 0x04,
+ buffer, len / 2);
+ break;
+ case PPA_EPP_32:
+ outsl(PPA_BASE(host_no) + 0x04,
+ buffer, len / 4);
+ break;
+ }
+ k = ppa_check_epp_status(host_no);
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return k;
#endif
- return (r & STATUS_MASK);
+ default:
+ printk("PPA: bug in ppa_outs()\n");
+ }
+ return 0;
}
-/* deprecated synchronous interface */
-
-int ppa_command( Scsi_Cmnd * cmd )
-
-{ int s;
-
- sti();
- s = 0;
- if (ppa_start(cmd))
- if (ppa_wait())
- s = ppa_completion(cmd);
- return s + (ppa_error_code << 16);
+static inline int ppa_outb(int host_no, char d)
+{
+ int k;
+
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ w_dtr(host_no, d);
+ w_ctr(host_no, 0xe);
+ w_ctr(host_no, 0xc);
+ return 1; /* assume transfer went OK */
+
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ w_ctr(host_no, 0x4);
+ w_epp(host_no, d);
+ k = ppa_check_epp_status(host_no);
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return k;
+
+ default:
+ printk("PPA: bug in ppa_outb()\n");
+ }
+ return 0;
}
-/* pseudo-interrupt queueing interface */
-
-/* Since the PPA itself doesn't generate interrupts, we use
- the scheduler's task queue to generate a stream of call-backs and
- complete the request when the drive is ready.
-*/
-
-static void ppa_interrupt( void *data);
-
-static struct tq_struct ppa_tq = {0,0,ppa_interrupt,NULL};
-
-static void ppa_interrupt( void *data)
+static inline int ppa_ins(int host_no, char *buffer, int len)
+{
+ int k, h, l;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+ int r;
+#endif
-{ Scsi_Cmnd *cmd;
- void (*done) (Scsi_Cmnd *);
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ for (k = len; k; k--) {
+ w_ctr(host_no, 0x4);
+ h = r_str(host_no);
+ w_ctr(host_no, 0x6);
+ l = r_str(host_no);
+ *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
+ }
+ w_ctr(host_no, 0xc);
+ return 1; /* assume transfer went OK */
+
+ case PPA_PS2:
+ /* 8 bit input, with a loop */
+ for (k = len; k; k--) {
+ w_ctr(host_no, 0x25);
+ *buffer++ = r_dtr(host_no);
+ w_ctr(host_no, 0x27);
+ }
+ w_ctr(host_no, 0x5);
+ w_ctr(host_no, 0x4);
+ w_ctr(host_no, 0xc);
+ return 1; /* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+ case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+ w_ctr(host_no, 0x24);
+ for (k = len; k; k -= 4) {
+ *buffer++ = r_epp(host_no);
+ *buffer++ = r_epp(host_no);
+ *buffer++ = r_epp(host_no);
+ *buffer++ = r_epp(host_no);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0x2c);
+ ecp_sync(host_no);
+ return 1;
+#endif
+ case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+ w_ctr(host_no, 0x24);
+ for (k = len; k; k -= 2) {
+ *buffer++ = r_epp(host_no);
+ *buffer++ = r_epp(host_no);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0x2c);
+ ecp_sync(host_no);
+ return 1;
+#endif
+ case PPA_EPP_8:
+ w_ctr(host_no, 0x24);
+ for (k = len; k; k--) {
+ *buffer++ = r_epp(host_no);
+ r = ppa_check_epp_status(host_no);
+ if (!r)
+ return r;
+ }
+ w_ctr(host_no, 0x2c);
+ ecp_sync(host_no);
+ return 1;
+ break;
+#else
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ w_ctr(host_no, 0x24);
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_EPP_8:
+ insb(PPA_BASE(host_no) + 0x04,
+ buffer, len);
+ break;
+ case PPA_EPP_16:
+ insw(PPA_BASE(host_no) + 0x04,
+ buffer, len / 2);
+ break;
+ case PPA_EPP_32:
+ insl(PPA_BASE(host_no) + 0x04,
+ buffer, len / 4);
+ break;
+ }
+ k = ppa_check_epp_status(host_no);
+ w_ctr(host_no, 0x2c);
+ ecp_sync(host_no);
+ return k;
+#endif
- cmd = ppa_current;
- done = ppa_done;
- if (!cmd) return;
-
- if (ppa_abort_flag) {
- ppa_disconnect();
- if(ppa_abort_flag == 1) cmd->result = DID_ABORT << 16;
- else { ppa_do_reset();
- cmd->result = DID_RESET << 16;
- }
- ppa_current = 0;
- done(cmd);
- return;
- }
- if (!( in_p(1) & 0x80)) {
- queue_task(&ppa_tq,&tq_scheduler);
- return;
- }
- cmd->result = ppa_completion(cmd) + (ppa_error_code << 16);
- ppa_current = 0;
- done(cmd);
- return;
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ }
+ return 0;
}
-int ppa_queuecommand( Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-
-{ if (ppa_current) return 0;
- sti();
- ppa_current = cmd;
- ppa_done = done;
- if (!ppa_start(cmd)) {
- cmd->result = ppa_error_code << 16;
- ppa_current = 0;
- done(cmd);
- return 0;
- }
- queue_task(&ppa_tq,&tq_scheduler);
- return 0;
+static int ppa_inb(int host_no, char *buffer)
+{
+ int h, l, k;
+
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input */
+ w_ctr(host_no, 0x4);
+ h = r_str(host_no);
+ w_ctr(host_no, 0x6);
+ l = r_str(host_no);
+ *buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
+ w_ctr(host_no, 0xc);
+ return 1; /* assume transfer went OK */
+
+ case PPA_PS2:
+ /* 8 bit input */
+ w_ctr(host_no, 0x25);
+ *buffer++ = r_dtr(host_no);
+ w_ctr(host_no, 0x27);
+ w_ctr(host_no, 0x5);
+ w_ctr(host_no, 0x4);
+ w_ctr(host_no, 0xc);
+ return 1; /* assume transfer went OK */
+
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ w_ctr(host_no, 0x24);
+ *buffer = r_epp(host_no);
+ k = ppa_check_epp_status(host_no);
+ w_ctr(host_no, 0xc);
+ ecp_sync(host_no);
+ return k;
+
+ default:
+ printk("PPA: bug in ppa_inb()\n");
+ }
+ return 0;
}
-int ppa_detect( Scsi_Host_Template * host )
-
-{ struct Scsi_Host *hreg;
- int rs;
-
- /* can we have the ports ? */
-
- if (check_region(ppa_base,3)) {
- printk("PPA: ports at 0x%3x are not available\n",ppa_base);
- return 0;
- }
-
- /* attempt to initialise the controller */
-
- ppa_port_delay = ppa_speed_low;
-
- rs = ppa_init();
- if (rs) {
- printk("PPA: unable to initialise controller at 0x%x, error %d\n",
- ppa_base,rs);
- return 0;
- }
-
- /* now the glue ... */
-
- host->proc_dir = &proc_scsi_ppa;
-
- request_region(ppa_base,3,"ppa");
-
- host->can_queue = PPA_CAN_QUEUE;
-
- hreg = scsi_register(host,0);
- hreg->io_port = ppa_base;
- hreg->n_io_port = 3;
- hreg->dma_channel = -1;
-
- sprintf(ppa_info_string,
- "PPA driver version %s using %d-bit mode on port 0x%x.",
- PPA_VERSION,8-ppa_nybble*4,ppa_base);
- host->name = ppa_info_string;
-
- return 1; /* 1 host detected */
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ *
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int ppa_completion(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+
+ char r, l, h, v;
+ int dir, cnt, blen, fast, bulk, status;
+ char *buffer;
+ struct scatterlist *sl;
+ int current_segment, nsegment;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) ||
+ (v == WRITE_6) ||
+ (v == WRITE_10));
+
+ /* code for scatter/gather: */
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ sl = (struct scatterlist *) cmd->request_buffer;
+ blen = sl->length;
+ buffer = sl->address;
+ } else {
+ /* else fill the only available buffer */
+ sl = NULL;
+ buffer = cmd->request_buffer;
+ blen = cmd->request_bufflen;
+ }
+ current_segment = 0;
+ nsegment = cmd->use_sg;
+
+ cnt = 0;
+
+ /* detect transfer direction */
+ dir = 0;
+ if (!(r = ppa_wait(host_no)))
+ return 0;
+ if (r == (char) 0xc0)
+ dir = 1; /* d0 = read c0 = write f0 = status */
+
+ while (r != (char) 0xf0) {
+ if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ /* determine if we should use burst I/O */
+ fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
+ ((((long)buffer + cnt)) & 0x3) == 0);
+
+ if (fast) {
+ if (dir)
+ status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
+ else
+ status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
+ cnt += PPA_BURST_SIZE;
+ } else {
+ if (dir)
+ status = ppa_outb(host_no, buffer[cnt]);
+ else
+ status = ppa_inb(host_no, &buffer[cnt]);
+ cnt++;
+ }
+
+ if (!status || !(r = ppa_wait(host_no)))
+ return 0;
+
+ if (sl && cnt == blen) {
+ /* if scatter/gather, advance to the next segment */
+ if (++current_segment < nsegment) {
+ ++sl;
+ blen = sl->length;
+ buffer = sl->address;
+ cnt = 0;
+ }
+ /*
+ * the else case will be captured by the (cnt >= blen)
+ * test above.
+ */
+ }
+ }
+
+ /* read status and message bytes */
+ if (!ppa_inb(host_no, &l)) /* read status byte */
+ return 0;
+ if (!(ppa_wait(host_no)))
+ return 0;
+ if (!ppa_inb(host_no, &h)) /* read message byte */
+ return 0;
+
+ ppa_disconnect(host_no);
+
+ ppa_hosts[host_no].error_code = DID_OK;
+ return (h << 8) | (l & STATUS_MASK);
}
-int ppa_biosparam( Disk * disk, kdev_t dev, int ip[])
-
-/* Apparently the the disk->capacity attribute is off by 1 sector
- for all disk drives. We add the one here, but it should really
- be done in sd.c. Even if it gets fixed there, this will still
- work.
-*/
+/* deprecated synchronous interface */
-{ ip[0] = 0x40;
- ip[1] = 0x20;
- ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
- if (ip[2] > 1024) {
- ip[0] = 0xff;
- ip[1] = 0x3f;
- ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
- if (ip[2] > 1023)
- ip[2] = 1023;
- }
- return 0;
+int ppa_command(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ int s;
+
+ sti();
+ s = 0;
+ if (ppa_start(cmd))
+ if (ppa_wait(host_no))
+ s = ppa_completion(cmd);
+ return s + (ppa_hosts[host_no].error_code << 16);
}
-int ppa_abort( Scsi_Cmnd * cmd )
-
-{ ppa_abort_flag = 1;
- return SCSI_ABORT_SNOOZE;
+/* pseudo-interrupt queueing interface */
+/*
+ * Since the PPA itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void ppa_interrupt(void *data)
+{
+ ppa_struct *tmp = (ppa_struct *) data;
+ Scsi_Cmnd *cmd = tmp->cur_cmd;
+ void (*done) (Scsi_Cmnd *) = tmp->done;
+ int host_no = cmd->host->unique_id;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ /* First check for any errors that may of occured
+ * Here we check for internal errors
+ */
+ if (tmp->ppa_failed) {
+ printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
+ tmp->error_code);
+ cmd->result = DID_ERROR << 16;
+ tmp->cur_cmd = 0;
+ done(cmd);
+ return;
+ }
+ /* Occasionally the mid level driver will abort a SCSI
+ * command because we are taking to long, if this occurs
+ * we should abort the command.
+ */
+ if (tmp->abort_flag) {
+ ppa_disconnect(host_no);
+ if (tmp->abort_flag == 1)
+ cmd->result = DID_ABORT << 16;
+ else {
+ ppa_do_reset(host_no);
+ cmd->result = DID_RESET << 16;
+ }
+ tmp->cur_cmd = 0;
+ done(cmd);
+ return;
+ }
+ /* Check to see if the device is now free, if not
+ * then throw this function onto the scheduler queue
+ * to be called back in a jiffy.
+ * (i386: 1 jiffy = 0.01 seconds)
+ */
+ if (!(r_str(host_no) & 0x80)) {
+ tmp->ppa_tq.data = (void *) tmp;
+ queue_task(&tmp->ppa_tq, &tq_scheduler);
+ return;
+ }
+ /* Device is now free and no errors have occured so
+ * it is safe to do the data phase
+ */
+ cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
+ tmp->cur_cmd = 0;
+ done(cmd);
+ return;
}
-int ppa_reset( Scsi_Cmnd * cmd, unsigned int ignored )
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ int host_no = cmd->host->unique_id;
+
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
+ return 0;
+ }
+ sti();
+ ppa_hosts[host_no].cur_cmd = cmd;
+ ppa_hosts[host_no].done = done;
+
+ if (!ppa_start(cmd)) {
+ cmd->result = ppa_hosts[host_no].error_code << 16;
+ ppa_hosts[host_no].cur_cmd = 0;
+ done(cmd);
+ return 0;
+ }
+ ppa_interrupt(ppa_hosts + host_no);
+
+ return 0;
+}
-{ ppa_abort_flag = 2;
- return SCSI_RESET_PUNT;
+/*
+ * Apparently the the disk->capacity attribute is off by 1 sector
+ * for all disk drives. We add the one here, but it should really
+ * be done in sd.c. Even if it gets fixed there, this will still
+ * work.
+ */
+int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
}
-const char *ppa_info( struct Scsi_Host * host )
+int ppa_abort(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
-{ return ppa_info_string;
+ ppa_hosts[host_no].abort_flag = 1;
+ ppa_hosts[host_no].error_code = DID_ABORT;
+ if (ppa_hosts[host_no].ppa_wait_q)
+ wake_up(&ppa_hosts[host_no].ppa_wait_q);
+
+ return SCSI_ABORT_SNOOZE;
}
-#ifndef MODULE
+int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
+{
+ int host_no = cmd->host->unique_id;
-/* Command line parameters (for built-in driver):
+ ppa_hosts[host_no].abort_flag = 2;
+ ppa_hosts[host_no].error_code = DID_RESET;
+ if (ppa_hosts[host_no].ppa_wait_q)
+ wake_up(&ppa_hosts[host_no].ppa_wait_q);
- Syntax: ppa=base[,speed_high[,speed_low[,nybble]]]
+ return SCSI_RESET_PUNT;
+}
- For example: ppa=0x378 or ppa=0x378,0,3
-*/
-void ppa_setup(char *str, int *ints)
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
-{ if (ints[0] > 0) ppa_base = ints[1];
- if (ints[0] > 1) ppa_speed_high = ints[2];
- if (ints[0] > 2) ppa_speed_low = ints[3];
- if (ints[0] > 3) ppa_nybble = ints[4];
- if (ints[0] > 4) ppa_nybble = ints[5];
-}
-#else
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
+#include "scsi_module.c"
+#endif
-Scsi_Host_Template driver_template = PPA;
+/*
+ * Start of Chipset kludges
+ */
+
+int ppa_detect(Scsi_Host_Template * host)
+{
+ struct Scsi_Host *hreg;
+ int rs;
+ int ports;
+ int i, nhosts;
+ struct parport *pb = parport_enumerate();
+
+ printk("PPA driver version: %s\n", PPA_VERSION);
+ nhosts = 0;
+
+ for (i = 0; pb; i++, pb=pb->next) {
+ int modes;
+
+ /* transfer global values here */
+ if (ppa_speed >= 0)
+ ppa_hosts[i].speed = ppa_speed;
+ if (ppa_speed_fast >= 0)
+ ppa_hosts[i].speed_fast = ppa_speed_fast;
+
+ ppa_hosts[i].dev =
+ parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+ NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
+
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ ppa_pb_claim(i);
+ w_ctr(i, 0x0c);
+ modes = ppa_hosts[i].dev->port->modes;
+
+ ppa_hosts[i].mode = PPA_NIBBLE;
+ if (modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) {
+ ppa_hosts[i].mode = PPA_EPP_32;
+ printk("PPA: Parport [ EPP ]\n");
+ } else if (modes & PARPORT_MODE_ECP) {
+ w_ecr(i, 0x20);
+ ppa_hosts[i].mode = PPA_PS2;
+ printk("PPA: Parport [ ECP in PS2 submode ]\n");
+ } else if (modes & PARPORT_MODE_PS2) {
+ ppa_hosts[i].mode = PPA_PS2;
+ printk("PPA: Parport [ PS2 ]\n");
+ }
+ /* Done configuration */
+ ppa_pb_release(i);
+
+ rs = ppa_init(i);
+ if (rs) {
+ parport_unregister_device(ppa_hosts[i].dev);
+ continue;
+ }
+ /* now the glue ... */
+ switch (ppa_hosts[i].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ ports = 3;
+ break;
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ ports = 8;
+ break;
+ default: /* Never gets here */
+ continue;
+ }
+
+ host->can_queue = PPA_CAN_QUEUE;
+ host->sg_tablesize = ppa_sg;
+ hreg = scsi_register(host, 0);
+ hreg->io_port = pb->base;
+ hreg->n_io_port = ports;
+ hreg->dma_channel = -1;
+ hreg->unique_id = i;
+ ppa_hosts[i]..host = hreg->host_no;
+ nhosts++;
+ }
+ if (nhosts == 0)
+ return 0;
+ else
+ return 1; /* return number of hosts detected */
+}
-#include "scsi_module.c"
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static int ppa_strncmp(const char *a, const char *b, int len)
+{
+ int loop;
+ for (loop = 0; loop < len; loop++)
+ if (a[loop] != b[loop])
+ return 1;
+
+ return 0;
+}
-#endif
+static int ppa_proc_write(int hostno, char *buffer, int length)
+{
+ unsigned long x;
+ const char *inv_num = "ppa /proc entry passed invalid number\n";
+
+ if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
+ x = simple_strtoul(buffer + 15, NULL, 0);
+ if (x <= ppa_hosts[hostno].speed)
+ ppa_hosts[hostno].speed_fast = x;
+ else
+ printk(inv_num);
+ return length;
+ }
+ if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
+ x = simple_strtoul(buffer + 10, NULL, 0);
+ if (x >= ppa_hosts[hostno].speed_fast)
+ ppa_hosts[hostno].speed = x;
+ else
+ printk(inv_num);
+ return length;
+ }
+ if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
+ x = simple_strtoul(buffer + 10, NULL, 0);
+ ppa_hosts[hostno].epp_speed = x;
+ return length;
+ }
+ if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
+ x = simple_strtoul(buffer + 12, NULL, 0);
+ ppa_hosts[hostno].timeout = x;
+ return length;
+ }
+ if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ ppa_hosts[hostno].mode = x;
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
+}
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ int i;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ for (i = 0; i < 4; i++)
+ if (ppa_hosts[i].host == hostno)
+ break;
+
+ if (inout)
+ return ppa_proc_write(i, buffer, length);
+
+ size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Parport : %s\n",
+ ppa_hosts[i].dev->port->name);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "Mode : %s\n",
+ PPA_MODE_STRING[ppa_hosts[i].mode]);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "\nTiming Parameters\n");
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "ppa_speed %i\n",
+ ppa_hosts[i].speed);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "ppa_speed_fast %i\n",
+ ppa_hosts[i].speed_fast);
+ len += size;
+ pos = begin + len;
+
+ if (IN_EPP_MODE(ppa_hosts[i].mode)) {
+ size = sprintf(buffer + len, "epp_speed %i\n",
+ ppa_hosts[i].epp_speed);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "\nInternal Counters\n");
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "epp_timeout %i\n",
+ ppa_hosts[i].timeout);
+ len += size;
+ pos = begin + len;
+ }
+ *start = buffer + (offset + begin);
+ len -= (offset - begin);
+ if (len > length)
+ len = length;
+ return len;
+}
/* end of ppa.c */
+/* Driver for the PPA3 parallel port SCSI HBA embedded in
+ * the Iomega ZIP drive
+ *
+ * (c) 1996 Grant R. Guenther grant@torque.net
+ */
+
#ifndef _PPA_H
#define _PPA_H
-/* Driver for the PPA3 parallel port SCSI HBA embedded in
- the Iomega ZIP drive
+#define PPA_VERSION "Curtin 1-08-BETA"
+
+/* This driver reqires a 1.3.37 kernel or higher!! */
+
+/* Use the following to enable certain chipset support
+ * Default is PEDANTIC = 3
+ */
+
+#include <linux/config.h>
+
+#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+#define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3
+#endif
+#ifndef CONFIG_SCSI_PPA_EPP_TIME
+#define CONFIG_SCSI_PPA_EPP_TIME 64
+#endif
- (c) 1996 Grant R. Guenther grant@torque.net
-*/
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/tqueue.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include "sd.h"
+#include "hosts.h"
+#include <linux/parport.h>
+/* batteries not included :-) */
#define PPA_INITIATOR 7
-int ppa_detect(Scsi_Host_Template * );
-const char * ppa_info(struct Scsi_Host *);
+int ppa_detect(Scsi_Host_Template *);
+const char *ppa_info(struct Scsi_Host *);
int ppa_command(Scsi_Cmnd *);
-int ppa_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
int ppa_abort(Scsi_Cmnd *);
int ppa_reset(Scsi_Cmnd *, unsigned int);
-int ppa_biosparam(Disk *, kdev_t, int[]);
-
-#define PPA { \
- 0, \
- 0, \
- 0, \
- 0, \
- 0, \
- ppa_detect, \
- 0, \
- ppa_info, \
- ppa_command, \
- ppa_queuecommand, \
- ppa_abort, \
- ppa_reset, \
- 0, \
- ppa_biosparam, \
- 0, \
- PPA_INITIATOR, \
- SG_NONE, \
- 1, \
- 0, \
- 0, \
- DISABLE_CLUSTERING \
-}
+int ppa_proc_info(char *, char **, off_t, int, int, int);
+int ppa_biosparam(Disk *, kdev_t, int*);
+int ppa_release(struct Scsi_Host *);
-#endif /* _PPA_H */
+#ifndef MODULE
+#ifdef PPA_CODE
+#define SKIP_PROC_DIR
+#endif
+#endif
+
+#ifndef SKIP_PROC_DIR
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#endif /* !PPA_CODE => hosts.c */
+
+#define PPA { /* next */ 0, \
+ /* usage_count */ 0, \
+ /* proc_dir */ &proc_scsi_ppa, \
+ /* proc_info */ ppa_proc_info, \
+ /* name */ "Iomega ZIP/JAZ Traveller", \
+ /* detect */ ppa_detect, \
+ /* release */ ppa_release, \
+ /* info */ 0, \
+ /* command */ ppa_command, \
+ /* queuecommand */ ppa_queuecommand, \
+ /* abort */ ppa_abort, \
+ /* reset */ ppa_reset, \
+ /* slave_attach */ 0, \
+ /* bios_param */ ppa_biosparam, \
+ /* can_queue */ 0, \
+ /* this_id */ PPA_INITIATOR, \
+ /* sg_tablesize */ SG_ALL, \
+ /* cmd_per_lun */ 1, \
+ /* present */ 0, \
+ /* unchecked_isa_dma */ 0, \
+ /* use_clustering */ ENABLE_CLUSTERING \
+}
+#endif /* _PPA_H */
if [ "$CONFIG_INET" = "y" ]; then
tristate 'NFS filesystem support' CONFIG_NFS_FS
if [ "$CONFIG_NFS_FS" = "y" ]; then
- define_bool CONFIG_SUNRPC y
- define_bool CONFIG_LOCKD y
bool ' Root file system on NFS' CONFIG_ROOT_NFS
if [ "$CONFIG_ROOT_NFS" = "y" ]; then
bool ' BOOTP support' CONFIG_RNFS_BOOTP
bool ' RARP support' CONFIG_RNFS_RARP
fi
fi
+ tristate 'NFS server support' CONFIG_NFSD
+ if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
+ define_bool CONFIG_SUNRPC y
+ define_bool CONFIG_LOCKD y
+ else
+ if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" ]; then
+ define_bool CONFIG_SUNRPC m
+ define_bool CONFIG_LOCKD m
+ else
+ define_bool CONFIG_SUNRPC n
+ define_bool CONFIG_LOCKD n
+ fi
+ fi
tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
if [ "$CONFIG_SMB_FS" != "n" ]; then
bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
{
struct wait_queue * wait;
+ inode->i_count++;
truncate_inode_pages(inode, 0);
wait_on_inode(inode);
if (IS_WRITABLE(inode)) {
remove_inode_hash(inode);
remove_inode_free(inode);
wait = ((volatile struct inode *) inode)->i_wait;
+ inode->i_count--;
if (inode->i_count)
nr_free_inodes++;
memset(inode,0,sizeof(*inode));
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;
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;
}
/* 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);
}
#include <linux/locks.h>
#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h>
#include <linux/lockd/bind.h>
#include <asm/system.h>
-# include <asm/uaccess.h>
+#include <asm/uaccess.h>
#define NFSDBG_FACILITY NFSDBG_VFS
NULL
};
+struct rpc_stat nfs_rpcstat = { &nfs_program };
/*
* The "read_inode" function doesn't actually do anything:
init_nfs_fs(void)
{
#ifdef CONFIG_PROC_FS
- rpcstat_register(&nfs_rpcstat);
+ rpc_proc_register(&nfs_rpcstat);
#endif
return register_filesystem(&nfs_fs_type);
}
cleanup_module(void)
{
#ifdef CONFIG_PROC_FS
- rpcstat_unregister(&nfs_rpcstat);
+ rpc_proc_unregister("nfs");
#endif
unregister_filesystem(&nfs_fs_type);
nfs_free_dircache();
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 */
-};
/*
- * nfsstat.c procfs-based user access to knfsd statistics
+ * linux/fs/nfsd/stats.c
+ *
+ * procfs-based user access to knfsd statistics
+ *
+ * /proc/net/rpc/nfsd
*
- * /proc/net/nfssrv
* Format:
- * net <packets> <udp> <tcp> <tcpconn>
- * rpc <packets> <badfmt> <badclnt>
- * auth <flavor> <creds> <upcalls> <badauth> <badverf> <authrej>
- * fh <hits> <misses> <avg_util> <stale> <cksum> <badcksum>
* rc <hits> <misses> <nocache>
- * proto <version> <nrprocs>
- * <calls> <time_msec>
- * ... (for each procedure and protocol version)
+ * Statistsics for the reply cache
+ * plus generic RPC stats (see net/sunrpc/stats.c)
*
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/kernel.h>
#include <linux/nfsd/stats.h>
struct nfsd_stats nfsdstats;
-
-static int nfsd_get_info(char *, char **, off_t, int, int);
-
-#ifndef PROC_NET_NFSSRV
-# define PROC_NET_NFSSRV 0
-#endif
-
-static struct proc_dir_entry proc_nfssrv = {
- PROC_NET_NFSSRV, 4, "nfsd",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 6, &proc_net_inode_operations,
- nfsd_get_info
-};
-
-struct svc_stat nfsd_svcstats = {
- NULL, &proc_nfssrv, &nfsd_program,
-};
+struct svc_stat nfsd_svcstats = { &nfsd_program, };
static int
-nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
+ int *eof, void *data)
{
- int len;
+ int len;
len = sprintf(buffer,
"rc %d %d %d\n",
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");
}
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);
"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) \
#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)))
#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;
};
struct lp_struct {
- int base;
- unsigned int irq;
+ struct ppd *dev;
int flags;
unsigned int chars;
unsigned int time;
--- /dev/null
+/* $Id: parport.h,v 1.1.2.5 1997/03/29 21:08:31 phil Exp $ */
+
+#ifndef _PARPORT_H_
+#define _PARPORT_H_
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+
+/* Maximum of 8 ports per machine */
+#define PARPORT_MAX 8
+
+/* Type classes for Plug-and-Play probe */
+
+typedef enum {
+ PARPORT_CLASS_LEGACY = 0, /* Non-IEEE1284 device */
+ PARPORT_CLASS_PRINTER,
+ PARPORT_CLASS_MODEM,
+ PARPORT_CLASS_NET,
+ PARPORT_CLASS_HDC, /* Hard disk controller */
+ PARPORT_CLASS_PCMCIA,
+ PARPORT_CLASS_MEDIA, /* Multimedia device */
+ PARPORT_CLASS_FDC, /* Floppy disk controller */
+ PARPORT_CLASS_PORTS,
+ PARPORT_CLASS_SCANNER,
+ PARPORT_CLASS_DIGCAM,
+ PARPORT_CLASS_OTHER, /* Anything else */
+ PARPORT_CLASS_UNSPEC /* No CLS field in ID */
+} parport_device_class;
+
+struct parport_device_info {
+ parport_device_class class;
+ char *mfr;
+ char *model;
+ char *cmdset;
+ char *description;
+};
+
+/* Definitions for parallel port sharing */
+
+/* Forward declare some stuff so we can use mutually circular structures */
+struct ppd;
+struct parport;
+
+/* Each device can have two callback functions:
+ * 1) a preemption function, called by the resource manager to request
+ * that the driver relinquish control of the port. The driver should
+ * return zero if it agrees to release the port, and nonzero if it
+ * refuses. Do not call parport_release() - the kernel will do this
+ * implicitly.
+ *
+ * 2) a wake-up function, called by the resource manager to tell drivers
+ * that the port is available to be claimed. If a driver wants to use
+ * the port, it should call parport_claim() here. The return value from
+ * this function is ignored.
+ */
+typedef int (*callback_func) (void *);
+
+/* This is an ordinary kernel IRQ handler routine.
+ * The dev_id field (void *) will point the the port structure
+ * associated with the interrupt request (to allow IRQ sharing)
+ * Please make code IRQ sharing as this function may be called
+ * when it isn't meant for you...
+ */
+typedef void (*irq_handler_func) (int, void *, struct pt_regs *);
+
+/* A parallel port device */
+struct ppd {
+ char *name;
+ struct parport *port; /* The port this is associated with */
+ callback_func preempt; /* preemption function */
+ callback_func wakeup; /* kick function */
+ void *private;
+ irq_handler_func irq_func;
+ int flags;
+ unsigned char ctr; /* SPP CTR register */
+ unsigned char ecr; /* ECP ECR register */
+ struct ppd *next;
+ struct ppd *prev;
+};
+
+/* A parallel port */
+struct parport {
+ unsigned int base; /* base address */
+ unsigned int size; /* IO extent */
+ char *name;
+ int irq; /* interrupt (or -1 for none) */
+ int dma;
+ unsigned int modes;
+ struct ppd *devices;
+ struct ppd *cad; /* port owner */
+ struct ppd *lurker;
+ unsigned int ctr; /* SPP CTR register */
+ unsigned int ecr; /* ECP ECR register */
+ struct parport *next;
+ unsigned int flags;
+ struct proc_dir_entry *proc_dir;
+ struct parport_device_info probe_info;
+};
+
+/* parport_register_port registers a new parallel port at the given address (if
+ * one does not already exist) and returns a pointer to it. This entails
+ * claiming the I/O region, IRQ and DMA.
+ * NULL is returned if initialisation fails.
+ */
+struct parport *parport_register_port(unsigned long base, int irq, int dma);
+
+/* parport_in_use returns nonzero if there are devices attached to a port. */
+#define parport_in_use(x) ((x)->devices != NULL)
+
+/* parport_destroy blows away a parallel port. This fails if any devices are
+ * registered.
+ */
+void parport_destroy(struct parport *);
+
+/* parport_enumerate returns a pointer to the linked list of all the ports
+ * in this machine.
+ */
+struct parport *parport_enumerate(void);
+
+/* parport_register_device declares that a device is connected to a port, and
+ * tells the kernel all it needs to know.
+ * pf is the preemption function (may be NULL for a transient driver)
+ * kf is the wake-up function (may be NULL for a transient driver)
+ * irq_func is the interrupt handler (may be NULL for no interrupts)
+ * Only one lurking driver can be used on a given port.
+ * handle is a user pointer that gets handed to callback functions.
+ */
+struct ppd *parport_register_device(struct parport *port, const char *name,
+ callback_func pf, callback_func kf,
+ irq_handler_func irq_func, int flags,
+ void *handle);
+
+/* parport_deregister causes the kernel to forget about a device */
+void parport_unregister_device(struct ppd *dev);
+
+/* parport_claim tries to gain ownership of the port for a particular driver.
+ * This may fail (return non-zero) if another driver is busy. If this
+ * driver has registered an interrupt handler, it will be enabled.
+ */
+int parport_claim(struct ppd *dev);
+
+/* parport_release reverses a previous parport_claim. This can never fail,
+ * though the effects are undefined (except that they are bad) if you didn't
+ * previously own the port. Once you have released the port you should make
+ * sure that neither your code nor the hardware on the port tries to initiate
+ * any communication without first re-claiming the port.
+ * If you mess with the port state (enabling ECP for example) you should
+ * clean up before releasing the port.
+ */
+void parport_release(struct ppd *dev);
+
+/* The "modes" entry in parport is a bit field representing the following
+ * modes.
+ * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
+ * 100% compatible with EPP.
+ */
+#define PARPORT_MODE_SPP 0x0001
+#define PARPORT_MODE_PS2 0x0002
+#define PARPORT_MODE_EPP 0x0004
+#define PARPORT_MODE_ECP 0x0008
+#define PARPORT_MODE_ECPEPP 0x0010
+#define PARPORT_MODE_ECR 0x0020 /* ECR Register Exists */
+#define PARPORT_MODE_ECPPS2 0x0040
+
+/* Flags used to identify what a device does
+ */
+#define PARPORT_DEV_TRAN 0x0000
+#define PARPORT_DEV_LURK 0x0001
+
+#define PARPORT_FLAG_COMA 1
+
+extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
+extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
+ char);
+
+/* Prototypes from parport_procfs */
+extern int parport_proc_register(struct parport *pp);
+extern void parport_proc_unregister(struct parport *pp);
+
+/* Prototypes from parport_ksyms.c */
+extern void dec_parport_count(void);
+extern void inc_parport_count(void);
+
+extern int parport_probe(struct parport *port, char *buffer, int len);
+extern void parport_probe_one(struct parport *port);
+
+/* Primitive port access functions */
+extern inline void parport_w_ctrl(struct parport *port, int val)
+{
+ outb(val, port->base+2);
+}
+
+extern inline int parport_r_ctrl(struct parport *port)
+{
+ return inb(port->base+2);
+}
+
+extern inline void parport_w_data(struct parport *port, int val)
+{
+ outb(val, port->base);
+}
+
+extern inline int parport_r_data(struct parport *port)
+{
+ return inb(port->base);
+}
+
+extern inline int parport_r_status(struct parport *port)
+{
+ return inb(port->base+1);
+}
+
+#endif /* _PARPORT_H_ */
PROC_RTC,
PROC_LOCKS,
PROC_ZORRO,
- PROC_SLABINFO
+ PROC_SLABINFO,
+ PROC_PARPORT
};
enum pid_directory_inos {
-/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */
#ifndef _SCC_H
#define _SCC_H
/* DEV ioctl() commands */
-#define SIOCSCCRESERVED (SIOCDEVPRIVATE+0)
-#define SIOCSCCCFG (SIOCDEVPRIVATE+1)
-#define SIOCSCCINI (SIOCDEVPRIVATE+2)
-#define SIOCSCCCHANINI (SIOCDEVPRIVATE+3)
-#define SIOCSCCSMEM (SIOCDEVPRIVATE+4)
-#define SIOCSCCGKISS (SIOCDEVPRIVATE+5)
-#define SIOCSCCSKISS (SIOCDEVPRIVATE+6)
-#define SIOCSCCGSTAT (SIOCDEVPRIVATE+7)
+enum SCC_ioctl_cmds {
+ SIOCSCCRESERVED = SIOCDEVPRIVATE,
+ SIOCSCCCFG,
+ SIOCSCCINI,
+ SIOCSCCCHANINI,
+ SIOCSCCSMEM,
+ SIOCSCCGKISS,
+ SIOCSCCSKISS,
+ SIOCSCCGSTAT,
+ SIOCSCCCAL
+};
/* magic number */
#define SCC_MAGIC 0x8530 /* ;-) */
-/* KISS state machine */
-
-#define KISS_IDLE 0
-#define KISS_DATA 1
-#define KISS_ESCAPE 2
-#define KISS_RXFRAME 3
-
/* Device parameter control (from WAMPES) */
-#define PARAM_TXDELAY 1
-#define PARAM_PERSIST 2
-#define PARAM_SLOTTIME 3
-#define PARAM_TXTAIL 4
-#define PARAM_FULLDUP 5
-#define PARAM_SOFTDCD 6 /* was: PARAM_HW */
-#define PARAM_MUTE 7 /* ??? */
-#define PARAM_DTR 8
-#define PARAM_RTS 9
-#define PARAM_SPEED 10
-#define PARAM_ENDDELAY 11 /* ??? */
-#define PARAM_GROUP 12
-#define PARAM_IDLE 13
-#define PARAM_MIN 14
-#define PARAM_MAXKEY 15
-#define PARAM_WAIT 16
-#define PARAM_MAXDEFER 17
-#define PARAM_TX 18
-#define PARAM_HWEVENT 31
-#define PARAM_RETURN 255 /* reset kiss mode */
+enum L1_params {
+ PARAM_DATA,
+ PARAM_TXDELAY,
+ PARAM_PERSIST,
+ PARAM_SLOTTIME,
+ PARAM_TXTAIL,
+ PARAM_FULLDUP,
+ PARAM_SOFTDCD, /* was: PARAM_HW */
+ PARAM_MUTE, /* ??? */
+ PARAM_DTR,
+ PARAM_RTS,
+ PARAM_SPEED,
+ PARAM_ENDDELAY, /* ??? */
+ PARAM_GROUP,
+ PARAM_IDLE,
+ PARAM_MIN,
+ PARAM_MAXKEY,
+ PARAM_WAIT,
+ PARAM_MAXDEFER,
+ PARAM_TX,
+ PARAM_HWEVENT = 31,
+ PARAM_RETURN = 255 /* reset kiss mode */
+};
/* fulldup parameter */
-#define KISS_DUPLEX_HALF 0 /* normal CSMA operation */
-#define KISS_DUPLEX_FULL 1 /* fullduplex, key down trx after transmission */
-#define KISS_DUPLEX_LINK 2 /* fullduplex, key down trx after 'idletime' sec */
-#define KISS_DUPLEX_OPTIMA 3 /* fullduplex, let the protocol layer control the hw */
+enum FULLDUP_modes {
+ KISS_DUPLEX_HALF, /* normal CSMA operation */
+ KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */
+ KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */
+ KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */
+};
/* misc. parameters */
/* HWEVENT parameter */
-#define HWEV_DCD_ON 0
-#define HWEV_DCD_OFF 1
-#define HWEV_ALL_SENT 2
+enum HWEVENT_opts {
+ HWEV_DCD_ON,
+ HWEV_DCD_OFF,
+ HWEV_ALL_SENT
+};
/* channel grouping */
/* Tx/Rx clock sources */
-#define CLK_DPLL 0 /* normal halfduplex operation */
-#define CLK_EXTERNAL 1 /* external clocking (G3RUH/DF9IC modems) */
-#define CLK_DIVIDER 2 /* Rx = DPLL, Tx = divider (fullduplex with */
- /* modems without clock regeneration */
+enum CLOCK_sources {
+ CLK_DPLL, /* normal halfduplex operation */
+ CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */
+ CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */
+ /* modems without clock regeneration */
+ CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */
+ /* MODEMs without clock recovery */
+};
/* Tx state */
-#define TXS_IDLE 0 /* Transmitter off, no data pending */
-#define TXS_BUSY 1 /* waiting for permission to send / tailtime */
-#define TXS_ACTIVE 2 /* Transmitter on, sending data */
-#define TXS_NEWFRAME 3 /* reset CRC and send (next) frame */
-#define TXS_IDLE2 4 /* Transmitter on, no data pending */
-#define TXS_WAIT 5 /* Waiting for Mintime to expire */
-#define TXS_TIMEOUT 6 /* We had a transmission timeout */
+enum TX_state {
+ TXS_IDLE, /* Transmitter off, no data pending */
+ TXS_BUSY, /* waiting for permission to send / tailtime */
+ TXS_ACTIVE, /* Transmitter on, sending data */
+ TXS_NEWFRAME, /* reset CRC and send (next) frame */
+ TXS_IDLE2, /* Transmitter on, no data pending */
+ TXS_WAIT, /* Waiting for Mintime to expire */
+ TXS_TIMEOUT /* We had a transmission timeout */
+};
typedef unsigned long io_port; /* type definition for an 'io port address' */
unsigned int bufsize; /* used buffersize */
};
-
struct scc_modem {
long speed; /* Line speed, bps */
char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */
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 */
#define RXINT 0x04
#define SPINT 0x06
-
#ifdef SCC_DELAY
#define Inb(port) inb_p(port)
#define Outb(port, val) outb_p(val, port)
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 */
#include <linux/proc_fs.h>
struct rpc_stat {
- struct rpc_stat * next;
- struct proc_dir_entry * entry;
struct rpc_program * program;
unsigned int netcnt,
};
struct svc_stat {
- struct svc_stat * next;
- struct proc_dir_entry * entry;
struct svc_program * program;
unsigned int netcnt,
rpcbadclnt;
};
-void rpcstat_init(void);
-void rpcstat_exit(void);
-
-void rpcstat_register(struct rpc_stat *);
-void rpcstat_unregister(struct rpc_stat *);
-int rpcstat_get_info(struct rpc_stat *, char *, char **,
- off_t, int);
-void rpcstat_zero_info(struct rpc_program *);
-void svcstat_register(struct svc_stat *);
-void svcstat_unregister(struct svc_stat *);
-int svcstat_get_info(struct svc_stat *, char *, char **,
- off_t, int);
-void svcstat_zero_info(struct svc_program *);
+void rpc_proc_init(void);
+void rpc_proc_exit(void);
+
+struct proc_dir_entry * rpc_proc_register(struct rpc_stat *);
+void rpc_proc_unregister(const char *);
+int rpc_proc_read(char *, char **, off_t, int,
+ int *, void *);
+void rpc_proc_zero(struct rpc_program *);
+struct proc_dir_entry * svc_proc_register(struct svc_stat *);
+void svc_proc_unregister(const char *);
+int svc_proc_read(char *, char **, off_t, int,
+ int *, void *);
+void svc_proc_zero(struct svc_program *);
#endif /* _LINUX_SUNRPC_STATS_H */
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;
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
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);
#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);
#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
#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 }
};
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);
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;
va_list args;
int i;
char *msg, *p, *buf_end;
+ int line_feed;
static signed char msg_level = -1;
long flags;
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)
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);
#include <linux/types.h>
#include <linux/ioport.h>
-#define IOTABLE_SIZE 64
+#define IOTABLE_SIZE 128
typedef struct resource_entry_t {
u_long from, num;
return;
}
#endif
- if (p == &init_task) {
+ if (!p->pid) {
static int nr = 0;
if (nr < 5) {
nr++;
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)
/* 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) {
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)++;
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);
*/
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;
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();
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,
*
* 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 $
*/
*/
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);
/*
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);
}
}
want_wakeup = 1;
}
- if (want_wakeup) {
- if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
+ if (want_wakeup) {
+ if (!kswapd_awake) {
wake_up(&kswapd_wait);
need_resched = 1;
}
- next_swap_jiffies = jiffies + swapout_interval;
+ /* low on memory, we need to start swapping soon */
+ if(last_wakeup_low)
+ next_swap_jiffies = jiffies;
+ else
+ next_swap_jiffies = jiffies + swapout_interval;
}
timer_active |= (1<<SWAP_TIMER);
}
+++ /dev/null
-torvalds@penguin.transmeta.com
\ No newline at end of file
#ifdef CONFIG_NET_RADIO
#include <linux/wireless.h>
#endif /* CONFIG_NET_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
/*
* The list of devices, that are able to output.
#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
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)
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;
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 */
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;
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");
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
- rpcstat_init();
+ rpc_proc_init();
return 0;
}
#ifdef RPC_DEBUG
rpc_unregister_sysctl();
#endif
- rpcstat_exit();
+ rpc_proc_exit();
}
#endif
/*
* linux/net/sunrpc/stats.c
*
- * procfs-based user access to RPC statistics
+ * procfs-based user access to generic RPC statistics. The stats files
+ * reside in /proc/net/rpc.
*
- * Everything is complicated by the fact that procfs doesn't pass the
- * proc_dir_info struct in the call to get_info. We need our own
- * inode_ops for /proc/net/rpc (we want to have a write op for zeroing
- * the current stats, anyway).
+ * The read routines assume that the buffer passed in is just big enough.
+ * If you implement an RPC service that has its own stats routine which
+ * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
+ * limit.
*
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/kernel.h>
#define RPCDBG_FACILITY RPCDBG_MISC
-/*
- * Generic stats object (same for clnt and svc stats).
- * Must agree with first two fields of either.
- */
-struct stats {
- struct stats * next;
- struct proc_dir_entry * entry;
-};
-
-/* Code disabled until updated to new dynamic /proc code */
-#if 0
-
-static struct stats * rpc_stats = NULL;
-static struct stats * svc_stats = NULL;
-
-static struct proc_dir_entry proc_rpc = {
- 0, 3, "rpc",
- S_IFDIR | S_IRUGO | S_IXUGO, 1, 0, 0,
- 0, NULL,
- NULL, NULL,
- NULL,
- NULL, NULL,
-};
-
-/*
- * Register/unregister a stats file
- */
-static void
-stats_register(struct stats **q, struct stats *p)
-{
- dprintk("RPC: registering /proc/net/rpc/%s\n",
- p->entry->name);
- /* return; */
- if (p->entry->low_ino)
- return;
- p->next = *q;
- *q = p;
- proc_register_dynamic(&proc_rpc, p->entry);
-}
-
-static void
-stats_unregister(struct stats **q, struct stats *p)
-{
- dprintk("RPC: unregistering /proc/net/rpc/%s\n",
- p->entry->name);
- /* return; */
- if (!p->entry->low_ino)
- return;
- while (*q) {
- if (*q == p) {
- *q = p->next;
- proc_unregister(&proc_rpc, p->entry->low_ino);
- return;
- }
- q = &((*q)->next);
- }
-}
+static struct proc_dir_entry *proc_net_rpc = 0;
/*
- * Client stats handling
+ * Get RPC client stats
*/
-void
-rpcstat_register(struct rpc_stat *statp)
-{
- stats_register(&rpc_stats, (struct stats *) statp);
-}
-
-void
-rpcstat_unregister(struct rpc_stat *statp)
-{
- stats_unregister(&rpc_stats, (struct stats *) statp);
-}
-
int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
- char **start, off_t offset, int length)
+rpc_proc_read(char *buffer, char **start, off_t offset, int count,
+ int *eof, void *data)
{
+ struct rpc_stat *statp = (struct rpc_stat *) data;
struct rpc_program *prog = statp->program;
struct rpc_version *vers;
int len, i, j;
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;
if (offset >= len) {
*start = buffer;
+ *eof = 1;
return 0;
}
*start = buffer + offset;
- if ((len -= offset) > length)
- len = length;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
return len;
}
/*
- * Register /proc/net/rpc
+ * Register/unregister RPC proc files
*/
-void
-rpcstat_init(void)
+static inline struct proc_dir_entry *
+do_register(const char *name, void *data, int issvc)
{
- dprintk("RPC: registering /proc/net/rpc\n");
- proc_rpc.ops = proc_net.ops; /* cheat */
- proc_register_dynamic(&proc_net, &proc_rpc);
-}
-
-/*
- * Unregister /proc/net/rpc
- */
-void
-rpcstat_exit(void)
-{
- while (rpc_stats)
- stats_unregister(&rpc_stats, rpc_stats);
- while (svc_stats)
- stats_unregister(&svc_stats, svc_stats);
- dprintk("RPC: unregistering /proc/net/rpc\n");
- proc_unregister(&proc_net, proc_rpc.low_ino);
-}
-
-#else
+ struct proc_dir_entry *ent;
-/* Various dummy functions */
+ dprintk("RPC: registering /proc/net/rpc/%s\n", name);
+ ent = create_proc_entry(name, 0, proc_net_rpc);
+ ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
+ ent->data = data;
-int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
- char **start, off_t offset, int length)
-{
- return 0;
+ return ent;
}
-int
-svcstat_get_info(struct svc_stat *statp, char *buffer,
- char **start, off_t offset, int length)
+struct proc_dir_entry *
+rpc_proc_register(struct rpc_stat *statp)
{
- return 0;
+ return do_register(statp->program->name, statp, 0);
}
void
-rpcstat_register(struct rpc_stat *statp)
+rpc_proc_unregister(const char *name)
{
+ remove_proc_entry(name, proc_net_rpc);
}
-void
-rpcstat_unregister(struct rpc_stat *statp)
+struct proc_dir_entry *
+svc_proc_register(struct svc_stat *statp)
{
+ return do_register(statp->program->pg_name, statp, 1);
}
void
-svcstat_register(struct svc_stat *statp)
+svc_proc_unregister(const char *name)
{
+ remove_proc_entry(name, proc_net_rpc);
}
void
-svcstat_unregister(struct svc_stat *statp)
-{
-}
-
-void
-rpcstat_init(void)
+rpc_proc_init(void)
{
+ dprintk("RPC: registering /proc/net/rpc\n");
+ if (!proc_net_rpc)
+ proc_net_rpc = create_proc_entry("net/rpc", S_IFDIR, 0);
}
void
-rpcstat_exit(void)
+rpc_proc_exit(void)
{
+ dprintk("RPC: unregistering /proc/net/rpc\n");
+ if (proc_net_rpc)
+ remove_proc_entry("net/rpc", 0);
+ proc_net_rpc = 0;
}
-
-#endif
#include <linux/config.h>
#include <linux/module.h>
-#ifdef CONFIG_MODULES
-
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sched.h>
/* 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 */
EXPORT_SYMBOL(nfs_debug);
EXPORT_SYMBOL(nfsd_debug);
EXPORT_SYMBOL(nlm_debug);
-
-#endif /* CONFIG_MODULES */
#
# 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.
#
# 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/?"
;;
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"
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
}
}
#
-# 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
#
#
# 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.
#----------------------------------------------------------------------------
#
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.
#
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
#
# 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
}
then
bool "$1" "$2"
else
- eval $2=\${$2:-'n'} x=\$$2
+ set_x "$2"
case $x in
y) flag="*" ;;
*) flag=" " ;;
esac
- echo -ne "'$2' '<$flag> $1' " >>MCmenu
+ echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
echo -e "
function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists
# 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
}
${DIALOG} --backtitle "$backtitle" \
--infobox "Saving your kernel configuration..." 3 40
+ #
+ # Macro for setting the newval varible. get's default from defconfig
+ # file if it's a new parameter and it has not been shown yet.
+ #
+ function set_newval () {
+ eval newval=\$$1
+ if [ -z "$newval" ]; then
+ get_def "$1"
+ eval newval=\${$1:-'n'}
+ fi
+ }
+
#
# Now, let's redefine the configuration functions for final
# output to the config files.
# 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 () {