From 33fad9187f9e0f2f7429e3d9a6831dbdac5f3e39 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:33:17 -0500 Subject: [PATCH] Import 2.3.99pre3-3 --- CREDITS | 8 + Documentation/DocBook/Makefile | 3 +- Documentation/DocBook/kernel-api.tmpl | 7 +- Documentation/DocBook/parportbook.sgml | 1747 ----------- Documentation/networking/tulip.txt | 12 +- Makefile | 1 + arch/i386/boot/compressed/misc.c | 14 +- arch/i386/kernel/io_apic.c | 134 +- drivers/char/rtc.c | 4 +- drivers/net/8139too.c | 2 +- drivers/net/Config.in | 10 +- drivers/net/epic100.c | 14 +- drivers/net/net_init.c | 28 +- drivers/net/pcmcia/3c575_cb.c | 724 ++--- drivers/net/pcmcia/Config.in | 1 + drivers/net/pcmcia/Makefile | 3 +- drivers/net/pcmcia/xircom_tulip_cb.c | 3159 ++++++++++++++++++++ drivers/net/starfire.c | 351 +-- drivers/net/tulip/eeprom.c | 42 +- drivers/net/tulip/interrupt.c | 5 +- drivers/net/tulip/tulip.h | 10 +- drivers/net/tulip/tulip_core.c | 60 +- drivers/net/via-rhine.c | 222 +- drivers/net/wan/Config.in | 6 +- drivers/sound/ac97_codec.c | 2 +- drivers/sound/sb.h | 1 + drivers/sound/sb_common.c | 5 +- drivers/sound/sb_mixer.c | 6 + drivers/usb/serial/Makefile-keyspan_pda_fw | 2 +- drivers/usb/serial/keyspan_pda.S | 1124 +++++++ drivers/video/riva/fbdev.c | 2 +- fs/dcache.c | 38 + fs/ext2/balloc.c | 29 +- fs/ext2/ialloc.c | 6 +- fs/ext2/super.c | 33 +- fs/lockd/clntproc.c | 8 +- fs/lockd/mon.c | 2 +- fs/nfs/dir.c | 3 + fs/nfs/read.c | 10 +- fs/nfs/write.c | 17 +- fs/udf/balloc.c | 102 +- fs/udf/file.c | 4 +- fs/udf/inode.c | 21 +- fs/udf/lowlevel.c | 22 +- fs/udf/misc.c | 8 +- fs/udf/namei.c | 4 +- fs/udf/super.c | 84 +- fs/udf/udf_sb.h | 6 + fs/udf/udfdecl.h | 4 +- fs/udf/unicode.c | 3 +- include/linux/dcache.h | 3 + include/linux/ext2_fs.h | 5 +- include/linux/pci.h | 3 + include/linux/sunrpc/auth.h | 14 +- include/linux/sunrpc/clnt.h | 24 +- include/linux/sunrpc/sched.h | 34 +- include/linux/sunrpc/xprt.h | 57 +- include/linux/udf_fs.h | 1 + include/linux/udf_fs_sb.h | 12 +- net/ipv4/tcp_input.c | 9 +- net/sunrpc/auth.c | 121 +- net/sunrpc/auth_null.c | 10 +- net/sunrpc/auth_unix.c | 21 +- net/sunrpc/clnt.c | 347 +-- net/sunrpc/clnt.c.rej | 248 ++ net/sunrpc/pmap_clnt.c | 3 +- net/sunrpc/sched.c | 383 ++- net/sunrpc/sunrpc_syms.c | 4 +- net/sunrpc/xprt.c | 943 +++--- 69 files changed, 6703 insertions(+), 3652 deletions(-) delete mode 100644 Documentation/DocBook/parportbook.sgml create mode 100644 drivers/net/pcmcia/xircom_tulip_cb.c create mode 100644 drivers/usb/serial/keyspan_pda.S create mode 100644 net/sunrpc/clnt.c.rej diff --git a/CREDITS b/CREDITS index b0c8c5421768..fb0bc2bd788e 100644 --- a/CREDITS +++ b/CREDITS @@ -891,6 +891,10 @@ S: Fichtenweg 3/511 S: 72076 Tübingen S: Germany +N: Justin Guyett +E: jguyett@andrew.cmu.edu +D: via-rhine net driver hacking + N: Danny ter Haar E: dth@cistron.nl D: /proc/procinfo, reboot on panic , kernel pre-patch tester ;) @@ -2609,6 +2613,10 @@ S: Eichenweg 16 S: 73650 Winterbach S: Germany +N: Urban Widmark +E: urban@svenskatest.se +D: via-rhine, misc net driver hacking + N: Marco van Wieringen E: mvw@planets.elm.net D: Author of process accounting and diskquota diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 155133a0a52d..19b977fe0e4b 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -26,8 +26,9 @@ kernel-api.sgml: kernel-api.tmpl $(TOPDIR)/arch/i386/kernel/mca.c \ $(TOPDIR)/arch/i386/kernel/mtrr.c \ $(TOPDIR)/drivers/char/misc.c \ - $(TOPDIR)/drivers/char/serial.c \ $(TOPDIR)/drivers/char/videodev.c \ + $(TOPDIR)/drivers/net/net_init.c \ + $(TOPDIR)/drivers/char/serial.c \ $(TOPDIR)/drivers/sound/sound_core.c \ $(TOPDIR)/drivers/sound/sound_firmware.c \ $(TOPDIR)/drivers/net/wan/syncppp.c \ diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 96dd9ecc03dd..0b8d3a64b424 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -49,7 +49,7 @@ - Miscellaneous Device + Miscellaneous Devices !Edrivers/char/misc.c @@ -58,6 +58,11 @@ !Edrivers/char/videodev.c + + Network devices +!Idrivers/net/net_init.c + + Sound Devices !Edrivers/sound/sound_core.c diff --git a/Documentation/DocBook/parportbook.sgml b/Documentation/DocBook/parportbook.sgml deleted file mode 100644 index 1644748adddc..000000000000 --- a/Documentation/DocBook/parportbook.sgml +++ /dev/null @@ -1,1747 +0,0 @@ - - - - - The Parallel Port Subsystem - - - - Tim - Waugh - -
- twaugh@redhat.com -
-
-
-
- - - 1999-2000 - Tim Waugh - - - - - This documentation is free software; you can redistribute - it and/or modify it under the terms of the GNU General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later - version. - - - - This program is distributed in the hope that it will be - useful, but WITHOUT ANY WARRANTY; without even the implied - warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, - MA 02111-1307 USA - - - - For more details see the file COPYING in the source - distribution of Linux. - - -
- - - - -Design goals - - -The problems - - - - - - -The first parallel port support for Linux came with the line -printer driver, lp. The printer driver is a -character special device, and (in Linux 2.0) had support for writing, -via write, and configuration and statistics -reporting via ioctl. - -The printer driver could be used on any computer that had an IBM -PC-compatible parallel port. Because some architectures have parallel -ports that aren't really the same as PC-style ports, other variants of -the printer driver were written in order to support Amiga and Atari -parallel ports. - -When the Iomega Zip drive was released, and a driver written for -it, a problem became apparent. The Zip drive is a parallel port -device that provides a parallel port of its own---it is designed to -sit between a computer and an attached printer, with the printer -plugged into the Zip drive, and the Zip drive plugged into the -computer. - -The problem was that, although printers and Zip drives were both -supported, for any given port only one could be used at a time. Only -one of the two drivers could be present in the kernel at once. This -was because of the fact that both drivers wanted to drive the same -hardware---the parallel port. When the printer driver initialised, it -would call the check_region function to make sure -that the IO region associated with the parallel port was free, and -then it would call request_region to allocate it. -The Zip drive used the same mechanism. Whichever driver initialised -first would gain exclusive control of the parallel port. - -The only way around this problem at the time was to make sure -that both drivers were available as loadable kernel modules. To use -the printer, load the printer driver module; then for the Zip drive, -unload the printer driver module and load the Zip driver -module. - -The net effect was that printing a document that was stored on a Zip -drive was a bit of an ordeal, at least if the Zip drive and printer -shared a parallel port. A better solution was needed. - -Zip drives are not the only devices that presented problems for -Linux. There are other devices with pass-through ports, for example -parallel port CD-ROM drives. There are also printers that report -their status textually rather than using simple error pins: sending a -command to the printer can cause it to report the number of pages that -it has ever printed, or how much free memory it has, or whether it is -running out of toner, and so on. The printer driver didn't originally -offer any facility for reading back this information (although Carsten -Gross added nibble mode readback support for kernel 2.2). - - - -The IEEE has issued a standards document called IEEE 1284, which -documents existing practice for parallel port communications in a -variety of modes. Those modes are: compatibility, -reverse nibble, reverse byte, ECP and EPP. Newer devices often use -the more advanced modes of transfer (ECP and EPP). In Linux 2.0, the -printer driver only supported compatibility mode -(i.e. normal printer protocol) and reverse nibble mode. - - - - -The solutions - - - -The parport code in Linux 2.2 was designed -to meet these problems of architectural differences in parallel ports, -of port-sharing between devices with pass-through ports, and of lack -of support for IEEE 1284 transfer modes. - - - -There are two layers to the -parport subsystem, only one of which deals -directly with the hardware. The other layer deals with sharing and -IEEE 1284 transfer modes. In this way, parallel support for a -particular architecture comes in the form of a module which registers -itself with the generic sharing layer. - - - -The sharing model provided by the parport -subsystem is one of exclusive access. A device driver, such as the -printer driver, must ask the parport layer for -access to the port, and can only use the port once access has been -granted. When it has finished a transaction, it can -tell the parport layer that it may release the -port for other device drivers to use. - - - -Devices with pass-through ports all manage to share a parallel -port with other devices in generally the same way. The device has a -latch for each of the pins on its pass-through port. The normal state -of affairs is pass-through mode, with the device copying the signal -lines between its host port and its pass-through port. When the -device sees a special signal from the host port, it latches the -pass-through port so that devices further downstream don't get -confused by the pass-through device's conversation with the host -parallel port: the device connected to the pass-through port (and any -devices connected in turn to it) are effectively cut off from the -computer. When the pass-through device has completed its transaction -with the computer, it enables the pass-through port again. - - - - - - - -This technique relies on certain special signals -being invisible to devices that aren't watching for them. This tends -to mean only changing the data signals and leaving the control signals -alone. IEEE 1284.3 documents a standard protocol for daisy-chaining -devices together with parallel ports. - - - -Support for standard transfer modes are provided as operations -that can be performed on a port, along with operations for setting the -data lines, or the control lines, or reading the status lines. These -operations appear to the device driver as function pointers; more -later. - - - - - - -Standard transfer modes - - - - -The standard transfer modes in use over the -parallel port are defined by a document called IEEE -1284. It really just codifies existing practice and documents -protocols (and variations on protocols) that have been in common use -for quite some time. - -The original definitions of which pin did what were set out by -Centronics Data Computer Corporation, but only the printer-side -interface signals were specified. - -By the early 1980s, IBM's host-side implementation had become -the most widely used. New printers emerged that claimed Centronics -compatibility, but although compatible with Centronics they differed -from one another in a number of ways. - -As a result of this, when IEEE 1284 was published in 1994, all -that it could really do was document the various protocols that are -used for printers (there are about six variations on a theme). - -In addition to the protocol used to talk to -Centronics-compatible printers, IEEE 1284 defined other protocols that -are used for unidirectional peripheral-to-host transfers (reverse -nibble and reverse byte) and for fast bidirectional transfers (ECP and -EPP). - - - - -Structure - - - - - - - - - - - - -Sharing core - - - -At the core of the parport subsystem is the -sharing mechanism (see drivers/parport/share.c). -This module, parport, is responsible for -keeping track of which ports there are in the system, which device -drivers might be interested in new ports, and whether or not each port -is available for use (or if not, which driver is currently using -it). - - - - -Parports and their overrides - - -The generic parport sharing code doesn't -directly handle the parallel port hardware. That is done instead by -low-level parport drivers. The -function of a low-level parport driver is to -detect parallel ports, register them with the sharing code, and -provide a list of access functions for each port. - -The most basic access functions that must be provided are ones -for examining the status lines, for setting the control lines, and for -setting the data lines. There are also access functions for setting -the direction of the data lines; normally they are in the -forward direction (that is, the computer drives them), -but some ports allow switching to reverse mode (driven -by the peripheral). There is an access function for examining the -data lines once in reverse mode. - - - - -IEEE 1284 transfer modes - - -Stacked on top of the sharing mechanism, but still in the -parport module, are functions for transferring -data. They are provided for the device drivers to use, and are very -much like library routines. Since these transfer functions are -provided by the generic parport core they must -use the lowest common denominator set of access -functions: they can set the control lines, examine the status lines, -and use the data lines. With some parallel ports the data lines can -only be set and not examined, and with other ports accessing the data -register causes control line activity; with these types of situations, -the IEEE 1284 transfer functions make a best effort attempt to do the -right thing. In some cases, it is not physically possible to use -particular IEEE 1284 transfer modes. - -The low-level parport drivers also provide -IEEE 1284 transfer functions, as names in the access function list. -The low-level driver can just name the generic IEEE 1284 transfer -functions for this. Some parallel ports can do IEEE 1284 transfers in -hardware; for those ports, the low-level driver can provide functions -to utilise that feature. - - - - - - - - -Pardevices and parport_drivers - -When a parallel port device driver (such as -lp) initialises it tells the sharing layer about -itself using parport_register_driver. The -information is put into a struct -parport_driver, which is put into a linked list. The -information in a struct parport_driver really -just amounts to some function pointers to callbacks in the parallel -port device driver. - -During its initialisation, a low-level port driver tells the -sharing layer about all the ports that it has found (using -parport_register_port), and the sharing layer -creates a struct parport for each of them. -Each struct parport contains (among other -things) a pointer to a struct -parport_operations, which is a list of function pointers -for the various operations that can be performed on a port. You can -think of a struct parport as a parallel port -object, if object-orientated programming -is your thing. The parport structures are -chained in a linked list, whose head is portlist -(in drivers/parport/share.c). - -Once the port has been registered, the low-level port driver -announces it. The parport_announce_port function -walks down the list of parallel port device drivers -(struct parport_drivers) calling the -attach function of each. - -Similarly, a low-level port driver can undo the effect of -registering a port with the -parport_unregister_port function, and device -drivers are notified using the detach -callback. - -Device drivers can undo the effect of registering themselves -with the parport_unregister_driver -function. - - - - - - -The IEEE 1284.3 API - -The ability to daisy-chain devices is very useful, but if every -device does it in a different way it could lead to lots of -complications for device driver writers. Fortunately, the IEEE are -standardising it in IEEE 1284.3, which covers daisy-chain devices and -port multiplexors. - -At the time of writing, IEEE 1284.3 has not been published, but -the draft specifies the on-the-wire protocol for daisy-chaining and -multiplexing, and also suggests a programming interface for using it. -That interface (or most of it) has been implemented in the -parport code in Linux. - -At initialisation of the parallel port bus, daisy-chained -devices are assigned addresses starting from zero. There can only be -four devices with daisy-chain addresses, plus one device on the end -that doesn't know about daisy-chaining and thinks it's connected -directly to a computer. - -Another way of connecting more parallel port devices is to use a -multiplexor. The idea is to have a device that is connected directly -to a parallel port on a computer, but has a number of parallel ports -on the other side for other peripherals to connect to (two or four -ports are allowed). The multiplexor switches control to different -ports under software control---it is, in effect, a programmable -printer switch. - -Combining the ability of daisy-chaining five devices together -with the ability to multiplex one parallel port between four gives the -potential to have twenty peripherals connected to the same parallel -port! - -In addition, of course, a single computer can have multiple -parallel ports. So, each parallel port peripheral in the system can -be identified with three numbers, or co-ordinates: the parallel port, -the multiplexed port, and the daisy-chain address. - - - - - - - - - - - - - - - -Each device in the system is numbered at initialisation (by -parport_daisy_init). You can convert between -this device number and its co-ordinates with -parport_device_num and -parport_device_coords. - - - int parport_device_num - int parport - int mux - int daisy - - - - int parport_device_coords - int devnum - int *parport - int *mux - int *daisy - - -Any parallel port peripheral will be connected directly or -indirectly to a parallel port on the system, but it won't have a -daisy-chain address if it does not know about daisy-chaining, and it -won't be connected through a multiplexor port if there is no -multiplexor. The special co-ordinate value -1 is -used to indicate these cases. - -Two functions are provided for finding devices based on their -IEEE 1284 Device ID: parport_find_device and -parport_find_class. - - - int parport_find_device - const char *mfg - const char *mdl - int from - - - - int parport_find_class - parport_device_class cls - int from - - -These functions take a device number (in addition to some other -things), and return another device number. They walk through the list -of detected devices until they find one that matches the requirements, -and then return that device number (or -1 if -there are no more such devices). They start their search at the -device after the one in the list with the number given (at -from+1, in other words). - - - - - - -Device driver's view - - - - - - - - - -This section is written from the point of view of the device -driver programmer, who might be writing a driver for a printer or a -scanner or else anything that plugs into the parallel port. It -explains how to use the parport interface to find -parallel ports, use them, and share them with other device -drivers. - -We'll start out with a description of the various functions that -can be called, and then look at a reasonably simple example of their -use: the printer driver. - -The interactions between the device driver and the -parport layer are as follows. First, the device -driver registers its existence with parport, in -order to get told about any parallel ports that have been (or will be) -detected. When it gets told about a parallel port, it then tells -parport that it wants to drive a device on that -port. Thereafter it can claim exclusive access to the port in order -to talk to its device. - -So, the first thing for the device driver to do is tell -parport that it wants to know what parallel ports -are on the system. To do this, it uses the -parport_register_device function: - - - - - - int parport_register_driver - struct parport_driver *driver - - -In other words, the device driver passes pointers to a couple of -functions to parport, and -parport calls attach for -each port that's detected (and detach for each -port that disappears -- yes, this can happen). - -The next thing that happens is that the device driver tells -parport that it thinks there's a device on the -port that it can drive. This typically will happen in the driver's -attach function, and is done with -parport_register_device: - - - struct pardevice *parport_register_device - struct parport *port - const char *name - int (*pf) - void * - void (*kf) - void * - void (*irq_func) - int, void *, struct pt_regs * - int flags - void *handle - - -The port comes from the parameter supplied -to the attach function when it is called, or -alternatively can be found from the list of detected parallel ports -directly with the (now deprecated) -parport_enumerate function. - -The next three parameters, pf, -kf, and irq_func, are -more function pointers. These callback functions get called under -various circumstances, and are always given the -handle as one of their parameters. - -The preemption callback, pf, is called -when the driver has claimed access to the port but another device -driver wants access. If the driver is willing to let the port go, it -should return zero and the port will be released on its behalf. There -is no need to call parport_release. If -pf gets called at a bad time for letting the -port go, it should return non-zero and no action will be taken. It is -good manners for the driver to try to release the port at the earliest -opportunity after its preemption callback is called. - -The kick callback, kf, is -called when the port can be claimed for exclusive access; that is, -parport_claim is guaranteed to succeed inside the -kick callback. If the driver wants to claim the port -it should do so; otherwise, it need not take any action. - -The irq_func callback is called, -predictably, when a parallel port interrupt is generated. But it is -not the only code that hooks on the interrupt. The sequence is this: -the lowlevel driver is the one that has done -request_irq; it then does whatever -hardware-specific things it needs to do to the parallel port hardware -(for PC-style ports, there is nothing special to do); it then tells -the IEEE 1284 code about the interrupt, which may involve reacting to -an IEEE 1284 event, depending on the current IEEE 1284 phase; and -finally the irq_func function is called. - -None of the callback functions are allowed to block. - -The flags are for telling -parport any requirements or hints that are -useful. The only useful value here (other than -0, which is the usual value) is -PARPORT_DEV_EXCL. The point of that flag is to -request exclusive access at all times---once a driver has successfully -called parport_register_device with that flag, no -other device drivers will be able to register devices on that port -(until the successful driver deregisters its device, of -course). - -The PARPORT_DEV_EXCL flag is for preventing -port sharing, and so should only be used when sharing the port with -other device drivers is impossible and would lead to incorrect -behaviour. Use it sparingly! - -Devices can also be registered by device drivers based on their -device numbers (the same device numbers as in the previous -section). - -The parport_open function is similar to -parport_register_device, and -parport_close is the equivalent of -parport_unregister_device. The difference is -that parport_open takes a device number rather -than a pointer to a struct parport. - - - struct pardevice *parport_open - int devnum - int (*pf) - void * - int (*kf) - void * - int (*irqf) - int, void *, struct pt_regs * - int flags - void *handle - - - - void parport_close - struct pardevice *dev - - - - struct pardevice *parport_register_device - struct parport *port - const char *name - int (*pf) - void * - int (*kf) - void * - int (*irqf) - int, void *, struct pt_regs * - int flags - void *handle - - - - void parport_unregister_device - struct pardevice *dev - - -The intended use of these functions is during driver -initialisation while the driver looks for devices that it supports, as -demonstrated by the following code fragment: - - - - -Once your device driver has registered its device and been -handed a pointer to a struct pardevice, the -next thing you are likely to want to do is communicate with the device -you think is there. To do that you'll need to claim access to the -port. - - - int parport_claim - struct pardevice *dev - - - - int parport_claim_or_block - struct pardevice *dev - - - - void parport_release - struct pardevice *dev - - -To claim access to the port, use -parport_claim or -parport_claim_or_block. The first of these will -not block, and so can be used from interrupt context. If -parport_claim succeeds it will return zero and -the port is available to use. It may fail (returning non-zero) if the -port is in use by another driver and that driver is not willing to -relinquish control of the port. - -The other function, parport_claim_or_block, -will block if necessary to wait for the port to be free. If it slept, -it returns 1; if it succeeded without needing to -sleep it returns 0. If it fails it will return a -negative error code. - -When you have finished communicating with the device, you can -give up access to the port so that other drivers can communicate with -their devices. The parport_release function -cannot fail, but it should not be called without the port claimed. -Similarly, you should not try to claim the port if you already have it -claimed. - -You may find that although there are convenient points for your -driver to relinquish the parallel port and allow other drivers to talk -to their devices, it would be preferable to keep hold of the port. -The printer driver only needs the port when there is data to print, -for example, but a network driver (such as PLIP) could be sent a -remote packet at any time. With PLIP, it is no huge catastrophe if a -network packet is dropped, since it will likely be sent again, so it -is possible for that kind of driver to share the port with other -(pass-through) devices. - -The parport_yield and -parport_yield_blocking functions are for marking -points in the driver at which other drivers may claim the port and use -their devices. Yielding the port is similar to releasing it and -reclaiming it, but it more efficient because nothing is done if there -are no other devices needing the port. In fact, nothing is done even -if there are other devices waiting but the current device is still -within its timeslice. The default timeslice is half a -second, but it can be adjusted via a /proc -entry. - - - int parport_yield - struct pardevice *dev - - - - int parport_yield_blocking - struct pardevice *dev - - -The first of these, parport_yield, will not -block but as a result may fail. The return value for -parport_yield is the same as for -parport_claim. The blocking version, -parport_yield_blocking, has the same return code -as parport_claim_or_block. - -Once the port has been claimed, the device driver can use the -functions in the struct parport_operations -pointer in the struct parport it has a -pointer to. For example: - - -ops->write_data (port, d); -]]> - -Some of these operations have shortcuts. For -instance, parport_write_data is equivalent to the -above, but may be a little bit faster (it's a macro that in some cases -can avoid needing to indirect through port and -ops). - - - - -Port drivers - - - -To recap, then: - - - - - -The device driver registers itself with parport. - - - - - -A low-level driver finds a parallel port and registers it with -parport (these first two things can happen in -either order). This registration creates a struct -parport which is linked onto a list of known ports. - - - - - -parport calls the attach -function of each registered device driver, passing it the pointer to -the new struct parport. - - - - - -The device driver gets a handle from parport, for -use with -parport_claim/release. This -handle takes the form of a pointer to a struct -pardevice, representing a particular device on the -parallel port, and is acquired using -parport_register_device. - - - - - -The device driver claims the port using -parport_claim (or -function_claim_or_block). - - - - - -Then it goes ahead and uses the port. When finished it releases the -port. - - - - - -The purpose of the low-level drivers, then, is to detect -parallel ports and provide methods of accessing them -(i.e. implementing the operations in struct -parport_operations). - - - - - - -A more complete description of which operation is supposed to do -what is available in -Documentation/parport-lowlevel.txt. - - - - -The printer driver - - - - -The printer driver, lp is a character -special device driver and a parport client. As a -character special device driver it registers a struct -file_operations using -register_chrdev, with pointers filled in for -write, ioctl, -open and -release. As a client of -parport, it registers a struct -parport_driver using -parport_register_driver, so that -parport knows to call -lp_attach when a new parallel port is discovered -(and lp_detach when it goes away). - -The parallel port console functionality is also implemented in -lp.c, but that won't be covered here (it's quite -simple though). - -The initialisation of the driver is quite easy to understand -(see lp_init). The lp_table -is an array of structures that contain information about a specific -device (the struct pardevice associated with -it, for example). That array is initialised to sensible values first -of all. - -Next, the printer driver calls -register_chrdev passing it a pointer to -lp_fops, which contains function pointers for the -printer driver's implementation of open, -write, and so on. This part is the same as for -any character special device driver. - -After successfully registering itself as a character special -device driver, the printer driver registers itself as a -parport client using -parport_register_driver. It passes a pointer to -this structure: - - - - -The lp_detach function is not very -interesting (it does nothing); the interesting bit is -lp_attach. What goes on here depends on whether -the user supplied any parameters. The possibilities are: no -parameters supplied, in which case the printer driver uses every port -that is detected; the user supplied the parameter auto, -in which case only ports on which the device ID string indicates a -printer is present are used; or the user supplied a list of parallel -port numbers to try, in which case only those are used. - -For each port that the printer driver wants to use (see -lp_register), it calls -parport_register_device and stores the resulting -struct pardevice pointer in the -lp_table. If the user told it to do so, it then -resets the printer. - -The other interesting piece of the printer driver, from the -point of view of parport, is -lp_write. In this function, the user space -process has data that it wants printed, and the printer driver hands -it off to the parport code to deal with. - -The parport functions it uses that we have -not seen yet are parport_negotiate, -parport_set_timeout, and -parport_write. These functions are part of the -IEEE 1284 implementation. - -The way the IEEE 1284 protocol works is that the host tells the -peripheral what transfer mode it would like to use, and the peripheral -either accepts that mode or rejects it; if the mode is rejected, the -host can try again with a different mode. This is the negotation -phase. Once the peripheral has accepted a particular transfer mode, -data transfer can begin that mode. - -The particular transfer mode that the printer driver wants to -use is named in IEEE 1284 as compatibility mode, and -the function to request a particular mode is called -parport_negotiate. - - - int parport_negotiate - struct parport *port - int mode - - -The modes parameter is a symbolic -constant representing an IEEE 1284 mode; in this instance, it is -IEEE1284_MODE_COMPAT. (Compatibility mode is -slightly different to the other modes---rather than being specifically -requested, it is the default until another mode is selected.) - -Back to lp_write then. First, access to -the parallel port is secured with -parport_claim_or_block. At this point the driver -might sleep, waiting for another driver (perhaps a Zip drive driver, -for instance) to let the port go. Next, it goes to compatibility mode -using parport_negotiate. - -The main work is done in the write-loop. In particular, the -line that hands the data over to parport -reads: - - - - -The parport_write function writes data to -the peripheral using the currently selected transfer mode -(compatibility mode, in this case). It returns the number of bytes -successfully written: - - - ssize_t parport_write - struct parport *port - const void *buf - size_t len - - - - ssize_t parport_read - struct parport *port - void *buf - size_t len - - -(parport_read does what it sounds like, but -only works for modes in which reverse transfer is possible. Of -course, parport_write only works in modes in -which forward transfer is possible, too.) - -The buf pointer should be to kernel space -memory, and obviously the len parameter -specifies the amount of data to transfer. - -In fact what parport_write does is call the -appropriate block transfer function from the struct -parport_operations: - - - - -The transfer code in parport will tolerate -a data transfer stall only for so long, and this timeout can be -specified with parport_set_timeout, which returns -the previous timeout: - - - long parport_set_timeout - struct pardevice *dev - long inactivity - - -This timeout is specific to the device, and is restored on -parport_claim. - - - - -User-level device drivers - - - -Introduction to ppdev - -The printer is accessible through /dev/lp0; -in the same way, the parallel port itself is accessible through -/dev/parport0. The difference is in the level of -control that you have over the wires in the parallel port -cable. - -With the printer driver, a user-space program (such as the -printer spooler) can send bytes in printer protocol. -Briefly, this means that for each byte, the eight data lines are set -up, then a strobe line tells the printer to look at the -data lines, and the printer sets an acknowledgement -line to say that it got the byte. The printer driver also allows the -user-space program to read bytes in nibble mode, which -is a way of transferring data from the peripheral to the computer half -a byte at a time (and so it's quite slow). - -In contrast, the ppdev driver (accessed via -/dev/parport0) allows you to: - - - - - -examine status lines, - - - - - -set control lines, - - - - - -set/examine data lines (and control the direction of the data lines), - - - - - -wait for an interrupt (triggered by one of the status lines), - - - - - -find out how many new interrupts have occurred, - - - - - -set up a response to an interrupt, - - - - - -use IEEE 1284 negotiation (for telling peripheral which transfer mode, -to use) - - - - - -transfer data using a specified IEEE 1284 mode. - - - - - - - - -User-level or kernel-level driver? - -The decision of whether to choose to write a kernel-level device -driver or a user-level device driver depends on several factors. One -of the main ones from a practical point of view is speed: kernel-level -device drivers get to run faster because they are not preemptable, -unlike user-level applications. - -Another factor is ease of development. It is in general easier -to write a user-level driver because (a) one wrong move does not -result in a crashed machine, (b) you have access to user libraries -(such as the C library), and (c) debugging is easier. - - - - -Programming interface - -The ppdev interface is largely the same as -that of other character special devices, in that it supports -open, close, -read, write, and -ioctl. - - -Starting and stopping: <function>open</function> and -<function>close</function> - -The device node /dev/parport0 represents -any device that is connected to parport0, the -first parallel port in the system. Each time the device node is -opened, it represents (to the process doing the opening) a different -device. It can be opened more than once, but only one instance can -actually be in control of the parallel port at any time. A process -that has opened /dev/parport0 shares the parallel -port in the same way as any other device driver. A user-land driver -may be sharing the parallel port with in-kernel device drivers as well -as other user-land drivers. - - - -Control: <function>ioctl</function> - -Most of the control is done, naturally enough, via the -ioctl call. Using ioctl, -the user-land driver can control both the ppdev -driver in the kernel and the physical parallel port itself. The -ioctl call takes as parameters a file descriptor -(the one returned from opening the device node), a command, and -optionally (a pointer to) some data. - - -PPCLAIM - - -Claims access to the port. As a user-land device driver writer, -you will need to do this before you are able to actually change the -state of the parallel port in any way. Note that some operations only -affect the ppdev driver and not the port, such as -PPSETMODE; they can be performed while access to -the port is not claimed. - - - -PPEXCL - - -Instructs the kernel driver to forbid any sharing of the port -with other drivers, i.e. it requests exclusivity. The -PPEXCL command is only valid when the port is not -already claimed for use, and it may mean that the next -PPCLAIM ioctl will fail: -some other driver may already have registered itself on that -port. - -Most device drivers don't need exclusive access to the port. -It's only provided in case it is really needed, for example for -devices where access to the port is required for extensive periods of -time (many seconds). - -Note that the PPEXCL -ioctl doesn't actually claim the port there and -then---action is deferred until the PPCLAIM -ioctl is performed. - - - -PPRELEASE - - -Releases the port. Releasing the port undoes the effect of -claiming the port. It allows other device drivers to talk to their -devices (assuming that there are any). - - - -PPYIELD - - -Yields the port to another driver. This -ioctl is a kind of short-hand for releasing the -port and immediately reclaiming it. It gives other drivers a chance -to talk to their devices, but afterwards claims the port back. An -example of using this would be in a user-land printer driver: once a -few characters have been written we could give the port to another -device driver for a while, but if we still have characters to send to -the printer we would want the port back as soon as possible. - -It is important not to claim the parallel port for too long, as -other device drivers will have no time to service their devices. If -your device does not allow for parallel port sharing at all, it is -better to claim the parallel port exclusively (see -PPEXCL). - - - -PPNEGOT - - -Performs IEEE 1284 negotiation into a particular mode. Briefly, -negotiation is the method by which the host and the peripheral decide -on a protocol to use when transferring data. - -An IEEE 1284 compliant device will start out in compatibility -mode, and then the host can negotiate to another mode (such as -ECP). - -The ioctl parameter should be a pointer to -an int; values for this are in -parport.h and include: - - -IEEE1284_MODE_COMPAT -IEEE1284_MODE_NIBBLE -IEEE1284_MODE_BYTE -IEEE1284_MODE_EPP -IEEE1284_MODE_ECP - - -The PPNEGOT ioctl -actually does two things: it performs the on-the-wire negotiation, and -it sets the behaviour of subsequent -read/write calls so that -they use that mode (but see PPSETMODE). - - - -PPSETMODE - - -Sets which IEEE 1284 protocol to use for the -read and write calls. - -The ioctl parameter should be a pointer to -an int. - - - -PPGETTIME - - -Retrieves the time-out value. The read and -write calls will time out if the peripheral -doesn't respond quickly enough. The PPGETTIME -ioctl retrieves the length of time that the -peripheral is allowed to have before giving up. - -The ioctl parameter should be a pointer to -a struct timeval. - - - -PPSETTIME - - -Sets the time-out. The ioctl parameter -should be a pointer to a struct -timeval. - - - -PPWCONTROL - - -Sets the control lines. The ioctl -parameter is a pointer to an unsigned char, the bitwise -OR of the control line values in -parport.h. - - - -PPRCONTROL - - -Returns the last value written to the control register, in the -form of an unsigned char: each bit corresponds to a -control line (although some are unused). The -ioctl parameter should be a pointer to an -unsigned char. - -This doesn't actually touch the hardware; the last value written -is remembered in software. This is because some parallel port -hardware does not offer read access to the control register. - -The control lines bits are defined in -parport.h: - - -PARPORT_CONTROL_STROBE -PARPORT_CONTROL_AUTOFD -PARPORT_CONTROL_SELECT -PARPORT_CONTROL_INIT - - - - -PPFCONTROL - - -Frobs the control lines. Since a common operation is to change -one of the control signals while leaving the others alone, it would be -quite inefficient for the user-land driver to have to use -PPRCONTROL, make the change, and then use -PPWCONTROL. Of course, each driver could -remember what state the control lines are supposed to be in (they are -never changed by anything else), but in order to provide -PPRCONTROL, ppdev must -remember the state of the control lines anyway. - -The PPFCONTROL ioctl -is for frobbing control lines, and is like -PPWCONTROL but acts on a restricted set of -control lines. The ioctl parameter is a pointer -to a struct ppdev_frob_struct: - - - - - -The mask and -val fields are bitwise ORs of control line -names (such as in PPWCONTROL). The operation -performed by PPFCONTROL is: - - - - - -In other words, the signals named in -mask are set to the values in -val. - - - -PPRSTATUS - - -Returns an unsigned char containing bits set for -each status line that is set (for instance, -PARPORT_STATUS_BUSY). The -ioctl parameter should be a pointer to an -unsigned char. - - - -PPDATADIR - - -Controls the data line drivers. Normally the computer's -parallel port will drive the data lines, but for byte-wide transfers -from the peripheral to the host it is useful to turn off those drivers -and let the peripheral drive the signals. (If the drivers on the -computer's parallel port are left on when this happens, the port might -be damaged.) - -This is only needed in conjunction with -PPWDATA or PPRDATA. - -The ioctl parameter is a pointer to an -int. If the int is zero, the drivers are -turned on (forward direction); if non-zero, the drivers are turned off -(reverse direction). - - - -PPWDATA - - -Sets the data lines (if in forward mode). The -ioctl parameter is a pointer to an unsigned -char. - - - -PPRDATA - - -Reads the data lines (if in reverse mode). The -ioctl parameter is a pointer to an unsigned -char. - - - -PPCLRIRQ - - -Clears the interrupt count. The ppdev -driver keeps a count of interrupts as they are triggered. -PPCLRIRQ stores this count in an -int, a pointer to which is passed in as the -ioctl parameter. - -In addition, the interrupt count is reset to zero. - - - -PPWCTLONIRQ - - -Set a trigger response. Afterwards when an interrupt is -triggered, the interrupt handler will set the control lines as -requested. The ioctl parameter is a pointer to -an unsigned char, which is interpreted in the same way as -for PPWCONTROL. - -The reason for this ioctl is simply speed. -Without this ioctl, responding to an interrupt -would start in the interrupt handler, switch context to the user-land -driver via poll or select, -and then switch context back to the kernel in order to handle -PPWCONTROL. Doing the whole lot in the interrupt -handler is a lot faster. - - - - - - - - - - -Transferring data: <function>read</function> and -<function>write</function> - -Transferring data using read and -write is straightforward. The data is -transferring using the current IEEE 1284 mode (see the -PPSETMODE ioctl). For modes -which can only transfer data in one direction, only the appropriate -function will work, of course. - - - -Waiting for events: <function>poll</function> and -<function>select</function> - -The ppdev driver provides user-land device -drivers with the ability to wait for interrupts, and this is done -using poll (and select, -which is implemented in terms of poll). - -When a user-land device driver wants to wait for an interrupt, -it sleeps with poll. When the interrupt arrives, -ppdev wakes it up (with a read -event, although strictly speaking there is nothing to actually -read). - - - - - - -Examples - -Presented here are two demonstrations of how to write a simple -printer driver for ppdev. Firstly we will use -the write function, and after that we will drive -the control and data lines directly. - -The first thing to do is to actually open the device. - - - -Here name should be something along the lines -of "/dev/parport0". (If you don't have any -/dev/parport files, you can make them with -mknod; they are character special device nodes with -major 99.) - -In order to do anything with the port we need to claim access to -it. - - - -Our printer driver will copy its input (from -stdin) to the printer, and it can do that it one of -two ways. The first way is to hand it all off to the kernel driver, -with the knowledge that the protocol that the printer speaks is IEEE -1284's compatibility mode. - - 0) { - int written = write_printer (fd, ptr, got); - - if (written < 0) { - perror ("write"); - close (fd); - return 1; - } - - ptr += written; - got -= written; - } - } -]]> - -The write_printer function is not pictured -above. This is because the main loop that is shown can be used for -both methods of driving the printer. Here is one implementation of -write_printer: - - - -We hand the data to the kernel-level driver (using -write) and it handles the printer -protocol. - -Now let's do it the hard way! In this particular example there -is no practical reason to do anything other than just call -write, because we know that the printer talks an -IEEE 1284 protocol. On the other hand, this particular example does -not even need a user-land driver since there is already a kernel-level -one; for the purpose of this discussion, try to imagine that the -printer speaks a protocol that is not already implemented under -Linux. - -So, here is the alternative implementation of -write_printer (for brevity, error checking has -been omitted): - - - -To show a bit more of the ppdev interface, -here is a small piece of code that is intended to mimic the printer's -side of printer protocol. - - 1) - fprintf (stderr, "Arghh! Missed %d interrupt%s!\n", - irqc - 1, irqc == 2 ? "s" : ""); - - /* Ack it. */ - ioctl (fd, PPWCONTROL, &acking); - usleep (2); - ioctl (fd, PPWCONTROL, &busy); - - putchar (ch); - } -]]> - - - - -
\ No newline at end of file diff --git a/Documentation/networking/tulip.txt b/Documentation/networking/tulip.txt index ecef178b0b41..f86787a90cbc 100644 --- a/Documentation/networking/tulip.txt +++ b/Documentation/networking/tulip.txt @@ -125,7 +125,7 @@ them. The MII transceiver status is polled using an kernel timer. Source tree tour ----------------- +================ The following is a list of files comprising the Tulip ethernet driver in drivers/net/tulip subdirectory. @@ -140,5 +140,15 @@ tulip_core.c - Driver core (a.k.a. where "everything else" goes) +Version history +=============== +0.9.4.1: +* Finish PCI DMA conversion (davem) +* Do not netif_start_queue() at end of tulip_tx_timeout() (kuznet) +* PCI DMA fix (kuznet) +* eeprom.c code cleanup +* Remove Xircom Tulip crud + + [EOF] diff --git a/Makefile b/Makefile index 96c739e8d9c8..92c07e107fb8 100644 --- a/Makefile +++ b/Makefile @@ -398,6 +398,7 @@ mrproper: clean archmrproper rm -f .hdepend scripts/mkdep scripts/split-include scripts/docproc rm -f $(TOPDIR)/include/linux/modversions.h rm -rf $(TOPDIR)/include/linux/modules + rm -f Documentation/DocBook/*.sgml distclean: mrproper rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index 8aac90800b9d..055b3ea5b451 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -145,7 +145,7 @@ static void gzip_release(void **ptr) free_mem_ptr = (long) *ptr; } -static void scroll() +static void scroll(void) { int i; @@ -197,6 +197,7 @@ void* memset(void* s, int c, size_t n) char *ss = (char*)s; for (i=0;i IRQ %d\n", i, ints[i+1]); + printk(KERN_DEBUG "... PIRQ%d -> IRQ %d\n", i, ints[i+1]); /* * PIRQs are mapped upside down, usually. */ @@ -289,7 +289,7 @@ static int __init EISA_ELCR(unsigned int irq) unsigned int port = 0x4d0 + (irq >> 3); return (inb(port) >> (irq & 7)) & 1; } - printk("Broken MPtable reports ISA irq %d\n", irq); + printk(KERN_INFO "Broken MPtable reports ISA irq %d\n", irq); return 0; } @@ -338,7 +338,7 @@ static int __init MPBIOS_polarity(int idx) } default: { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } @@ -352,7 +352,7 @@ static int __init MPBIOS_polarity(int idx) } case 2: /* reserved */ { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } @@ -363,7 +363,7 @@ static int __init MPBIOS_polarity(int idx) } default: /* invalid */ { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); polarity = 1; break; } @@ -402,7 +402,7 @@ static int __init MPBIOS_trigger(int idx) } default: { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); trigger = 1; break; } @@ -416,7 +416,7 @@ static int __init MPBIOS_trigger(int idx) } case 2: /* reserved */ { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); trigger = 1; break; } @@ -427,7 +427,7 @@ static int __init MPBIOS_trigger(int idx) } default: /* invalid */ { - printk("broken BIOS!!\n"); + printk(KERN_WARNING "broken BIOS!!\n"); trigger = 0; break; } @@ -454,7 +454,7 @@ static int __init pin_2_irq(int idx, int apic, int pin) * Debugging check, we are in big trouble if this message pops up! */ if (mp_irqs[idx].mpc_dstirq != pin) - printk("broken BIOS or MPTABLE parser, ayiee!!\n"); + printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n"); switch (mp_bus_id_to_type[bus]) { @@ -477,7 +477,7 @@ static int __init pin_2_irq(int idx, int apic, int pin) } default: { - printk("unknown bus type %d.\n",bus); + printk(KERN_ERR "unknown bus type %d.\n",bus); irq = 0; break; } @@ -489,10 +489,10 @@ static int __init pin_2_irq(int idx, int apic, int pin) if ((pin >= 16) && (pin <= 23)) { if (pirq_entries[pin-16] != -1) { if (!pirq_entries[pin-16]) { - printk("disabling PIRQ%d\n", pin-16); + printk(KERN_DEBUG "disabling PIRQ%d\n", pin-16); } else { irq = pirq_entries[pin-16]; - printk("using PIRQ%d -> IRQ %d\n", + printk(KERN_DEBUG "using PIRQ%d -> IRQ %d\n", pin-16, irq); } } @@ -549,7 +549,7 @@ void __init setup_IO_APIC_irqs(void) struct IO_APIC_route_entry entry; int apic, pin, idx, irq, first_notcon = 1, vector; - printk("init IO_APIC IRQs\n"); + printk(KERN_DEBUG "init IO_APIC IRQs\n"); for (apic = 0; apic < nr_ioapics; apic++) { for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { @@ -567,7 +567,7 @@ void __init setup_IO_APIC_irqs(void) idx = find_irq_entry(apic,pin,mp_INT); if (idx == -1) { if (first_notcon) { - printk(" IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin); + printk(KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin); first_notcon = 0; } else printk(", %d-%d", mp_ioapics[apic].mpc_apicid, pin); @@ -658,8 +658,8 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) void __init UNEXPECTED_IO_APIC(void) { - printk(" WARNING: unexpected IO-APIC, please mail\n"); - printk(" to linux-smp@vger.rutgers.edu\n"); + printk(KERN_WARNING " WARNING: unexpected IO-APIC, please mail\n"); + printk(KERN_WARNING " to linux-smp@vger.rutgers.edu\n"); } void __init print_IO_APIC(void) @@ -669,15 +669,16 @@ void __init print_IO_APIC(void) struct IO_APIC_reg_01 reg_01; struct IO_APIC_reg_02 reg_02; - printk("number of MP IRQ sources: %d.\n", mp_irq_entries); + printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); for (i = 0; i < nr_ioapics; i++) - printk("number of IO-APIC #%d registers: %d.\n", mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]); + printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", + mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]); /* * We are a bit conservative about what we expect. We have to * know about every hardware change ASAP. */ - printk("testing the IO APIC.......................\n"); + printk(KERN_INFO "testing the IO APIC.......................\n"); for (apic = 0; apic < nr_ioapics; apic++) { @@ -686,14 +687,15 @@ void __init print_IO_APIC(void) if (reg_01.version >= 0x10) *(int *)®_02 = io_apic_read(apic, 2); - printk("\nIO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); - printk(".... register #00: %08X\n", *(int *)®_00); - printk("....... : physical APIC id: %02X\n", reg_00.ID); + printk("\n"); + printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid); + printk(KERN_DEBUG ".... register #00: %08X\n", *(int *)®_00); + printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.ID); if (reg_00.__reserved_1 || reg_00.__reserved_2) UNEXPECTED_IO_APIC(); - printk(".... register #01: %08X\n", *(int *)®_01); - printk("....... : max redirection entries: %04X\n", reg_01.entries); + printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)®_01); + printk(KERN_DEBUG "....... : max redirection entries: %04X\n", reg_01.entries); if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */ (reg_01.entries != 0x17) && /* typical ISA+PCI boards */ (reg_01.entries != 0x1b) && /* Compaq Proliant boards */ @@ -704,7 +706,7 @@ void __init print_IO_APIC(void) ) UNEXPECTED_IO_APIC(); - printk("....... : IO APIC version: %04X\n", reg_01.version); + printk(KERN_DEBUG "....... : IO APIC version: %04X\n", reg_01.version); if ( (reg_01.version != 0x01) && /* 82489DX IO-APICs */ (reg_01.version != 0x10) && /* oldest IO-APICs */ (reg_01.version != 0x11) && /* Pentium/Pro IO-APICs */ @@ -715,16 +717,16 @@ void __init print_IO_APIC(void) UNEXPECTED_IO_APIC(); if (reg_01.version >= 0x10) { - printk(".... register #02: %08X\n", *(int *)®_02); - printk("....... : arbitration: %02X\n", reg_02.arbitration); + printk(KERN_DEBUG ".... register #02: %08X\n", *(int *)®_02); + printk(KERN_DEBUG "....... : arbitration: %02X\n", reg_02.arbitration); if (reg_02.__reserved_1 || reg_02.__reserved_2) UNEXPECTED_IO_APIC(); } - printk(".... IRQ redirection table:\n"); + printk(KERN_DEBUG ".... IRQ redirection table:\n"); - printk(" NR Log Phy "); - printk("Mask Trig IRR Pol Stat Dest Deli Vect: \n"); + printk(KERN_DEBUG " NR Log Phy "); + printk(KERN_DEBUG "Mask Trig IRR Pol Stat Dest Deli Vect: \n"); for (i = 0; i <= reg_01.entries; i++) { struct IO_APIC_route_entry entry; @@ -732,7 +734,7 @@ void __init print_IO_APIC(void) *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); - printk(" %02x %03X %02X ", + printk(KERN_DEBUG " %02x %03X %02X ", i, entry.dest.logical.logical_dest, entry.dest.physical.physical_dest @@ -765,7 +767,7 @@ void __init print_IO_APIC(void) printk("\n"); } - printk(".................................... done.\n"); + printk(KERN_INFO ".................................... done.\n"); return; } @@ -775,7 +777,7 @@ static void print_APIC_bitfield (int base) unsigned int v; int i, j; - printk("0123456789abcdef0123456789abcdef\n"); + printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG); for (i = 0; i < 8; i++) { v = apic_read(base + i*0x10); for (j = 0; j < 32; j++) { @@ -792,40 +794,40 @@ void /*__init*/ print_local_APIC(void * dummy) { unsigned int v, ver, maxlvt; - printk("\nprinting local APIC contents on CPU#%d/%d:\n", + printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", smp_processor_id(), hard_smp_processor_id()); v = apic_read(APIC_ID); - printk("... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(v)); + printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(v)); v = apic_read(APIC_LVR); - printk("... APIC VERSION: %08x\n", v); + printk(KERN_INFO "... APIC VERSION: %08x\n", v); ver = GET_APIC_VERSION(v); maxlvt = get_maxlvt(); v = apic_read(APIC_TASKPRI); - printk("... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK); + printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK); if (APIC_INTEGRATED(ver)) { /* !82489DX */ v = apic_read(APIC_ARBPRI); - printk("... APIC ARBPRI: %08x (%02x)\n", v, + printk(KERN_DEBUG "... APIC ARBPRI: %08x (%02x)\n", v, v & APIC_ARBPRI_MASK); v = apic_read(APIC_PROCPRI); - printk("... APIC PROCPRI: %08x\n", v); + printk(KERN_DEBUG "... APIC PROCPRI: %08x\n", v); } v = apic_read(APIC_EOI); - printk("... APIC EOI: %08x\n", v); + printk(KERN_DEBUG "... APIC EOI: %08x\n", v); v = apic_read(APIC_LDR); - printk("... APIC LDR: %08x\n", v); + printk(KERN_DEBUG "... APIC LDR: %08x\n", v); v = apic_read(APIC_DFR); - printk("... APIC DFR: %08x\n", v); + printk(KERN_DEBUG "... APIC DFR: %08x\n", v); v = apic_read(APIC_SPIV); - printk("... APIC SPIV: %08x\n", v); + printk(KERN_DEBUG "... APIC SPIV: %08x\n", v); - printk("... APIC ISR field:\n"); + printk(KERN_DEBUG "... APIC ISR field:\n"); print_APIC_bitfield(APIC_ISR); - printk("... APIC TMR field:\n"); + printk(KERN_DEBUG "... APIC TMR field:\n"); print_APIC_bitfield(APIC_TMR); - printk("... APIC IRR field:\n"); + printk(KERN_DEBUG "... APIC IRR field:\n"); print_APIC_bitfield(APIC_IRR); if (APIC_INTEGRATED(ver)) { /* !82489DX */ @@ -837,37 +839,37 @@ void /*__init*/ print_local_APIC(void * dummy) apic_write(APIC_ESR, 0); } v = apic_read(APIC_ESR); - printk("... APIC ESR: %08x\n", v); + printk(KERN_DEBUG "... APIC ESR: %08x\n", v); } v = apic_read(APIC_ICR); - printk("... APIC ICR: %08x\n", v); + printk(KERN_DEBUG "... APIC ICR: %08x\n", v); v = apic_read(APIC_ICR2); - printk("... APIC ICR2: %08x\n", v); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); v = apic_read(APIC_LVTT); - printk("... APIC LVTT: %08x\n", v); + printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); if (maxlvt > 3) { /* PC is LVT#4. */ v = apic_read(APIC_LVTPC); - printk("... APIC LVTPC: %08x\n", v); + printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); } v = apic_read(APIC_LVT0); - printk("... APIC LVT0: %08x\n", v); + printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); v = apic_read(APIC_LVT1); - printk("... APIC LVT1: %08x\n", v); + printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); if (maxlvt > 2) { /* ERR is LVT#3. */ v = apic_read(APIC_LVTERR); - printk("... APIC LVTERR: %08x\n", v); + printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); } v = apic_read(APIC_TMICT); - printk("... APIC TMICT: %08x\n", v); + printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); v = apic_read(APIC_TMCCT); - printk("... APIC TMCCT: %08x\n", v); + printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); v = apic_read(APIC_TDCR); - printk("... APIC TDCR: %08x\n", v); + printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); printk("\n"); } @@ -958,7 +960,7 @@ static void __init setup_ioapic_ids_from_mpc (void) * Read the right value from the MPC table and * write it into the ID register. */ - printk("...changing IO-APIC physical APIC ID to %d ...", + printk(KERN_INFO "...changing IO-APIC physical APIC ID to %d ...", mp_ioapics[apic].mpc_apicid); /* @@ -1061,7 +1063,7 @@ static int __init nmi_irq_works(void) for (j = 0; j < smp_num_cpus; j++) { cpu = cpu_logical_map(j); if (atomic_read(&nmi_counter(cpu)) - atomic_read(&tmp[cpu].__nmi_counter) <= 3) { - printk("CPU#%d NMI appears to be stuck.\n", cpu); + printk(KERN_WARNING "CPU#%d NMI appears to be stuck.\n", cpu); return 0; } } @@ -1270,7 +1272,7 @@ static void setup_nmi (void) * is from Maciej W. Rozycki - so we do not have to EOI from * the NMI handler or the timer interrupt. */ - printk("activating NMI Watchdog ..."); + printk(KERN_INFO "activating NMI Watchdog ..."); smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1); enable_NMI_through_LVT0(NULL); @@ -1299,7 +1301,7 @@ static inline void check_timer(void) pin1 = find_timer_pin(mp_INT); pin2 = find_timer_pin(mp_ExtINT); - printk("..TIMER: vector=%d pin1=%d pin2=%d\n", vector, pin1, pin2); + printk(KERN_INFO "..TIMER: vector=%d pin1=%d pin2=%d\n", vector, pin1, pin2); /* * Ok, does IRQ0 through the IOAPIC work? @@ -1317,11 +1319,11 @@ static inline void check_timer(void) } if (pin1 != -1) { - printk("..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); + printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); clear_IO_APIC_pin(0, pin1); } - printk("...trying to set up timer (IRQ0) through the 8259A ... "); + printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); if (pin2 != -1) { printk("\n..... (found pin %d) ...", pin2); /* @@ -1345,11 +1347,11 @@ static inline void check_timer(void) printk(" failed.\n"); if (nmi_watchdog) { - printk("timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n"); + printk(KERN_WARNING "timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n"); nmi_watchdog = 0; } - printk("...trying to set up timer as Virtual Wire IRQ..."); + printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); disable_8259A_irq(0); irq_desc[0].handler = &lapic_irq_type; diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 863b2fc42b32..db799a4e6759 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -670,10 +670,10 @@ found: if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) BCD_TO_BIN(year); /* This should never happen... */ - if (year > 10 && year < 44) { + if (year >= 20 && year < 48) { epoch = 1980; guess = "ARC console"; - } else if (year < 96) { + } else if (year >= 48 && year < 100) { epoch = 1952; guess = "Digital UNIX"; } diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 6169eb742315..336a66949eda 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -1838,7 +1838,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev) void *ioaddr = tp->mmio_addr; u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; - unsigned long flags; + unsigned long flags=0; DPRINTK ("ENTER\n"); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index b62986f83701..0a834621d1b4 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -124,9 +124,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then bool ' EISA, VLB, PCI and on board controllers' CONFIG_NET_PCI if [ "$CONFIG_NET_PCI" = "y" ]; then tristate ' AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE - fi + dep_tristate ' Adaptec Starfire support (EXPERIMENTAL)' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI $CONFIG_EXPERIMENTAL if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate ' Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi @@ -134,12 +132,12 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then tristate ' Apricot Xen-II on board Ethernet' CONFIG_APRICOT tristate ' CS89x0 support' CONFIG_CS89x0 tristate ' Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5 - tristate ' DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP + dep_tristate ' DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP $CONFIG_PCI tristate ' Digi Intl. RightSwitch SE-X support' CONFIG_DGRS if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate ' DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102 fi - tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100 + dep_tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100 $CONFIG_PCI if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_EEPRO100" = "y" -o "$CONFIG_EEPRO100" = "m" ]; then bool ' Enable Power Management (EXPERIMENTAL)' CONFIG_EEPRO100_PM @@ -147,7 +145,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 fi - tristate ' PCI NE2000 support' CONFIG_NE2K_PCI + dep_tristate ' PCI NE2000 support' CONFIG_NE2K_PCI $CONFIG_PCI # tristate ' Sundance Alta support' CONFIG_ALTA if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate ' RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)' CONFIG_RTL8129 diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index f855d19789e6..2832775ae601 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -1099,6 +1099,12 @@ static int __devinit epic100_init_one (struct pci_dev *pdev, struct net_device *dev; long ioaddr; static int card_idx = -1; + static int printed_version = 0; + + if (!printed_version) { + printk (KERN_INFO "%s", version); + printed_version = 1; + } chip_idx = ent->driver_data; @@ -1280,13 +1286,7 @@ static struct pci_driver epic100_driver = { static int __init epic100_init (void) { - printk (KERN_INFO "%s", version); - - if (pci_register_driver (&epic100_driver) > 0) - return 0; - - pci_unregister_driver (&epic100_driver); - return -ENODEV; + return pci_module_init (&epic100_driver); } diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index f2976e562309..214341b53154 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -1,4 +1,4 @@ -/* netdrv_init.c: Initialization for network devices. */ +/* net_init.c: Initialization for network devices. */ /* Written 1993,1994,1995 by Donald Becker. @@ -27,6 +27,8 @@ 08/11/99 - Alan Cox: Got fed up of the mess in this file and cleaned it up. We now share common code and have regularised name allocation setups. Abolished the 16 card limits. + 03/19/2000 - jgarzik and Urban Widmark: init_etherdev 32-byte align + */ #include @@ -139,14 +141,22 @@ static struct net_device *init_netdev(struct net_device *dev, int sizeof_priv, c return dev; } -/* Fill in the fields of the device structure with ethernet-generic values. - - If no device structure is passed, a new one is constructed, complete with - a SIZEOF_PRIVATE private data area. - - If an empty string area is passed as dev->name, or a new structure is made, - a new name string is constructed. The passed string area should be 8 bytes - long. +/** + * init_etherdev - Register ethernet device + * @dev: An ethernet device structure to be filled in, or %NULL if a new + * struct should be allocated. + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this ethernet device + * + * Fill in the fields of the device structure with ethernet-generic values. + * + * If no device structure is passed, a new one is constructed, complete with + * a private data area of size @sizeof_priv. A 32-byte (not bit) + * alignment is enforced for this private data area. + * + * If an empty string area is passed as dev->name, or a new structure is made, + * a new name string is constructed. The passed string area should be 8 bytes + * long. */ struct net_device *init_etherdev(struct net_device *dev, int sizeof_priv) diff --git a/drivers/net/pcmcia/3c575_cb.c b/drivers/net/pcmcia/3c575_cb.c index e98e1eda8430..bff7bc9ed2a1 100644 --- a/drivers/net/pcmcia/3c575_cb.c +++ b/drivers/net/pcmcia/3c575_cb.c @@ -12,10 +12,16 @@ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Linux Kernel Additions: + + LK1.1.2 (March 19, 2000) + * New PCI interface (jgarzik) + */ static char *version = -"3c59x.c:v0.99L 5/28/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; +"3c575_cb.c:v0.99L+LK1.1.2 3/19/2000 Donald Becker and others http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. @@ -64,6 +70,7 @@ static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; #include #include #include +#include #include #include #include @@ -85,9 +92,6 @@ MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(compaq_ioaddr, "i"); -MODULE_PARM(compaq_irq, "i"); -MODULE_PARM(compaq_device_id, "i"); /* Operational parameter that usually are not changed. */ @@ -103,6 +107,10 @@ MODULE_PARM(compaq_device_id, "i"); code size of a per-interface flag is not worthwhile. */ static char mii_preamble_required = 0; +#define PFX "3c575_cb: " + + + /* Theory of Operation @@ -186,76 +194,134 @@ enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, }; -struct pci_id_info { - const char *name; - u16 vendor_id, device_id, device_id_mask, flags; - int drv_flags, io_size; - struct net_device *(*probe1)(struct pci_dev *pdev, struct net_device *dev, - long ioaddr, int irq, int chip_idx, int fnd_cnt); -}; enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, }; -static struct net_device *vortex_probe1(struct pci_dev *pdev, - struct net_device *dev, long ioaddr, - int irq, int dev_id, int card_idx); -static struct pci_id_info pci_tbl[] = { - {"3c590 Vortex 10Mbps", 0x10B7, 0x5900, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, - {"3c595 Vortex 100baseTx", 0x10B7, 0x5950, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, - {"3c595 Vortex 100baseT4", 0x10B7, 0x5951, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, - {"3c595 Vortex 100base-MII", 0x10B7, 0x5952, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, - {"3Com Vortex", 0x10B7, 0x5900, 0xff00, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, - {"3c900 Boomerang 10baseT", 0x10B7, 0x9000, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, - {"3c900 Boomerang 10Mbps Combo", 0x10B7, 0x9001, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, - {"3c900 Cyclone 10Mbps Combo", 0x10B7, 0x9005, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c900B-FL Cyclone 10base-FL", 0x10B7, 0x900A, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c905 Boomerang 100baseTx", 0x10B7, 0x9050, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, - {"3c905 Boomerang 100baseT4", 0x10B7, 0x9051, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, - {"3c905B Cyclone 100baseTx", 0x10B7, 0x9055, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1}, - {"3c905B Cyclone 10/100/BNC", 0x10B7, 0x9058, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1}, - {"3c905B-FX Cyclone 100baseFx", 0x10B7, 0x905A, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c905C Tornado", 0x10B7, 0x9200, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c980 Cyclone", 0x10B7, 0x9800, 0xfff0, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3cSOHO100-TX Hurricane", 0x10B7, 0x7646, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c555 Laptop Hurricane", 0x10B7, 0x5055, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, - {"3c575 Boomerang CardBus", 0x10B7, 0x5057, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, - {"3CCFE575 Cyclone CardBus", 0x10B7, 0x5157, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, - 128, vortex_probe1}, - {"3CCFE575CT Cyclone CardBus", 0x10B7, 0x5257, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, - 128, vortex_probe1}, - {"3CCFE656 Cyclone CardBus", 0x10B7, 0x6560, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, - 128, vortex_probe1}, - {"3CCFEM656 Cyclone CardBus", 0x10B7, 0x6562, 0xffff, - PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, - 128, vortex_probe1}, - {"3c575 series CardBus (unknown version)", 0x10B7, 0x5057, 0xf0ff, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, - {"3Com Boomerang (unknown version)", 0x10B7, 0x9000, 0xff00, - PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, + + +enum vortex_chips { + CH_3C590 = 0, + CH_3C595_1, + CH_3C595_2, + CH_3C595_3, + CH_VORTEX, + CH_3C900_1, + CH_3C900_2, + CH_3C900_3, + CH_3C900B_FL, + CH_3C905_1, + CH_3C905_2, + CH_3C905B_1, + CH_3C905B_2, + CH_3C905B_FX, + CH_3C905C, + CH_3C980, + CH_3CSOHO100_TX, + CH_3C555, + CH_3C575_1, + CH_3CCFE575, + CH_3CCFE575CT, + CH_3CCFE656, + CH_3CCFEM656, + CH_3C575_2, + CH_BOOMERANG, +}; + + +/* note: this array directly indexed by above enums, and MUST + * be kept in sync with both the enums above, and the PCI device + * table below + */ +static struct vortex_chip_info { + const char *name; + int flags; + int drv_flags; + int io_size; +} vortex_info_tbl[] = { + {"3c590 Vortex 10Mbps", + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, + {"3c595 Vortex 100baseTx", + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, + {"3c595 Vortex 100baseT4", + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, + {"3c595 Vortex 100base-MII", + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, }, + {"3Com Vortex", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, }, + {"3c900 Boomerang 10baseT", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, }, + {"3c900 Boomerang 10Mbps Combo", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, }, + {"3c900 Cyclone 10Mbps Combo", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c900B-FL Cyclone 10base-FL", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c905 Boomerang 100baseTx", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, }, + {"3c905 Boomerang 100baseT4", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, }, + {"3c905B Cyclone 100baseTx", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, }, + {"3c905B Cyclone 10/100/BNC", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, }, + {"3c905B-FX Cyclone 100baseFx", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c905C Tornado", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c980 Cyclone", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3cSOHO100-TX Hurricane", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c555 Laptop Hurricane", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, }, + {"3c575 Boomerang CardBus", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, }, + {"3CCFE575 Cyclone CardBus", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, }, + {"3CCFE575CT Cyclone CardBus", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, }, + {"3CCFE656 Cyclone CardBus", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, }, + {"3CCFEM656 Cyclone CardBus", + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, 128, }, + {"3c575 series CardBus (unknown version)", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, }, + {"3Com Boomerang (unknown version)", + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, }, + {0,}, /* 0 terminated list. */ +}; + + +static struct pci_device_id vortex_pci_tbl[] __devinit = { + { 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C590 }, + { 0x10B7, 0x5950, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_1 }, + { 0x10B7, 0x5951, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_2 }, + { 0x10B7, 0x5952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C595_3 }, + { 0x10B7, 0x5900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_VORTEX }, + { 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_1 }, + { 0x10B7, 0x9001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_2 }, + { 0x10B7, 0x9005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900_3 }, + { 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL }, + { 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 }, + { 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 }, + { 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 }, + { 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 }, + { 0x10B7, 0x905A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_FX }, + { 0x10B7, 0x9200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905C }, + { 0x10B7, 0x9800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C980 }, + { 0x10B7, 0x7646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CSOHO100_TX }, + { 0x10B7, 0x5055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C555 }, + { 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_1 }, + { 0x10B7, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575 }, + { 0x10B7, 0x5257, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE575CT }, + { 0x10B7, 0x6560, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFE656 }, + { 0x10B7, 0x6562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3CCFEM656 }, + { 0x10B7, 0x5057, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C575_2 }, + { 0x10B7, 0x9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_BOOMERANG }, {0,}, /* 0 terminated list. */ }; +MODULE_DEVICE_TABLE(pci, vortex_pci_tbl); + /* Operational definitions. These are not used by other compilation units and thus are not @@ -400,7 +466,7 @@ struct vortex_private { /* The addresses of transmit- and receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; struct sk_buff* tx_skbuff[TX_RING_SIZE]; - struct net_device *next_module; + struct net_device *next_module; /* NULL if PCI device */ void *priv_addr; unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ @@ -463,9 +529,8 @@ static struct media_table { { "Default", 0, 0xFF, XCVR_10baseT, 10000}, }; -#ifndef CARDBUS -static int vortex_scan(struct net_device *dev, struct pci_id_info pci_tbl[]); -#endif +static int vortex_probe1(struct pci_dev *pdev, long ioaddr, int irq, + int chip_idx, int card_idx); static void vortex_up(struct net_device *dev); static void vortex_down(struct net_device *dev); static int vortex_open(struct net_device *dev); @@ -484,7 +549,6 @@ static struct net_device_stats *vortex_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void vortex_tx_timeout(struct net_device *dev); -static void acpi_wake(struct pci_dev *pdev); static void acpi_set_WOL(struct net_device *dev); /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ @@ -492,280 +556,181 @@ static void acpi_set_WOL(struct net_device *dev); #define MAX_UNITS 8 static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,}; static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -/* A list of all installed Vortex devices, for removing the driver module. */ -static struct net_device *root_vortex_dev = NULL; -#ifdef MODULE -#ifndef CARDBUS -/* Variables to work-around the Compaq PCI BIOS32 problem. */ -static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900; -#endif -#ifdef CARDBUS +/* A list of all installed Vortex EISA devices, for removing the driver module. */ +static struct net_device *root_vortex_eisa_dev = NULL; -#include +static int vortex_cards_found = 0; -static void vortex_reap(void) -{ - struct net_device **devp, **next; - printk(KERN_DEBUG "vortex_reap()\n"); - for (devp = &root_vortex_dev; *devp; devp = next) { - struct vortex_private *vp = (*devp)->priv; - next = &vp->next_module; - if (vp->open || !vp->reap) continue; - unregister_netdev(*devp); - if (vp->cb_fn_base) iounmap(vp->cb_fn_base); - kfree(*devp); - kfree(vp->priv_addr); - *devp = *next; next = devp; - } -} -static dev_node_t *vortex_attach(dev_locator_t *loc) -{ - u16 dev_id, vendor_id; - u32 io; - u8 irq; - struct net_device *dev; - int chip_idx; - struct pci_dev *pdev; - vortex_reap(); - if (loc->bus != LOC_PCI) return NULL; - pdev = pci_find_slot (loc->b.pci.bus, loc->b.pci.devfn); - if (!pdev) return NULL; - io = pci_resource_start (pdev, 0); - irq = pdev->irq; - vendor_id = pdev->vendor; - dev_id = pdev->device; - printk(KERN_INFO "vortex_attach(bus %d, function %d, device %4.4x)\n", - pdev->bus->number, pdev->devfn, dev_id); - if (io == 0 || irq == 0) { - printk(KERN_ERR "The 3Com CardBus Ethernet interface was not " - "assigned an %s.\n" KERN_ERR " It will not be activated.\n", - io == 0 ? "I/O address" : "IRQ"); - return NULL; - } - for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) - if (vendor_id == pci_tbl[chip_idx].vendor_id - && (dev_id & pci_tbl[chip_idx].device_id_mask) == - pci_tbl[chip_idx].device_id) - break; - if (pci_tbl[chip_idx].vendor_id == 0) { /* Compiled out! */ - printk(KERN_INFO "Unable to match chip type %4.4x %4.4x in " - "vortex_attach().\n", vendor_id, dev_id); - return NULL; - } - dev = vortex_probe1(pdev, NULL, io, irq, chip_idx, MAX_UNITS+1); - if (dev) { - dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); - strcpy(node->dev_name, dev->name); - node->major = node->minor = 0; - node->next = NULL; - MOD_INC_USE_COUNT; - return node; - } - return NULL; -} -static void vortex_detach(dev_node_t *node) +static void vortex_suspend (struct pci_dev *pdev) { - struct net_device *dev, *next; - printk(KERN_DEBUG "vortex_detach(%s)\n", node->dev_name); - for (dev = root_vortex_dev; dev; dev = next) { - next = ((struct vortex_private *)dev->priv)->next_module; - if (strcmp(dev->name, node->dev_name) == 0) break; - } - if (dev && dev->priv) { - struct vortex_private *vp = dev->priv; - if (vp->open) vortex_down(dev); - vp->reap = 1; - kfree(node); - MOD_DEC_USE_COUNT; - } -} + struct net_device *dev = pdev->driver_data; + + printk(KERN_DEBUG "vortex_suspend(%s)\n", dev->name); -static void vortex_suspend(dev_node_t *node) -{ - struct net_device *dev, *next; - printk(KERN_DEBUG "vortex_suspend(%s)\n", node->dev_name); - for (dev = root_vortex_dev; dev; dev = next) { - next = ((struct vortex_private *)dev->priv)->next_module; - if (strcmp(dev->name, node->dev_name) == 0) break; - } if (dev && dev->priv) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - if (vp->open) vortex_down(dev); + if (vp->open) { + netif_device_detach(dev); + vortex_down(dev); + } } } -static void vortex_resume(dev_node_t *node) + +static void vortex_resume (struct pci_dev *pdev) { - struct net_device *dev, *next; - printk(KERN_DEBUG "vortex_resume(%s)\n", node->dev_name); - for (dev = root_vortex_dev; dev; dev = next) { - next = ((struct vortex_private *)dev->priv)->next_module; - if (strcmp(dev->name, node->dev_name) == 0) break; - } + struct net_device *dev = pdev->driver_data; + + printk(KERN_DEBUG "vortex_resume(%s)\n", dev->name); + if (dev && dev->priv) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - if (vp->open) vortex_up(dev); + if (vp->open) { + vortex_up(dev); + netif_device_attach(dev); + } } } -struct driver_operations vortex_ops = { - "3c575_cb", vortex_attach, vortex_suspend, vortex_resume, vortex_detach -}; -#endif /* Cardbus support */ - -int init_module(void) +/* returns count found (>= 0), or negative on error */ +static int __init vortex_eisa_init (void) { - if (vortex_debug) - printk(KERN_INFO "%s", version); -#ifdef CARDBUS - register_driver(&vortex_ops); - return 0; -#else - return vortex_scan(0, pci_tbl); -#endif -} - -#else -int tc59x_probe(struct net_device *dev) -{ - static int did_version = -1; - if (++did_version <= 0) - printk(KERN_INFO "%s", version); - return vortex_scan(dev, pci_tbl); -} -#endif /* not MODULE */ - -#ifndef CARDBUS -static int vortex_scan(struct net_device *dev, struct pci_id_info pci_tbl[]) -{ - int cards_found = 0; + long ioaddr; + int rc; + int orig_cards_found = vortex_cards_found; - /* Allow an EISA-only driver. */ - struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[0] & ~3; - irq = pdev->irq; - } + /* Now check all slots of the EISA bus. */ + if (!EISA_bus) + return 0; - if (ioaddr == 0) { - printk(KERN_WARNING " A 3Com network adapter has been found, " - "however it has not been assigned an I/O address.\n" - " You may need to power-cycle the machine for this " - "device to work!\n"); - continue; - } + for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + int device_id; - /* Activate the card. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled the device " - "at %d/%d. Updating PCI command %4.4x->%4.4x.\n", - pci_bus, pci_device_fn, pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + if (request_region(ioaddr, VORTEX_TOTAL_SIZE, "vortex") == NULL) + continue; - dev = vortex_probe1(pci_bus, pci_device_fn, dev, ioaddr, irq, - chip_idx, cards_found); - - if (dev) { - /* Get and check the latency values. On the 3c590 series - the latency timer must be set to the maximum value to avoid - data corruption that occurs when the timer expires during - a transfer -- a bug in the Vortex chip only. */ - u8 pci_latency; - u8 new_latency = (device & 0xff00) == 0x5900 ? 248 : 32; - - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < new_latency) { - printk(KERN_INFO "%s: Overriding PCI latency" - " timer (CFLT) setting of %d, new value is %d.\n", - dev->name, pci_latency, new_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, new_latency); - } - dev = 0; - cards_found++; - } + /* Check the standard EISA ID register for an encoded '3Com'. */ + if (inw(ioaddr + 0xC80) != 0x6d50) { + release_region (ioaddr, VORTEX_TOTAL_SIZE); + continue; } - } - /* Now check all slots of the EISA bus. */ - if (EISA_bus) { - static long ioaddr = 0x1000; - for ( ; ioaddr < 0x9000; ioaddr += 0x1000) { - int device_id; - if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) - continue; - /* Check the standard EISA ID register for an encoded '3Com'. */ - if (inw(ioaddr + 0xC80) != 0x6d50) - continue; - /* Check for a product that we support, 3c59{2,7} any rev. */ - device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); - if ((device_id & 0xFF00) != 0x5900) - continue; - vortex_probe1(0, 0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12, - 4, cards_found); - dev = 0; - cards_found++; + /* Check for a product that we support, 3c59{2,7} any rev. */ + device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); + if ((device_id & 0xFF00) != 0x5900) { + release_region (ioaddr, VORTEX_TOTAL_SIZE); + continue; } - } -#ifdef MODULE - /* Special code to work-around the Compaq PCI BIOS32 problem. */ - if (compaq_ioaddr) { - vortex_probe1(0, 0, dev, compaq_ioaddr, compaq_irq, - compaq_device_id, cards_found++); - dev = 0; + rc = vortex_probe1(NULL, ioaddr, inw(ioaddr + 0xC88) >> 12, + 4, /* XXX is 4 correct eisa idx? */ + vortex_cards_found); + if (rc == 0) + vortex_cards_found++; + else + release_region (ioaddr, VORTEX_TOTAL_SIZE); } -#endif - return cards_found ? 0 : -ENODEV; + return vortex_cards_found - orig_cards_found; +} + + +/* returns count (>= 0), or negative on error */ +static int __devinit vortex_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int rc; + + rc = vortex_probe1 (pdev, pci_resource_start (pdev, 0), pdev->irq, + ent->driver_data, vortex_cards_found); + if (rc == 0) + vortex_cards_found++; + + return rc; } -#endif /* ! Cardbus */ -static struct net_device *vortex_probe1(struct pci_dev *pdev, - struct net_device *dev, long ioaddr, - int irq, int chip_idx, int card_idx) + +/* NOTE: pdev can be NULL, for the case of an EISA driver */ +static int __devinit vortex_probe1(struct pci_dev *pdev, + long ioaddr, int irq, + int chip_idx, int card_idx) { struct vortex_private *vp; int option; unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ int i; + struct net_device *dev; + static int printed_version = 0; + + if (!printed_version) { + printk (KERN_INFO "%s", version); + printed_version = 1; + } - dev = init_etherdev(dev, 0); - - printk(KERN_INFO "%s: 3Com %s at 0x%lx, ", - dev->name, pci_tbl[chip_idx].name, ioaddr); - + dev = init_etherdev(NULL, sizeof(*vp)); + if (!dev) { + printk (KERN_ERR PFX "unable to allocate etherdev, aborting\n"); + return -ENOMEM; + } + + printk(KERN_INFO "%s: 3Com %s %s at 0x%lx, ", + dev->name, + pdev ? "PCI" : "EISA", + vortex_info_tbl[chip_idx].name, + ioaddr); + + /* private struct aligned and zeroed by init_etherdev */ + vp = dev->priv; + vp->priv_addr = vp; dev->base_addr = ioaddr; dev->irq = irq; dev->mtu = mtu; - /* Make certain the descriptor lists are aligned. */ - { - void *mem = kmalloc(sizeof(*vp) + 15, GFP_KERNEL); - vp = (void *)(((long)mem + 15) & ~15); - memset(vp, 0, sizeof(*vp)); - vp->priv_addr = mem; + /* module list only for EISA devices */ + if (pdev == NULL) { + vp->next_module = root_vortex_eisa_dev; + root_vortex_eisa_dev = dev; } - dev->priv = vp; + + /* PCI-only startup logic */ + if (pdev) { + /* EISA resources already marked, so only PCI needs to do this here */ + if (!request_region (ioaddr, vortex_info_tbl[chip_idx].io_size, + dev->name)) { + printk (KERN_ERR "%s: Cannot reserve I/O resource 0x%x @ 0x%lx, aborting\n", + dev->name, vortex_info_tbl[chip_idx].io_size, ioaddr); + kfree (dev); + return -EBUSY; + } + + /* wake up and enable device */ + if (pci_enable_device (pdev)) { + printk (KERN_ERR "%s: Cannot enable device, aborting\n", dev->name); + release_region (ioaddr, vortex_info_tbl[chip_idx].io_size); + kfree (dev); + return -EIO; + } - vp->next_module = root_vortex_dev; - root_vortex_dev = dev; + /* enable bus-mastering if necessary */ + if (vortex_info_tbl[chip_idx].flags & PCI_USES_MASTER) + pci_set_master (pdev); + } vp->lock = SPIN_LOCK_UNLOCKED; vp->chip_id = chip_idx; vp->pdev = pdev; + /* if we are a PCI driver, we store info in pdev->driver_data + * instead of a module list */ + if (pdev) + pdev->driver_data = dev; + /* The lower four bits are the media type. */ if (dev->mem_start) option = dev->mem_start; @@ -793,7 +758,7 @@ static struct net_device *vortex_probe1(struct pci_dev *pdev, EL3WINDOW(0); for (i = 0; i < 0x40; i++) { int timer; -#ifdef CARDBUS +#if 1 /* ifdef CARDBUS */ outw(0x230 + i, ioaddr + Wn0EepromCmd); #else outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); @@ -835,7 +800,7 @@ static struct net_device *vortex_probe1(struct pci_dev *pdev, dev->irq); #endif - if (pci_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) { + if (pdev && vortex_info_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) { u32 fn_st_addr; /* Cardbus function status space */ fn_st_addr = pci_resource_start (pdev, 2); if (fn_st_addr) @@ -922,9 +887,6 @@ static struct net_device *vortex_probe1(struct pci_dev *pdev, vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; } - /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); - /* The 3c59x-specific entries in the device structure. */ dev->open = &vortex_open; dev->hard_start_xmit = &vortex_start_xmit; @@ -932,10 +894,10 @@ static struct net_device *vortex_probe1(struct pci_dev *pdev, dev->get_stats = &vortex_get_stats; dev->do_ioctl = &vortex_ioctl; dev->set_multicast_list = &set_rx_mode; - dev->tx_timeout = vortex_tx_timeout; + dev->tx_timeout = &vortex_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; - return dev; + return 0; } static void wait_for_completion(struct net_device *dev, int cmd) @@ -956,11 +918,13 @@ vortex_up(struct net_device *dev) long ioaddr = dev->base_addr; struct vortex_private *vp = (struct vortex_private *)dev->priv; union wn3_config config; - int i; - - /* Should be if(HAS_ACPI) */ - acpi_wake(vp->pdev); + int i, device_id; + if (vp->pdev) + device_id = vp->pdev->device; + else + device_id = 0x5900; /* EISA */ + /* Before initializing select the active media port. */ EL3WINDOW(3); config.i = inl(ioaddr + Wn3_Config); @@ -972,7 +936,7 @@ vortex_up(struct net_device *dev) media_tbl[vp->media_override].name); dev->if_port = vp->media_override; } else if (vp->autoselect) { - if (pci_tbl[vp->chip_id].drv_flags & HAS_NWAY) + if (vortex_info_tbl[vp->chip_id].drv_flags & HAS_NWAY) dev->if_port = XCVR_NWAY; else { /* Find first available media type, starting with 100baseTx. */ @@ -995,7 +959,7 @@ vortex_up(struct net_device *dev) vp->full_duplex = vp->force_fd; config.u.xcvr = dev->if_port; - if ( ! (pci_tbl[vp->chip_id].drv_flags & HAS_NWAY)) + if ( ! (vortex_info_tbl[vp->chip_id].drv_flags & HAS_NWAY)) outl(config.i, ioaddr + Wn3_Config); if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { @@ -1045,12 +1009,11 @@ vortex_up(struct net_device *dev) if (vp->cb_fn_base) { u_short n = inw(ioaddr + Wn2_ResetOptions); /* Inverted LED polarity */ - if (pci_tbl[vp->chip_id].device_id != 0x5257) + if (device_id != 0x5257) n |= 0x0010; /* Inverted polarity of MII power bit */ - if ((pci_tbl[vp->chip_id].device_id == 0x6560) || - (pci_tbl[vp->chip_id].device_id == 0x6562) || - (pci_tbl[vp->chip_id].device_id == 0x5257)) + if ((device_id == 0x6560) || (device_id == 0x6562) || + (device_id == 0x5257)) n |= 0x4000; outw(n, ioaddr + Wn2_ResetOptions); } @@ -2088,66 +2051,107 @@ static void acpi_set_WOL(struct net_device *dev) /* Change the power state to D3; RxEnable doesn't take effect. */ pci_write_config_word(vp->pdev, 0xe0, 0x8103); } -/* Change from D3 (sleep) to D0 (active). - Problem: The Cyclone forgets all PCI config info during the transition! */ -static void acpi_wake(struct pci_dev *pdev) + + +static void __devexit vortex_remove_one (struct pci_dev *pdev) { - u32 base0, base1, romaddr; - u16 pci_command, pwr_command; - u8 pci_latency, pci_cacheline, irq; + struct net_device *dev = pdev->driver_data; + struct vortex_private *vp; - pci_read_config_word(pdev, 0xe0, &pwr_command); - if ((pwr_command & 3) == 0) + if (!dev) return; - pci_read_config_word( pdev, PCI_COMMAND, &pci_command); - pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base0); - pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &base1); - pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &romaddr); - pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &pci_latency); - pci_read_config_byte( pdev, PCI_CACHE_LINE_SIZE, &pci_cacheline); - pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq); - - pci_write_config_word( pdev, 0xe0, 0x0000); - pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, base0); - pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, base1); - pci_write_config_dword(pdev, PCI_ROM_ADDRESS, romaddr); - pci_write_config_byte( pdev, PCI_INTERRUPT_LINE, irq); - pci_write_config_byte( pdev, PCI_LATENCY_TIMER, pci_latency); - pci_write_config_byte( pdev, PCI_CACHE_LINE_SIZE, pci_cacheline); - pci_write_config_word( pdev, PCI_COMMAND, pci_command | 5); + + vp = (void *)(dev->priv); + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + unregister_netdev(dev); + outw(TotalReset, dev->base_addr + EL3_CMD); + release_region(dev->base_addr, vortex_info_tbl[vp->chip_id].io_size); + kfree(dev); } -#ifdef MODULE -void cleanup_module(void) + +static struct pci_driver vortex_driver = { + name: "3c575_cb", + probe: vortex_init_one, + remove: vortex_remove_one, + suspend: vortex_suspend, + resume: vortex_resume, + id_table: vortex_pci_tbl, +}; + + +static int vortex_have_pci = 0; +static int vortex_have_eisa = 0; + + +static int __init vortex_init (void) { - struct net_device *next_dev; + int rc; + + MOD_INC_USE_COUNT; + + rc = pci_module_init (&vortex_driver); + if (rc < 0) + goto out; + if (rc > 0) + vortex_have_pci = 1; + + rc = vortex_eisa_init (); + if (rc < 0) + goto out; + if (rc > 0) + vortex_have_eisa = 1; + +out: + MOD_DEC_USE_COUNT; + return rc; +} -#ifdef CARDBUS - unregister_driver(&vortex_ops); - vortex_reap(); -#endif - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (root_vortex_dev) { - struct vortex_private *vp=(void *)(root_vortex_dev->priv); - next_dev = vp->next_module; - unregister_netdev(root_vortex_dev); - outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD); - release_region(root_vortex_dev->base_addr, - pci_tbl[vp->chip_id].io_size); - kfree(root_vortex_dev); - kfree(vp->priv_addr); - root_vortex_dev = next_dev; +static void __exit vortex_eisa_cleanup (void) +{ + struct net_device *dev, *tmp; + struct vortex_private *vp; + long ioaddr; + + dev = root_vortex_eisa_dev; + + while (dev) { + vp = dev->priv; + ioaddr = dev->base_addr; + + unregister_netdev (dev); + outw (TotalReset, ioaddr + EL3_CMD); + release_region (ioaddr, VORTEX_TOTAL_SIZE); + + tmp = dev; + dev = vp->next_module; + + kfree (tmp); } } -#endif /* MODULE */ + +static void __exit vortex_cleanup (void) +{ + if (vortex_have_pci) + pci_unregister_driver (&vortex_driver); + if (vortex_have_eisa) + vortex_eisa_cleanup (); +} + + +module_init(vortex_init); +module_exit(vortex_cleanup); + + /* * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c" - * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/linux/pcmcia-cs-3.0.9/include/" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c" + * cardbus-compile-command: "gcc -DCONFIG_HOTPLUG -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c575_cb.c -o 3c575_cb.o -I/usr/src/linux/pcmcia-cs-3.0.9/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in index 534a4bdbd378..d8e4eb88db9e 100644 --- a/drivers/net/pcmcia/Config.in +++ b/drivers/net/pcmcia/Config.in @@ -18,6 +18,7 @@ if [ "$CONFIG_NET_PCMCIA" = "y" ]; then if [ "$CONFIG_CARDBUS" = "y" ]; then dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 m + dep_tristate ' Xircom Tulip-like CardBus support' CONFIG_PCMCIA_XIRTULIP m fi bool 'Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile index 5d0d36f4a3fa..65938d96db86 100644 --- a/drivers/net/pcmcia/Makefile +++ b/drivers/net/pcmcia/Makefile @@ -19,8 +19,6 @@ obj- := # Things that need to export symbols export-objs := ray_cs.o -CFLAGS_3c575_cb.o = -DCARDBUS -DMODULE - # 16-bit client drivers obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o @@ -39,6 +37,7 @@ obj-$(CONFIG_AIRONET4500_CS) += aironet4500_cs.o # Cardbus client drivers obj-$(CONFIG_PCMCIA_3C575) += 3c575_cb.o +obj-$(CONFIG_PCMCIA_XIRTULIP) += xircom_tulip_cb.o O_OBJS := $(filter-out $(export-objs), $(obj-y)) OX_OBJS := $(filter $(export-objs), $(obj-y)) diff --git a/drivers/net/pcmcia/xircom_tulip_cb.c b/drivers/net/pcmcia/xircom_tulip_cb.c new file mode 100644 index 000000000000..1c3fa232b691 --- /dev/null +++ b/drivers/net/pcmcia/xircom_tulip_cb.c @@ -0,0 +1,3159 @@ +/* tulip.c: A DEC 21040-family ethernet driver for Linux. */ +/* + Written/copyright 1994-1999 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 Digital "Tulip" Ethernet adapter interface. + It should work with most DEC 21*4*-based chips/ethercards, as well as + with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html +*/ + +#define SMP_CHECK +#define CARDBUS 1 +static const char version[] = "xircom_tulip_cb.c:v0.91 4/14/99 becker@cesdis.gsfc.nasa.gov (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n"; + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 8 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; +static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ + +/* The possible media types that can be set in options[] are: */ +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__powerpc__) +static int csr0 = 0x01B00000 | 0x8000; +#elif defined(__sparc__) +static int csr0 = 0x01B00080 | 0x8000; +#elif defined(__i386__) +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. + This is only in the support-all-kernels source code. */ + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +#define RUN_AT(x) (jiffies + (x)) + +#define tulip_debug debug +#ifdef TULIP_DEBUG +static int tulip_debug = TULIP_DEBUG; +#else +static int tulip_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the DECchip "Tulip", Digital's +single-chip ethernet controllers for PCI. Supported members of the family +are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike +chips from Lite-On, Macronics, ASIX, Compex and other listed below are also +supported. + +These chips are used on at least 140 unique PCI board designs. The great +number of chips and board designs supported is the reason for the +driver size and complexity. Almost of the increasing complexity is in the +board configuration and media selection code. There is very little +increasing in the operational critical path length. + +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 preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. + +Some boards have EEPROMs tables with default media entry. The factory default +is usually "autoselect". This should only be overridden when using +transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) +for forcing full-duplex when used with old link partners that do not do +autonegotiation. + +III. Driver operation + +IIIa. Ring buffers + +The Tulip can use either ring buffers or lists of Tx and Rx descriptors. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Tulip as receive data buffers. When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Tulip only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. + +IIIC. 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 'tp->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 'tp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. +Greg LaPolla at Linksys provided PNIC and other Linksys boards. +Znyx provided a four-port card for testing. + +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840A.html +http://www.asix.com.tw/pmac.htm +http://www.admtek.com.tw/ + +IVc. Errata + +The old DEC databooks were light on details. +The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last +register of the set CSR12-15 written. Hmmm, now how is that possible? + +The DEC SROM format is very badly designed not precisely defined, leading to +part of the media selection junkheap below. Some boards do not have EEPROM +media tables and need to be patched up. Worse, other boards use the DEC +design kit media table when it isn't correct for their board. + +We cannot use MII interrupts because there is no defined GPIO pin to attach +them. The MII transceiver status is polled using an kernel timer. + +*/ + +/* This table use during operation for capabilities and media timer. */ + +static void tulip_timer(unsigned long data); +static void t21142_timer(unsigned long data); +static void mxic_timer(unsigned long data); +static void pnic_timer(unsigned long data); +static void comet_timer(unsigned long data); + +enum tbl_flag { + HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, + HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ + HAS_NWAY143=0x40, /* Uses 21143-like internal NWay. */ +}; +static struct tulip_chip_table { + char *chip_name; + int io_size; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; + void (*media_timer)(unsigned long data); +} tulip_tbl[] = { +#if 0 /* these entries conflict with regular tulip driver */ + { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, + { "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, + { "Digital DS21143 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, t21142_timer }, + { "Lite-On 82c168 PNIC", 256, 0x0001ebef, + HAS_MII, pnic_timer }, + { "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer }, + { "Lite-On PNIC-II", 256, 0x0001ebef, + HAS_MII | HAS_NWAY143, pnic_timer }, + { "ADMtek Comet", 256, 0x0001abef, + MC_HASH_ONLY, comet_timer }, + { "Compex 9881 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, +#endif + { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff, + HAS_MII | HAS_ACPI, tulip_timer }, + {0}, +}; +/* This matches the table above. Note 21142 == 21143. */ +enum chips { +#if 0 /* these entries conflict with regular tulip driver */ + DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881, + X3201_3, +#else + X3201_3 = 0, +#endif +}; + +/* A full-duplex map for media types. */ +enum MediaIs { + MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; +static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0}; +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ +static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum tulip_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + NormalIntr=0x10000, AbnormalIntr=0x8000, + RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct tulip_rx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; +}; + +struct tulip_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +enum desc_status_bits { + DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicates ring mode, but always write the 'next' field for + chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + +#ifdef CARDBUS +#define EEPROM_ADDRLEN (chip_rev == 65 ? 8 : 6) +#else +#define EEPROM_ADDRLEN 6 +#endif +#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + +struct mediatable { + u16 defaultmedia; + u8 leafcount, csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1, has_nonmii:1, has_reset:6; + u32 csr15dir, csr15val; /* 21143 NWay setting. */ + struct medialeaf mleaf[0]; +}; + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + unsigned char *info; +}; + +struct tulip_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct tulip_rx_desc rx_ring[RX_RING_SIZE]; + struct tulip_tx_desc tx_ring[TX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; +#ifdef CARDBUS + /* The X3201-3 requires double word aligned tx bufs */ + struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; +#endif + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + char *rx_buffs; /* Address of temporary Rx buffers. */ + u8 setup_buf[96*sizeof(u16) + 7]; + u16 *setup_frame; /* Pseudo-Tx frame to init address table. */ + int chip_id; + int revision; + struct net_device_stats stats; + struct timer_list timer; /* Media selection timer. */ + int interrupt; /* In-interrupt flag. */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int full_duplex_lock:1; + unsigned int fake_addr:1; /* Multiport board faked address. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int nway:1, nwayset:1; /* 21143 internal NWay. */ + unsigned int open:1; + unsigned int csr0; /* CSR0 setting. */ + unsigned int csr6; /* Current CSR6 control settings. */ + unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 lpar; /* 21143 Link partner ability. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ + struct mediatable *mtable; + int cur_index; /* Current media index. */ + int saved_if_port; + struct pci_dev *pdev; + spinlock_t lock; + int pad0, pad1; /* Used for 8-byte alignment */ +}; + +static void parse_eeprom(struct net_device *dev); +static int read_eeprom(long ioaddr, int location, int addr_len); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static void select_media(struct net_device *dev, int startup); +static void tulip_up(struct net_device *dev); +static void tulip_down(struct net_device *dev); +static int tulip_open(struct net_device *dev); +static void tulip_timer(unsigned long data); +static void t21142_start_nway(struct net_device *dev); +static void tulip_tx_timeout(struct net_device *dev); +static void tulip_init_ring(struct net_device *dev); +static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int tulip_rx(struct net_device *dev); +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int tulip_close(struct net_device *dev); +static struct net_device_stats *tulip_get_stats(struct net_device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#endif +static void set_rx_mode(struct net_device *dev); + +/* The Xircom cards are picky about when certain bits in CSR6 can be + manipulated. Keith Owens . */ + +static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx) +{ + const int strict_bits = 0x0060e202; + int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; + long flags; + save_flags(flags); + cli(); + if (chip_idx != X3201_3) { + outl(newcsr6, ioaddr + CSR6); + restore_flags(flags); + return; + } + newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */ + /* read 0 on the Xircom cards */ + newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */ + currcsr6 = inl(ioaddr + CSR6); + if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || + ((currcsr6 & ~0x2002) == 0)) { + outl(newcsr6, ioaddr + CSR6); /* safe */ + restore_flags(flags); + return; + } + /* make sure the transmitter and receiver are stopped first */ + currcsr6 &= ~0x2002; + while (1) { + csr5 = inl(ioaddr + CSR5); + if (csr5 == 0xffffffff) + break; /* cannot read csr5, card removed? */ + csr5_22_20 = csr5 & 0x700000; + csr5_19_17 = csr5 & 0x0e0000; + if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && + (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) + break; /* both are stopped or suspended */ + if (!--attempts) { + printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts," + "csr5=0x%08x\n", csr5); + outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */ + restore_flags(flags); + return; + } + outl(currcsr6, ioaddr + CSR6); + udelay(1); + } + /* now it is safe to change csr6 */ + outl(newcsr6, ioaddr + CSR6); + restore_flags(flags); +} + +static struct net_device *tulip_probe1(struct pci_dev *pdev, + struct net_device *dev, long ioaddr, int irq, + int chip_idx, int board_idx) +{ + static int did_version = 0; /* Already printed version info. */ + struct tulip_private *tp; + /* See note below on the multiport cards. */ + static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + static int last_irq = 0; + static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ + u8 chip_rev; + int i; + unsigned short sum; + + if (tulip_debug > 0 && did_version++ == 0) + printk(KERN_INFO "%s", version); + + dev = init_etherdev(dev, 0); + + pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); + /* Bring the 21143 out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (tulip_tbl[chip_idx].flags & HAS_ACPI) + pci_write_config_dword(pdev, 0x40, 0x00000000); + + printk(KERN_INFO "%s: %s rev %d at %#3lx,", + dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr); + + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx); + /* Clear the missed-packet counter. */ + (volatile int)inl(ioaddr + CSR8); + + if (chip_idx == DC21041) { + if (inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_idx = DC21040; + } else { + printk(" 21041 mode,"); + } + } + + /* The station address ROM is read byte serially. The register must + be polled, waiting for the value to be read bit serially from the + EEPROM. + */ + sum = 0; + if (chip_idx == DC21040) { + outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + dev->dev_addr[i] = value; + sum += value & 0xff; + } + } else if (chip_idx == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); + sum += value & 0xffff; + } + } else if (chip_idx == COMET) { + /* No need to read the EEPROM. */ + put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); + put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); + for (i = 0; i < 6; i ++) + sum += dev->dev_addr[i]; + } else if (chip_idx == X3201_3) { + /* Xircom has its address stored in the CIS + * we access it through the boot rom interface for now + * this might not work, as the CIS is not parsed but I + * (danilo) use the offset I found on my card's CIS !!! + * + * Doug Ledford: I changed this routine around so that it + * walks the CIS memory space, parsing the config items, and + * finds the proper lan_node_id tuple and uses the data + * stored there. + */ + unsigned char j, tuple, link, data_id, data_count; + outl(1<<12, ioaddr + CSR9); /* enable boot rom access */ + for (i = 0x100; i < 0x1f7; i += link+2) { + outl(i, ioaddr + CSR10); + tuple = inl(ioaddr + CSR9) & 0xff; + outl(i + 1, ioaddr + CSR10); + link = inl(ioaddr + CSR9) & 0xff; + outl(i + 2, ioaddr + CSR10); + data_id = inl(ioaddr + CSR9) & 0xff; + outl(i + 3, ioaddr + CSR10); + data_count = inl(ioaddr + CSR9) & 0xff; + if ( (tuple == 0x22) && + (data_id == 0x04) && (data_count == 0x06) ) { + /* + * This is it. We have the data we want. + */ + for (j = 0; j < 6; j++) { + outl(i + j + 4, ioaddr + CSR10); + dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff; + } + break; + } else if (link == 0) { + break; + } + } + sum = 1; // to make check below fail! + } else { /* Must be a new chip, with a serial EEPROM interface. */ + /* We read the whole EEPROM, and sort it out later. DEC has a + specification _Digital Semiconductor 21X4 Serial ROM Format_ + but early vendor boards just put the address in the first six + EEPROM locations. */ + unsigned char ee_data[EEPROM_SIZE]; + int sa_offset = 0; + + for (i = 0; i < sizeof(ee_data)/2; i++) + ((u16 *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN)); + + /* Detect the simple EEPROM format by the duplicated station addr. */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + multiport_cnt = 4; + } + for (i = 0; i < 6; i ++) { + dev->dev_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* Lite-On boards have the address byte-swapped. */ + if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) + && dev->dev_addr[1] == 0x00) + for (i = 0; i < 6; i+=2) { + char tmp = dev->dev_addr[i]; + dev->dev_addr[i] = dev->dev_addr[i+1]; + dev->dev_addr[i+1] = tmp; + } + /* On the Zynx 315 Etherarray and other multiport boards only the + first Tulip has an EEPROM. + The addresses of the subsequent ports are derived from the first. + Many PCI BIOSes also incorrectly report the IRQ line, so we correct + that here as well. */ + if (sum == 0 || sum == 6*0xff) { + printk(" EEPROM not present,"); + for (i = 0; i < 5; i++) + dev->dev_addr[i] = last_phys_addr[i]; + dev->dev_addr[i] = last_phys_addr[i] + 1; +#if defined(__i386__) /* Patch up x86 BIOS bug. */ + if (last_irq) + irq = last_irq; +#endif + } + + for (i = 0; i < 6; i++) + printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); + printk(", IRQ %d.\n", irq); + last_irq = irq; + + /* We do a request_region() only to register /proc/ioports info. */ + /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ + request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the data structures are quadword aligned. */ + tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + + tp->lock = SPIN_LOCK_UNLOCKED; + tp->pdev = pdev; + tp->chip_id = chip_idx; + tp->revision = chip_rev; + tp->csr0 = csr0; + tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7); + + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. + And the ASIX must have a burst limit or horrible things happen. */ + if ( (chip_idx == DC21143 && chip_rev == 65) || + (chip_idx == X3201_3) ) + tp->csr0 &= ~0x01000000; + else if (chip_idx == AX88140) + tp->csr0 |= 0x2000; + +#ifdef TULIP_FULL_DUPLEX + tp->full_duplex = 1; + tp->full_duplex_lock = 1; +#endif +#ifdef TULIP_DEFAULT_MEDIA + tp->default_port = TULIP_DEFAULT_MEDIA; +#endif +#ifdef TULIP_NO_MEDIA_SWITCH + tp->medialock = 1; +#endif + + /* The lower four bits are the media type. */ + if (board_idx >= 0 && board_idx < MAX_UNITS) { + tp->default_port = options[board_idx] & 15; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + tp->medialock = 1; + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->full_duplex_lock = 1; + + /* This is logically part of probe1(), but too complex to write inline. */ + if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE) + parse_eeprom(dev); + + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = 0x03e1; + + if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) { + int phy, phy_idx; + if (tp->mtable && tp->mtable->has_mii) { + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == 11) { + tp->cur_index = i; + tp->saved_if_port = dev->if_port; + select_media(dev, 1); + dev->if_port = tp->saved_if_port; + break; + } + } + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + phy++) { + int mii_status = mdio_read(dev, phy, 1); + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { + int mii_reg0 = mdio_read(dev, phy, 0); + int mii_advert = mdio_read(dev, phy, 4); + int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; + tp->phys[phy_idx] = phy; + tp->advertising[phy_idx++] = reg4; + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != reg4) { + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," + " previously advertising %4.4x.\n", + dev->name, reg4, phy, mii_advert); + mdio_write(dev, phy, 4, reg4); + } + /* Enable autonegotiation: some boards default to off. */ + mdio_write(dev, phy, 0, mii_reg0 | + (tp->full_duplex ? 0x1100 : 0x1000) | + (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); + } + } + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { + printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + tp->phys[0] = 1; + } + } + + /* The Tulip-specific entries in the device structure. */ + dev->open = &tulip_open; + dev->hard_start_xmit = &tulip_start_xmit; + dev->stop = &tulip_close; + dev->get_stats = &tulip_get_stats; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &private_ioctl; +#endif +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_rx_mode; +#endif + dev->tx_timeout = tulip_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (chip_idx) { + case DC21041: + outl(0x00000000, ioaddr + CSR13); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ + outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr, chip_idx); + outl(0x0000EF05, ioaddr + CSR13); + break; + case DC21040: + outl(0x00000000, ioaddr + CSR13); + outl(0x00000004, ioaddr + CSR13); + break; + case DC21140: default: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); + break; + case DC21142: + case PNIC2: + if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) { + outl_CSR6(0x82020000, ioaddr, chip_idx); + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl_CSR6(0x820E0000, ioaddr, chip_idx); + } else { + outl_CSR6(0x82420200, ioaddr, chip_idx); + outl(0x0001, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + } + break; + case X3201_3: + outl(0x0008, ioaddr + CSR15); + udelay(5); /* The delays are Xircom recommended to give the + * chipset time to reset the actual hardware + * on the PCMCIA card + */ + outl(0xa8050000, ioaddr + CSR15); + udelay(5); + outl(0xa00f0000, ioaddr + CSR15); + udelay(5); + outl_CSR6(0x32000200, ioaddr, chip_idx); + break; + case LC82C168: + if ( ! tp->mii_cnt) { + outl_CSR6(0x00420000, ioaddr, chip_idx); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + } + break; + case MX98713: case COMPEX9881: + outl_CSR6(0x00000000, ioaddr, chip_idx); + outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + outl(0x00000001, ioaddr + CSR13); + break; + case MX98715: case MX98725: + outl_CSR6(0x01a80000, ioaddr, chip_idx); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00001000, ioaddr + CSR12); + break; + case COMET: + /* No initialization necessary. */ + break; + } + + return dev; +} + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. + Search www.digital.com for "21X4 SROM" to get details. + This code is very complex, and will require changes to support + additional cards, so I'll be verbose about what is going on. + */ + +/* Known cards that have old-style EEPROMs. */ +static struct fixups { + char *name; + unsigned char addr0, addr1, addr2; + u16 newtable[32]; /* Max length below. */ +} eeprom_fixups[] = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + 0x0000, 0x009E, /* 10baseT */ + 0x0903, 0x006D, /* 100baseTx */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ + 0x0103, 0x006D, /* 100baseTx */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + }}, + {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + +#if defined(__i386__) /* AKA get_unaligned() */ +#define get_u16(ptr) (*(u16 *)(ptr)) +#else +#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) +#endif + +static void parse_eeprom(struct net_device *dev) +{ + /* The last media info list parsed, for multiport boards. */ + static struct mediatable *last_mediatable = NULL; + static unsigned char *last_ee_data = NULL; + static int controller_index = 0; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + unsigned char *ee_data = tp->eeprom; + int i; +#ifdef CARDBUS + int chip_rev = tp->revision; +#endif + + tp->mtable = 0; + for (i = 0; i < EEPROM_SIZE/2; i++) + ((u16 *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN)); + + /* Detect an old-style (SA only) EEPROM layout: + memcmp(eedata, eedata+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + printk(KERN_INFO "%s: Controller %d of multiport board.\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + printk(KERN_INFO "%s: Missing EEPROM, this interface may " + "not work correctly!\n", + dev->name); + return; + } + /* Do a fix-up based on the vendor half of the station address prefix. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 + && dev->dev_addr[1] == eeprom_fixups[i].addr1 + && dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); + printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using" + " substitute media control info.\n", + dev->name, eeprom_fixups[i].name); + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ + printk(KERN_INFO "%s: Old style EEPROM with no media selection " + "information.\n", + dev->name); + return; + } + } + + controller_index = 0; + if (ee_data[19] > 1) { /* Multiport board. */ + last_ee_data = ee_data; + } +subsequent_board: + + if (ee_data[27] == 0) { /* No valid media table. */ + } else if (tp->chip_id == DC21041) { + unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; + short media; + int count; + + media = get_u16(p); + p += 2; + count = *p++; + + printk(KERN_INFO "%s:21041 Media information at %d, default media " + "%4.4x (%s).\n", dev->name, ee_data[27], media, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + unsigned char media_code = *p++; + u16 csrvals[3]; + int idx; + for (idx = 0; idx < 3; idx++) { + csrvals[idx] = get_u16(p); + p += 2; + } + if (media_code & 0x40) { + printk(KERN_INFO "%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } else + printk(KERN_INFO "%s: 21041 media #%d, %s.\n", + dev->name, media_code & 15, medianame[media_code & 15]); + } + } else { + unsigned char *p = (void *)ee_data + ee_data[27]; + unsigned char csr12dir = 0; + int count; + struct mediatable *mtable; + u16 media = get_u16(p); + + p += 2; + if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) + csr12dir = *p++; + count = *p++; + mtable = (struct mediatable *) + kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), + GFP_KERNEL); + if (mtable == NULL) + return; /* Horrible, impossible failure. */ + last_mediatable = tp->mtable = mtable; + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; + mtable->csr15dir = mtable->csr15val = 0; + + printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; + p += 4; + } else { + leaf->type = p[1]; + if (p[1] == 0x05) { + mtable->has_reset = i; + leaf->media = p[2] & 0x0f; + } else if (p[1] & 1) { + mtable->has_mii = 1; + leaf->media = 11; + } else { + mtable->has_nonmii = 1; + leaf->media = p[2] & 0x0f; + if (p[1] == 2) { + if (leaf->media == 0) { + mtable->csr15dir = get_unaligned((u16*)&p[3])<<16; + mtable->csr15val = get_unaligned((u16*)&p[5])<<16; + } else if (leaf->media == 0x40) { + u32 base15 = get_unaligned((u16*)&p[7]); + mtable->csr15dir = + (get_unaligned((u16*)&p[9])<<16) + base15; + mtable->csr15val = + (get_unaligned((u16*)&p[11])<<16) + base15; + } + } + } + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " + "sequences %d/%d long, capabilities %2.2x %2.2x.\n", + dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); + } + printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " + "by a %s (%d) block.\n", + dev->name, i, medianame[leaf->media], leaf->media, + block_name[leaf->type], leaf->type); + } + } +} +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* 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. + Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. + We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + +static int read_eeprom(long ioaddr, int location, int addr_len) +{ + int i; + unsigned short retval = 0; + long ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + if (tp->chip_id == LC82C168) { + int i = 1000; + outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + return 0xffff; + } + + if (tp->chip_id == COMET) { + if (phy_id == 1) { + if (location < 7) + return inl(ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + return inl(ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + return inl(ioaddr + 0xD4 + ((location-29)<<2)); + } + return 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; + + if (tp->chip_id == LC82C168) { + int i = 1000; + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + return; + } + + if (tp->chip_id == COMET) { + if (phy_id != 1) + return; + if (location < 7) + outl(value, ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + outl(value, ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + outl(value, ioaddr + 0xD4 + ((location-29)<<2)); + return; + } + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return; +} + +static void +tulip_up(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) + outl_CSR6(0x00040000, ioaddr, tp->chip_id); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(0x00000001, ioaddr + CSR0); + + /* Deassert reset. */ + outl(tp->csr0, ioaddr + CSR0); + udelay(2); + + if (tulip_tbl[tp->chip_id].flags & HAS_ACPI) + pci_write_config_dword(tp->pdev, 0x40, 0x00000000); + + /* Clear the tx ring */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0x00000000; + } + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); + + if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4))); + if (tp->chip_id == AX88140) { + outl(0, ioaddr + CSR13); + outl(addr_low, ioaddr + CSR14); + outl(1, ioaddr + CSR13); + outl(addr_high, ioaddr + CSR14); + } else if (tp->chip_id == COMET) { + outl(addr_low, ioaddr + 0xA4); + outl(addr_high, ioaddr + 0xA8); + outl(0, ioaddr + 0xAC); + outl(0, ioaddr + 0xB0); + } + } else if (tp->chip_id != X3201_3) { + /* This is set_rx_mode(), but without starting the transmitter. */ + u16 *eaddrs = (u16 *)dev->dev_addr; + u16 *setup_frm = &tp->setup_frame[15*6]; + + /* 21140 bug: you must add the broadcast address. */ + memset(tp->setup_frame, 0xff, 96*sizeof(u16)); + /* Fill the final entry of the table with our physical address. */ + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + /* Put the setup frame on the Tx list. */ + tp->tx_ring[0].length = 0x08000000 | 192; + tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[0].status = DescOwned; + + tp->cur_tx++; + } else { /* X3201_3 */ + u16 *eaddrs = (u16 *)dev->dev_addr; + u16 *setup_frm = &tp->setup_frame[0*6]; + + /* fill the table with the broadcast address */ + memset(tp->setup_frame, 0xff, 96*sizeof(u16)); + /* re-fill the first 14 table entries with our address */ + for(i=0; i<14; i++) { + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + } + + /* Put the setup frame on the Tx list. */ + tp->tx_ring[0].length = 0x08000000 | 192; + /* Lie about the address of our setup frame to make the */ + /* chip happy */ + tp->tx_ring[0].buffer1 = (virt_to_bus(tp->setup_frame) + 4); + tp->tx_ring[0].status = DescOwned; + + tp->cur_tx++; + } + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + tp->saved_if_port = dev->if_port; + if (dev->if_port == 0) + dev->if_port = tp->default_port; + if (tp->chip_id == DC21041 && dev->if_port > 4) + /* Invalid: Select initial TP, autosense, autonegotiate. */ + dev->if_port = 4; + + /* Allow selecting a default media. */ + i = 0; + if (tp->mtable == NULL) + goto media_picked; + if (dev->if_port) { + int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : + (dev->if_port == 12 ? 0 : dev->if_port); + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) { + int looking_for = tp->mtable->defaultmedia & 15; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", + dev->name, medianame[looking_for]); + goto media_picked; + } + } + /* Start sensing first non-full-duplex media. */ + for (i = tp->mtable->leafcount - 1; + (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) + ; +media_picked: + + tp->csr6 = 0; + tp->cur_index = i; + if (dev->if_port == 0 && tp->chip_id == DC21142) { + if (tp->mii_cnt) { + select_media(dev, 1); + if (tulip_debug > 1) + printk(KERN_INFO "%s: Using MII transceiver %d, status " + "%4.4x.\n", + dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); + outl_CSR6(0x82020000, ioaddr, tp->chip_id); + tp->csr6 = 0x820E0000; + dev->if_port = 11; + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + } else + t21142_start_nway(dev); + } else if ((tp->chip_id == LC82C168 || tp->chip_id == PNIC2) + && tp->mii_cnt && ! tp->medialock) { + dev->if_port = 11; + tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) + && ! tp->medialock) { + dev->if_port = 0; + tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { + /* Provided by BOLO, Macronix - 12/10/1998. */ + dev->if_port = 0; + tp->csr6 = 0x01880200; + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); + } else if (tp->chip_id == DC21143 && + media_cap[dev->if_port] & MediaIsMII) { + /* We must reset the media CSRs when we force-select MII mode. */ + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + } else if (tp->chip_id == X3201_3) { + outl(0x0008, ioaddr + CSR15); + udelay(5); + outl(0xa8050000, ioaddr + CSR15); + udelay(5); + outl(0xa00f0000, ioaddr + CSR15); + udelay(5); + tp->csr6 = 0x32400000; + } else if (tp->chip_id == COMET) { + dev->if_port = 0; + tp->csr6 = 0x00040000; + } else + select_media(dev, 1); + + /* Start the chip's Tx to process setup frame. */ + outl_CSR6(tp->csr6, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id); + + /* Enable interrupts by setting the interrupt mask. */ + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + outl(0, ioaddr + CSR2); /* Rx poll demand */ + + netif_start_queue (dev); + + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR6)); + } + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT(5*HZ); + tp->timer.data = (unsigned long)dev; + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; + add_timer(&tp->timer); +} + +static int +tulip_open(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + tulip_init_ring(dev); + + tulip_up(dev); + tp->open = 1; + MOD_INC_USE_COUNT; + + return 0; +} + +/* Set up the transceiver control registers for the selected media type. */ +static void select_media(struct net_device *dev, int startup) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int i; + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" + " with control setting %2.2x.\n", + dev->name, p[1]); + dev->if_port = p[0]; + if (startup) + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + outl(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 2: case 4: { + u16 setup[5]; + u32 csr13val, csr14val, csr15dir, csr15val; + for (i = 0; i < 5; i++) + setup[i] = get_u16(&p[i*2 + 1]); + + dev->if_port = p[0] & 15; + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Resetting the transceiver.\n", + dev->name); + for (i = 0; i < rst[0]; i++) + outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control " + "%4.4x/%4.4x.\n", + dev->name, medianame[dev->if_port], setup[0], setup[1]); + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + csr13val = setup[0]; + csr14val = setup[1]; + csr15dir = (setup[3]<<16) | setup[2]; + csr15val = (setup[4]<<16) | setup[2]; + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + outl(csr13val, ioaddr + CSR13); + } else { + csr13val = 1; + csr14val = 0x0003FF7F; + csr15dir = (setup[0]<<16) | 0x0008; + csr15val = (setup[1]<<16) | 0x0008; + if (dev->if_port <= 4) + csr14val = t21142_csr14[dev->if_port]; + if (startup) { + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + } + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + if (startup) outl(csr13val, ioaddr + CSR13); + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n", + dev->name, csr15dir, csr15val); + if (mleaf->type == 4) + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; + break; + } + case 1: case 3: { + int phy_num = p[0]; + int init_length = p[1]; + u16 *misc_info; + u16 to_advertise; + + dev->if_port = 11; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) + for (i = 0; i < reset_length; i++) + outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + for (i = 0; i < init_length; i++) + outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i], ioaddr + CSR12); + } + for (i = 0; i < init_length; i++) + outl(init_sequence[i], ioaddr + CSR12); + } + to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; + tp->advertising[phy_num] = to_advertise; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", + dev->name, to_advertise, phy_num, tp->phys[phy_num]); + /* Bogus: put in by a committee? */ + mdio_write(dev, tp->phys[phy_num], 4, to_advertise); + break; + } + default: + printk(KERN_DEBUG "%s: Invalid media table selection %d.\n", + dev->name, mleaf->type); + new_csr6 = 0x020E0000; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12) & 0xff); + } else if (tp->chip_id == DC21041) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", + dev->name, medianame[dev->if_port & 15], + inl(ioaddr + CSR12) & 0xffff); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + new_csr6 = 0x80020000; + } else if (tp->chip_id == LC82C168 || tp->chip_id == PNIC2) { + if (startup && ! tp->medialock) + dev->if_port = tp->mii_cnt ? 11 : 0; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," + " media %s.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), + medianame[dev->if_port]); + if (tp->mii_cnt) { + new_csr6 = 0x810C0000; + outl(0x0001, ioaddr + CSR15); + outl(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } else if (dev->if_port == 3 || dev->if_port == 5) { + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + if (startup) + outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ + else + outl(0x1F868, ioaddr + 0xB8); + } else { + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + } else if (tp->chip_id == DC21040) { /* 21040 */ + /* Turn on the xcvr interface. */ + int csr12 = inl(ioaddr + CSR12); + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", + dev->name, medianame[dev->if_port], csr12); + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + new_csr6 = 0x20000; + /* Set the full duplux match frame. */ + outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + if (t21040_csr13[dev->if_port] & 8) { + outl(0x0705, ioaddr + CSR14); + outl(0x0006, ioaddr + CSR15); + } else { + outl(0xffff, ioaddr + CSR14); + outl(0x0000, ioaddr + CSR15); + } + outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13); + } else if (tp->chip_id == X3201_3) { /* Xircom */ + if (tp->default_port == 0) + dev->if_port = tp->mii_cnt ? 11 : 3; +/* Someone is on crack, the Xircom only does MII, no Fx */ +/* if (media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000;*/ + new_csr6 = 0x324c0000; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Xircom CardBus Adapter: " + "%s transceiver, CSR12 %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12)); + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + dev->if_port = tp->mii_cnt ? 11 : 3; + if (media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No media description table, assuming " + "%s transceiver, CSR12 %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12)); + } + + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + return; +} + +/* + Check the MII negotiated duplex, and change the CSR6 setting if + required. + Return 0 if everything is OK. + Return < 0 if the transceiver is missing or has no link beat. + */ +static int check_duplex(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int mii_reg1, mii_reg5, negotiated, duplex; + + if (tp->full_duplex_lock) + return 0; + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x.\n", dev->name, mii_reg1, mii_reg5); + if (mii_reg1 == 0xffff) + return -2; + if ((mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + if (tulip_debug > 1) + printk(KERN_INFO "%s: No link beat on the MII interface," + " status %4.4x.\n", dev->name, new_reg1); + return -1; + } + } + negotiated = mii_reg5 & tp->advertising[0]; + duplex = ((negotiated & 0x0300) == 0x0100 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (tp->full_duplex) tp->csr6 |= 0x0200; + else tp->csr6 &= ~0x0200; + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + if (tulip_debug > 0) + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + "#%d link partner capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + } + return 0; +} + +static void tulip_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 csr12 = inl(ioaddr + CSR12); + int next_tick = 2*HZ; + + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " + "SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), + csr12, inl(ioaddr + CSR13), + inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + } + switch (tp->chip_id) { + case DC21040: + if (!tp->medialock && csr12 & 0x0002) { /* Network error */ + printk(KERN_INFO "%s: No link beat found.\n", + dev->name); + dev->if_port = (dev->if_port == 2 ? 0 : 2); + select_media(dev, 0); + dev->trans_start = jiffies; + } + break; + case DC21041: + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", + dev->name, csr12); + switch (dev->if_port) { + case 0: case 3: case 4: + if (csr12 & 0x0004) { /*LnkFail */ + /* 10baseT is dead. Check for activity on alternate port. */ + tp->mediasense = 1; + if (csr12 & 0x0200) + dev->if_port = 2; + else + dev->if_port = 1; + printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n", + dev->name, medianame[dev->if_port]); + outl(0, ioaddr + CSR13); /* Reset */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + next_tick = 10*HZ; /* 2.4 sec. */ + } else + next_tick = 30*HZ; + break; + case 1: /* 10base2 */ + case 2: /* AUI */ + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", + dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; + } + break; + case DC21140: case DC21142: case MX98713: case COMPEX9881: default: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Not much that can be done. + Assume this a generic MII or SYM transceiver. */ + next_tick = 60*HZ; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " + "CSR12 0x%2.2x.\n", + dev->name, inl(ioaddr + CSR6), csr12 & 0xff); + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ + int offset = mleaf->type == 4 ? 5 : 2; + s8 bitnum = p[offset]; + if (p[offset+1] & 0x80) { + if (tulip_debug > 1) + printk(KERN_DEBUG"%s: Transceiver monitor tick " + "CSR12=%#2.2x, no media sense.\n", + dev->name, csr12); + if (mleaf->type == 4) { + if (mleaf->media == 3 && (csr12 & 0x02)) + goto select_next_media; + } + break; + } + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" + " bit %d is %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ + goto actually_mii; + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_cap[dev->if_port] & MediaIsFD) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: /* 21140, 21142 MII */ + actually_mii: + check_duplex(dev); + next_tick = 60*HZ; + break; + case 2: /* 21142 serial block has no link beat. */ + default: + break; + } + } + break; + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list + of available transceivers. */ +static void t21142_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = 0; + + if ((tulip_debug > 2) && !(media_cap[dev->if_port] & MediaIsMII)) + printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n", + dev->name, csr12, medianame[dev->if_port]); + if (media_cap[dev->if_port] & MediaIsMII) { + check_duplex(dev); + next_tick = 60*HZ; + } else if (tp->nwayset) { + /* Don't screw up a negotiated session! */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n", + dev->name, medianame[dev->if_port], csr12); + } else if (tp->medialock) { + ; + } else if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, " + "trying NWay.\n", dev->name, csr12); + t21142_start_nway(dev); + next_tick = 3*HZ; + } + } else if (((csr12 & 0x7000) != 0x5000) + && tp->chip_id != X3201_3) { + /* Negotiation failed. Search media types. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", + dev->name, csr12); + if (!(csr12 & 4)) { /* 10mbps link beat good. */ + new_csr6 = 0x82420000; + dev->if_port = 0; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outw(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else { + /* Select 100mbps port to check for link beat. */ + new_csr6 = 0x83860000; + dev->if_port = 3; + outl(0, ioaddr + CSR13); + outl(0x0003FF7F, ioaddr + CSR14); + outw(8, ioaddr + CSR15); + outl(1, ioaddr + CSR13); + } + if (tulip_debug > 1) + printk(KERN_INFO"%s: Testing new 21143 media %s.\n", + dev->name, medianame[dev->if_port]); + if (new_csr6 != (tp->csr6 & ~0x00D5)) { + tp->csr6 &= 0x00D5; + tp->csr6 |= new_csr6; + outl(0x0301, ioaddr + CSR12); + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } + next_tick = 3*HZ; + } + if (tp->cur_tx - tp->dirty_tx > 0 && + jiffies - dev->trans_start > TX_TIMEOUT) { + printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + tulip_tx_timeout(dev); + } + + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void t21142_start_nway(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr14 = ((tp->to_advertise & 0x0180) << 9) | + ((tp->to_advertise&0x0020)<<1) | 0xffbf; + + dev->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + if (debug > 1) + printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n", + dev->name, csr14); + outl(0x0001, ioaddr + CSR13); + outl(csr14, ioaddr + CSR14); + tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0); + outl_CSR6(tp->csr6, ioaddr, tp->chip_id); + if (tp->mtable && tp->mtable->csr15dir) { + outl(tp->mtable->csr15dir, ioaddr + CSR15); + outl(tp->mtable->csr15val, ioaddr + CSR15); + } else + outw(0x0008, ioaddr + CSR15); + outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */ +} + +static void t21142_lnk_change(struct net_device *dev, int csr5) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " + "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14)); + + /* If NWay finished and we have a negotiated partner capability. */ + if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { + int setup_done = 0; + tp->lpar = csr12 >> 16; + tp->nwayset = 1; + if (csr12 & 0x01000000) dev->if_port = 5; + else if (csr12 & 0x00800000) dev->if_port = 3; + else if (csr12 & 0x00400000) dev->if_port = 4; + else if (csr12 & 0x00200000) dev->if_port = 0; + else { + tp->nwayset = 0; + if ( ! (csr12 & 2)) dev->if_port = 3; + else if ( ! (csr12 & 4)) dev->if_port = 0; + } + tp->full_duplex = (media_cap[tp->default_port] & MediaAlwaysFD) ? 1:0; + + if (tulip_debug > 1) { + if (tp->nwayset) + printk(KERN_INFO "%s: Switching to %s based on link partner " + "advertisement %4.4x.\n", + dev->name, medianame[dev->if_port], tp->lpar); + else + printk(KERN_INFO "%s: Switching to %s based on link beat " + "status of %4.4x.\n", + dev->name, medianame[dev->if_port], csr12); + } + + if (tp->mtable) { + int i; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == dev->if_port) { + tp->cur_index = i; + select_media(dev, 0); + setup_done = 1; + break; + } + } + if ( ! setup_done) { + tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + outw(0x0000, ioaddr + CSR13); + outw(0x0000, ioaddr + CSR14); + } + outl_CSR6(tp->csr6 | 0x0000, ioaddr, tp->chip_id); + if (debug > 2) + printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n", + dev->name, inl(ioaddr + CSR5)); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } else if ((tp->nwayset && (csr5 & 0x08000000) + && (dev->if_port == 3 || dev->if_port == 5) + && (csr12 & 2) == 2) || + (tp->nway && (csr5 & (TPLnkFail)))) { + /* Link blew? Maybe restart NWay. */ + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } else if (dev->if_port == 3 || dev->if_port == 5) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21143 %s link beat %s.\n", + dev->name, medianame[dev->if_port], + (csr12 & 2) ? "failed" : "good"); + if ((csr12 & 2) && ! tp->medialock) { + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } + } else if (dev->if_port == 0 || dev->if_port == 4) { + if ((csr12 & 4) == 0) + printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", + dev->name); + } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ + if (tulip_debug) + printk(KERN_INFO"%s: 21143 10mbps sensed media.\n", + dev->name); + dev->if_port = 0; + } else if (tp->nwayset) { + if (tulip_debug) + printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n", + dev->name, medianame[dev->if_port], tp->csr6); + } else { /* 100mbps link beat good. */ + if (tulip_debug) + printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n", + dev->name); + dev->if_port = 3; + tp->csr6 = 0x83860000; + outl(0x0003FF7F, ioaddr + CSR14); + outl(0x0301, ioaddr + CSR12); + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } +} + +static void mxic_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) { + printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, + inl(ioaddr + CSR12)); + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void pnic_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = tp->csr6 & ~0x40C40200; + + if (media_cap[dev->if_port] & MediaIsMII) { + int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC negotiated capability %8.8x, " + "CSR5 %8.8x.\n", + dev->name, negotiated, inl(ioaddr + CSR5)); + + if (negotiated & 0x0380) /* 10 vs 100mbps */ + new_csr6 |= 0x810E0000; + else + new_csr6 |= 0x814E0000; + if (((negotiated & 0x0300) == 0x0100) /* Duplex */ + || (negotiated & 0x00C0) == 0x0040 + || tp->full_duplex_lock) { + tp->full_duplex = 1; + new_csr6 |= 0x0200; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC MII PHY status %4.4x, Link " + "partner report %4.4x, csr6 %8.8x/%8.8x.\n", + dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, + tp->csr6, inl(ioaddr + CSR6)); + } else { + int phy_reg = inl(ioaddr + 0xB8); + int csr5 = inl(ioaddr + CSR5); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC PHY status %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, csr5); + + if (phy_reg & 0x04000000) { /* Remote link fault */ + /*outl(0x0201F078, ioaddr + 0xB8);*/ + next_tick = 3*HZ; + } + if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " + "CSR5 %8.8x, PHY %3.3x.\n", + dev->name, medianame[dev->if_port], csr12, + inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + if (tp->medialock) { + } else if (dev->if_port == 0) { + dev->if_port = 3; + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + outl(0x1F868, ioaddr + 0xB8); + } else { + dev->if_port = 0; + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + new_csr6 |= (tp->csr6 & 0xfdff); + next_tick = 3*HZ; + } else + new_csr6 = tp->csr6; + if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + } + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); /* Restart Tx */ + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + dev->trans_start = jiffies; + if (tulip_debug > 1) + printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " + "CSR6 %8.8x.\n", + dev->name, tp->full_duplex ? "full" : "half", new_csr6); + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void comet_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " + "%4.4x.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8)); + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void tulip_tx_timeout(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } else if (tp->chip_id == DC21040) { + if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) { + dev->if_port = (dev->if_port == 2 ? 0 : 2); + printk(KERN_INFO "%s: transmit timed out, switching to " + "%s.\n", + dev->name, medianame[dev->if_port]); + select_media(dev, 0); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + int csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " + "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if ( ! tp->medialock) { + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + } + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) { + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if ( ! tp->medialock && tp->mtable) { + do + --tp->cur_index; + while (tp->cur_index >= 0 + && (media_cap[tp->mtable->mleaf[tp->cur_index].media] + & MediaIsFD)); + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s " + "media.\n", dev->name, medianame[dev->if_port]); + } + } else { + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " + "%8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); + dev->if_port = 0; + } + +#if defined(way_too_many_messages) + if (tulip_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " + "%2.2x %2.2x %2.2x.\n", + i, (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) printk(" %2.2x", buf[j]); + printk(" j=%d.\n", j); + } + printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); + } +#endif + + /* Stop and restart the chip's Tx processes . */ + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + netif_wake_queue (dev); + tp->stats.tx_errors++; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void tulip_init_ring(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0x00000000; + tp->rx_ring[i].length = PKT_BUF_SZ; + tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_skbuff[i] = NULL; + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP; + tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); +#ifdef CARDBUS + if (tp->chip_id == X3201_3) + tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); +#endif CARDBUS + } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); +} + +static int +tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry; + u32 flag; + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; +#ifdef CARDBUS + if (tp->chip_id == X3201_3) { + memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); + } else +#endif + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + } else { + /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + tp->tx_full = 1; + } + if (entry == TX_RING_SIZE-1) + flag |= 0xe0000000 | DESC_RING_WRAP; + + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */ + tp->cur_tx++; + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); + + dev->trans_start = jiffies; + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_instance; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr5, work_budget = max_interrupt_work; + + spin_lock (&tp->lock); + + do { + csr5 = inl(ioaddr + CSR5); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(csr5 & 0x0001ffff, ioaddr + CSR5); + + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); + + if (csr5 == 0xffffffff) + break; /* all bits set, assume PCMCIA card removed */ + + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) + break; + + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= tulip_rx(dev); + + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + unsigned int dirty_tx; + + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int status = tp->tx_ring[entry].status; + + if (status < 0) + break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (tp->tx_skbuff[entry] == NULL) + continue; + + if (status & 0x8000) { + /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + tp->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS + if (status & 0x0100) tp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if (status & 0x0001) tp->stats.tx_deferred++; +#endif + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb_irq(tp->tx_skbuff[entry]); + tp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (tp->tx_full && + tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) + /* The ring is no longer full */ + tp->tx_full = 0; + + if (tp->tx_full) + netif_stop_queue (dev); + else + netif_wake_queue (dev); + + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 2) + printk(KERN_WARNING "%s: The transmitter stopped." + " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } + } + + /* Log errors. */ + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; + if (csr5 & TxJabber) tp->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id); + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id); + } + if (csr5 & TimerInt) { + if (tulip_debug > 2) + printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", + dev->name, csr5); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if ( tp->chip_id == DC21142) + t21142_lnk_change(dev, csr5); + } + /* Clear all error sources, included undocumented ones! */ + outl(0x0800f7ba, ioaddr + CSR5); + } + if (--work_budget < 0) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Too much work during an interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); +#ifdef notdef + /* Clear all but standard interrupt sources. */ + outl((~csr5) & 0x0001ebef, ioaddr + CSR7); +#endif + break; + } + } while (1); + + if (tulip_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + spin_unlock (&tp->lock); +} + +static int +tulip_rx(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; + + if (tulip_debug > 4) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + /* If we own the next entry, it's a new packet. Send it up. */ + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + + if (tulip_debug > 5) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); + if (--rx_work_limit < 0) + break; + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & RxDescFatalErr) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; + } + } else { + /* Omit the four octet CRC from the length. */ + short pkt_len = ((status >> 16) & 0x7ff) - 4; + struct sk_buff *skb; + +#ifndef final_version + if (pkt_len > 1518) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); +#endif + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); + tp->rx_skbuff[entry] = NULL; +#ifndef final_version + if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal fault: The skbuff addresses " + "do not match in tulip_rx: %p vs. %p / %p.\n", + dev->name, bus_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); +#endif + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + tp->stats.rx_packets++; + tp->stats.rx_bytes += pkt_len; + } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } + + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); + work_done++; + } + tp->rx_ring[entry].status = DescOwned; + } + + return work_done; +} + +static void +tulip_down(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x00000000, ioaddr + CSR7); + /* Stop the chip's Tx and Rx processes. */ + outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id); + /* 21040 -- Leave the card in 10baseT state. */ + if (tp->chip_id == DC21040) + outl(0x00000004, ioaddr + CSR13); + + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + dev->if_port = tp->saved_if_port; +} + +static int +tulip_close(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + + netif_stop_queue(dev); + + if (netif_device_present(dev)) + tulip_down(dev); + + del_timer(&tp->timer); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { + dev_kfree_skb(skb); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i]); + tp->tx_skbuff[i] = 0; + } + + MOD_DEC_USE_COUNT; + tp->open = 0; + return 0; +} + +static struct net_device_stats *tulip_get_stats(struct net_device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (netif_device_present(dev)) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + return &tp->stats; +} + +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + if (tp->mii_cnt) + data[0] = phy; + else if (tp->chip_id == DC21142) /* 21142 pseudo-MII */ + data[0] = 32; + else if (tp->chip_id == PNIC2) + data[0] = 32; + else if (tp->chip_id == COMET) + data[0] = 1; + else + return -ENODEV; + return 0; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + if (data[0] == 32 && + (tp->chip_id == DC21142 || tp->chip_id == PNIC2)) { + int csr12 = inl(ioaddr + CSR12); + int csr14 = inl(ioaddr + CSR14); + switch (data[1]) { + case 0: { + data[3] = (csr14<<5) & 0x1000; + break; } + case 1: + data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + + (csr12&0x06 ? 0x04 : 0); + break; + case 4: { + data[3] = ((csr14>>9)&0x0380) + + ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1; + break; + } + case 5: data[3] = csr12 >> 16; break; + default: data[3] = 0; break; + } + } else { + save_flags(flags); + cli(); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + } + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ +#if defined(CAP_NET_ADMIN) + if (!capable(CAP_NET_ADMIN)) + return -EPERM; +#else + if (!suser()) + return -EPERM; +#endif + if (data[0] == 32 && tp->chip_id == DC21142) { + if (data[1] == 5) + tp->to_advertise = data[2]; + } else { + save_flags(flags); + cli(); + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + } + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} +#endif /* HAVE_PRIVATE_IOCTL */ + +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline u32 ether_crc_le(int length, unsigned char *data) +{ + u32 crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + tp->csr6 |= 0x00C0; + csr6 |= 0x00C0; + /* Unconditionally log net taps. */ + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else if (tulip_tbl[tp->chip_id].flags & MC_HASH_ONLY) { + /* Some work-alikes have only a 64-entry hash filter table. */ + /* Should verify correctness on big-endian/__powerpc__ */ + struct dev_mc_list *mclist; + int i; + u32 mc_filter[2]; /* Multicast hash filter */ + if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else { + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter); + if (tp->chip_id == AX88140) { + outl(2, ioaddr + CSR13); + outl(mc_filter[0], ioaddr + CSR14); + outl(3, ioaddr + CSR13); + outl(mc_filter[1], ioaddr + CSR14); + } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */ + outl(mc_filter[0], ioaddr + 0xAC); + outl(mc_filter[1], ioaddr + 0xB0); + } + } + } else { + u16 *eaddrs, *setup_frm = tp->setup_frame; + struct dev_mc_list *mclist; + u32 tx_flags = 0x08000000 | 192; + int i; + + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ + if ((dev->mc_count > 14) || ((dev->mc_count > 6) && (tp->chip_id == X3201_3))) { /* Must use a multicast hash table. */ + u16 hash_table[32]; + tx_flags = 0x08400000 | 192; /* Use hash filter. */ + memset(hash_table, 0, sizeof(hash_table)); + set_bit(255, hash_table); /* Broadcast entry */ + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + for (i = 0; i < 32; i++) { + *setup_frm++ = hash_table[i]; + *setup_frm++ = hash_table[i]; + } + setup_frm = &tp->setup_frame[13*6]; + } else if(tp->chip_id != X3201_3) { + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &tp->setup_frame[15*6]; + } else { + /* fill the first two table entries with our address */ + eaddrs = (u16 *)dev->dev_addr; + for(i=0; i<2; i++) { + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + } + /* Double fill each entry to accomodate chips that */ + /* don't like to parse these correctly */ + for (i=0, mclist=dev->mc_list; imc_count; + i++, mclist=mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + } + i=((i+1)*2); + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &tp->setup_frame[15*6]; + } + + /* Fill the final entry with our physical address. */ + eaddrs = (u16 *)dev->dev_addr; + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; + /* Now add this frame to the Tx list. */ + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + } else { + unsigned long flags; + unsigned int entry, dummy = -1; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0; + tp->tx_ring[entry].buffer1 = 0; + /* race with chip, set DescOwned later */ + dummy = entry; + entry = tp->cur_tx++ % TX_RING_SIZE; + } + + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE-1) + tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + if(tp->chip_id == X3201_3) + tp->tx_ring[entry].buffer1 = (virt_to_bus(tp->setup_frame) + 4); + else + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = DescOwned; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + tp->tx_full = 1; + netif_stop_queue (dev); + } + if (dummy >= 0) + tp->tx_ring[dummy].status = DescOwned; + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + } + outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id); +} + +static struct pci_device_id tulip_pci_table[] __devinitdata = { +#if 0 /* these entries conflict with regular tulip driver */ + { 0x1011, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21040 }, + { 0x1011, 0x0014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21041 }, + { 0x1011, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, + { 0x1011, 0x0019, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21142 }, + { 0x11AD, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, LC82C168 }, + { 0x10d9, 0x0512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98713 }, + { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, + { 0x10d9, 0x0531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98725 }, + { 0x125B, 0x1400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AX88140 }, + { 0x11AD, 0xc115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PNIC2 }, + { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, +#endif + { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, + {0}, +}; + +MODULE_DEVICE_TABLE(pci, tulip_pci_table); + +static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *dev; + static int board_idx = 0; + + printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name); + + pci_enable_device (pdev); + pci_set_master (pdev); + dev = tulip_probe1(pdev, NULL, + pci_resource_start (pdev, 0), pdev->irq, + id->driver_data, board_idx++); + if (dev) { + pdev->driver_data = dev; + return 0; + } + return -ENODEV; +} + +static void tulip_suspend(struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + printk(KERN_INFO "tulip_suspend(%s)\n", dev->name); + if (tp->open) tulip_down(dev); +} + +static void tulip_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + printk(KERN_INFO "tulip_resume(%s)\n", dev->name); + if (tp->open) tulip_up(dev); +} + +static void __devexit tulip_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + + printk(KERN_INFO "tulip_detach(%s)\n", dev->name); + unregister_netdev(dev); + kfree(dev); + kfree(tp); +} + +static struct pci_driver tulip_ops = { + name: "tulip_cb", + id_table: tulip_pci_table, + probe: tulip_pci_probe, + remove: tulip_remove, + suspend: tulip_suspend, + resume: tulip_resume +}; + +static int __init tulip_init(void) +{ + pci_register_driver(&tulip_ops); + return 0; +} + +static __exit void tulip_exit(void) +{ + pci_unregister_driver(&tulip_ops); +} + +module_init(tulip_init) +module_exit(tulip_exit) + + +/* + * Local variables: + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index de085602052b..5036b01af783 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -12,10 +12,16 @@ Support and updates available at http://cesdis.gsfc.nasa.gov/linux/drivers/starfire.html + + LK1.1.1 (jgarzik): + - Use PCI driver interface + - Fix MOD_xxx races + - softnet fixups + */ static const char *versionA = -"starfire.c:v0.12 5/28/99 Written by Donald Becker\n", +"starfire.c:v0.12+LK1.1.1 3/19/2000 Written by Donald Becker and others\n", *versionB =" Undates and info at http://www.beowulf.org/linux/drivers.html\n"; /* A few user-configurable values. These may be modified when a driver @@ -26,7 +32,6 @@ static int interrupt_mitigation = 0x0; static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ static int max_interrupt_work = 20; -static int min_pci_latency = 64; static int mtu = 0; /* Maximum number of multicast addresses to filter (vs. rx-all-multicast). The Starfire has a 512 element hash table based on the Ethernet CRC. */ @@ -62,6 +67,9 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define PFX "starfire: " + + #if !defined(__OPTIMIZE__) || !defined(__KERNEL__) #warning You must compile this file with the correct options! #warning See the last lines of the source file. @@ -94,7 +102,6 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(min_pci_latency, "i"); MODULE_PARM(mtu, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(rx_copybreak, "i"); @@ -196,38 +203,33 @@ enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, }; -struct pci_id_info { - const char *name; - u16 vendor_id, device_id, device_id_mask, flags; - int io_size; - struct net_device *(*probe1)(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_idx, int fnd_cnt); -}; - -static struct net_device *starfire_probe1(struct pci_dev *pdev, int pci_bus, - int pci_devfn, long ioaddr, - int irq, int chp_idx, int fnd_cnt); #if 0 #define ADDR_64BITS 1 /* This chip uses 64 bit addresses. */ #endif #define MEM_ADDR_SZ 0x80000 /* And maps in 0.5MB(!). */ -static struct pci_id_info pci_tbl[] = { - { "Adaptec Starfire 6915", - 0x9004, 0x6915, 0xffff, PCI_USES_MASTER, 128, starfire_probe1}, - {0,}, /* 0 terminated list. */ + +enum chipset { + CH_6915 = 0, }; -/* A chip capabilities table, matching the entries in pci_tbl[] above. */ +static struct pci_device_id starfire_pci_tbl[] __devinitdata = { + { 0x9004, 0x6915, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_6915 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, starfire_pci_tbl); + + +/* A chip capabilities table, matching the CH_xxx entries in xxx_pci_tbl[] above. */ enum chip_capability_flags {CanHaveMII=1, }; -struct chip_info { +static struct chip_info { char *chip_name; int io_size; int flags; - void (*media_timer)(unsigned long data); -} static skel_netdrv_tbl[] = { - {"Adaptec Starfire 6915", 128, CanHaveMII, 0, }, +} netdrv_tbl[] = { + { "Adaptec Starfire 6915", 128, CanHaveMII }, }; @@ -322,8 +324,6 @@ struct netdev_private { struct starfire_tx_desc *tx_ring; dma_addr_t rx_ring_dma; dma_addr_t tx_ring_dma; - struct net_device *next_module; /* Link for devices of this type. */ - const char *product_name; /* The addresses of rx/tx-in-place skbuffs. */ struct ring_info rx_info[RX_RING_SIZE]; struct ring_info tx_info[TX_RING_SIZE]; @@ -340,7 +340,6 @@ struct netdev_private { /* Frequently used values: keep some adjacent for cache effect. */ int chip_id; struct pci_dev *pdev; - unsigned char pci_bus, pci_devfn; unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ unsigned int cur_tx, dirty_tx; unsigned int rx_buf_sz; /* Based on MTU+slack. */ @@ -378,107 +377,62 @@ static struct net_device_stats *get_stats(struct net_device *dev); static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int netdev_close(struct net_device *dev); - -/* A list of our installed devices, for removing the driver module. */ -static struct net_device *root_net_dev = NULL; - -/* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well when dynamically adding drivers. So instead we detect just the - cards we know about in slot order. */ - -static int pci_etherdev_probe(struct pci_id_info pci_tbl[]) +static int __devinit starfire_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { - int cards_found = 0; - int pci_index = 0; - unsigned char pci_bus, pci_device_fn; + struct netdev_private *np; + int i, irq, option, chip_id = ent->driver_data; struct net_device *dev; - - for (;pci_index < 0xff; pci_index++) { - struct pci_dev *pdev; - u16 vendor, device, pci_command, new_command; - int chip_idx, irq; - long pciaddr; - long ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) - break; - pdev = pci_find_slot (pci_bus, pci_device_fn); - if (!pdev) continue; - vendor = pdev->vendor; - device = pdev->device; - - for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) - if (vendor == pci_tbl[chip_idx].vendor_id - && (device & pci_tbl[chip_idx].device_id_mask) == - pci_tbl[chip_idx].device_id) - break; - if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ - continue; - - pciaddr = pdev->resource[0].start; -#if defined(ADDR_64BITS) && defined(__alpha__) - pciaddr |= ((long)pdev->base_address[1]) << 32; -#endif - irq = pdev->irq; - - if (debug > 2) - printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", - pci_tbl[chip_idx].name, pciaddr, irq); - - if ((pci_tbl[chip_idx].flags & PCI_USES_IO)) { - if (check_region(pciaddr, pci_tbl[chip_idx].io_size)) - continue; - ioaddr = pciaddr; - } else if ((ioaddr = (long)ioremap(pciaddr&~0xf, MEM_ADDR_SZ)) == 0) { - printk(KERN_INFO "Failed to map PCI address %#lx.\n", - pciaddr); - continue; - } - - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - new_command = pci_command | (pci_tbl[chip_idx].flags & 7); - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled the" - " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", - pci_bus, pci_device_fn, pci_command, new_command); - pci_write_config_word(pdev, PCI_COMMAND, new_command); - } - - dev = pci_tbl[chip_idx].probe1(pdev, pci_bus, pci_device_fn, ioaddr, - irq, chip_idx, cards_found); - - if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { - u8 pci_latency; - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < min_pci_latency) { - printk(KERN_INFO " PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to %d clocks.\n", - pci_latency, min_pci_latency); - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, min_pci_latency); - } - } - cards_found++; + static int card_idx = 0; + static int printed_version = 0; + long ioaddr; + int io_size = netdrv_tbl[chip_id].io_size; + + ioaddr = pci_resource_start (pdev, 0); + if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_MEM) == 0)) { + printk (KERN_ERR PFX "no PCI MEM resources, aborting\n"); + return -ENODEV; + } + + dev = init_etherdev(NULL, sizeof(*np)); + if (!dev) { + printk (KERN_ERR PFX "cannot alloc etherdev, aborting\n"); + return -ENOMEM; + } + + irq = pdev->irq; + + if (request_mem_region (ioaddr, io_size, dev->name) == NULL) { + printk (KERN_ERR PFX "resource 0x%x @ 0x%lx busy, aborting\n", + io_size, ioaddr); + goto err_out_free_netdev; + } + + if (pci_enable_device (pdev)) { + printk (KERN_ERR PFX "cannot enable PCI device, aborting\n"); + goto err_out_free_res; + } + + ioaddr = (long) ioremap (ioaddr, io_size); + if (!ioaddr) { + printk (KERN_ERR PFX "cannot remap 0x%x @ 0x%lx, aborting\n", + io_size, ioaddr); + goto err_out_free_res; } - return cards_found ? 0 : -ENODEV; -} - -static struct net_device * -starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, int irq, int chip_id, int card_idx) -{ - struct netdev_private *np; - int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; - struct net_device *dev = init_etherdev(NULL, 0); - - if (!dev) - return NULL; + pci_set_master (pdev); + + option = card_idx < MAX_UNITS ? options[card_idx] : 0; + card_idx++; + + if (!printed_version) { + printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + printed_version = 1; + } - printk(KERN_INFO "%s: %s at 0x%lx, ", - dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr); + printk (KERN_INFO "%s: %s at 0x%lx, ", + dev->name, netdrv_tbl[chip_id].chip_name, ioaddr); /* Serial EEPROM reads are hidden by the hardware. */ for (i = 0; i < 6; i++) @@ -500,18 +454,13 @@ starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, i dev->base_addr = ioaddr; dev->irq = irq; - /* Make certain the descriptor lists are aligned. */ - np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 15) & ~15); - memset(np, 0, sizeof(*np)); - dev->priv = np; - - np->next_module = root_net_dev; - root_net_dev = dev; + /* private struct aligned and zeroed by init_etherdev */ + np = dev->priv; np->pdev = pdev; - np->pci_bus = pci_bus; - np->pci_devfn = pci_devfn; np->chip_id = chip_id; + + pdev->driver_data = dev; if (dev->mem_start) option = dev->mem_start; @@ -533,7 +482,7 @@ starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, i /* The chip-specific entries in the device structure. */ dev->open = &netdev_open; dev->hard_start_xmit = &start_tx; - dev->tx_timeout = tx_timeout; + dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->stop = &netdev_close; dev->get_stats = &get_stats; @@ -543,7 +492,7 @@ starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, i if (mtu) dev->mtu = mtu; - if (skel_netdrv_tbl[np->chip_id].flags & CanHaveMII) { + if (netdrv_tbl[np->chip_id].flags & CanHaveMII) { int phy, phy_idx = 0; for (phy = 0; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(dev, phy, 1); @@ -558,7 +507,14 @@ starfire_probe1(struct pci_dev *pdev, int pci_bus, int pci_devfn, long ioaddr, i np->mii_cnt = phy_idx; } - return dev; + return 0; + +err_out_free_res: + release_mem_region (ioaddr, io_size); +err_out_free_netdev: + unregister_netdev (dev); + kfree (dev); + return -ENODEV; } @@ -590,10 +546,13 @@ static int netdev_open(struct net_device *dev) long ioaddr = dev->base_addr; int i; + MOD_INC_USE_COUNT; /* Do we need to reset the chip??? */ - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) - return -EAGAIN; + if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { + MOD_DEC_USE_COUNT; + return -EBUSY; + } /* Disable the Rx and Tx, and reset the chip. */ writel(0, ioaddr + GenCtrl); @@ -624,11 +583,10 @@ static int netdev_open(struct net_device *dev) if (np->rx_ring) pci_free_consistent(np->pdev, PAGE_SIZE, np->rx_ring, np->rx_ring_dma); + MOD_DEC_USE_COUNT; return -ENOMEM; } - MOD_INC_USE_COUNT; - init_ring(dev); /* Set the size of the Rx buffers. */ writel((np->rx_buf_sz<<16) | 0xA000, ioaddr + RxDescQCtrl); @@ -691,6 +649,8 @@ static int netdev_open(struct net_device *dev) /* Enable the Rx and Tx units. */ writel(0x000F, ioaddr + GenCtrl); + netif_start_queue(dev); + if (debug > 2) printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); @@ -762,6 +722,7 @@ static void netdev_timer(unsigned long data) add_timer(&np->timer); } + static void tx_timeout(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; @@ -783,15 +744,16 @@ static void tx_timeout(struct net_device *dev) } #endif - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - /* Stop and restart the chip's Tx processes . */ + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + + /* Stop and restart the chip's Tx processes . */ + /* XXX todo */ - /* Trigger an immediate transmit demand. */ + /* Trigger an immediate transmit demand. */ + /* XXX todo */ - netif_wake_queue(dev); - np->stats.tx_errors++; - return; + np->stats.tx_errors++; } @@ -849,8 +811,6 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) struct netdev_private *np = (struct netdev_private *)dev->priv; unsigned entry; - netif_stop_queue(dev); - /* Caution: the write order is important here, set the field with the "ownership" bits last. */ @@ -883,10 +843,10 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) /* Update the producer index. */ writel(++entry, dev->base_addr + TxProducerIdx); - if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1) + if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1) { np->tx_full = 1; - if (! np->tx_full) - netif_start_queue(dev); + netif_stop_queue(dev); + } dev->trans_start = jiffies; if (debug > 4) { @@ -1347,56 +1307,69 @@ static int netdev_close(struct net_device *dev) return 0; } -static int __init starfire_init_module (void) + +static void __devexit starfire_remove_one (struct pci_dev *pdev) { - if (debug) /* Emit version even if no cards detected. */ - printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); -#ifdef CARDBUS - register_driver(ðerdev_ops); - return 0; -#else - if (pci_etherdev_probe(pci_tbl)) { - printk(KERN_INFO " No Starfire adapters detected, driver not loaded.\n"); - return -ENODEV; + struct net_device *dev = pdev->driver_data; + struct netdev_private *np; + + if (!dev) { + printk (KERN_WARNING "bug: removing starfire pci dev without driver\n"); + return; } - return 0; -#endif + + np = dev->priv; + + unregister_netdev(dev); + iounmap((char *)dev->base_addr); + + if (np->tx_done_q) + pci_free_consistent(np->pdev, PAGE_SIZE, + np->tx_done_q, np->tx_done_q_dma); + if (np->rx_done_q) + pci_free_consistent(np->pdev, PAGE_SIZE, + np->rx_done_q, np->rx_done_q_dma); + if (np->tx_ring) + pci_free_consistent(np->pdev, PAGE_SIZE, + np->tx_ring, np->tx_ring_dma); + if (np->rx_ring) + pci_free_consistent(np->pdev, PAGE_SIZE, + np->rx_ring, np->rx_ring_dma); + + kfree(dev); } -static void __exit starfire_cleanup_module (void) + +static struct pci_driver starfire_driver = { + name: "starfire", + probe: starfire_init_one, + remove: starfire_remove_one, + id_table: starfire_pci_tbl, +}; + + +static int __init starfire_init (void) { - struct net_device *next_dev; + int rc; + + MOD_INC_USE_COUNT; + + rc = pci_module_init (&starfire_driver); + + MOD_DEC_USE_COUNT; + + return rc; +} -#ifdef CARDBUS - unregister_driver(ðerdev_ops); -#endif - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (root_net_dev) { - struct netdev_private *np = - (struct netdev_private *)root_net_dev->priv; - next_dev = np->next_module; - unregister_netdev(root_net_dev); - iounmap((char *)root_net_dev->base_addr); - if (np->tx_done_q) - pci_free_consistent(np->pdev, PAGE_SIZE, - np->tx_done_q, np->tx_done_q_dma); - if (np->rx_done_q) - pci_free_consistent(np->pdev, PAGE_SIZE, - np->rx_done_q, np->rx_done_q_dma); - if (np->tx_ring) - pci_free_consistent(np->pdev, PAGE_SIZE, - np->tx_ring, np->tx_ring_dma); - if (np->rx_ring) - pci_free_consistent(np->pdev, PAGE_SIZE, - np->rx_ring, np->rx_ring_dma); - kfree(root_net_dev); - root_net_dev = next_dev; - } +static void __exit starfire_cleanup (void) +{ + pci_unregister_driver (&starfire_driver); } -module_init(starfire_init_module); -module_exit(starfire_cleanup_module); + +module_init(starfire_init); +module_exit(starfire_cleanup); /* diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c index b009a8a4b197..344c01d9e1db 100644 --- a/drivers/net/tulip/eeprom.c +++ b/drivers/net/tulip/eeprom.c @@ -30,31 +30,31 @@ /* Known cards that have old-style EEPROMs. */ static struct eeprom_fixup eeprom_fixups[] __devinitdata = { {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, - 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, - 0x0000, 0x009E, /* 10baseT */ - 0x0004, 0x009E, /* 10baseT-FD */ - 0x0903, 0x006D, /* 100baseTx */ - 0x0905, 0x006D, /* 100baseTx-FD */ }}, + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0903, 0x006D, /* 100baseTx */ + 0x0905, 0x006D, /* 100baseTx-FD */ }}, {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, - 0x0107, 0x8021, /* 100baseFx */ - 0x0108, 0x8021, /* 100baseFx-FD */ - 0x0100, 0x009E, /* 10baseT */ - 0x0104, 0x009E, /* 10baseT-FD */ - 0x0103, 0x006D, /* 100baseTx */ - 0x0105, 0x006D, /* 100baseTx-FD */ }}, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ + 0x0100, 0x009E, /* 10baseT */ + 0x0104, 0x009E, /* 10baseT-FD */ + 0x0103, 0x006D, /* 100baseTx */ + 0x0105, 0x006D, /* 100baseTx-FD */ }}, {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, - 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ - 0x0000, 0x009E, /* 10baseT */ - 0x0004, 0x009E, /* 10baseT-FD */ - 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ - 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ + 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, - 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ - 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ - 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ - 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ - 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ }}, {"NetWinder", 0x00, 0x10, 0x57, /* Default media = MII diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 12b7af968e16..1f0ec9d45e61 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -211,9 +211,12 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (status < 0) break; /* It still has not been Txed */ + /* Check for Rx filter setup frames. */ if (tp->tx_buffers[entry].skb == NULL) { - pci_unmap_single(tp->pdev, + /* test because dummy frames not mapped */ + if (tp->tx_buffers[entry].mapping) + pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping, sizeof(tp->setup_frame), PCI_DMA_TODEVICE); diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 8196723ae18a..d2e90b76042f 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -58,7 +58,6 @@ enum chips { COMET, COMPEX9881, I21145, - X3201_3, }; @@ -334,7 +333,14 @@ extern u8 t21040_csr13[]; extern u16 t21041_csr13[]; extern u16 t21041_csr14[]; extern u16 t21041_csr15[]; -void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6); + + +extern inline void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6) +{ + long ioaddr = tp->base_addr; + + outl (newcsr6, ioaddr + CSR6); +} #endif /* __NET_TULIP_H__ */ diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 6881093ea147..865dff5a3390 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -19,7 +19,7 @@ */ -static const char version[] = "Linux Tulip driver version 0.9.4 (Feb 28, 2000)\n"; +static const char version[] = "Linux Tulip driver version 0.9.4.1 (Mar 18, 2000)\n"; #include #include "tulip.h" @@ -92,9 +92,6 @@ static int csr0 = 0x00A00000 | 0x4800; #define TX_TIMEOUT (4*HZ) -/* Kernel compatibility defines, some common to David Hind's PCMCIA package. - This is only in the support-all-kernels source code. */ - MODULE_AUTHOR("The Linux Kernel Team"); MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); MODULE_PARM(tulip_debug, "i"); @@ -148,9 +145,6 @@ struct tulip_chip_table tulip_tbl[] = { { "Intel DS21145 Tulip", 128, 0x0801fbff, HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_NWAY143, t21142_timer }, - { "Xircom tulip work-alike", 128, 0x0801fbff, - HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143, - t21142_timer }, {0}, }; @@ -169,10 +163,9 @@ static struct pci_device_id tulip_pci_tbl[] __devinitdata = { { 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 }, - { 0x115d, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 }, {0}, }; -MODULE_DEVICE_TABLE(pci,tulip_pci_tbl); +MODULE_DEVICE_TABLE(pci, tulip_pci_tbl); /* A full-duplex map for media types. */ @@ -198,54 +191,6 @@ static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void set_rx_mode(struct net_device *dev); -/* The Xircom cards are picky about when certain bits in CSR6 can be - manipulated. Keith Owens . */ - -void tulip_outl_CSR6 (struct tulip_private *tp, u32 newcsr6) -{ - long ioaddr = tp->base_addr; - const int strict_bits = 0x0060e202; - int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200; - - /* common path */ - if (tp->chip_id != X3201_3) { - outl (newcsr6, ioaddr + CSR6); - return; - } - - newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */ - /* read 0 on the Xircom cards */ - newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */ - currcsr6 = inl (ioaddr + CSR6); - if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) || - ((currcsr6 & ~0x2002) == 0)) - goto out_write; - - /* make sure the transmitter and receiver are stopped first */ - currcsr6 &= ~0x2002; - while (1) { - csr5 = inl (ioaddr + CSR5); - if (csr5 == 0xffffffff) - break; /* cannot read csr5, card removed? */ - csr5_22_20 = csr5 & 0x700000; - csr5_19_17 = csr5 & 0x0e0000; - if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) && - (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000)) - break; /* both are stopped or suspended */ - if (!--attempts) { - printk (KERN_INFO "tulip.c: tulip_outl_CSR6 too many attempts," - "csr5=0x%08x\n", csr5); - goto out_write; - } - outl (currcsr6, ioaddr + CSR6); - udelay (1); - } - -out_write: - /* now it is safe to change csr6 */ - outl (newcsr6, ioaddr + CSR6); -} - static void tulip_up(struct net_device *dev) { @@ -561,7 +506,6 @@ static void tulip_tx_timeout(struct net_device *dev) out: dev->trans_start = jiffies; - netif_start_queue (dev); spin_unlock_irqrestore (&tp->lock, flags); } diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 4c52f055cb0d..a6b695642538 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -27,11 +27,14 @@ LK1.1.1: - Justin Guyett: softnet and locking fixes - Jeff Garzik: use PCI interface - + + LK1.1.2: + - Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions + */ static const char *versionA = -"via-rhine.c:v1.01-LK1.1.1 3/2/2000 Written by Donald Becker\n"; +"via-rhine.c:v1.03a-LK1.1.2 3/19/2000 Written by Donald Becker\n"; static const char *versionB = " http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n"; @@ -74,6 +77,12 @@ static const int multicast_filter_limit = 32; #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error See the last lines of the source file for the proper compile-command. +#endif + #include #include #include @@ -91,15 +100,17 @@ static const int multicast_filter_limit = 32; #include #include -/* This driver was written to use PCI memory space, however some x86 - motherboards only configure I/O space accesses correctly. */ -#if defined(__i386__) && !defined(VIA_USE_MEMORY) -#define VIA_USE_IO -#endif -#if defined(__alpha__) -#define VIA_USE_IO -#endif -#ifdef VIA_USE_IO +/* Condensed bus+endian portability operations. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +/* This driver was written to use PCI memory space, however most versions + of the Rhine only work correctly with I/O space accesses. */ +#if defined(VIA_USE_MEMORY) +#warning Many adapters using the VIA Rhine chip are not configured to work +#warning with PCI memory space accesses. +#else +#define USE_IO #undef readb #undef readw #undef readl @@ -231,57 +242,49 @@ enum pci_flags_bit { }; enum via_rhine_chips { - vt86c100a = 0, - vt3043, + VT86C100A = 0, + VT3043, }; struct via_rhine_chip_info { const char *name; - u16 flags; + u16 pci_flags; int io_size; + int drv_flags; }; +enum chip_capability_flags {CanHaveMII=1, }; + +#if defined(VIA_USE_MEMORY) +#define RHINE_IOTYPE (PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR1) +#else +#define RHINE_IOTYPE (PCI_USES_IO | PCI_USES_MASTER | PCI_ADDR0) +#endif + /* directly indexed by enum via_rhine_chips, above */ static struct via_rhine_chip_info via_rhine_chip_info[] __devinitdata = { - {"VIA VT86C100A Rhine-II", - PCI_USES_MEM | PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER, - 128,}, - {"VIA VT3043 Rhine", - PCI_USES_IO | PCI_USES_MEM | PCI_USES_MASTER, - 128,}, + { "VIA VT86C100A Rhine-II", RHINE_IOTYPE, 128, CanHaveMII }, + { "VIA VT3043 Rhine", RHINE_IOTYPE, 128, CanHaveMII } }; static struct pci_device_id via_rhine_pci_tbl[] __devinitdata = { - {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt86c100a}, - {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt3043}, + {0x1106, 0x6100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT86C100A}, + {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VT3043}, {0,}, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, via_rhine_pci_tbl); - -/* A chip capabilities table, matching the entries in pci_tbl[] above. */ -enum chip_capability_flags {CanHaveMII=1, }; -struct chip_info { - int io_size; - int flags; -} static cap_tbl[] = { - {128, CanHaveMII, }, - {128, CanHaveMII, }, -}; - - -/* Offsets to the device registers. -*/ +/* Offsets to the device registers. */ enum register_offsets { StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, IntrStatus=0x0C, IntrEnable=0x0E, MulticastFilter0=0x10, MulticastFilter1=0x14, RxRingPtr=0x18, TxRingPtr=0x1C, - MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E, + MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E, MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E, }; @@ -295,21 +298,19 @@ enum intr_status_bits { IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, IntrTxAborted=0x2000, IntrLinkChange=0x4000, IntrRxWakeUp=0x8000, - IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260, + IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260, }; /* The Rx and Tx buffer descriptors. */ struct rx_desc { - u16 rx_status; - u16 rx_length; + s32 rx_status; u32 desc_length; u32 addr; u32 next_desc; }; struct tx_desc { - u16 tx_status; - u16 tx_own; + s32 tx_status; u32 desc_length; u32 addr; u32 next_desc; @@ -317,9 +318,11 @@ struct tx_desc { /* Bits in *_desc.status */ enum rx_status_bits { - RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F}; + RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F +}; + enum desc_status_bits { - DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000, + DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000, }; /* Bits in ChipCmd. */ @@ -346,8 +349,8 @@ struct netdev_private { /* Frequently used values: keep some adjacent for cache effect. */ int chip_id; struct rx_desc *rx_head_desc; - unsigned short int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned short int cur_tx, dirty_tx; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int cur_tx, dirty_tx; unsigned int rx_buf_sz; /* Based on MTU+slack. */ u16 chip_cmd; /* Current setting for ChipCmd */ unsigned int tx_full:1; /* The Tx queue is full. */ @@ -393,6 +396,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, static int did_version = 0; long ioaddr; int io_size; + int pci_flags; /* print version once and once only */ if (! did_version++) { @@ -403,13 +407,9 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, card_idx++; option = card_idx < MAX_UNITS ? options[card_idx] : 0; io_size = via_rhine_chip_info[chip_id].io_size; + pci_flags = via_rhine_chip_info[chip_id].pci_flags; - -#ifdef VIA_USE_IO - ioaddr = pci_resource_start (pdev, 0); -#else - ioaddr = pci_resource_start (pdev, 1); -#endif + ioaddr = pci_resource_start (pdev, pci_flags & PCI_ADDR0 ? 0 : 1); if (pci_enable_device (pdev)) { printk (KERN_ERR "unable to init PCI device (card #%d)\n", @@ -417,7 +417,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, goto err_out; } - if (via_rhine_chip_info[chip_id].flags & PCI_USES_MASTER) + if (pci_flags & PCI_USES_MASTER) pci_set_master (pdev); dev = init_etherdev(NULL, sizeof(*np)); @@ -440,7 +440,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, goto err_out_free_pio; } -#ifndef VIA_USE_IO +#ifndef USE_IO ioaddr = (long) ioremap (ioaddr, io_size); if (!ioaddr) { printk (KERN_ERR "ioremap failed for device %s, region 0x%X @ 0x%X\n", @@ -499,7 +499,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, pdev->driver_data = dev; - if (cap_tbl[np->chip_id].flags & CanHaveMII) { + if (via_rhine_chip_info[chip_id].drv_flags & CanHaveMII) { int phy, phy_idx = 0; np->phys[0] = 1; /* Standard for this chip. */ for (phy = 1; phy < 32 && phy_idx < 4; phy++) { @@ -518,12 +518,12 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, return 0; -#ifndef VIA_USE_IO +#ifndef USE_IO /* note this is ifdef'd because the ioremap is ifdef'd... * so additional exit conditions above this must move * release_mem_region outside of the ifdef */ err_out_free_mmio: - release_mem_region(pci_resource_start (pdev, 1), io_size, dev->name)); + release_mem_region(pci_resource_start (pdev, 1), io_size)); #endif err_out_free_pio: release_region(pci_resource_start (pdev, 0), io_size); @@ -557,9 +557,12 @@ static int mdio_read(struct net_device *dev, int phy_id, int regnum) static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value) { + struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int boguscnt = 1024; + if (phy_id == np->phys[0] && regnum == 4) + np->advertising = value; /* Wait for a previous command to complete. */ while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) ; @@ -599,7 +602,7 @@ static int via_rhine_open(struct net_device *dev) writeb(dev->dev_addr[i], ioaddr + StationAddr + i); /* Initialize other registers. */ - writew(0x0006, ioaddr + PCIConfig); /* Tune configuration??? */ + writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */ /* Configure the FIFO thresholds. */ writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */ np->tx_thresh = 0x20; @@ -666,6 +669,7 @@ static void via_rhine_check_duplex(struct net_device *dev) } } + static void via_rhine_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; @@ -677,12 +681,14 @@ static void via_rhine_timer(unsigned long data) printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", dev->name, readw(ioaddr + IntrStatus)); } + via_rhine_check_duplex(dev); np->timer.expires = RUN_AT(next_tick); add_timer(&np->timer); } + static void via_rhine_tx_timeout (struct net_device *dev) { struct netdev_private *np = (struct netdev_private *) dev->priv; @@ -693,16 +699,17 @@ static void via_rhine_tx_timeout (struct net_device *dev) dev->name, readw (ioaddr + IntrStatus), mdio_read (dev, np->phys[0], 1)); - /* Perhaps we should reinitialize the hardware here. */ + /* XXX Perhaps we should reinitialize the hardware here. */ dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + /* XXX to do */ /* Trigger an immediate transmit demand. */ + /* XXX to do */ dev->trans_start = jiffies; np->stats.tx_errors++; - - netif_start_queue (dev); } @@ -720,13 +727,12 @@ static void via_rhine_init_ring(struct net_device *dev) for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].rx_status = 0; - np->rx_ring[i].rx_length = 0; - np->rx_ring[i].desc_length = np->rx_buf_sz; - np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); + np->rx_ring[i].desc_length = cpu_to_le32(np->rx_buf_sz); + np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); np->rx_skbuff[i] = 0; } /* Mark the last entry as wrapping the ring. */ - np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); + np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); /* Fill in the Rx buffers. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -735,19 +741,18 @@ static void via_rhine_init_ring(struct net_device *dev) if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ - np->rx_ring[i].addr = virt_to_bus(skb->tail); - np->rx_ring[i].rx_status = 0; - np->rx_ring[i].rx_length = DescOwn; + np->rx_ring[i].addr = virt_to_le32desc(skb->tail); + np->rx_ring[i].rx_status = cpu_to_le32(DescOwn); } for (i = 0; i < TX_RING_SIZE; i++) { np->tx_skbuff[i] = 0; - np->tx_ring[i].tx_own = 0; + np->tx_ring[i].tx_status = 0; np->tx_ring[i].desc_length = 0x00e08000; - np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]); + np->tx_ring[i].next_desc = virt_to_le32desc(&np->tx_ring[i+1]); np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL); } - np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]); + np->tx_ring[i-1].next_desc = virt_to_le32desc(&np->tx_ring[0]); return; } @@ -771,16 +776,18 @@ static int via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev) if ((long)skb->data & 3) { /* Must use alignment buffer. */ if (np->tx_buf[entry] == NULL && - (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) + (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) { + spin_unlock_irqrestore (&np->lock, flags); return 1; + } memcpy(np->tx_buf[entry], skb->data, skb->len); - np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]); + np->tx_ring[entry].addr = virt_to_le32desc(np->tx_buf[entry]); } else - np->tx_ring[entry].addr = virt_to_bus(skb->data); + np->tx_ring[entry].addr = virt_to_le32desc(skb->data); - np->tx_ring[entry].desc_length = 0x00E08000 | - (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); - np->tx_ring[entry].tx_own = DescOwn; + np->tx_ring[entry].desc_length = + cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); + np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn); np->cur_tx++; @@ -848,7 +855,7 @@ static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs } /* This routine is logically part of the interrupt handler, but isolated - for clarity and better register allocation. */ + for clarity. */ static void via_rhine_tx(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; @@ -858,15 +865,15 @@ static void via_rhine_tx(struct net_device *dev) /* if tx_full is set, they're all dirty, not clean */ while (np->dirty_tx != np->cur_tx) { - if (np->tx_ring[entry].tx_own) /* transmit request pending */ + txstatus = le32_to_cpu(np->tx_ring[entry].tx_status); + if (txstatus & DescOwn) break; - txstatus = np->tx_ring[entry].tx_status; if (debug > 6) - printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n", + printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n", entry, txstatus); if (txstatus & 0x8000) { if (debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n", + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", dev->name, txstatus); np->stats.tx_errors++; if (txstatus & 0x0400) np->stats.tx_carrier_errors++; @@ -879,10 +886,10 @@ static void via_rhine_tx(struct net_device *dev) np->stats.collisions += (txstatus >> 3) & 15; np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff; np->stats.tx_packets++; - } - /* Free the original skb. */ - dev_kfree_skb_irq(np->tx_skbuff[entry]); - np->tx_skbuff[entry] = NULL; + } + /* Free the original skb. */ + dev_kfree_skb_irq(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = NULL; entry = (++np->dirty_tx) % TX_RING_SIZE; } if ((np->cur_tx - np->dirty_tx) <= TX_RING_SIZE/2) @@ -896,29 +903,29 @@ static void via_rhine_tx(struct net_device *dev) static void via_rhine_rx(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; - int entry = (np->dirty_rx = np->cur_rx) % RX_RING_SIZE; - int boguscnt = RX_RING_SIZE; + int entry = np->cur_rx % RX_RING_SIZE; + int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; if (debug > 4) { - printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %4.4x.\n", - entry, np->rx_head_desc->rx_length); + printk(KERN_DEBUG " In via_rhine_rx(), entry %d status %8.8x.\n", + entry, np->rx_head_desc->rx_status); } /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while ( ! (np->rx_head_desc->rx_length & DescOwn)) { + while ( ! (np->rx_head_desc->rx_status & cpu_to_le32(DescOwn))) { struct rx_desc *desc = np->rx_head_desc; - int data_size = desc->rx_length; - u16 desc_status = desc->rx_status; + u32 desc_status = le32_to_cpu(desc->rx_status); + int data_size = desc_status >> 16; if (debug > 4) - printk(KERN_DEBUG " via_rhine_rx() status is %4.4x.\n", + printk(KERN_DEBUG " via_rhine_rx() status is %8.8x.\n", desc_status); if (--boguscnt < 0) break; if ( (desc_status & (RxWholePkt | RxErr)) != RxWholePkt) { if ((desc_status & RxWholePkt) != RxWholePkt) { printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, entry %#x length %d status %4.4x!\n", + "multiple buffers, entry %#x length %d status %8.8x!\n", dev->name, entry, data_size, desc_status); printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", dev->name, np->rx_head_desc, @@ -958,9 +965,9 @@ static void via_rhine_rx(struct net_device *dev) np->rx_skbuff[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); - np->stats.rx_bytes+=skb->len; netif_rx(skb); dev->last_rx = jiffies; + np->stats.rx_bytes += skb->len; np->stats.rx_packets++; } entry = (++np->cur_rx) % RX_RING_SIZE; @@ -968,19 +975,18 @@ static void via_rhine_rx(struct net_device *dev) } /* Refill the Rx ring buffers. */ - while (np->dirty_rx != np->cur_rx) { + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { struct sk_buff *skb; - entry = np->dirty_rx++ % RX_RING_SIZE; + entry = np->dirty_rx % RX_RING_SIZE; if (np->rx_skbuff[entry] == NULL) { skb = dev_alloc_skb(np->rx_buf_sz); np->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ skb->dev = dev; /* Mark as being used by this device. */ - np->rx_ring[entry].addr = virt_to_bus(skb->tail); + np->rx_ring[entry].addr = virt_to_le32desc(skb->tail); } - np->rx_ring[entry].rx_status = 0; - np->rx_ring[entry].rx_length = DescOwn; + np->rx_ring[entry].rx_status = cpu_to_le32(DescOwn); } /* Pre-emptively restart Rx engine. */ @@ -1020,7 +1026,8 @@ static void via_rhine_error(struct net_device *dev, int intr_status) printk(KERN_INFO "%s: Transmitter underrun, increasing Tx " "threshold setting to %2.2x.\n", dev->name, np->tx_thresh); } - if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug > 1) { + if ((intr_status & ~( IntrLinkChange | IntrStatsMax | + IntrTxAbort | IntrTxAborted)) && debug > 1) { printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); /* Recovery for other fault sources not known. */ @@ -1076,6 +1083,8 @@ static void via_rhine_set_rx_mode(struct net_device *dev) } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ + writel(0xffffffff, ioaddr + MulticastFilter0); + writel(0xffffffff, ioaddr + MulticastFilter1); rx_mode = 0x0C; } else { struct dev_mc_list *mclist; @@ -1083,12 +1092,11 @@ static void via_rhine_set_rx_mode(struct net_device *dev) memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { - set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, - mc_filter); + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } writel(mc_filter[0], ioaddr + MulticastFilter0); writel(mc_filter[1], ioaddr + MulticastFilter1); - rx_mode = 0x08; + rx_mode = 0x0C; } writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig); } @@ -1138,7 +1146,7 @@ static int via_rhine_close(struct net_device *dev) /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].rx_length = 0; + np->rx_ring[i].rx_status = 0; np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ if (np->rx_skbuff[i]) { dev_kfree_skb(np->rx_skbuff[i]); @@ -1169,7 +1177,7 @@ static void __devexit via_rhine_remove_one (struct pci_dev *pdev) release_mem_region(pci_resource_start (pdev, 1), via_rhine_chip_info[np->chip_id].io_size); -#ifndef VIA_USE_IO +#ifndef USE_IO iounmap((char *)(dev->base_addr)); #endif diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in index e0a6f5e8ff34..4ffe0730fccf 100644 --- a/drivers/net/wan/Config.in +++ b/drivers/net/wan/Config.in @@ -55,9 +55,11 @@ if [ "$CONFIG_WAN" = "y" ]; then if [ "$CONFIG_VENDOR_SANGOMA" != "n" ]; then int ' Maximum number of cards' CONFIG_WANPIPE_CARDS 1 bool ' WANPIPE Cisco HDLC support' CONFIG_WANPIPE_CHDLC - bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR + if [ "$CONFIG_OBSOLETE" = "y" ]; then + bool ' WANPIPE Frame Relay support' CONFIG_WANPIPE_FR + bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 + fi bool ' WANPIPE PPP support' CONFIG_WANPIPE_PPP - bool ' WANPIPE X.25 support' CONFIG_WANPIPE_X25 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' Cyclom 2X(tm) cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_ROUTER_DRIVERS diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c index 0a0e30775c40..ae866148bb7e 100644 --- a/drivers/sound/ac97_codec.c +++ b/drivers/sound/ac97_codec.c @@ -25,7 +25,7 @@ * v0.3 Feb 22 2000 Ollie Lho * bug fix for record mask setting * v0.2 Feb 10 2000 Ollie Lho - * add ac97_read_proc for /proc/driver/vnedor/ac97 + * add ac97_read_proc for /proc/driver/{vendor}/ac97 * v0.1 Jan 14 2000 Ollie Lho * Isolated from trident.c to support multiple ac97 codec */ diff --git a/drivers/sound/sb.h b/drivers/sound/sb.h index 282e644a36bd..6e24d6e8975e 100644 --- a/drivers/sound/sb.h +++ b/drivers/sound/sb.h @@ -151,6 +151,7 @@ int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio); int sb_dsp_init (struct address_info *hw_config); void sb_dsp_unload(struct address_info *hw_config, int sbmpu); int sb_mixer_init(sb_devc *devc); +void sb_mixer_unload(sb_devc *devc); void sb_mixer_set_stereo (sb_devc *devc, int mode); void smw_mixer_init(sb_devc *devc); void sb_dsp_midi_init (sb_devc *devc); diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index 5fb40f46d3ff..80c282a5d5dd 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -908,13 +908,10 @@ void sb_dsp_unload(struct address_info *hw_config, int sbmpu) } if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) { - extern int sbmixnum; - if (devc->irq > 0) free_irq(devc->irq, devc); - sound_unload_mixerdev(devc->my_mixerdev); - sbmixnum--; + sb_mixer_unload(devc); /* We don't have to do this bit any more the UART401 is its own master -- Krzysztof Halasa */ /* But we have to do it, if UART401 is not detected */ diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c index f441be0aac4a..a955db7e3890 100644 --- a/drivers/sound/sb_mixer.c +++ b/drivers/sound/sb_mixer.c @@ -740,3 +740,9 @@ int sb_mixer_init(sb_devc * devc) sb_mixer_reset(devc); return 1; } + +void sb_mixer_unload(sb_devc *devc) +{ + sound_unload_mixerdev(devc->my_mixerdev); + sbmixnum--; +} diff --git a/drivers/usb/serial/Makefile-keyspan_pda_fw b/drivers/usb/serial/Makefile-keyspan_pda_fw index 219708e462f4..c20baf728011 100644 --- a/drivers/usb/serial/Makefile-keyspan_pda_fw +++ b/drivers/usb/serial/Makefile-keyspan_pda_fw @@ -5,7 +5,7 @@ all: keyspan_pda_fw.h -%.asm: %.s +%.asm: %.S gcc -x assembler-with-cpp -P -E -o $@ $< %.hex: %.asm diff --git a/drivers/usb/serial/keyspan_pda.S b/drivers/usb/serial/keyspan_pda.S new file mode 100644 index 000000000000..10c99eadd36f --- /dev/null +++ b/drivers/usb/serial/keyspan_pda.S @@ -0,0 +1,1124 @@ +/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $ + * + * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on + * the EzUSB microcontroller. + * + * (C) Copyright 2000 Brian Warner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the + * company. + * + * This serial adapter is basically an EzUSB chip and an RS-232 line driver + * in a little widget that has a DB-9 on one end and a USB plug on the other. + * It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2 + * as a baud-rate generator. The wiring is: + * PC0/RxD0 <- rxd (DB9 pin 2) PC4 <- dsr pin 6 + * PC1/TxD0 -> txd pin 3 PC5 <- ri pin 9 + * PC2 -> rts pin 7 PC6 <- dcd pin 1 + * PC3 <- cts pin 8 PC7 -> dtr pin 4 + * PB1 -> line driver standby + * + * The EzUSB register constants below come from their excellent documentation + * and sample code (which used to be available at www.anchorchips.com, but + * that has now been absorbed into Cypress' site and the CD-ROM contents + * don't appear to be available online anymore). If we get multiple + * EzUSB-based drivers into the kernel, it might be useful to pull them out + * into a separate .h file. + * + * THEORY OF OPERATION: + * + * There are two 256-byte ring buffers, one for tx, one for rx. + * + * EP2out is pure tx data. When it appears, the data is copied into the tx + * ring and serial transmission is started if it wasn't already running. The + * "tx buffer empty" interrupt may kick off another character if the ring + * still has data. If the host is tx-blocked because the ring filled up, + * it will request a "tx unthrottle" interrupt. If sending a serial character + * empties the ring below the desired threshold, we set a bit that will send + * up the tx unthrottle message as soon as the rx buffer becomes free. + * + * EP2in (interrupt) is used to send both rx chars and rx status messages + * (only "tx unthrottle" at this time) back up to the host. The first byte + * of the rx message indicates data (0) or status msg (1). Status messages + * are sent before any data. + * + * Incoming serial characters are put into the rx ring by the serial + * interrupt, and the EP2in buffer sent if it wasn't already in transit. + * When the EP2in buffer returns, the interrupt prompts us to send more + * rx chars (or status messages) if they are pending. + * + * Device control happens through "vendor specific" control messages on EP0. + * All messages are destined for the "Interface" (with the index always 0, + * so that if their two-port device might someday use similar firmware, we + * can use index=1 to refer to the second port). The messages defined are: + * + * bRequest = 0 : set baud/bits/parity + * 1 : unused + * 2 : reserved for setting HW flow control (CTSRTS) + * 3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc) + * 4 : set break (on/off) + * 5 : reserved for requesting interrupts on pin state change + * 6 : query buffer room or chars in tx buffer + * 7 : request tx unthrottle interrupt + * + * The host-side driver is set to recognize the device ID values stashed in + * serial EEPROM (0x06cd, 0x0103), program this firmware into place, then + * start it running. This firmware will use EzUSB's "renumeration" trick by + * simulating a bus disconnect, then reconnect with a different device ID + * (encoded in the desc_device descriptor below). The host driver then + * recognizes the new device ID and glues it to the real serial driver code. + * + * USEFUL DOCS: + * EzUSB Technical Reference Manual: + * 8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is + * basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports + * use totally different registers! + * USB 1.1 spec: www.usb.org + * + * HOW TO BUILD: + * gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s + * as31 -l keyspan_pda.asm + * mv keyspan_pda.obj keyspan_pda.hex + * perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h + * Get as31 from , and hack on it + * a bit to make it build. + * + * THANKS: + * Greg Kroah-Hartman, for coordinating the whole usb-serial thing. + * AnchorChips, for making such an incredibly useful little microcontroller. + * KeySpan, for making a handy, cheap ($40) widget that was so easy to take + * apart and trace with an ohmmeter. + * + * TODO: + * lots. grep for TODO. Interrupt safety needs stress-testing. Better flow + * control. Interrupting host upon change in DCD, etc, counting transitions. + * Need to find a safe device id to use (the one used by the Keyspan firmware + * under Windows would be ideal.. can anyone figure out what it is?). Parity. + * More baud rates. Oh, and the string-descriptor-length silicon bug + * workaround should be implemented, but I'm lazy, and the consequence is + * that the device name strings that show up in your kernel log will have + * lots of trailing binary garbage in them (appears as ????). Device strings + * should be made more accurate. + * + * Questions, bugs, patches to Brian. + * + * -Brian Warner + * + */ + +#define HIGH(x) (((x) & 0xff00) / 256) +#define LOW(x) ((x) & 0xff) + +#define dpl1 0x84 +#define dph1 0x85 +#define dps 0x86 + +;;; our bit assignments +#define TX_RUNNING 0 +#define DO_TX_UNTHROTTLE 1 + + ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60 +#define STACK #0x60-1 + +#define EXIF 0x91 +#define EIE 0xe8 + .flag EUSB, EIE.0 + .flag ES0, IE.4 + +#define EP0CS #0x7fb4 +#define EP0STALLbit #0x01 +#define IN0BUF #0x7f00 +#define IN0BC #0x7fb5 +#define OUT0BUF #0x7ec0 +#define OUT0BC #0x7fc5 +#define IN2BUF #0x7e00 +#define IN2BC #0x7fb9 +#define IN2CS #0x7fb8 +#define OUT2BC #0x7fc9 +#define OUT2CS #0x7fc8 +#define OUT2BUF #0x7dc0 +#define IN4BUF #0x7d00 +#define IN4BC #0x7fbd +#define IN4CS #0x7fbc +#define OEB #0x7f9d +#define OUTB #0x7f97 +#define OEC #0x7f9e +#define OUTC #0x7f98 +#define PINSC #0x7f9b +#define PORTCCFG #0x7f95 +#define IN07IRQ #0x7fa9 +#define OUT07IRQ #0x7faa +#define IN07IEN #0x7fac +#define OUT07IEN #0x7fad +#define USBIRQ #0x7fab +#define USBIEN #0x7fae +#define USBBAV #0x7faf +#define USBCS #0x7fd6 +#define SUDPTRH #0x7fd4 +#define SUDPTRL #0x7fd5 +#define SETUPDAT #0x7fe8 + + ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91) + + .org 0 + ljmp start + ;; interrupt vectors + .org 23H + ljmp serial_int + .byte 0 + + .org 43H + ljmp USB_Jump_Table + .byte 0 ; filled in by the USB core + +;;; local variables. These are not initialized properly: do it by hand. + .org 30H +rx_ring_in: .byte 0 +rx_ring_out: .byte 0 +tx_ring_in: .byte 0 +tx_ring_out: .byte 0 +tx_unthrottle_threshold: .byte 0 + + .org 0x100H ; wants to be on a page boundary +USB_Jump_Table: + ljmp ISR_Sudav ; Setup Data Available + .byte 0 + ljmp 0 ; Start of Frame + .byte 0 + ljmp 0 ; Setup Data Loading + .byte 0 + ljmp 0 ; Global Suspend + .byte 0 + ljmp 0 ; USB Reset + .byte 0 + ljmp 0 ; Reserved + .byte 0 + ljmp 0 ; End Point 0 In + .byte 0 + ljmp 0 ; End Point 0 Out + .byte 0 + ljmp 0 ; End Point 1 In + .byte 0 + ljmp 0 ; End Point 1 Out + .byte 0 + ljmp ISR_Ep2in + .byte 0 + ljmp ISR_Ep2out + .byte 0 + + + .org 0x200 + +start: mov SP,STACK-1 ; set stack + ;; clear local variables + clr a + mov tx_ring_in, a + mov tx_ring_out, a + mov rx_ring_in, a + mov rx_ring_out, a + mov tx_unthrottle_threshold, a + clr TX_RUNNING + clr DO_TX_UNTHROTTLE + + ;; clear fifo with "fe" + mov r1, 0 + mov a, #0xfe + mov dptr, #tx_ring +clear_tx_ring_loop: + movx @dptr, a + inc dptr + djnz r1, clear_tx_ring_loop + + mov a, #0xfd + mov dptr, #rx_ring +clear_rx_ring_loop: + movx @dptr, a + inc dptr + djnz r1, clear_rx_ring_loop + +;;; turn on the RS-232 driver chip (bring the STANDBY pin low) + ;; set OEB.1 + mov a, #02H + mov dptr,OEB + movx @dptr,a + ;; clear PB1 + mov a, #00H + mov dptr,OUTB + movx @dptr,a + ;; set OEC.[127] + mov a, #0x86 + mov dptr,OEC + movx @dptr,a + ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port + mov dptr, PORTCCFG + mov a, #0x03 + movx @dptr, a + + ;; set up interrupts, autovectoring + mov dptr, USBBAV + movx a,@dptr + setb acc.0 ; AVEN bit to 0 + movx @dptr, a + + mov a,#0x01 ; enable SUDAV: setup data available (for ep0) + mov dptr, USBIRQ + movx @dptr, a ; clear SUDAVI + mov dptr, USBIEN + movx @dptr, a + + mov dptr, IN07IEN + mov a,#0x04 ; enable IN2 int + movx @dptr, a + + mov dptr, OUT07IEN + mov a,#0x04 ; enable OUT2 int + movx @dptr, a + mov dptr, OUT2BC + movx @dptr, a ; arm OUT2 + + mov a, #0x84 ; turn on RTS, DTR + mov dptr,OUTC + movx @dptr, a + ;; setup the serial port. 9600 8N1. + mov a,#01010011 ; mode 1, enable rx, clear int + mov SCON, a + ;; using timer2, in 16-bit baud-rate-generator mode + ;; (xtal 12MHz, internal fosc 24MHz) + ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud) + ;; 57600: 0xFFF2.F, say 0xFFF3 + ;; 9600: 0xFFB1.E, say 0xFFB2 + ;; 300: 0xF63C +#define BAUD 9600 +#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate)) +#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate)) +#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate)) + + mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later) + mov r3, #5 + acall set_baud + setb TR2 + mov SCON, #050h + +#if 0 + mov r1, #0x40 + mov a, #0x41 +send: + mov SBUF, a + inc a + anl a, #0x3F + orl a, #0x40 +; xrl a, #0x02 +wait1: + jnb TI, wait1 + clr TI + djnz r1, send +;done: sjmp done + +#endif + + setb EUSB + setb EA + setb ES0 + ;acall dump_stat + + ;; hey, what say we RENUMERATE! (TRM p.62) + mov a, #0 + mov dps, a + mov dptr, USBCS + mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1 + movx @dptr, a + ;; now presence pin is floating, simulating disconnect. wait 0.5s + mov r1, #46 +renum_wait1: + mov r2, #0 +renum_wait2: + mov r3, #0 +renum_wait3: + djnz r3, renum_wait3 + djnz r2, renum_wait2 + djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks + mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1 + movx @dptr, a + ;; we are back online. the host device will now re-query us + + +main: sjmp main + + + +ISR_Sudav: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, USBIRQ ; clear USB int + mov a,#01h + movx @dptr,a + + ;; get request type + mov dptr, SETUPDAT + movx a, @dptr + mov r1, a ; r1 = bmRequestType + inc dptr + movx a, @dptr + mov r2, a ; r2 = bRequest + inc dptr + movx a, @dptr + mov r3, a ; r3 = wValueL + inc dptr + movx a, @dptr + mov r4, a ; r4 = wValueH + + ;; main switch on bmRequest.type: standard or vendor + mov a, r1 + anl a, #0x60 + cjne a, #0x00, setup_bmreq_type_not_standard + ;; standard request: now main switch is on bRequest + ljmp setup_bmreq_is_standard + +setup_bmreq_type_not_standard: + ;; a still has bmreq&0x60 + cjne a, #0x40, setup_bmreq_type_not_vendor + ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones + ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1 + cjne r2, #0x00, setup_ctrl_not_00 + ;; 00 is set baud, wValue[0] has baud rate index + lcall set_baud ; index in r3, carry set if error + jc setup_bmreq_type_not_standard__do_stall + ljmp setup_done_ack +setup_bmreq_type_not_standard__do_stall: + ljmp setup_stall +setup_ctrl_not_00: + cjne r2, #0x01, setup_ctrl_not_01 + ;; 01 is reserved for set bits (parity). TODO + ljmp setup_stall +setup_ctrl_not_01: + cjne r2, #0x02, setup_ctrl_not_02 + ;; 02 is set HW flow control. TODO + ljmp setup_stall +setup_ctrl_not_02: + cjne r2, #0x03, setup_ctrl_not_03 + ;; 03 is control pins (RTS, DTR). + ljmp control_pins ; will jump to setup_done_ack, + ; or setup_return_one_byte +setup_ctrl_not_03: + cjne r2, #0x04, setup_ctrl_not_04 + ;; 04 is send break (really "turn break on/off"). TODO + cjne r3, #0x00, setup_ctrl_do_break_on + ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port + mov dptr, PORTCCFG + movx a, @dptr + orl a, #0x02 + movx @dptr, a + ljmp setup_done_ack +setup_ctrl_do_break_on: + ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low) + mov dptr, OUTC + movx a, @dptr + anl a, #0xfd ; ~0x02 + movx @dptr, a + mov dptr, PORTCCFG + movx a, @dptr + anl a, #0xfd ; ~0x02 + movx @dptr, a + ljmp setup_done_ack +setup_ctrl_not_04: + cjne r2, #0x05, setup_ctrl_not_05 + ;; 05 is set desired interrupt bitmap. TODO + ljmp setup_stall +setup_ctrl_not_05: + cjne r2, #0x06, setup_ctrl_not_06 + ;; 06 is query room + cjne r3, #0x00, setup_ctrl_06_not_00 + ;; 06, wValue[0]=0 is query write_room + mov a, tx_ring_out + setb c + subb a, tx_ring_in ; out-1-in = 255 - (in-out) + ljmp setup_return_one_byte +setup_ctrl_06_not_00: + cjne r3, #0x01, setup_ctrl_06_not_01 + ;; 06, wValue[0]=1 is query chars_in_buffer + mov a, tx_ring_in + clr c + subb a, tx_ring_out ; in-out + ljmp setup_return_one_byte +setup_ctrl_06_not_01: + ljmp setup_stall +setup_ctrl_not_06: + cjne r2, #0x07, setup_ctrl_not_07 + ;; 07 is request tx unthrottle interrupt + mov tx_unthrottle_threshold, r3; wValue[0] is threshold value + ljmp setup_done_ack +setup_ctrl_not_07: + ljmp setup_stall + +setup_bmreq_type_not_vendor: + ljmp setup_stall + + +setup_bmreq_is_standard: + cjne r2, #0x00, setup_breq_not_00 + ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int) + cjne r1, #0x80, setup_Get_Status_not_device + ;; Get_Status(device) + ;; are we self-powered? no. can we do remote wakeup? no + ;; so return two zero bytes. This is reusable +setup_return_two_zero_bytes: + mov dptr, IN0BUF + clr a + movx @dptr, a + inc dptr + movx @dptr, a + mov dptr, IN0BC + mov a, #2 + movx @dptr, a + ljmp setup_done_ack +setup_Get_Status_not_device: + cjne r1, #0x82, setup_Get_Status_not_endpoint + ;; Get_Status(endpoint) + ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0 + ;; for now: cheat. TODO + sjmp setup_return_two_zero_bytes +setup_Get_Status_not_endpoint: + cjne r1, #0x81, setup_Get_Status_not_interface + ;; Get_Status(interface): return two zeros + sjmp setup_return_two_zero_bytes +setup_Get_Status_not_interface: + ljmp setup_stall + +setup_breq_not_00: + cjne r2, #0x01, setup_breq_not_01 + ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup) + cjne r3, #0x00, setup_Clear_Feature_not_stall + ;; Clear_Feature(stall). should clear a stall bit. TODO + ljmp setup_stall +setup_Clear_Feature_not_stall: + cjne r3, #0x01, setup_Clear_Feature_not_rwake + ;; Clear_Feature(remote wakeup). ignored. + ljmp setup_done_ack +setup_Clear_Feature_not_rwake: + ljmp setup_stall + +setup_breq_not_01: + cjne r2, #0x03, setup_breq_not_03 + ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup) + cjne r3, #0x00, setup_Set_Feature_not_stall + ;; Set_Feature(stall). Should set a stall bit. TODO + ljmp setup_stall +setup_Set_Feature_not_stall: + cjne r3, #0x01, setup_Set_Feature_not_rwake + ;; Set_Feature(remote wakeup). ignored. + ljmp setup_done_ack +setup_Set_Feature_not_rwake: + ljmp setup_stall + +setup_breq_not_03: + cjne r2, #0x06, setup_breq_not_06 + ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n]) + cjne r4, #0x01, setup_Get_Descriptor_not_device + ;; Get_Descriptor(device) + mov dptr, SUDPTRH + mov a, #HIGH(desc_device) + movx @dptr, a + mov dptr, SUDPTRL + mov a, #LOW(desc_device) + movx @dptr, a + ljmp setup_done_ack +setup_Get_Descriptor_not_device: + cjne r4, #0x02, setup_Get_Descriptor_not_config + ;; Get_Descriptor(config[n]) + cjne r3, #0x00, setup_stall; only handle n==0 + ;; Get_Descriptor(config[0]) + mov dptr, SUDPTRH + mov a, #HIGH(desc_config1) + movx @dptr, a + mov dptr, SUDPTRL + mov a, #LOW(desc_config1) + movx @dptr, a + ljmp setup_done_ack +setup_Get_Descriptor_not_config: + cjne r4, #0x03, setup_Get_Descriptor_not_string + ;; Get_Descriptor(string[wValueL]) + ;; if (wValueL >= maxstrings) stall + mov a, #((desc_strings_end-desc_strings)/2) + clr c + subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall + jc setup_stall + jz setup_stall + mov a, r3 + add a, r3 ; a = 2*wValueL + mov dptr, #desc_strings + add a, dpl + mov dpl, a + mov a, #0 + addc a, dph + mov dph, a ; dph = desc_strings[a]. big endian! (handy) + ;; it looks like my adapter uses a revision of the EZUSB that + ;; contains "rev D errata number 8", as hinted in the EzUSB example + ;; code. I cannot find an actual errata description on the Cypress + ;; web site, but from the example code it looks like this bug causes + ;; the length of string descriptors to be read incorrectly, possibly + ;; sending back more characters than the descriptor has. The workaround + ;; is to manually send out all of the data. The consequence of not + ;; using the workaround is that the strings gathered by the kernel + ;; driver are too long and are filled with trailing garbage (including + ;; leftover strings). Writing this out by hand is a nuisance, so for + ;; now I will just live with the bug. + movx a, @dptr + mov r1, a + inc dptr + movx a, @dptr + mov r2, a + mov dptr, SUDPTRH + mov a, r1 + movx @dptr, a + mov dptr, SUDPTRL + mov a, r2 + movx @dptr, a + ;; done + ljmp setup_done_ack + +setup_Get_Descriptor_not_string: + ljmp setup_stall + +setup_breq_not_06: + cjne r2, #0x08, setup_breq_not_08 + ;; Get_Configuration. always 1. return one byte. + ;; this is reusable + mov a, #1 +setup_return_one_byte: + mov dptr, IN0BUF + movx @dptr, a + mov a, #1 + mov dptr, IN0BC + movx @dptr, a + ljmp setup_done_ack +setup_breq_not_08: + cjne r2, #0x09, setup_breq_not_09 + ;; 09: Set_Configuration. ignored. + ljmp setup_done_ack +setup_breq_not_09: + cjne r2, #0x0a, setup_breq_not_0a + ;; 0a: Get_Interface. get the current altsetting for int[wIndexL] + ;; since we only have one interface, ignore wIndexL, return a 0 + mov a, #0 + ljmp setup_return_one_byte +setup_breq_not_0a: + cjne r2, #0x0b, setup_breq_not_0b + ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored + ljmp setup_done_ack +setup_breq_not_0b: + ljmp setup_stall + + +setup_done_ack: + ;; now clear HSNAK + mov dptr, EP0CS + mov a, #0x02 + movx @dptr, a + sjmp setup_done +setup_stall: + ;; unhandled. STALL + ;EP0CS |= bmEPSTALL + mov dptr, EP0CS + movx a, @dptr + orl a, EP0STALLbit + movx @dptr, a + sjmp setup_done + +setup_done: + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +;;; ============================================================== + +set_baud: ; baud index in r3 + ;; verify a < 10 + mov a, r3 + jb ACC.7, set_baud__badbaud + clr c + subb a, #10 + jnc set_baud__badbaud + mov a, r3 + rl a ; a = index*2 + add a, #LOW(baud_table) + mov dpl, a + mov a, #HIGH(baud_table) + addc a, #0 + mov dph, a + ;; TODO: shut down xmit/receive + ;; TODO: wait for current xmit char to leave + ;; TODO: shut down timer to avoid partial-char glitch + movx a,@dptr ; BAUD_HIGH + mov RCAP2H, a + mov TH2, a + inc dptr + movx a,@dptr ; BAUD_LOW + mov RCAP2L, a + mov TL2, a + ;; TODO: restart xmit/receive + ;; TODO: reenable interrupts, resume tx if pending + clr c ; c=0: success + ret +set_baud__badbaud: + setb c ; c=1: failure + ret + +;;; ================================================== +control_pins: + cjne r1, #0x41, control_pins_in +control_pins_out: + mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS + xrl a, #0xff ; 1 means active, 0V, +12V ? + anl a, #0x84 + mov r3, a + mov dptr, OUTC + movx a, @dptr ; only change bits 7 and 2 + anl a, #0x7b ; ~0x84 + orl a, r3 + movx @dptr, a ; other pins are inputs, bits ignored + ljmp setup_done_ack +control_pins_in: + mov dptr, PINSC + movx a, @dptr + xrl a, #0xff + ljmp setup_return_one_byte + +;;; ======================================== + +ISR_Ep2in: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, IN07IRQ ; clear USB int + mov a,#04h + movx @dptr,a + + ;; do stuff + lcall start_in + + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +ISR_Ep2out: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, OUT07IRQ ; clear USB int + mov a,#04h + movx @dptr,a + + ;; do stuff + + ;; copy data into buffer. for now, assume we will have enough space + mov dptr, OUT2BC ; get byte count + movx a,@dptr + mov r1, a + clr a + mov dps, a + mov dptr, OUT2BUF ; load DPTR0 with source + mov dph1, #HIGH(tx_ring) ; load DPTR1 with target + mov dpl1, tx_ring_in +OUT_loop: + movx a,@dptr ; read + inc dps ; switch to DPTR1: target + inc dpl1 ; target = tx_ring_in+1 + movx @dptr,a ; store + mov a,dpl1 + cjne a, tx_ring_out, OUT_no_overflow + sjmp OUT_overflow +OUT_no_overflow: + inc tx_ring_in ; tx_ring_in++ + inc dps ; switch to DPTR0: source + inc dptr + djnz r1, OUT_loop + sjmp OUT_done +OUT_overflow: + ;; signal overflow + ;; fall through +OUT_done: + ;; ack + mov dptr,OUT2BC + movx @dptr,a + + ;; start tx + acall maybe_start_tx + ;acall dump_stat + + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +dump_stat: + ;; fill in EP4in with a debugging message: + ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out + ;; tx_active + ;; tx_ring[0..15] + ;; 0xfc + ;; rx_ring[0..15] + clr a + mov dps, a + + mov dptr, IN4CS + movx a, @dptr + jb acc.1, dump_stat__done; busy: cannot dump, old one still pending + mov dptr, IN4BUF + + mov a, tx_ring_in + movx @dptr, a + inc dptr + mov a, tx_ring_out + movx @dptr, a + inc dptr + + mov a, rx_ring_in + movx @dptr, a + inc dptr + mov a, rx_ring_out + movx @dptr, a + inc dptr + + clr a + jnb TX_RUNNING, dump_stat__no_tx_running + inc a +dump_stat__no_tx_running: + movx @dptr, a + inc dptr + ;; tx_ring[0..15] + inc dps + mov dptr, #tx_ring ; DPTR1: source + mov r1, #16 +dump_stat__tx_ring_loop: + movx a, @dptr + inc dptr + inc dps + movx @dptr, a + inc dptr + inc dps + djnz r1, dump_stat__tx_ring_loop + inc dps + + mov a, #0xfc + movx @dptr, a + inc dptr + + ;; rx_ring[0..15] + inc dps + mov dptr, #rx_ring ; DPTR1: source + mov r1, #16 +dump_stat__rx_ring_loop: + movx a, @dptr + inc dptr + inc dps + movx @dptr, a + inc dptr + inc dps + djnz r1, dump_stat__rx_ring_loop + + ;; now send it + clr a + mov dps, a + mov dptr, IN4BC + mov a, #38 + movx @dptr, a +dump_stat__done: + ret + +;;; ============================================================ + +maybe_start_tx: + ;; make sure the tx process is running. + jb TX_RUNNING, start_tx_done +start_tx: + ;; is there work to be done? + mov a, tx_ring_in + cjne a,tx_ring_out, start_tx__work + ret ; no work +start_tx__work: + ;; tx was not running. send the first character, setup the TI int + inc tx_ring_out ; [++tx_ring_out] + mov dph, #HIGH(tx_ring) + mov dpl, tx_ring_out + movx a, @dptr + mov sbuf, a + setb TX_RUNNING +start_tx_done: + ;; can we unthrottle the host tx process? + ;; step 1: do we care? + mov a, #0 + cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx + ;; nope +start_tx_really_done: + ret +start_tx__maybe_unthrottle_tx: + ;; step 2: is there now room? + mov a, tx_ring_out + setb c + subb a, tx_ring_in + ;; a is now write_room. If thresh >= a, we can unthrottle + clr c + subb a, tx_unthrottle_threshold + jc start_tx_really_done ; nope + ;; yes, we can unthrottle. remove the threshold and mark a request + mov tx_unthrottle_threshold, #0 + setb DO_TX_UNTHROTTLE + ;; prod rx, which will actually send the message when in2 becomes free + ljmp start_in + + +serial_int: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + jnb TI, serial_int__not_tx + ;; tx finished. send another character if we have one + clr TI ; clear int + clr TX_RUNNING + lcall start_tx +serial_int__not_tx: + jnb RI, serial_int__not_rx + lcall get_rx_char + clr RI ; clear int +serial_int__not_rx: + ;; return + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +get_rx_char: + mov dph, #HIGH(rx_ring) + mov dpl, rx_ring_in + inc dpl ; target = rx_ring_in+1 + mov a, sbuf + movx @dptr, a + ;; check for overflow before incrementing rx_ring_in + mov a, dpl + cjne a, rx_ring_out, get_rx_char__no_overflow + ;; signal overflow + ret +get_rx_char__no_overflow: + inc rx_ring_in + ;; kick off USB INpipe + acall start_in + ret + +start_in: + ;; check if the inpipe is already running. + mov dptr, IN2CS + movx a, @dptr + jb acc.1, start_in__done; int will handle it + jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle + ;; see if there is any work to do. a serial interrupt might occur + ;; during this sequence? + mov a, rx_ring_in + cjne a, rx_ring_out, start_in__have_work + ret ; nope +start_in__have_work: + ;; now copy as much data as possible into the pipe. 63 bytes max. + clr a + mov dps, a + mov dph, #HIGH(rx_ring) ; load DPTR0 with source + inc dps + mov dptr, IN2BUF ; load DPTR1 with target + movx @dptr, a ; in[0] signals that rest of IN is rx data + inc dptr + inc dps + ;; loop until we run out of data, or we have copied 64 bytes + mov r1, #1 ; INbuf size counter +start_in__loop: + mov a, rx_ring_in + cjne a, rx_ring_out, start_in__still_copying + sjmp start_in__kick +start_in__still_copying: + inc rx_ring_out + mov dpl, rx_ring_out + movx a, @dptr + inc dps + movx @dptr, a ; write into IN buffer + inc dptr + inc dps + inc r1 + cjne r1, #64, start_in__loop; loop +start_in__kick: + ;; either we ran out of data, or we copied 64 bytes. r1 has byte count + ;; kick off IN + mov dptr, IN2BC + mov a, r1 + jz start_in__done + movx @dptr, a + ;; done +start_in__done: + ;acall dump_stat + ret +start_in__do_tx_unthrottle: + ;; special sequence: send a tx unthrottle message + clr DO_TX_UNTHROTTLE + clr a + mov dps, a + mov dptr, IN2BUF + mov a, #1 + movx @dptr, a + inc dptr + mov a, #2 + movx @dptr, a + mov dptr, IN2BC + movx @dptr, a + ret + +putchar: + clr TI + mov SBUF, a +putchar_wait: + jnb TI, putchar_wait + clr TI + ret + + +baud_table: ; baud_high, then baud_low + ;; baud[0]: 110 + .byte BAUD_HIGH(110) + .byte BAUD_LOW(110) + ;; baud[1]: 300 + .byte BAUD_HIGH(300) + .byte BAUD_LOW(300) + ;; baud[2]: 1200 + .byte BAUD_HIGH(1200) + .byte BAUD_LOW(1200) + ;; baud[3]: 2400 + .byte BAUD_HIGH(2400) + .byte BAUD_LOW(2400) + ;; baud[4]: 4800 + .byte BAUD_HIGH(4800) + .byte BAUD_LOW(4800) + ;; baud[5]: 9600 + .byte BAUD_HIGH(9600) + .byte BAUD_LOW(9600) + ;; baud[6]: 19200 + .byte BAUD_HIGH(19200) + .byte BAUD_LOW(19200) + ;; baud[7]: 38400 + .byte BAUD_HIGH(38400) + .byte BAUD_LOW(38400) + ;; baud[8]: 57600 + .byte BAUD_HIGH(57600) + .byte BAUD_LOW(57600) + ;; baud[9]: 115200 + .byte BAUD_HIGH(115200) + .byte BAUD_LOW(115200) + +desc_device: + .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40 + .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01 +;;; The "real" device id, which must match the host driver, is that +;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104 + +desc_config1: + .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32 + .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00 + .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01 + .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00 + +desc_strings: + .word string_langids, string_mfg, string_product, string_serial +desc_strings_end: + +string_langids: .byte string_langids_end-string_langids + .byte 3 + .word 0 +string_langids_end: + + ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now + ;; *that* is a pain in the ass to encode. And they are little-endian + ;; too. Use this perl snippet to get the bytecodes: + /* while (<>) { + @c = split(//); + foreach $c (@c) { + printf("0x%02x, 0x00, ", ord($c)); + } + } + */ + +string_mfg: .byte string_mfg_end-string_mfg + .byte 3 +; .byte "ACME usb widgets" + .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00 +string_mfg_end: + +string_product: .byte string_product_end-string_product + .byte 3 +; .byte "ACME USB serial widget" + .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00 +string_product_end: + +string_serial: .byte string_serial_end-string_serial + .byte 3 +; .byte "47" + .byte 0x34, 0x00, 0x37, 0x00 +string_serial_end: + +;;; ring buffer memory + ;; tx_ring_in+1 is where the next input byte will go + ;; [tx_ring_out] has been sent + ;; if tx_ring_in == tx_ring_out, theres no work to do + ;; there are (tx_ring_in - tx_ring_out) chars to be written + ;; dont let _in lap _out + ;; cannot inc if tx_ring_in+1 == tx_ring_out + ;; write [tx_ring_in+1] then tx_ring_in++ + ;; if (tx_ring_in+1 == tx_ring_out), overflow + ;; else tx_ring_in++ + ;; read/send [tx_ring_out+1], then tx_ring_out++ + + ;; rx_ring_in works the same way + + .org 0x1000 +tx_ring: + .skip 0x100 ; 256 bytes +rx_ring: + .skip 0x100 ; 256 bytes + + + .END + diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 82c916d8a499..eef60b04e33e 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -521,7 +521,7 @@ static int __devinit rivafb_init_one (struct pci_dev *pd, rinfo->base1_region_size = pci_resource_len (pd, 1); assert (rinfo->base0_region_size >= 0x00800000); /* from GGI */ - assert (rinfo->base0_region_size >= 0x01000000); /* from GGI */ + assert (rinfo->base1_region_size >= 0x01000000); /* from GGI */ rinfo->ctrl_base_phys = rinfo->pd->resource[0].start; rinfo->fb_base_phys = rinfo->pd->resource[1].start; diff --git a/fs/dcache.c b/fs/dcache.c index dc424305fc27..3106a809f7ed 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -336,6 +336,44 @@ resume: return (count > 1); /* remaining users? */ } +/* + * Search for at least 1 mount point in the dentry's subdirs. + * We descend to the next level whenever the d_subdirs + * list is non-empty and continue searching. + */ +int have_submounts(struct dentry *parent) +{ + struct dentry *this_parent = parent; + struct list_head *next; + + if (parent->d_mounts != parent) + return 1; +repeat: + next = this_parent->d_subdirs.next; +resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + next = tmp->next; + /* Have we found a mount point ? */ + if (dentry->d_mounts != dentry) + return 1; + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; + goto repeat; + } + } + /* + * All done at this level ... ascend and resume the search. + */ + if (this_parent != parent) { + next = this_parent->d_child.next; + this_parent = this_parent->d_parent; + goto resume; + } + return 0; /* No mount points found in tree */ +} + /* * Search the dentry child list for the specified parent, * and move any unused dentries to the end of the unused diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 904f5cb8f363..2d5ad9140c19 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -300,21 +300,20 @@ do_more: if (!gdp) goto error_return; - if (test_opt (sb, CHECK_STRICT) && - (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) || - in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) || - in_range (block, le32_to_cpu(gdp->bg_inode_table), - sb->u.ext2_sb.s_itb_per_group) || - in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table), - sb->u.ext2_sb.s_itb_per_group))) - ext2_panic (sb, "ext2_free_blocks", + if (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) || + in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) || + in_range (block, le32_to_cpu(gdp->bg_inode_table), + sb->u.ext2_sb.s_itb_per_group) || + in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table), + sb->u.ext2_sb.s_itb_per_group)) + ext2_error (sb, "ext2_free_blocks", "Freeing blocks in system zones - " "Block = %lu, count = %lu", block, count); for (i = 0; i < count; i++) { if (!ext2_clear_bit (bit + i, bh->b_data)) - ext2_warning (sb, "ext2_free_blocks", + ext2_error (sb, "ext2_free_blocks", "bit already cleared for block %lu", block); else { @@ -527,11 +526,11 @@ got_block: tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block); - if (test_opt (sb, CHECK_STRICT) && - (tmp == le32_to_cpu(gdp->bg_block_bitmap) || - tmp == le32_to_cpu(gdp->bg_inode_bitmap) || - in_range (tmp, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group))) - ext2_panic (sb, "ext2_new_block", + if (tmp == le32_to_cpu(gdp->bg_block_bitmap) || + tmp == le32_to_cpu(gdp->bg_inode_bitmap) || + in_range (tmp, le32_to_cpu(gdp->bg_inode_table), + sb->u.ext2_sb.s_itb_per_group)) + ext2_error (sb, "ext2_new_block", "Allocating block in system zone - " "block = %u", tmp); @@ -679,6 +678,7 @@ int ext2_group_sparse(int group) test_root(group, 7)); } +#ifdef CONFIG_EXT2_CHECK /* Called at mount-time, super-block is locked */ void ext2_check_blocks_bitmap (struct super_block * sb) { @@ -753,3 +753,4 @@ void ext2_check_blocks_bitmap (struct super_block * sb) "stored = %lu, counted = %lu", (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count); } +#endif diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 3a3e4a69cbb0..e520c4e8afa0 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -236,7 +236,7 @@ void ext2_free_inode (struct inode * inode) /* Ok, now we can actually update the inode bitmaps.. */ if (!ext2_clear_bit (bit, bh->b_data)) - ext2_warning (sb, "ext2_free_inode", + ext2_error (sb, "ext2_free_inode", "bit already cleared for inode %lu", ino); else { gdp = ext2_get_group_desc (sb, block_group, &bh2); @@ -401,7 +401,7 @@ repeat: EXT2_INODES_PER_GROUP(sb))) < EXT2_INODES_PER_GROUP(sb)) { if (ext2_set_bit (j, bh->b_data)) { - ext2_warning (sb, "ext2_new_inode", + ext2_error (sb, "ext2_new_inode", "bit already set for inode %d", j); goto repeat; } @@ -527,6 +527,7 @@ unsigned long ext2_count_free_inodes (struct super_block * sb) #endif } +#ifdef CONFIG_EXT2_CHECK /* Called at mount-time, super-block is locked */ void ext2_check_inodes_bitmap (struct super_block * sb) { @@ -565,3 +566,4 @@ void ext2_check_inodes_bitmap (struct super_block * sb) (unsigned long) le32_to_cpu(es->s_free_inodes_count), bitmap_count); } +#endif diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 73be71e61be0..4f48a72f5ffb 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -153,23 +153,14 @@ static int parse_options (char * options, unsigned long * sb_block, set_opt (*mount_options, NO_UID32); } else if (!strcmp (this_char, "check")) { - if (!value || !*value) - set_opt (*mount_options, CHECK_NORMAL); - else if (!strcmp (value, "none")) { - clear_opt (*mount_options, CHECK_NORMAL); - clear_opt (*mount_options, CHECK_STRICT); - } - else if (!strcmp (value, "normal")) - set_opt (*mount_options, CHECK_NORMAL); - else if (!strcmp (value, "strict")) { - set_opt (*mount_options, CHECK_NORMAL); - set_opt (*mount_options, CHECK_STRICT); - } - else { - printk ("EXT2-fs: Invalid check option: %s\n", - value); - return 0; - } + if (!value || !*value || !strcmp (value, "none")) + clear_opt (*mount_options, CHECK); + else +#ifdef CONFIG_EXT2_CHECK + set_opt (*mount_options, CHECK); +#else + printk("EXT2 Check option not supported\n"); +#endif } else if (!strcmp (this_char, "debug")) set_opt (*mount_options, DEBUG); @@ -205,10 +196,6 @@ static int parse_options (char * options, unsigned long * sb_block, set_opt (*mount_options, GRPID); else if (!strcmp (this_char, "minixdf")) set_opt (*mount_options, MINIX_DF); - else if (!strcmp (this_char, "nocheck")) { - clear_opt (*mount_options, CHECK_NORMAL); - clear_opt (*mount_options, CHECK_STRICT); - } else if (!strcmp (this_char, "nogrpid") || !strcmp (this_char, "sysvgroups")) clear_opt (*mount_options, GRPID); @@ -305,10 +292,12 @@ static void ext2_setup_super (struct super_block * sb, EXT2_BLOCKS_PER_GROUP(sb), EXT2_INODES_PER_GROUP(sb), sb->u.ext2_sb.s_mount_opt); +#ifdef CONFIG_EXT2_CHECK if (test_opt (sb, CHECK)) { ext2_check_blocks_bitmap (sb); ext2_check_inodes_bitmap (sb); } +#endif } #if 0 /* ibasket's still have unresolved bugs... -DaveM */ @@ -398,7 +387,6 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, } sb->u.ext2_sb.s_mount_opt = 0; - set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL); if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, &sb->u.ext2_sb.s_mount_opt)) { return NULL; @@ -674,7 +662,6 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) /* * Allow the "check" option to be passed as a remount option. */ - new_mount_opt = EXT2_MOUNT_CHECK_NORMAL; if (!parse_options (data, &tmp, &resuid, &resgid, &new_mount_opt)) return -EINVAL; diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 517456326048..20b9bb4902cb 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -296,6 +296,7 @@ nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) struct rpc_clnt *clnt; struct nlm_args *argp = &req->a_args; struct nlm_res *resp = &req->a_res; + struct rpc_message msg; int status; dprintk("lockd: call procedure %s on %s (async)\n", @@ -306,8 +307,11 @@ nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) return -ENOLCK; /* bootstrap and kick off the async RPC call */ - status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC, - callback, req); + msg.rpc_proc = proc; + msg.rpc_argp = argp; + msg.rpc_resp =resp; + msg.rpc_cred = NULL; + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); /* If the async call is proceeding, increment host refcount */ if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC)) diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index a2f280bdc66e..55dee3886e4b 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -163,7 +163,7 @@ xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) *p++ = htonl(argp->proc); /* This is the private part. Needed only for SM_MON call */ - if (rqstp->rq_task->tk_proc == SM_MON) { + if (rqstp->rq_task->tk_msg.rpc_proc == SM_MON) { *p++ = argp->addr; *p++ = 0; *p++ = 0; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 37b2b682b188..30730b7de5bd 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -599,6 +599,9 @@ out_bad: d_drop(dentry); if (!list_empty(&dentry->d_subdirs)) shrink_dcache_parent(dentry); + /* If we have submounts, don't unhash ! */ + if (have_submounts(dentry)) + goto out_valid; /* Purge readdir caches. */ if (dentry->d_parent->d_inode) { nfs_zap_caches(dentry->d_parent->d_inode); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 717d12bbbe18..aa17780e52ff 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -171,6 +171,7 @@ static inline int nfs_readpage_async(struct dentry *dentry, struct inode *inode, struct page *page) { + struct rpc_message msg; unsigned long address; struct nfs_rreq *req; int result = -1, flags; @@ -195,8 +196,13 @@ nfs_readpage_async(struct dentry *dentry, struct inode *inode, /* Start the async call */ dprintk("NFS: executing async READ request.\n"); - result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ, - &req->ra_args, &req->ra_res, flags, + + msg.rpc_proc = NFSPROC_READ; + msg.rpc_argp = &req->ra_args; + msg.rpc_resp = &req->ra_res; + msg.rpc_cred = NULL; + + result = rpc_call_async(NFS_CLIENT(inode), &msg, flags, nfs_readpage_result, req); if (result < 0) goto out_free; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 5f847bec8b78..6dc37d6ec5e5 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -285,6 +285,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_wreq *wreq; struct rpc_task *task; + struct rpc_message msg; dprintk("NFS: create_write_request(%s/%s, %ld+%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, @@ -298,13 +299,16 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, task = &wreq->wb_task; rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE); - task->tk_calldata = wreq; - task->tk_action = nfs_wback_begin; - - rpcauth_lookupcred(task); /* Obtain user creds */ + msg.rpc_proc = NFSPROC_WRITE; + msg.rpc_argp = &wreq->wb_args; + msg.rpc_resp = &wreq->wb_fattr; + msg.rpc_cred = NULL; + rpc_call_setup(task, &msg, 0); if (task->tk_status < 0) goto out_req; + task->tk_calldata = wreq; + /* Put the task on inode's writeback request list. */ get_file(file); wreq->wb_file = file; @@ -360,6 +364,7 @@ schedule_write_request(struct nfs_wreq *req, int sync) task->tk_pid); /* Page is already locked */ rpc_clnt_sigmask(clnt, &oldmask); + nfs_wback_begin(task); rpc_execute(task); rpc_clnt_sigunmask(clnt, &oldmask); } else { @@ -367,7 +372,7 @@ schedule_write_request(struct nfs_wreq *req, int sync) task->tk_pid); task->tk_flags |= RPC_TASK_ASYNC; task->tk_timeout = NFS_WRITEBACK_DELAY; - rpc_sleep_on(&write_queue, task, NULL, NULL); + rpc_sleep_on(&write_queue, task, nfs_wback_begin, NULL); } return sync; @@ -641,8 +646,6 @@ nfs_wback_begin(struct rpc_task *task) req->wb_args.count = req->wb_bytes; req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset); - rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0); - return; } diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 1aa3aa1c4c00..2be4e856261b 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -85,14 +85,14 @@ found_middle: #define find_first_one_bit(addr, size)\ find_next_one_bit((addr), (size), 0) -static int read_block_bitmap(struct super_block * sb, unsigned int block, - unsigned long bitmap_nr) +static int read_block_bitmap(struct super_block * sb, Uint32 bitmap, + unsigned int block, unsigned long bitmap_nr) { struct buffer_head *bh = NULL; int retval = 0; lb_addr loc; - loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap; + loc.logicalBlockNum = bitmap; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block), sb->s_blocksize); @@ -105,7 +105,8 @@ static int read_block_bitmap(struct super_block * sb, unsigned int block, return retval; } -static int __load_block_bitmap(struct super_block * sb, unsigned int block_group) +static int __load_block_bitmap(struct super_block * sb, Uint32 bitmap, + unsigned int block_group) { int i, j, retval = 0; unsigned long block_bitmap_number; @@ -125,7 +126,7 @@ static int __load_block_bitmap(struct super_block * sb, unsigned int block_group if (UDF_SB_BLOCK_BITMAP_NUMBER(sb, block_group) == block_group) return block_group; } - retval = read_block_bitmap(sb, block_group, block_group); + retval = read_block_bitmap(sb, bitmap, block_group, block_group); if (retval < 0) return retval; return block_group; @@ -150,7 +151,7 @@ static int __load_block_bitmap(struct super_block * sb, unsigned int block_group UDF_SB_BLOCK_BITMAP(sb, 0) = block_bitmap; if (!block_bitmap) - retval = read_block_bitmap(sb, block_group, 0); + retval = read_block_bitmap(sb, bitmap, block_group, 0); } else { @@ -163,12 +164,12 @@ static int __load_block_bitmap(struct super_block * sb, unsigned int block_group UDF_SB_BLOCK_BITMAP_NUMBER(sb, j) = UDF_SB_BLOCK_BITMAP_NUMBER(sb, j-1); UDF_SB_BLOCK_BITMAP(sb, j) = UDF_SB_BLOCK_BITMAP(sb, j-1); } - retval = read_block_bitmap(sb, block_group, 0); + retval = read_block_bitmap(sb, bitmap, block_group, 0); } return retval; } -static inline int load_block_bitmap(struct super_block *sb, +static inline int load_block_bitmap(struct super_block *sb, Uint32 bitmap, unsigned int block_group) { int slot; @@ -189,7 +190,7 @@ static inline int load_block_bitmap(struct super_block *sb, } else { - slot = __load_block_bitmap(sb, block_group); + slot = __load_block_bitmap(sb, bitmap, block_group); } if (slot < 0) @@ -201,8 +202,8 @@ static inline int load_block_bitmap(struct super_block *sb, return slot; } -void udf_free_blocks(const struct inode * inode, lb_addr bloc, Uint32 offset, - Uint32 count) +static void udf_bitmap_free_blocks(const struct inode * inode, Uint32 bitmap, + lb_addr bloc, Uint32 offset, Uint32 count) { struct buffer_head * bh = NULL; unsigned long block; @@ -220,9 +221,6 @@ void udf_free_blocks(const struct inode * inode, lb_addr bloc, Uint32 offset, return; } - if (UDF_SB_PARTMAPS(sb)[bloc.partitionReferenceNum].s_uspace_bitmap == 0xFFFFFFFF) - return; - lock_super(sb); if (bloc.logicalBlockNum < 0 || (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) @@ -248,7 +246,7 @@ do_more: overflow = bit + count - (sb->s_blocksize << 3); count -= overflow; } - bitmap_nr = load_block_bitmap(sb, block_group); + bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; @@ -285,8 +283,8 @@ error_return: return; } -int udf_prealloc_blocks(const struct inode * inode, Uint16 partition, - Uint32 first_block, Uint32 block_count) +static int udf_bitmap_prealloc_blocks(const struct inode * inode, Uint32 bitmap, + Uint16 partition, Uint32 first_block, Uint32 block_count) { int alloc_count = 0; int bit, block, block_group, group_start; @@ -312,7 +310,7 @@ repeat: block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); - bitmap_nr = load_block_bitmap(sb, block_group); + bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto out; bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); @@ -351,7 +349,8 @@ out: return alloc_count; } -int udf_new_block(const struct inode * inode, Uint16 partition, Uint32 goal, int *err) +static int udf_bitmap_new_block(const struct inode * inode, Uint32 bitmap, + Uint16 partition, Uint32 goal, int *err) { int tmp, newbit, bit=0, block, block_group, group_start; int end_goal, nr_groups, bitmap_nr, i; @@ -379,7 +378,7 @@ repeat: block_group = block >> (sb->s_blocksize_bits + 3); group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); - bitmap_nr = load_block_bitmap(sb, block_group); + bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); @@ -419,7 +418,7 @@ repeat: block_group = 0; group_start = block_group ? 0 : sizeof(struct SpaceBitmapDesc); - bitmap_nr = load_block_bitmap(sb, block_group); + bitmap_nr = load_block_bitmap(sb, bitmap, block_group); if (bitmap_nr < 0) goto error_return; bh = UDF_SB_BLOCK_BITMAP(sb, bitmap_nr); @@ -497,3 +496,64 @@ error_return: unlock_super(sb); return 0; } + +inline void udf_free_blocks(const struct inode * inode, lb_addr bloc, + Uint32 offset, Uint32 count) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_uspace.bitmap, + bloc, offset, count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_fspace.bitmap, + bloc, offset, count); + } + else + return; +} + +inline int udf_prealloc_blocks(const struct inode * inode, Uint16 partition, + Uint32 first_block, Uint32 block_count) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.bitmap, + partition, first_block, block_count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.bitmap, + partition, first_block, block_count); + } + else + return 0; +} + +inline int udf_new_block(const struct inode * inode, Uint16 partition, + Uint32 goal, int *err) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.bitmap, + partition, goal, err); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.bitmap, + partition, goal, err); + } + else + { + *err = -EIO; + return 0; + } +} + diff --git a/fs/udf/file.c b/fs/udf/file.c index 73d47ac10418..36644ace95dc 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -246,7 +246,7 @@ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, struct FileEntry *fe; fe = (struct FileEntry *)bh->b_data; - eaicb = fe->extendedAttrICB; + eaicb = lela_to_cpu(fe->extendedAttrICB); if (UDF_I_LENEATTR(inode)) ea = fe->extendedAttr; } @@ -255,7 +255,7 @@ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, struct ExtendedFileEntry *efe; efe = (struct ExtendedFileEntry *)bh->b_data; - eaicb = efe->extendedAttrICB; + eaicb = lela_to_cpu(efe->extendedAttrICB); if (UDF_I_LENEATTR(inode)) ea = efe->extendedAttr; } diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 2640861350aa..954e118454f4 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -74,10 +74,13 @@ static int udf_get_block(struct inode *, long, struct buffer_head *, int); */ void udf_put_inode(struct inode * inode) { - lock_kernel(); - udf_discard_prealloc(inode); - write_inode_now(inode); - unlock_kernel(); + if (!(inode->i_sb->s_flags & MS_RDONLY)) + { + lock_kernel(); + udf_discard_prealloc(inode); + write_inode_now(inode); + unlock_kernel(); + } } /* @@ -1554,16 +1557,18 @@ int udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset, case ICB_FLAG_AD_SHORT: { sad = (short_ad *)sptr; - sad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | - inode->i_sb->s_blocksize; + sad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize); sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); break; } case ICB_FLAG_AD_LONG: { lad = (long_ad *)sptr; - lad->extLength = EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | - inode->i_sb->s_blocksize; + lad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize); lad->extLocation = cpu_to_lelb(*bloc); break; } diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c index 65399703386d..1403cec5234b 100644 --- a/fs/udf/lowlevel.c +++ b/fs/udf/lowlevel.c @@ -70,8 +70,6 @@ udf_get_last_session(struct super_block *sb) unsigned int udf_get_last_block(struct super_block *sb) { - extern int *blksize_size[]; - kdev_t dev = sb->s_dev; struct block_device *bdev = sb->s_bdev; int ret; unsigned long lblock = 0; @@ -80,28 +78,10 @@ udf_get_last_block(struct super_block *sb) if (ret) /* Hard Disk */ { - unsigned int hbsize = get_hardblocksize(dev); - unsigned int blocksize = sb->s_blocksize; - unsigned int mult = 0; - unsigned int div = 0; - - if (!hbsize) - hbsize = blksize_size[MAJOR(dev)][MINOR(dev)]; - - if (hbsize > blocksize) - mult = hbsize / blocksize; - else if (blocksize > hbsize) - div = blocksize / hbsize; - ret = ioctl_by_bdev(bdev, BLKGETSIZE, (unsigned long) &lblock); if (!ret && lblock != 0x7FFFFFFF) - { - if (mult) - lblock *= mult; - else if (div) - lblock /= div; - } + lblock = ((512 * lblock) / sb->s_blocksize); } if (!ret && lblock) diff --git a/fs/udf/misc.c b/fs/udf/misc.c index bed1e3984553..ae998258e490 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -90,7 +90,7 @@ udf_add_extendedattr(struct inode * inode, Uint32 size, Uint32 type, struct FileEntry *fe; fe = (struct FileEntry *)(*bh)->b_data; - eaicb = fe->extendedAttrICB; + eaicb = lela_to_cpu(fe->extendedAttrICB); offset = sizeof(struct FileEntry); } else @@ -98,7 +98,7 @@ udf_add_extendedattr(struct inode * inode, Uint32 size, Uint32 type, struct ExtendedFileEntry *efe; efe = (struct ExtendedFileEntry *)(*bh)->b_data; - eaicb = efe->extendedAttrICB; + eaicb = lela_to_cpu(efe->extendedAttrICB); offset = sizeof(struct ExtendedFileEntry); } @@ -206,7 +206,7 @@ udf_get_extendedattr(struct inode * inode, Uint32 type, Uint8 subtype, struct FileEntry *fe; fe = (struct FileEntry *)(*bh)->b_data; - eaicb = fe->extendedAttrICB; + eaicb = lela_to_cpu(fe->extendedAttrICB); if (UDF_I_LENEATTR(inode)) ea = fe->extendedAttr; } @@ -215,7 +215,7 @@ udf_get_extendedattr(struct inode * inode, Uint32 type, Uint8 subtype, struct ExtendedFileEntry *efe; efe = (struct ExtendedFileEntry *)(*bh)->b_data; - eaicb = efe->extendedAttrICB; + eaicb = lela_to_cpu(efe->extendedAttrICB); if (UDF_I_LENEATTR(inode)) ea = efe->extendedAttr; } diff --git a/fs/udf/namei.c b/fs/udf/namei.c index c371b5d52cd3..a44e19043780 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -935,7 +935,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * inode->i_data.a_ops = &udf_symlink_aops; inode->i_op = &page_symlink_inode_operations; - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + if (UDF_I_ALLOCTYPE(inode) != ICB_FLAG_AD_IN_ICB) { struct buffer_head *bh = NULL; lb_addr bloc, eloc; @@ -964,7 +964,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize); ea = bh->b_data + udf_ext0_offset(inode); - eoffset = inode->i_sb->s_blocksize - (ea - bh->b_data); + eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode); pc = (struct PathComponent *)ea; if (*symname == '/') diff --git a/fs/udf/super.c b/fs/udf/super.c index 81f59e9a3831..5f76abbb0a0a 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -100,14 +100,14 @@ static DECLARE_FSTYPE_DEV(udf_fstype, "udf", udf_read_super); /* Superblock operations */ static struct super_operations udf_sb_ops = { - read_inode: udf_read_inode, + read_inode: udf_read_inode, write_inode: udf_write_inode, - put_inode: udf_put_inode, + put_inode: udf_put_inode, delete_inode: udf_delete_inode, - put_super: udf_put_super, + put_super: udf_put_super, write_super: udf_write_super, - statfs: udf_statfs, - remount_fs: udf_remount_fs, + statfs: udf_statfs, + remount_fs: udf_remount_fs, }; struct udf_options @@ -127,7 +127,6 @@ struct udf_options uid_t uid; }; - static int __init init_udf_fs(void) { printk(KERN_NOTICE "udf: registering filesystem\n"); @@ -745,8 +744,9 @@ udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) { if (udf_CS0toUTF8(&outstr, &instr)) { - udf_debug("volIdent[] = '%s'\n", outstr.u_name); - strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, outstr.u_len); + strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, + outstr.u_len > 31 ? 31 : outstr.u_len); + udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb)); } } @@ -788,7 +788,6 @@ udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) { UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation) + UDF_SB_SESSION(sb); - UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF; if (UDF_SB_PARTTYPE(sb,i) == UDF_SPARABLE_MAP15) udf_fill_spartable(sb, &UDF_SB_TYPESPAR(sb,i), UDF_SB_PARTLEN(sb,i)); @@ -803,17 +802,24 @@ udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) udf_debug("unallocatedSpaceTable (part %d)\n", i); if (phd->unallocatedSpaceBitmap.extLength) { - UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = + UDF_SB_PARTMAPS(sb)[i].s_uspace.bitmap = le32_to_cpu(phd->unallocatedSpaceBitmap.extPosition); + UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; udf_debug("unallocatedSpaceBitmap (part %d) @ %d\n", - i, UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap); + i, UDF_SB_PARTMAPS(sb)[i].s_uspace.bitmap); } if (phd->partitionIntegrityTable.extLength) udf_debug("partitionIntegrityTable (part %d)\n", i); if (phd->freedSpaceTable.extLength) udf_debug("freedSpaceTable (part %d)\n", i); if (phd->freedSpaceBitmap.extLength) - udf_debug("freedSpaceBitmap (part %d\n", i); + { + UDF_SB_PARTMAPS(sb)[i].s_fspace.bitmap = + le32_to_cpu(phd->freedSpaceBitmap.extPosition); + UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP; + udf_debug("freedSpaceBitmap (part %d) @ %d\n", + i, UDF_SB_PARTMAPS(sb)[i].s_fspace.bitmap); + } } break; } @@ -1184,7 +1190,6 @@ udf_load_partition(struct super_block *sb, lb_addr *fileset) } UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); - UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF; } } } @@ -1520,34 +1525,27 @@ static unsigned int udf_count_free(struct super_block *sb) { struct buffer_head *bh = NULL; - unsigned int accum=0; - int index; - int block=0, newblock; + unsigned int accum = 0; lb_addr loc; - Uint32 bytes; - Uint8 value; - Uint8 * ptr; - Uint16 ident; - - if (UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap == 0xFFFFFFFF) - { - if (UDF_SB_LVIDBH(sb)) - { - if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) - accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); - - if (accum == 0xFFFFFFFF) - accum = 0; + Uint32 bitmap; - return accum; - } - else - return 0; - } + if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) + bitmap = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.bitmap; + else if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) + bitmap = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.bitmap; else + bitmap = 0xFFFFFFFF; + + if (bitmap != 0xFFFFFFFF) { struct SpaceBitmapDesc *bm; - loc.logicalBlockNum = UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace_bitmap; + int block = 0, newblock, index; + Uint16 ident; + Uint32 bytes; + Uint8 value; + Uint8 * ptr; + + loc.logicalBlockNum = bitmap; loc.partitionReferenceNum = UDF_SB_PARTITION(sb); bh = udf_read_ptagged(sb, loc, 0, &ident); @@ -1593,6 +1591,18 @@ udf_count_free(struct super_block *sb) } } udf_release_data(bh); - return accum; } + else + { + if (UDF_SB_LVIDBH(sb)) + { + if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) + accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); + + if (accum == 0xFFFFFFFF) + accum = 0; + } + } + + return accum; } diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 6084c5613502..6988a7238884 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -18,6 +18,11 @@ #define UDF_FLAG_UNDELETE 6 #define UDF_FLAG_UNHIDE 7 #define UDF_FLAG_VARCONV 8 + +#define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001 +#define UDF_PART_FLAG_UNALLOC_TABLE 0x0002 +#define UDF_PART_FLAG_FREED_BITMAP 0x0004 +#define UDF_PART_FLAG_FREED_TABLE 0x0008 #define UDF_SB_FREE(X)\ {\ @@ -52,6 +57,7 @@ #define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing ) #define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual ) #define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func ) +#define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags ) #define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) #define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index b92eed7db895..7dd00bc19bee 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -106,7 +106,7 @@ struct ktm struct ustr { Uint8 u_cmpID; - Uint8 u_name[UDF_NAME_LEN-1]; + Uint8 u_name[UDF_NAME_LEN]; Uint8 u_len; Uint8 padding; unsigned long u_hash; @@ -182,6 +182,8 @@ extern void udf_truncate(struct inode *); extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32); extern int udf_prealloc_blocks(const struct inode *, Uint16, Uint32, Uint32); extern int udf_new_block(const struct inode *, Uint16, Uint32, int *); + +/* fsync.c */ extern int udf_sync_file(struct file *, struct dentry *); /* directory.c */ diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 134b3c755598..7cb2d3c1f9cf 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -177,7 +177,8 @@ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) return 0; } - for (i = 0; (i < ocu_len) && (utf_o->u_len < UDF_NAME_LEN) ;) { + for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) + { /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index c5ef464b466e..4cc4c56ced46 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -157,6 +157,9 @@ extern struct dentry * d_alloc_root(struct inode *); /* test whether root is busy without destroying dcache */ extern int is_root_busy(struct dentry *); +/* test whether we have any submounts in a subdir tree */ +extern int have_submounts(struct dentry *); + /* * This adds the entry to the hash queues. */ diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 85e8d092fd01..25178b66b6db 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -306,10 +306,7 @@ struct ext2_inode { /* * Mount flags */ -#define EXT2_MOUNT_CHECK_NORMAL 0x0001 /* Do some more checks */ -#define EXT2_MOUNT_CHECK_STRICT 0x0002 /* Do again more checks */ -#define EXT2_MOUNT_CHECK (EXT2_MOUNT_CHECK_NORMAL | \ - EXT2_MOUNT_CHECK_STRICT) +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ #define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ #define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ #define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 472c9d87ed64..55423de5c756 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -551,6 +551,8 @@ const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, co #ifndef CONFIG_PCI extern inline int pcibios_present(void) { return 0; } +extern inline int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn) +{ return PCIBIOS_DEVICE_NOT_FOUND; } #define _PCI_NOP(o,s,t) \ extern inline int pcibios_##o##_config_##s## (u8 bus, u8 dfn, u8 where, t val) \ @@ -583,6 +585,7 @@ extern inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUS extern inline int pci_register_driver(struct pci_driver *drv) { return 0;} extern inline void pci_unregister_driver(struct pci_driver *drv) { } extern inline int scsi_to_pci_dma_dir(unsigned char scsi_dir) { return scsi_dir; } +extern inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; } #else diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index f374af3f4426..d106c881abb5 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -64,10 +64,10 @@ struct rpc_authops { struct rpc_auth * (*create)(struct rpc_clnt *); void (*destroy)(struct rpc_auth *); - struct rpc_cred * (*crcreate)(struct rpc_task *); + struct rpc_cred * (*crcreate)(int); void (*crdestroy)(struct rpc_cred *); - int (*crmatch)(struct rpc_task *, struct rpc_cred*); + int (*crmatch)(struct rpc_cred *, int); u32 * (*crmarshal)(struct rpc_task *, u32 *, int); int (*crrefresh)(struct rpc_task *); u32 * (*crvalidate)(struct rpc_task *, u32 *); @@ -83,10 +83,14 @@ int rpcauth_register(struct rpc_authops *); int rpcauth_unregister(struct rpc_authops *); struct rpc_auth * rpcauth_create(unsigned int, struct rpc_clnt *); void rpcauth_destroy(struct rpc_auth *); -struct rpc_cred * rpcauth_lookupcred(struct rpc_task *); +struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); +struct rpc_cred * rpcauth_bindcred(struct rpc_task *); void rpcauth_holdcred(struct rpc_task *); -void rpcauth_releasecred(struct rpc_task *); -int rpcauth_matchcred(struct rpc_task *, struct rpc_cred *); +void rpcauth_releasecred(struct rpc_auth *, + struct rpc_cred *); +void rpcauth_unbindcred(struct rpc_task *); +int rpcauth_matchcred(struct rpc_auth *, + struct rpc_cred *, int); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); int rpcauth_refreshcred(struct rpc_task *); diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index d0be5f044c0a..0d09eb48ad51 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -111,21 +111,23 @@ void rpc_release_client(struct rpc_clnt *); void rpc_getport(struct rpc_task *, struct rpc_clnt *); int rpc_register(u32, u32, int, unsigned short, int *); -int rpc_call(struct rpc_clnt *clnt, u32 proc, - void *argp, void *resp, int flags); -int rpc_call_async(struct rpc_task *task, u32 proc, - void *argp, void *resp, int flags); -void rpc_call_setup(struct rpc_task *task, u32 proc, - void *argp, void *resp, int flags); -int rpc_do_call(struct rpc_clnt *clnt, u32 proc, - void *argp, void *resp, int flags, - rpc_action callback, void *clntdata); +void rpc_call_setup(struct rpc_task *, struct rpc_message *, int); + +int rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, + int flags, rpc_action callback, void *clntdata); +int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, + int flags); void rpc_restart_call(struct rpc_task *); void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset); -#define rpc_call(clnt, proc, argp, resp, flags) \ - rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL) +static __inline__ +int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) +{ + struct rpc_message msg = { proc, argp, resp, NULL }; + return rpc_call_sync(clnt, &msg, flags); +} + extern __inline__ void rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index d43dfaf0c9ce..279636434e74 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -20,6 +20,16 @@ */ #undef CONFIG_RPC_FASTSCHED +/* + * This is the actual RPC procedure call info. + */ +struct rpc_message { + __u32 rpc_proc; /* Procedure number */ + void * rpc_argp; /* Arguments */ + void * rpc_resp; /* Result */ + struct rpc_cred * rpc_cred; /* Credentials */ +}; + /* * This is the RPC task struct */ @@ -33,17 +43,14 @@ struct rpc_task { struct rpc_task * tk_prev_task; /* global list of tasks */ struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ - struct rpc_cred * tk_cred; /* RPC credentials */ int tk_status; /* result of last operation */ struct rpc_wait_queue * tk_rpcwait; /* RPC wait queue we're on */ /* * RPC call state */ - __u32 tk_proc; /* procedure number */ + struct rpc_message tk_msg; /* RPC call info */ __u32 * tk_buffer; /* XDR buffer */ - void * tk_argp; /* argument storage */ - void * tk_resp; /* result storage */ __u8 tk_garb_retry, tk_cred_retry, tk_suid_retry; @@ -67,6 +74,10 @@ struct rpc_task { wait_queue_head_t tk_wait; /* sync: sleep on this q */ unsigned long tk_timeout; /* timeout for rpc_sleep() */ unsigned short tk_flags; /* misc flags */ + unsigned short tk_lock; /* Task lock counter */ + unsigned int tk_wakeup : 1,/* Task waiting to wake up */ + tk_sleeping : 1,/* Task is truly asleep */ + tk_active : 1;/* Task has been activated */ #ifdef RPC_DEBUG unsigned short tk_pid; /* debugging aid */ #endif @@ -100,6 +111,8 @@ typedef void (*rpc_action)(struct rpc_task *); #define RPC_DO_CALLBACK(t) ((t)->tk_flags & RPC_TASK_CALLBACK) #define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) +#define RPC_IS_SLEEPING(t) ((t)->tk_sleeping) +#define RPC_IS_ACTIVATED(t) ((t)->tk_active) /* * RPC synchronization objects @@ -126,29 +139,28 @@ void rpc_init_task(struct rpc_task *, struct rpc_clnt *, rpc_action exitfunc, int flags); void rpc_release_task(struct rpc_task *); void rpc_killall_tasks(struct rpc_clnt *); -void rpc_execute(struct rpc_task *); +int rpc_execute(struct rpc_task *); void rpc_run_child(struct rpc_task *parent, struct rpc_task *child, rpc_action action); int rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *); void rpc_remove_wait_queue(struct rpc_task *); void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, rpc_action action, rpc_action timer); -void rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *, - unsigned char *, - rpc_action action, rpc_action timer); +void rpc_sleep_locked(struct rpc_wait_queue *, struct rpc_task *, + rpc_action action, rpc_action timer); +void rpc_add_timer(struct rpc_task *, rpc_action); void rpc_wake_up_task(struct rpc_task *); void rpc_wake_up(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); void rpc_wake_up_status(struct rpc_wait_queue *, int); -void rpc_add_timer(struct rpc_task *, rpc_action); -void rpc_del_timer(struct rpc_task *); +int rpc_lock_task(struct rpc_task *); +void rpc_unlock_task(struct rpc_task *); void rpc_delay(struct rpc_task *, unsigned long); void * rpc_allocate(unsigned int flags, unsigned int); void rpc_free(void *); int rpciod_up(void); void rpciod_down(void); void rpciod_wake_up(void); -void rpciod_tcp_dispatcher(void); #ifdef RPC_DEBUG void rpc_show_tasks(void); #endif diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 5e01407fcbf3..e5e66c1de166 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -47,7 +47,7 @@ ((xprt)->cong >= (xprt)->cwnd) /* Default timeout values */ -#define RPC_MAX_UDP_TIMEOUT (6*HZ) +#define RPC_MAX_UDP_TIMEOUT (60*HZ) #define RPC_MAX_TCP_TIMEOUT (600*HZ) /* RPC call and reply header size as number of 32bit words (verifier @@ -96,8 +96,7 @@ struct rpc_rqst { struct rpc_task * rq_task; /* RPC task data */ __u32 rq_xid; /* request XID */ struct rpc_rqst * rq_next; /* free list */ - unsigned char rq_gotit; /* reply received */ - unsigned char rq_damaged; /* being received */ + unsigned char rq_damaged; /* reply being received */ /* * For authentication (e.g. auth_des) @@ -122,12 +121,6 @@ struct rpc_rqst { #define rq_rlen rq_rcv_buf.io_len struct rpc_xprt { - struct rpc_xprt * link; /* list of all clients */ - struct rpc_xprt * rx_pending; /* receive pending list */ - - int rx_pending_flag;/* are we on the rcv pending list ? */ - - struct file * file; /* VFS layer */ struct socket * sock; /* BSD socket layer */ struct sock * inet; /* INET layer */ @@ -145,9 +138,9 @@ struct rpc_xprt { struct rpc_wait_queue reconn; /* waiting for reconnect */ struct rpc_rqst * free; /* free slots */ struct rpc_rqst slot[RPC_MAXREQS]; - unsigned char connected; /* TCP: connected */ - unsigned char write_space; /* TCP: can send */ - unsigned int shutdown : 1, /* being shut down */ + unsigned int connected : 1, /* TCP: connected */ + write_space: 1, /* TCP: can send */ + shutdown : 1, /* being shut down */ nocong : 1, /* no congestion control */ stream : 1, /* TCP */ tcp_more : 1, /* more record fragments */ @@ -156,16 +149,12 @@ struct rpc_xprt { /* * State of TCP reply receive stuff */ - union { /* record marker & XID */ - u32 header[2]; - u8 data[8]; - } tcp_recm; - struct rpc_rqst * tcp_rqstp; - struct iovec tcp_iovec[MAX_IOVEC]; - u32 tcp_total; /* overall record length */ - u32 tcp_reclen; /* fragment length */ - u32 tcp_offset; /* fragment offset */ - u32 tcp_copied; /* copied to request */ + u32 tcp_recm; /* Fragment header */ + u32 tcp_xid; /* Current XID */ + unsigned int tcp_reclen, /* fragment length */ + tcp_offset, /* fragment offset */ + tcp_copied; /* copied to request */ + struct list_head rx_pending; /* receive pending list */ /* * Send stuff @@ -179,17 +168,13 @@ struct rpc_xprt { wait_queue_head_t cong_wait; }; -#define tcp_reclen tcp_recm.header[0] -#define tcp_xid tcp_recm.header[1] #ifdef __KERNEL__ -struct rpc_xprt * xprt_create(struct file *socket, - struct sockaddr_in *addr, - struct rpc_timeout *toparms); struct rpc_xprt * xprt_create_proto(int proto, struct sockaddr_in *addr, struct rpc_timeout *toparms); int xprt_destroy(struct rpc_xprt *); +void xprt_shutdown(struct rpc_xprt *); void xprt_default_timeout(struct rpc_timeout *, int); void xprt_set_timeout(struct rpc_timeout *, unsigned int, unsigned long); @@ -201,8 +186,22 @@ int xprt_adjust_timeout(struct rpc_timeout *); void xprt_release(struct rpc_task *); void xprt_reconnect(struct rpc_task *); int xprt_clear_backlog(struct rpc_xprt *); - -int xprt_tcp_pending(void); +void __rpciod_tcp_dispatcher(void); + +extern struct list_head rpc_xprt_pending; + +static inline +int xprt_tcp_pending(void) +{ + return !list_empty(&rpc_xprt_pending); +} + +static inline +void rpciod_tcp_dispatcher(void) +{ + if (xprt_tcp_pending()) + __rpciod_tcp_dispatcher(); +} #endif /* __KERNEL__*/ diff --git a/include/linux/udf_fs.h b/include/linux/udf_fs.h index 4f842ac30888..47980eaaf7ef 100644 --- a/include/linux/udf_fs.h +++ b/include/linux/udf_fs.h @@ -60,6 +60,7 @@ /* * Function prototypes (all other prototypes included in udfdecl.h) */ +extern int init_udf_fs(void); #endif /* __KERNEL__ */ diff --git a/include/linux/udf_fs_sb.h b/include/linux/udf_fs_sb.h index 15696cf339f9..99cd246e9e7d 100644 --- a/include/linux/udf_fs_sb.h +++ b/include/linux/udf_fs_sb.h @@ -49,7 +49,16 @@ struct udf_virtual_data struct udf_part_map { - __u32 s_uspace_bitmap; + union + { + __u32 bitmap; + struct inode *table; + } s_uspace; + union + { + __u32 bitmap; + struct inode *table; + } s_fspace; __u32 s_partition_root; __u32 s_partition_len; __u16 s_partition_type; @@ -61,6 +70,7 @@ struct udf_part_map } s_type_specific; __u32 (*s_partition_func)(struct super_block *, __u32, __u16, __u32); __u16 s_volumeseqnum; + __u16 s_partition_flags; }; #pragma pack() diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d431c682c460..091a9eb06893 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1944,10 +1944,8 @@ queue_and_out: if (eaten) { kfree_skb(skb); - } else if (!sk->dead) { - wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket,1, POLL_IN); - } + } else + sk->data_ready(sk, 0); return; } @@ -2531,8 +2529,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, /* FIN bit check is not done since if FIN is set in * this frame, the pred_flags won't match up. -DaveM */ - wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket,1, POLL_IN); + sk->data_ready(sk, 0); } tcp_event_data_recv(tp, skb); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index af9923ec8b23..b8c0a7be877c 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -4,12 +4,9 @@ * Generic RPC authentication API. * * Copyright (C) 1996, Olaf Kirch - * - * Modified May 1999, Horst von Brand */ #include -#include #include #include #include @@ -84,6 +81,15 @@ rpcauth_init_credcache(struct rpc_auth *auth) auth->au_nextgc = jiffies + (auth->au_expire >> 1); } +static inline void +rpcauth_crdestroy(struct rpc_auth *auth, struct rpc_cred *cred) +{ + if (auth->au_ops->crdestroy) + auth->au_ops->crdestroy(cred); + else + rpc_free(cred); +} + /* * Clear the RPC credential cache */ @@ -115,18 +121,15 @@ static void rpcauth_gc_credcache(struct rpc_auth *auth) { struct rpc_cred **q, *cred, *free = NULL; - int i, safe = 0; + int i; dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth); spin_lock(&rpc_credcache_lock); for (i = 0; i < RPC_CREDCACHE_NR; i++) { q = &auth->au_credcache[i]; while ((cred = *q) != NULL) { - if (++safe > 500) { - printk("RPC: rpcauth_gc_credcache looping!\n"); - break; - } - if (!cred->cr_count && time_before(cred->cr_expire, jiffies)) { + if (!cred->cr_count && + time_before(cred->cr_expire, jiffies)) { *q = cred->cr_next; cred->cr_next = free; free = cred; @@ -138,7 +141,7 @@ rpcauth_gc_credcache(struct rpc_auth *auth) spin_unlock(&rpc_credcache_lock); while ((cred = free) != NULL) { free = cred->cr_next; - rpc_free(cred); + rpcauth_crdestroy(auth, cred); } auth->au_nextgc = jiffies + auth->au_expire; } @@ -146,7 +149,7 @@ rpcauth_gc_credcache(struct rpc_auth *auth) /* * Insert credential into cache */ -inline void +void rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred) { int nr; @@ -155,8 +158,8 @@ rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred) spin_lock(&rpc_credcache_lock); cred->cr_next = auth->au_credcache[nr]; auth->au_credcache[nr] = cred; - cred->cr_expire = jiffies + auth->au_expire; cred->cr_count++; + cred->cr_expire = jiffies + auth->au_expire; spin_unlock(&rpc_credcache_lock); } @@ -164,13 +167,13 @@ rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred) * Look up a process' credentials in the authentication cache */ static struct rpc_cred * -rpcauth_lookup_credcache(struct rpc_task *task) +rpcauth_lookup_credcache(struct rpc_auth *auth, int taskflags) { - struct rpc_auth *auth = task->tk_auth; struct rpc_cred **q, *cred = NULL; - int nr; + int nr = 0; - nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR); + if (!(taskflags & RPC_TASK_ROOTCREDS)) + nr = current->uid % RPC_CREDCACHE_NR; if (time_before(auth->au_nextgc, jiffies)) rpcauth_gc_credcache(auth); @@ -178,7 +181,8 @@ rpcauth_lookup_credcache(struct rpc_task *task) spin_lock(&rpc_credcache_lock); q = &auth->au_credcache[nr]; while ((cred = *q) != NULL) { - if (auth->au_ops->crmatch(task, cred)) { + if (!(cred->cr_flags & RPCAUTH_CRED_DEAD) && + auth->au_ops->crmatch(cred, taskflags)) { *q = cred->cr_next; break; } @@ -187,7 +191,7 @@ rpcauth_lookup_credcache(struct rpc_task *task) spin_unlock(&rpc_credcache_lock); if (!cred) - cred = auth->au_ops->crcreate(task); + cred = auth->au_ops->crcreate(taskflags); if (cred) rpcauth_insert_credcache(auth, cred); @@ -198,7 +202,7 @@ rpcauth_lookup_credcache(struct rpc_task *task) /* * Remove cred handle from cache */ -static inline void +static void rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred) { struct rpc_cred **q, *cr; @@ -210,7 +214,8 @@ rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred) while ((cr = *q) != NULL) { if (cred == cr) { *q = cred->cr_next; - return; + cred->cr_next = NULL; + break; } q = &cred->cr_next; } @@ -218,58 +223,78 @@ rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred) } struct rpc_cred * -rpcauth_lookupcred(struct rpc_task *task) +rpcauth_lookupcred(struct rpc_auth *auth, int taskflags) +{ + dprintk("RPC: looking up %s cred\n", + auth->au_ops->au_name); + return rpcauth_lookup_credcache(auth, taskflags); +} + +struct rpc_cred * +rpcauth_bindcred(struct rpc_task *task) { + struct rpc_auth *auth = task->tk_auth; + dprintk("RPC: %4d looking up %s cred\n", task->tk_pid, task->tk_auth->au_ops->au_name); - return task->tk_cred = rpcauth_lookup_credcache(task); + task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, task->tk_flags); + if (task->tk_msg.rpc_cred == 0) + task->tk_status = -ENOMEM; + return task->tk_msg.rpc_cred; } int -rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred) +rpcauth_matchcred(struct rpc_auth *auth, struct rpc_cred *cred, int taskflags) { - struct rpc_auth *auth = task->tk_auth; - - dprintk("RPC: %4d matching %s cred %p\n", - task->tk_pid, auth->au_ops->au_name, task->tk_cred); - return auth->au_ops->crmatch(task, cred); + dprintk("RPC: matching %s cred %d\n", + auth->au_ops->au_name, taskflags); + return auth->au_ops->crmatch(cred, taskflags); } void rpcauth_holdcred(struct rpc_task *task) { dprintk("RPC: %4d holding %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred); - if (task->tk_cred) - task->tk_cred->cr_count++; + task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred); + if (task->tk_msg.rpc_cred) { + task->tk_msg.rpc_cred->cr_count++; + task->tk_msg.rpc_cred->cr_expire = jiffies + task->tk_auth->au_expire; + } } void -rpcauth_releasecred(struct rpc_task *task) +rpcauth_releasecred(struct rpc_auth *auth, struct rpc_cred *cred) { - struct rpc_auth *auth = task->tk_auth; - struct rpc_cred *cred; - - dprintk("RPC: %4d releasing %s cred %p\n", - task->tk_pid, auth->au_ops->au_name, task->tk_cred); - if ((cred = task->tk_cred) != NULL) { + if (cred != NULL && cred->cr_count > 0) { cred->cr_count--; if (cred->cr_flags & RPCAUTH_CRED_DEAD) { rpcauth_remove_credcache(auth, cred); if (!cred->cr_count) - auth->au_ops->crdestroy(cred); + rpcauth_crdestroy(auth, cred); } - task->tk_cred = NULL; } } +void +rpcauth_unbindcred(struct rpc_task *task) +{ + struct rpc_auth *auth = task->tk_auth; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d releasing %s cred %p\n", + task->tk_pid, auth->au_ops->au_name, cred); + + rpcauth_releasecred(auth, cred); + task->tk_msg.rpc_cred = NULL; +} + u32 * rpcauth_marshcred(struct rpc_task *task, u32 *p) { struct rpc_auth *auth = task->tk_auth; dprintk("RPC: %4d marshaling %s cred %p\n", - task->tk_pid, auth->au_ops->au_name, task->tk_cred); + task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred); return auth->au_ops->crmarshal(task, p, task->tk_flags & RPC_CALL_REALUID); } @@ -280,7 +305,7 @@ rpcauth_checkverf(struct rpc_task *task, u32 *p) struct rpc_auth *auth = task->tk_auth; dprintk("RPC: %4d validating %s cred %p\n", - task->tk_pid, auth->au_ops->au_name, task->tk_cred); + task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred); return auth->au_ops->crvalidate(task, p); } @@ -290,7 +315,7 @@ rpcauth_refreshcred(struct rpc_task *task) struct rpc_auth *auth = task->tk_auth; dprintk("RPC: %4d refreshing %s cred %p\n", - task->tk_pid, auth->au_ops->au_name, task->tk_cred); + task->tk_pid, auth->au_ops->au_name, task->tk_msg.rpc_cred); task->tk_status = auth->au_ops->crrefresh(task); return task->tk_status; } @@ -299,14 +324,14 @@ void rpcauth_invalcred(struct rpc_task *task) { dprintk("RPC: %4d invalidating %s cred %p\n", - task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred); - if (task->tk_cred) - task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; + task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred); + if (task->tk_msg.rpc_cred) + task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; } int rpcauth_uptodatecred(struct rpc_task *task) { - return !(task->tk_cred) || - (task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE); + return !(task->tk_msg.rpc_cred) || + (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE); } diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index be6d196376a2..d2e645acd26b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -38,6 +38,7 @@ static void nul_destroy(struct rpc_auth *auth) { dprintk("RPC: destroying NULL authenticator %p\n", auth); + rpcauth_free_credcache(auth); rpc_free(auth); } @@ -45,15 +46,12 @@ nul_destroy(struct rpc_auth *auth) * Create NULL creds for current process */ static struct rpc_cred * -nul_create_cred(struct rpc_task *task) +nul_create_cred(int flags) { struct rpc_cred *cred; - if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred)))) { - task->tk_status = -ENOMEM; + if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred)))) return NULL; - } - cred->cr_count = 0; cred->cr_flags = RPCAUTH_CRED_UPTODATE; @@ -73,7 +71,7 @@ nul_destroy_cred(struct rpc_cred *cred) * Match cred handle against current process */ static int -nul_match(struct rpc_task *task, struct rpc_cred *cred) +nul_match(struct rpc_cred *cred, int taskflags) { return 1; } diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 6596085b31ee..8033ed6c1695 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -4,12 +4,9 @@ * UNIX-style authentication; no AUTH_SHORT support * * Copyright (C) 1996, Olaf Kirch - * - * Modified May 1999 Horst von Brand */ #include -#include #include #include #include @@ -63,7 +60,7 @@ unx_destroy(struct rpc_auth *auth) } static struct rpc_cred * -unx_create_cred(struct rpc_task *task) +unx_create_cred(int flags) { struct unx_cred *cred; int i; @@ -71,14 +68,12 @@ unx_create_cred(struct rpc_task *task) dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", current->uid, current->gid); - if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred)))) { - task->tk_status = -ENOMEM; + if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred)))) return NULL; - } cred->uc_count = 0; cred->uc_flags = RPCAUTH_CRED_UPTODATE; - if (RPC_DO_ROOTOVERRIDE(task)) { + if (flags & RPC_TASK_ROOTCREDS) { cred->uc_uid = cred->uc_fsuid = 0; cred->uc_gid = cred->uc_fsgid = 0; cred->uc_gids[0] = NOGROUP; @@ -119,7 +114,7 @@ authunix_fake_cred(struct rpc_task *task, uid_t uid, gid_t gid) cred->uc_fsgid = gid; cred->uc_gids[0] = (gid_t) NOGROUP; - return task->tk_cred = (struct rpc_cred *) cred; + return task->tk_msg.rpc_cred = (struct rpc_cred *) cred; } static void @@ -134,12 +129,12 @@ unx_destroy_cred(struct rpc_cred *cred) * request root creds (e.g. for NFS swapping). */ static int -unx_match(struct rpc_task * task, struct rpc_cred *rcred) +unx_match(struct rpc_cred *rcred, int taskflags) { struct unx_cred *cred = (struct unx_cred *) rcred; int i; - if (!RPC_DO_ROOTOVERRIDE(task)) { + if (!(taskflags & RPC_TASK_ROOTCREDS)) { int groups; if (cred->uc_uid != current->uid @@ -169,7 +164,7 @@ static u32 * unx_marshal(struct rpc_task *task, u32 *p, int ruid) { struct rpc_clnt *clnt = task->tk_client; - struct unx_cred *cred = (struct unx_cred *) task->tk_cred; + struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred; u32 *base, *hold; int i, n; @@ -210,7 +205,7 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid) static int unx_refresh(struct rpc_task *task) { - task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; + task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE; return task->tk_status = -EACCES; } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b4d6bd81a3a8..828389e32d8d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -42,14 +42,13 @@ static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); -static void call_bind(struct rpc_task *task); static void call_reserve(struct rpc_task *task); static void call_reserveresult(struct rpc_task *task); static void call_allocate(struct rpc_task *task); static void call_encode(struct rpc_task *task); static void call_decode(struct rpc_task *task); +static void call_bind(struct rpc_task *task); static void call_transmit(struct rpc_task *task); -static void call_receive(struct rpc_task *task); static void call_status(struct rpc_task *task); static void call_refresh(struct rpc_task *task); static void call_refreshresult(struct rpc_task *task); @@ -98,7 +97,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, clnt->cl_port = xprt->addr.sin_port; clnt->cl_prog = program->number; clnt->cl_vers = version->number; - clnt->cl_prot = IPPROTO_UDP; + clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait"); @@ -117,10 +116,10 @@ out: return clnt; out_no_clnt: - printk("RPC: out of memory in rpc_create_client\n"); + printk(KERN_INFO "RPC: out of memory in rpc_create_client\n"); goto out; out_no_auth: - printk("RPC: Couldn't create auth handle (flavor %d)\n", + printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n", flavor); rpc_free(clnt); clnt = NULL; @@ -140,7 +139,7 @@ rpc_shutdown_client(struct rpc_clnt *clnt) clnt->cl_protname, clnt->cl_server); while (clnt->cl_users) { #ifdef RPC_DEBUG - printk("rpc_shutdown_client: client %s, tasks=%d\n", + dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n", clnt->cl_protname, clnt->cl_users); #endif /* Don't let rpc_release_client destroy us */ @@ -240,42 +239,72 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset) /* * New rpc_call implementation */ -int -rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, - int flags, rpc_action func, void *data) +int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) { struct rpc_task my_task, *task = &my_task; sigset_t oldset; - int async, status; + int status; /* If this client is slain all further I/O fails */ if (clnt->cl_dead) return -EIO; + if (flags & RPC_TASK_ASYNC) { + printk("rpc_call_sync: Illegal flag combination for synchronous task\n"); + flags &= ~RPC_TASK_ASYNC; + } + rpc_clnt_sigmask(clnt, &oldset); /* Create/initialize a new RPC task */ - if ((async = (flags & RPC_TASK_ASYNC)) != 0) { - if (!func) - func = rpc_default_callback; - status = -ENOMEM; - if (!(task = rpc_new_task(clnt, func, flags))) - goto out; - task->tk_calldata = data; - } else { - rpc_init_task(task, clnt, NULL, flags); - } + rpc_init_task(task, clnt, NULL, flags); + rpc_call_setup(task, msg, 0); - /* Bind the user cred, set up the call info struct and - * execute the task */ - if (rpcauth_lookupcred(task) != NULL) { - rpc_call_setup(task, proc, argp, resp, 0); - rpc_execute(task); - } else - async = 0; + /* Set up the call info struct and execute the task */ + if (task->tk_status == 0) + status = rpc_execute(task); + else + status = task->tk_status; + rpc_release_task(task); + + rpc_clnt_sigunmask(clnt, &oldset); + + return status; +} - status = 0; - if (!async) { +/* + * New rpc_call implementation + */ +int +rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags, + rpc_action callback, void *data) +{ + struct rpc_task *task; + sigset_t oldset; + int status; + + /* If this client is slain all further I/O fails */ + if (clnt->cl_dead) + return -EIO; + + flags |= RPC_TASK_ASYNC; + + rpc_clnt_sigmask(clnt, &oldset); + + /* Create/initialize a new RPC task */ + if (!callback) + callback = rpc_default_callback; + status = -ENOMEM; + if (!(task = rpc_new_task(clnt, callback, flags))) + goto out; + task->tk_calldata = data; + + rpc_call_setup(task, msg, 0); + + /* Set up the call info struct and execute the task */ + if (task->tk_status == 0) + status = rpc_execute(task); + else { status = task->tk_status; rpc_release_task(task); } @@ -288,18 +317,24 @@ out: void -rpc_call_setup(struct rpc_task *task, u32 proc, - void *argp, void *resp, int flags) +rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags) { - task->tk_action = call_bind; - task->tk_proc = proc; - task->tk_argp = argp; - task->tk_resp = resp; + task->tk_msg = *msg; task->tk_flags |= flags; + /* Bind the user cred */ + if (task->tk_msg.rpc_cred != NULL) { + rpcauth_holdcred(task); + } else + rpcauth_bindcred(task); + + if (task->tk_status == 0) + task->tk_action = call_reserve; + else + task->tk_action = NULL; /* Increment call count */ - if (task->tk_proc < task->tk_client->cl_maxproc) - rpcproc_count(task->tk_client, proc)++; + if (task->tk_msg.rpc_proc < task->tk_client->cl_maxproc) + rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++; } /* @@ -313,27 +348,8 @@ rpc_restart_call(struct rpc_task *task) rpc_release_task(task); return; } - task->tk_action = call_bind; - rpcproc_count(task->tk_client, task->tk_proc)++; -} - -/* - * 0. Get the server port number if not yet set - */ -static void -call_bind(struct rpc_task *task) -{ - struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; - - if (xprt->stream && !xprt->connected) - task->tk_action = call_reconnect; - else - task->tk_action = call_reserve; - task->tk_status = 0; - - if (!clnt->cl_port) - rpc_getport(task, clnt); + task->tk_action = call_reserve; + rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++; } /* @@ -344,26 +360,22 @@ call_reserve(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; - dprintk("RPC: %4d call_reserve\n", task->tk_pid); - if (!clnt->cl_port) { - printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n", - clnt->cl_protname, clnt->cl_server, - clnt->cl_softrtry? "giving up" : "retrying"); - if (!clnt->cl_softrtry) { - task->tk_action = call_bind; - rpc_delay(task, 5*HZ); - return; - } + if (task->tk_msg.rpc_proc > clnt->cl_maxproc) { + printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", + clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc); rpc_exit(task, -EIO); return; } + + dprintk("RPC: %4d call_reserve\n", task->tk_pid); if (!rpcauth_uptodatecred(task)) { task->tk_action = call_refresh; return; } + + task->tk_status = 0; task->tk_action = call_reserveresult; task->tk_timeout = clnt->cl_timeout.to_resrvval; - task->tk_status = 0; clnt->cl_stats->rpccnt++; xprt_reserve(task); } @@ -374,6 +386,8 @@ call_reserve(struct rpc_task *task) static void call_reserveresult(struct rpc_task *task) { + int status = task->tk_status; + dprintk("RPC: %4d call_reserveresult (status %d)\n", task->tk_pid, task->tk_status); /* @@ -382,7 +396,7 @@ call_reserveresult(struct rpc_task *task) */ if ((task->tk_status >= 0 && !task->tk_rqstp) || (task->tk_status < 0 && task->tk_rqstp)) - printk("call_reserveresult: status=%d, request=%p??\n", + printk(KERN_ERR "call_reserveresult: status=%d, request=%p??\n", task->tk_status, task->tk_rqstp); if (task->tk_status >= 0) { @@ -390,11 +404,11 @@ call_reserveresult(struct rpc_task *task) return; } - switch (task->tk_status) { + task->tk_status = 0; + switch (status) { case -EAGAIN: case -ENOBUFS: task->tk_timeout = task->tk_client->cl_timeout.to_resrvval; - task->tk_status = 0; task->tk_action = call_reserve; break; case -ETIMEDOUT: @@ -402,11 +416,11 @@ call_reserveresult(struct rpc_task *task) task->tk_action = call_timeout; break; default: - task->tk_action = NULL; if (!task->tk_rqstp) { - printk("RPC: task has no request, exit EIO\n"); + printk(KERN_INFO "RPC: task has no request, exit EIO\n"); rpc_exit(task, -EIO); - } + } else + rpc_exit(task, status); } } @@ -428,13 +442,13 @@ call_allocate(struct rpc_task *task) /* FIXME: compute buffer requirements more exactly using * auth->au_wslack */ - bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE; + bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE; if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL) return; - printk("RPC: buffer allocation failed for task %p\n", task); + printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); - if (!signalled()) { + if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) { xprt_release(task); task->tk_action = call_reserve; rpc_delay(task, HZ>>4); @@ -460,10 +474,10 @@ call_encode(struct rpc_task *task) dprintk("RPC: %4d call_encode (status %d)\n", task->tk_pid, task->tk_status); - task->tk_action = call_transmit; + task->tk_action = call_bind; /* Default buffer setup */ - bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE; + bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE; req->rq_svec[0].iov_base = task->tk_buffer; req->rq_svec[0].iov_len = bufsiz; req->rq_slen = 0; @@ -474,23 +488,16 @@ call_encode(struct rpc_task *task) req->rq_rnr = 1; req->rq_damaged = 0; - if (task->tk_proc > clnt->cl_maxproc) { - printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", - clnt->cl_protname, clnt->cl_vers, task->tk_proc); - rpc_exit(task, -EIO); - return; - } - /* Zero buffer so we have automatic zero-padding of opaque & string */ memset(task->tk_buffer, 0, bufsiz); /* Encode header and provided arguments */ - encode = rpcproc_encode(clnt, task->tk_proc); + encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc); if (!(p = call_header(task))) { - printk("RPC: call_header failed, exit EIO\n"); + printk(KERN_INFO "RPC: call_header failed, exit EIO\n"); rpc_exit(task, -EIO); } else - if (encode && (status = encode(req, p, task->tk_argp)) < 0) { + if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) { printk(KERN_WARNING "%s: can't encode arguments: %d\n", clnt->cl_protname, -status); rpc_exit(task, status); @@ -498,38 +505,59 @@ call_encode(struct rpc_task *task) } /* - * 4. Transmit the RPC request + * 4. Get the server port number if not yet set */ static void -call_transmit(struct rpc_task *task) +call_bind(struct rpc_task *task) { - dprintk("RPC: %4d call_transmit (status %d)\n", - task->tk_pid, task->tk_status); + struct rpc_clnt *clnt = task->tk_client; + struct rpc_xprt *xprt = clnt->cl_xprt; - task->tk_action = call_receive; - task->tk_status = 0; - xprt_transmit(task); + task->tk_action = (xprt->connected) ? call_transmit : call_reconnect; + + if (!clnt->cl_port) { + task->tk_action = call_reconnect; + task->tk_timeout = clnt->cl_timeout.to_maxval; + rpc_getport(task, clnt); + } } /* - * 5. Wait for the RPC reply + * 4a. Reconnect to the RPC server (TCP case) */ static void -call_receive(struct rpc_task *task) +call_reconnect(struct rpc_task *task) { - dprintk("RPC: %4d call_receive (status %d)\n", - task->tk_pid, task->tk_status); + struct rpc_clnt *clnt = task->tk_client; - task->tk_action = call_status; + dprintk("RPC: %4d call_reconnect status %d\n", + task->tk_pid, task->tk_status); - /* Need to ensure cleanups are performed by xprt_receive_status() */ - xprt_receive(task); + task->tk_action = call_transmit; + if (task->tk_status < 0 || !clnt->cl_xprt->stream) + return; + clnt->cl_stats->netreconn++; + xprt_reconnect(task); +} - /* If we have no decode function, this means we're performing - * a void call (a la lockd message passing). */ - if (!rpcproc_decode(task->tk_client, task->tk_proc)) { - task->tk_action = NULL; +/* + * 5. Transmit the RPC request, and wait for reply + */ +static void +call_transmit(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + + dprintk("RPC: %4d call_transmit (status %d)\n", + task->tk_pid, task->tk_status); + + task->tk_action = call_status; + if (task->tk_status < 0) return; + xprt_transmit(task); + if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc)) { + task->tk_action = NULL; + rpc_wake_up_task(task); } } @@ -558,34 +586,30 @@ call_status(struct rpc_task *task) case -ETIMEDOUT: task->tk_action = call_timeout; break; - case -EAGAIN: - if (!req) - task->tk_action = call_reserve; - else if (!task->tk_buffer) - task->tk_action = call_allocate; - else if (req->rq_damaged) { - task->tk_action = call_encode; - clnt->cl_stats->rpcretrans++; - } else { - task->tk_action = call_transmit; - clnt->cl_stats->rpcretrans++; - } - break; case -ECONNREFUSED: case -ENOTCONN: + req->rq_bytes_sent = 0; if (clnt->cl_autobind || !clnt->cl_port) { clnt->cl_port = 0; task->tk_action = call_bind; - } else if (xprt->stream) + break; + } + if (xprt->stream) { task->tk_action = call_reconnect; - else { - rpc_delay(task, 5*HZ); /* Hope it all wears off */ - if (req->rq_damaged) - task->tk_action = call_encode; - else - task->tk_action = call_transmit; - clnt->cl_stats->rpcretrans++; + break; } + /* + * Sleep and dream of an open connection + */ + task->tk_timeout = 5 * HZ; + rpc_sleep_on(&xprt->sending, task, NULL, NULL); + case -ENOMEM: + case -EAGAIN: + if (req->rq_damaged) + task->tk_action = call_encode; + else + task->tk_action = call_transmit; + clnt->cl_stats->rpcretrans++; break; default: if (clnt->cl_chatty) @@ -614,15 +638,13 @@ call_timeout(struct rpc_task *task) task->tk_pid); goto minor_timeout; } - to->to_initval <<= 1; - if (to->to_initval > to->to_maxval) - to->to_initval = to->to_maxval; + to->to_retries = clnt->cl_timeout.to_retries; } dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid); if (clnt->cl_softrtry) { if (clnt->cl_chatty && !task->tk_exit) - printk("%s: server %s not responding, timed out\n", + printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); rpc_exit(task, -EIO); return; @@ -630,24 +652,26 @@ call_timeout(struct rpc_task *task) if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) { task->tk_flags |= RPC_CALL_MAJORSEEN; if (req) - printk("%s: server %s not responding, still trying\n", + printk(KERN_NOTICE "%s: server %s not responding, still trying\n", clnt->cl_protname, clnt->cl_server); else - printk("%s: task %d can't get a request slot\n", + printk(KERN_NOTICE "%s: task %d can't get a request slot\n", clnt->cl_protname, task->tk_pid); } if (clnt->cl_autobind) clnt->cl_port = 0; minor_timeout: - if (!clnt->cl_port) { + if (!req) + task->tk_action = call_reserve; + else if (req->rq_damaged) { + task->tk_action = call_encode; + clnt->cl_stats->rpcretrans++; + } else if (!clnt->cl_port) { task->tk_action = call_bind; + clnt->cl_stats->rpcretrans++; } else if (clnt->cl_xprt->stream && !clnt->cl_xprt->connected) { task->tk_action = call_reconnect; - } else if (!req) { - task->tk_action = call_reserve; - } else if (req->rq_damaged) { - task->tk_action = call_encode; clnt->cl_stats->rpcretrans++; } else { task->tk_action = call_transmit; @@ -656,21 +680,6 @@ minor_timeout: task->tk_status = 0; } -/* - * 6b. Reconnect to the RPC server (TCP case) - */ -static void -call_reconnect(struct rpc_task *task) -{ - struct rpc_clnt *clnt = task->tk_client; - dprintk("RPC: %4d call_reconnect status %d\n", - task->tk_pid, task->tk_status); - task->tk_action = call_reserve; - task->tk_status = 0; - clnt->cl_stats->netreconn++; - xprt_reconnect(task); -} - /* * 7. Decode the RPC reply */ @@ -679,20 +688,20 @@ call_decode(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; struct rpc_rqst *req = task->tk_rqstp; - kxdrproc_t decode = rpcproc_decode(clnt, task->tk_proc); + kxdrproc_t decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc); u32 *p; dprintk("RPC: %4d call_decode (status %d)\n", task->tk_pid, task->tk_status); if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) { - printk("%s: server %s OK\n", + printk(KERN_NOTICE "%s: server %s OK\n", clnt->cl_protname, clnt->cl_server); task->tk_flags &= ~RPC_CALL_MAJORSEEN; } if (task->tk_status < 12) { - printk("%s: too small RPC reply size (%d bytes)\n", + printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n", clnt->cl_protname, task->tk_status); rpc_exit(task, -EIO); return; @@ -719,7 +728,7 @@ call_decode(struct rpc_task *task) task->tk_action = NULL; if (decode) - task->tk_status = decode(req, p, task->tk_resp); + task->tk_status = decode(req, p, task->tk_msg.rpc_resp); dprintk("RPC: %4d call_decode result %d\n", task->tk_pid, task->tk_status); } @@ -751,7 +760,7 @@ call_refreshresult(struct rpc_task *task) if (task->tk_status < 0) rpc_exit(task, -EACCES); else - task->tk_action = call_bind; + task->tk_action = call_reserve; } /* @@ -772,7 +781,7 @@ call_header(struct rpc_task *task) *p++ = htonl(RPC_VERSION); /* RPC version */ *p++ = htonl(clnt->cl_prog); /* program number */ *p++ = htonl(clnt->cl_vers); /* program version */ - *p++ = htonl(task->tk_proc); /* procedure */ + *p++ = htonl(task->tk_msg.rpc_proc); /* procedure */ return rpcauth_marshcred(task, p); } @@ -787,14 +796,14 @@ call_verify(struct rpc_task *task) p += 1; /* skip XID */ if ((n = ntohl(*p++)) != RPC_REPLY) { - printk("call_verify: not an RPC reply: %x\n", n); + printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n); goto garbage; } if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { int error = -EACCES; if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) { - printk("call_verify: RPC call rejected: %x\n", n); + printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n); } else switch ((n = ntohl(*p++))) { case RPC_AUTH_REJECTEDCRED: @@ -816,10 +825,10 @@ call_verify(struct rpc_task *task) task->tk_action = call_encode; return NULL; case RPC_AUTH_TOOWEAK: - printk("call_verify: server requires stronger " + printk(KERN_NOTICE "call_verify: server requires stronger " "authentication.\n"); default: - printk("call_verify: unknown auth error: %x\n", n); + printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n); error = -EIO; } dprintk("RPC: %4d call_verify: call rejected %d\n", @@ -828,7 +837,7 @@ call_verify(struct rpc_task *task) return NULL; } if (!(p = rpcauth_checkverf(task, p))) { - printk("call_verify: auth check failed\n"); + printk(KERN_WARNING "call_verify: auth check failed\n"); goto garbage; /* bad verifier, retry */ } switch ((n = ntohl(*p++))) { @@ -837,7 +846,7 @@ call_verify(struct rpc_task *task) case RPC_GARBAGE_ARGS: break; /* retry */ default: - printk("call_verify: server accept status: %x\n", n); + printk(KERN_WARNING "call_verify: server accept status: %x\n", n); /* Also retry */ } @@ -845,11 +854,11 @@ garbage: dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid); task->tk_client->cl_stats->rpcgarbage++; if (task->tk_garb_retry--) { - printk("RPC: garbage, retrying %4d\n", task->tk_pid); + printk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid); task->tk_action = call_encode; return NULL; } - printk("RPC: garbage, exit EIO\n"); + printk(KERN_WARNING "RPC: garbage, exit EIO\n"); rpc_exit(task, -EIO); return NULL; } diff --git a/net/sunrpc/clnt.c.rej b/net/sunrpc/clnt.c.rej new file mode 100644 index 000000000000..492d9670e26b --- /dev/null +++ b/net/sunrpc/clnt.c.rej @@ -0,0 +1,248 @@ +*************** +*** 290,296 **** + rpc_call_setup(struct rpc_task *task, u32 proc, + void *argp, void *resp, int flags) + { +- task->tk_action = call_reserve; + task->tk_proc = proc; + task->tk_argp = argp; + task->tk_resp = resp; +--- 291,297 ---- + rpc_call_setup(struct rpc_task *task, u32 proc, + void *argp, void *resp, int flags) + { ++ task->tk_action = call_bind; + task->tk_proc = proc; + task->tk_argp = argp; + task->tk_resp = resp; +*************** +*** 312,322 **** + rpc_release_task(task); + return; + } +- task->tk_action = call_reserve; + rpcproc_count(task->tk_client, task->tk_proc)++; + } + + /* + * 1. Reserve an RPC call slot + */ + static void +--- 313,342 ---- + rpc_release_task(task); + return; + } ++ task->tk_action = call_bind; + rpcproc_count(task->tk_client, task->tk_proc)++; + } + + /* ++ * 0. Get the server port number if not yet set ++ */ ++ static void ++ call_bind(struct rpc_task *task) ++ { ++ struct rpc_clnt *clnt = task->tk_client; ++ struct rpc_xprt *xprt = clnt->cl_xprt; ++ ++ if (xprt->stream && !xprt->connected) ++ task->tk_action = call_reconnect; ++ else ++ task->tk_action = call_reserve; ++ task->tk_status = 0; ++ ++ if (!clnt->cl_port) ++ rpc_getport(task, clnt); ++ } ++ ++ /* + * 1. Reserve an RPC call slot + */ + static void +*************** +*** 324,345 **** + { + struct rpc_clnt *clnt = task->tk_client; + +- if (task->tk_proc > clnt->cl_maxproc) { +- printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", +- clnt->cl_protname, clnt->cl_vers, task->tk_proc); + rpc_exit(task, -EIO); + return; + } +- +- dprintk("RPC: %4d call_reserve\n", task->tk_pid); + if (!rpcauth_uptodatecred(task)) { + task->tk_action = call_refresh; + return; + } +- +- task->tk_status = 0; + task->tk_action = call_reserveresult; + task->tk_timeout = clnt->cl_timeout.to_resrvval; + clnt->cl_stats->rpccnt++; + xprt_reserve(task); + } +--- 344,369 ---- + { + struct rpc_clnt *clnt = task->tk_client; + ++ dprintk("RPC: %4d call_reserve\n", task->tk_pid); ++ if (!clnt->cl_port) { ++ printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n", ++ clnt->cl_protname, clnt->cl_server, ++ clnt->cl_softrtry? "giving up" : "retrying"); ++ if (!clnt->cl_softrtry) { ++ task->tk_action = call_bind; ++ rpc_delay(task, 5*HZ); ++ return; ++ } + rpc_exit(task, -EIO); + return; + } + if (!rpcauth_uptodatecred(task)) { + task->tk_action = call_refresh; + return; + } + task->tk_action = call_reserveresult; + task->tk_timeout = clnt->cl_timeout.to_resrvval; ++ task->tk_status = 0; + clnt->cl_stats->rpccnt++; + xprt_reserve(task); + } +*************** +*** 452,464 **** + req->rq_rnr = 1; + req->rq_damaged = 0; + + /* Zero buffer so we have automatic zero-padding of opaque & string */ + memset(task->tk_buffer, 0, bufsiz); + + /* Encode header and provided arguments */ + encode = rpcproc_encode(clnt, task->tk_proc); + if (!(p = call_header(task))) { +- printk(KERN_INFO "RPC: call_header failed, exit EIO\n"); + rpc_exit(task, -EIO); + } else + if (encode && (status = encode(req, p, task->tk_argp)) < 0) { +--- 474,493 ---- + req->rq_rnr = 1; + req->rq_damaged = 0; + ++ if (task->tk_proc > clnt->cl_maxproc) { ++ printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", ++ clnt->cl_protname, clnt->cl_vers, task->tk_proc); ++ rpc_exit(task, -EIO); ++ return; ++ } ++ + /* Zero buffer so we have automatic zero-padding of opaque & string */ + memset(task->tk_buffer, 0, bufsiz); + + /* Encode header and provided arguments */ + encode = rpcproc_encode(clnt, task->tk_proc); + if (!(p = call_header(task))) { ++ printk("RPC: call_header failed, exit EIO\n"); + rpc_exit(task, -EIO); + } else + if (encode && (status = encode(req, p, task->tk_argp)) < 0) { +*************** +*** 469,527 **** + } + + /* +- * 4. Get the server port number if not yet set + */ + static void +- call_bind(struct rpc_task *task) + { +- struct rpc_clnt *clnt = task->tk_client; +- struct rpc_xprt *xprt = clnt->cl_xprt; + +- task->tk_action = (xprt->connected) ? call_transmit : call_reconnect; +- +- if (!clnt->cl_port) { +- task->tk_action = call_reconnect; +- task->tk_timeout = clnt->cl_timeout.to_maxval; +- rpc_getport(task, clnt); +- } + } + + /* +- * 4a. Reconnect to the RPC server (TCP case) + */ + static void +- call_reconnect(struct rpc_task *task) + { +- struct rpc_clnt *clnt = task->tk_client; + +- dprintk("RPC: %4d call_reconnect status %d\n", +- task->tk_pid, task->tk_status); + +- task->tk_action = call_transmit; +- if (task->tk_status < 0 || !clnt->cl_xprt->stream) +- return; +- clnt->cl_stats->netreconn++; +- xprt_reconnect(task); +- } + +- /* +- * 5. Transmit the RPC request, and wait for reply +- */ +- static void +- call_transmit(struct rpc_task *task) +- { +- struct rpc_clnt *clnt = task->tk_client; +- +- dprintk("RPC: %4d call_transmit (status %d)\n", +- task->tk_pid, task->tk_status); +- +- task->tk_action = call_status; +- if (task->tk_status < 0) + return; +- xprt_transmit(task); +- if (!rpcproc_decode(clnt, task->tk_proc)) { +- task->tk_action = NULL; +- rpc_wake_up_task(task); + } + } + +--- 498,535 ---- + } + + /* ++ * 4. Transmit the RPC request + */ + static void ++ call_transmit(struct rpc_task *task) + { ++ dprintk("RPC: %4d call_transmit (status %d)\n", ++ task->tk_pid, task->tk_status); + ++ task->tk_action = call_receive; ++ task->tk_status = 0; ++ xprt_transmit(task); + } + + /* ++ * 5. Wait for the RPC reply + */ + static void ++ call_receive(struct rpc_task *task) + { ++ dprintk("RPC: %4d call_receive (status %d)\n", ++ task->tk_pid, task->tk_status); + ++ task->tk_action = call_status; + ++ /* Need to ensure cleanups are performed by xprt_receive_status() */ ++ xprt_receive(task); + ++ /* If we have no decode function, this means we're performing ++ * a void call (a la lockd message passing). */ ++ if (!rpcproc_decode(task->tk_client, task->tk_proc)) { ++ task->tk_action = NULL; + return; + } + } + diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index 2bbe9d50a68d..6afb28e88744 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -41,6 +41,7 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) { struct rpc_portmap *map = &clnt->cl_pmap; struct sockaddr_in *sap = &clnt->cl_xprt->addr; + struct rpc_message msg = { PMAP_GETPORT, map, &clnt->cl_port, NULL }; struct rpc_clnt *pmap_clnt; struct rpc_task *child; @@ -66,7 +67,7 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) goto bailout; /* Setup the call info struct */ - rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0); + rpc_call_setup(child, &msg, 0); /* ... and run the child task */ rpc_run_child(task, child, pmap_getport_done); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index ffd4c18ad5a2..a33c4055b645 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -68,6 +68,16 @@ static unsigned int rpciod_users = 0; static pid_t rpciod_pid = 0; static int rpc_inhibit = 0; +/* + * Spinlock for wait queues. Access to the latter also has to be + * interrupt-safe in order to allow timers to wake up sleeping tasks. + */ +spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED; +/* + * Spinlock for other critical sections of code. + */ +spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED; + /* * This is the last-ditch buffer for NFS swap requests */ @@ -87,14 +97,52 @@ static __inline__ void rpc_unlock_swapbuf(void) } /* - * Spinlock for wait queues. Access to the latter also has to be - * interrupt-safe in order to allow timers to wake up sleeping tasks. + * Set up a timer for the current task. */ -spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED; +static inline void +__rpc_add_timer(struct rpc_task *task, rpc_action timer) +{ + if (!task->tk_timeout) + return; + + dprintk("RPC: %4d setting alarm for %lu ms\n", + task->tk_pid, task->tk_timeout * 1000 / HZ); + + if (timer_pending(&task->tk_timer)) { + printk(KERN_ERR "RPC: Bug! Overwriting active timer\n"); + del_timer(&task->tk_timer); + } + if (!timer) + timer = __rpc_default_timer; + init_timer(&task->tk_timer); + task->tk_timer.expires = jiffies + task->tk_timeout; + task->tk_timer.data = (unsigned long) task; + task->tk_timer.function = (void (*)(unsigned long)) timer; + add_timer(&task->tk_timer); +} + /* - * Spinlock for other critical sections of code. + * Set up a timer for an already sleeping task. */ -spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED; +void rpc_add_timer(struct rpc_task *task, rpc_action timer) +{ + spin_lock_bh(&rpc_queue_lock); + if (!(RPC_IS_RUNNING(task) || task->tk_wakeup)) + __rpc_add_timer(task, timer); + spin_unlock_bh(&rpc_queue_lock); +} + +/* + * Delete any timer for the current task. + */ +static inline void +__rpc_del_timer(struct rpc_task *task) +{ + dprintk("RPC: %4d deleting timer\n", task->tk_pid); + if (timer_pending(&task->tk_timer)) + del_timer(&task->tk_timer); + task->tk_timeout = 0; +} /* * Add new request to wait queue. @@ -104,16 +152,15 @@ spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED; * improve overall performance. * Everyone else gets appended to the queue to ensure proper FIFO behavior. */ -static int +static inline int __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) { - if (task->tk_rpcwait) { - if (task->tk_rpcwait != queue) - { - printk(KERN_WARNING "RPC: doubly enqueued task!\n"); - return -EWOULDBLOCK; - } + if (task->tk_rpcwait == queue) return 0; + + if (task->tk_rpcwait) { + printk(KERN_WARNING "RPC: doubly enqueued task!\n"); + return -EWOULDBLOCK; } if (RPC_IS_SWAPPER(task)) rpc_insert_list(&queue->task, task); @@ -130,7 +177,7 @@ __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) int rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task) { - int result; + int result; spin_lock_bh(&rpc_queue_lock); result = __rpc_add_wait_queue(q, task); @@ -142,13 +189,14 @@ rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task) * Remove request from queue. * Note: must be called with spin lock held. */ -static void +static inline void __rpc_remove_wait_queue(struct rpc_task *task) { - struct rpc_wait_queue *queue; + struct rpc_wait_queue *queue = task->tk_rpcwait; - if (!(queue = task->tk_rpcwait)) + if (!queue) return; + rpc_remove_list(&queue->task, task); task->tk_rpcwait = NULL; @@ -159,50 +207,13 @@ __rpc_remove_wait_queue(struct rpc_task *task) void rpc_remove_wait_queue(struct rpc_task *task) { + if (!task->tk_rpcwait) + return; spin_lock_bh(&rpc_queue_lock); __rpc_remove_wait_queue(task); spin_unlock_bh(&rpc_queue_lock); } -/* - * Set up a timer for the current task. - */ -inline void -rpc_add_timer(struct rpc_task *task, rpc_action timer) -{ - unsigned long expires = jiffies + task->tk_timeout; - - dprintk("RPC: %4d setting alarm for %lu ms\n", - task->tk_pid, task->tk_timeout * 1000 / HZ); - if (!timer) - timer = __rpc_default_timer; - if (time_before(expires, jiffies)) { - printk(KERN_ERR "RPC: bad timeout value %ld - setting to 10 sec!\n", - task->tk_timeout); - expires = jiffies + 10 * HZ; - } - task->tk_timer.expires = expires; - task->tk_timer.data = (unsigned long) task; - task->tk_timer.function = (void (*)(unsigned long)) timer; - task->tk_timer.prev = NULL; - task->tk_timer.next = NULL; - add_timer(&task->tk_timer); -} - -/* - * Delete any timer for the current task. - * Must be called with interrupts off. - */ -inline void -rpc_del_timer(struct rpc_task *task) -{ - if (task->tk_timeout) { - dprintk("RPC: %4d deleting timer\n", task->tk_pid); - del_timer(&task->tk_timer); - task->tk_timeout = 0; - } -} - /* * Make an RPC task runnable. * @@ -218,31 +229,44 @@ rpc_make_runnable(struct rpc_task *task) } task->tk_flags |= RPC_TASK_RUNNING; if (RPC_IS_ASYNC(task)) { - int status; - status = __rpc_add_wait_queue(&schedq, task); - if (status) - { - printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); - task->tk_status = status; + if (RPC_IS_SLEEPING(task)) { + int status; + status = __rpc_add_wait_queue(&schedq, task); + if (status < 0) { + printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); + task->tk_status = status; + } else + task->tk_sleeping = 0; } wake_up(&rpciod_idle); } else { + task->tk_sleeping = 0; wake_up(&task->tk_wait); } } +/* + * Place a newly initialized task on the schedq. + */ +static inline void +rpc_schedule_run(struct rpc_task *task) +{ + /* Don't run a child twice! */ + if (RPC_IS_ACTIVATED(task)) + return; + task->tk_active = 1; + task->tk_sleeping = 1; + rpc_make_runnable(task); +} /* * For other people who may need to wake the I/O daemon * but should (for now) know nothing about its innards */ - void rpciod_wake_up(void) { if(rpciod_pid==0) - { printk(KERN_ERR "rpciod: wot no daemon?\n"); - } wake_up(&rpciod_idle); } @@ -261,19 +285,25 @@ __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid, rpc_qname(q), jiffies); + if (!RPC_IS_ASYNC(task) && !RPC_IS_ACTIVATED(task)) { + printk(KERN_ERR "RPC: Inactive synchronous task put to sleep!\n"); + return; + } + + /* Mark the task as being activated if so needed */ + if (!RPC_IS_ACTIVATED(task)) { + task->tk_active = 1; + task->tk_sleeping = 1; + } + status = __rpc_add_wait_queue(q, task); - if (status) - { + if (status) { printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); task->tk_status = status; - task->tk_flags |= RPC_TASK_RUNNING; - } - else - { - task->tk_callback = action; - if (task->tk_timeout) - rpc_add_timer(task, timer); + } else { task->tk_flags &= ~RPC_TASK_RUNNING; + task->tk_callback = action; + __rpc_add_timer(task, timer); } return; @@ -291,6 +321,19 @@ rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, spin_unlock_bh(&rpc_queue_lock); } +void +rpc_sleep_locked(struct rpc_wait_queue *q, struct rpc_task *task, + rpc_action action, rpc_action timer) +{ + /* + * Protect the queue operations. + */ + spin_lock_bh(&rpc_queue_lock); + __rpc_sleep_on(q, task, action, timer); + rpc_lock_task(task); + spin_unlock_bh(&rpc_queue_lock); +} + /* * Wake up a single task -- must be invoked with spin lock held. * @@ -307,16 +350,33 @@ __rpc_wake_up(struct rpc_task *task) if (task->tk_magic != 0xf00baa) { printk(KERN_ERR "RPC: attempt to wake up non-existing task!\n"); rpc_debug = ~0; + rpc_show_tasks(); return; } #endif - rpc_del_timer(task); + /* Has the task been executed yet? If not, we cannot wake it up! */ + if (!RPC_IS_ACTIVATED(task)) { + printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task); + return; + } + if (RPC_IS_RUNNING(task)) + return; + + __rpc_del_timer(task); + + /* If the task has been locked, then set tk_wakeup so that + * rpc_unlock_task() wakes us up... */ + if (task->tk_lock) { + task->tk_wakeup = 1; + return; + } else + task->tk_wakeup = 0; + if (task->tk_rpcwait != &schedq) __rpc_remove_wait_queue(task); - if (!RPC_IS_RUNNING(task)) { - task->tk_flags |= RPC_TASK_CALLBACK; - rpc_make_runnable(task); - } + task->tk_flags |= RPC_TASK_CALLBACK; + rpc_make_runnable(task); + dprintk("RPC: __rpc_wake_up done\n"); } @@ -338,6 +398,8 @@ __rpc_default_timer(struct rpc_task *task) void rpc_wake_up_task(struct rpc_task *task) { + if (RPC_IS_RUNNING(task)) + return; spin_lock_bh(&rpc_queue_lock); __rpc_wake_up(task); spin_unlock_bh(&rpc_queue_lock); @@ -388,6 +450,30 @@ rpc_wake_up_status(struct rpc_wait_queue *queue, int status) spin_unlock_bh(&rpc_queue_lock); } +/* + * Lock down a sleeping task to prevent it from waking up + * and disappearing from beneath us. + * + * This function should always be called with the + * rpc_queue_lock held. + */ +int +rpc_lock_task(struct rpc_task *task) +{ + if (!RPC_IS_RUNNING(task)) + return ++task->tk_lock; + return 0; +} + +void +rpc_unlock_task(struct rpc_task *task) +{ + spin_lock_bh(&rpc_queue_lock); + if (task->tk_lock && !--task->tk_lock && task->tk_wakeup) + __rpc_wake_up(task); + spin_unlock_bh(&rpc_queue_lock); +} + /* * Run a task at a later time */ @@ -426,7 +512,7 @@ __rpc_execute(struct rpc_task *task) /* * Execute any pending callback. */ - if (task->tk_flags & RPC_TASK_CALLBACK) { + if (RPC_DO_CALLBACK(task)) { /* Define a callback save pointer */ void (*save_callback)(struct rpc_task *); @@ -445,102 +531,90 @@ __rpc_execute(struct rpc_task *task) } } - /* - * No handler for next step means exit. - */ - if (!task->tk_action) - break; - /* * Perform the next FSM step. * tk_action may be NULL when the task has been killed * by someone else. */ - if (RPC_IS_RUNNING(task) && task->tk_action) + if (RPC_IS_RUNNING(task)) { + if (!task->tk_action) + break; task->tk_action(task); + } /* * Check whether task is sleeping. - * Note that if the task may go to sleep in tk_action, + * Note that if the task goes to sleep in tk_action, * and the RPC reply arrives before we get here, it will * have state RUNNING, but will still be on schedq. + * 27/9/99: The above has been attempted fixed by + * introduction of task->tk_sleeping. */ spin_lock_bh(&rpc_queue_lock); - if (RPC_IS_RUNNING(task)) { - if (task->tk_rpcwait == &schedq) - __rpc_remove_wait_queue(task); - } else while (!RPC_IS_RUNNING(task)) { + if (!RPC_IS_RUNNING(task)) { + task->tk_sleeping = 1; if (RPC_IS_ASYNC(task)) { spin_unlock_bh(&rpc_queue_lock); return 0; } + } else + task->tk_sleeping = 0; + spin_unlock_bh(&rpc_queue_lock); + while (RPC_IS_SLEEPING(task)) { /* sync task: sleep here */ dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); if (current->pid == rpciod_pid) printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); - spin_unlock_bh(&rpc_queue_lock); - __wait_event(task->tk_wait, RPC_IS_RUNNING(task)); - spin_lock_bh(&rpc_queue_lock); + __wait_event(task->tk_wait, !RPC_IS_SLEEPING(task)); + dprintk("RPC: %4d sync task resuming\n", task->tk_pid); /* - * When the task received a signal, remove from - * any queues etc, and make runnable again. + * When a sync task receives a signal, it exits with + * -ERESTARTSYS. In order to catch any callbacks that + * clean up after sleeping on some queue, we don't + * break the loop here, but go around once more. */ - if (signalled()) - __rpc_wake_up(task); - - dprintk("RPC: %4d sync task resuming\n", - task->tk_pid); - } - spin_unlock_bh(&rpc_queue_lock); - - /* - * When a sync task receives a signal, it exits with - * -ERESTARTSYS. In order to catch any callbacks that - * clean up after sleeping on some queue, we don't - * break the loop here, but go around once more. - */ - if (!RPC_IS_ASYNC(task) && signalled()) { - dprintk("RPC: %4d got signal\n", task->tk_pid); - rpc_exit(task, -ERESTARTSYS); + if (task->tk_client->cl_intr && signalled()) { + dprintk("RPC: %4d got signal\n", task->tk_pid); + task->tk_flags |= RPC_TASK_KILLED; + rpc_exit(task, -ERESTARTSYS); + rpc_wake_up_task(task); + } } } dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status); - if (task->tk_exit) { - status = task->tk_status; + status = task->tk_status; + if (task->tk_exit) task->tk_exit(task); - } return status; } /* * User-visible entry point to the scheduler. - * The recursion protection is for debugging. It should go away once - * the code has stabilized. + * + * This may be called recursively if e.g. an async NFS task updates + * the attributes and finds that dirty pages must be flushed. */ -void +int rpc_execute(struct rpc_task *task) { - static int executing = 0; - int incr = RPC_IS_ASYNC(task)? 1 : 0; - - if (incr) { - if (rpc_inhibit) { - printk(KERN_INFO "RPC: execution inhibited!\n"); - return; - } - if (executing) - printk(KERN_WARNING "RPC: %d tasks executed\n", executing); + if (rpc_inhibit) { + printk(KERN_INFO "RPC: execution inhibited!\n"); + return -EIO; + } + task->tk_flags |= RPC_TASK_RUNNING; + if (task->tk_active) { + printk(KERN_ERR "RPC: active task was run twice!\n"); + return -EWOULDBLOCK; } + task->tk_active = 1; - executing += incr; - __rpc_execute(task); - executing -= incr; + return __rpc_execute(task); } /* @@ -551,28 +625,33 @@ __rpc_schedule(void) { struct rpc_task *task; int count = 0; - int need_resched = current->need_resched; dprintk("RPC: rpc_schedule enter\n"); while (1) { + /* Ensure equal rights for tcp tasks... */ + rpciod_tcp_dispatcher(); + spin_lock_bh(&rpc_queue_lock); if (!(task = schedq.task)) { spin_unlock_bh(&rpc_queue_lock); break; } - rpc_del_timer(task); + if (task->tk_lock) { + spin_unlock_bh(&rpc_queue_lock); + printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n"); + rpc_debug = ~0; + rpc_show_tasks(); + break; + } __rpc_remove_wait_queue(task); - task->tk_flags |= RPC_TASK_RUNNING; spin_unlock_bh(&rpc_queue_lock); __rpc_execute(task); - if (++count >= 200) { + if (++count >= 200 || current->need_resched) { count = 0; - need_resched = 1; - } - if (need_resched) schedule(); + } } dprintk("RPC: rpc_schedule leave\n"); } @@ -646,8 +725,9 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, rpc_action callback, int flags) { memset(task, 0, sizeof(*task)); + init_timer(&task->tk_timer); task->tk_client = clnt; - task->tk_flags = RPC_TASK_RUNNING | flags; + task->tk_flags = flags; task->tk_exit = callback; init_waitqueue_head(&task->tk_wait); if (current->uid != current->fsuid || current->gid != current->fsgid) @@ -717,6 +797,15 @@ rpc_release_task(struct rpc_task *task) dprintk("RPC: %4d release task\n", task->tk_pid); +#ifdef RPC_DEBUG + if (task->tk_magic != 0xf00baa) { + printk(KERN_ERR "RPC: attempt to release a non-existing task!\n"); + rpc_debug = ~0; + rpc_show_tasks(); + return; + } +#endif + /* Remove from global task list */ spin_lock(&rpc_sched_lock); prev = task->tk_prev_task; @@ -734,18 +823,20 @@ rpc_release_task(struct rpc_task *task) spin_lock_bh(&rpc_queue_lock); /* Delete any running timer */ - rpc_del_timer(task); + __rpc_del_timer(task); /* Remove from any wait queue we're still on */ __rpc_remove_wait_queue(task); + task->tk_active = 0; + spin_unlock_bh(&rpc_queue_lock); /* Release resources */ if (task->tk_rqstp) xprt_release(task); - if (task->tk_cred) - rpcauth_releasecred(task); + if (task->tk_msg.rpc_cred) + rpcauth_unbindcred(task); if (task->tk_buffer) { rpc_free(task->tk_buffer); task->tk_buffer = NULL; @@ -824,7 +915,7 @@ rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func) spin_lock_bh(&rpc_queue_lock); /* N.B. Is it possible for the child to have already finished? */ __rpc_sleep_on(&childq, task, func, NULL); - rpc_make_runnable(child); + rpc_schedule_run(child); spin_unlock_bh(&rpc_queue_lock); } @@ -903,8 +994,6 @@ rpciod(void *ptr) schedule(); rounds = 0; } - dprintk("RPC: rpciod running checking dispatch\n"); - rpciod_tcp_dispatcher(); if (!rpciod_task_pending()) { dprintk("RPC: rpciod back to sleep\n"); @@ -1049,7 +1138,7 @@ void rpc_show_tasks(void) for (; t; t = next) { next = t->tk_next_task; printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n", - t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status, + t->tk_pid, t->tk_msg.rpc_proc, t->tk_flags, t->tk_status, t->tk_client, t->tk_client->cl_prog, t->tk_rqstp, t->tk_timeout, t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " ", diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 9a186104168b..5882c601ac58 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -41,7 +41,8 @@ EXPORT_SYMBOL(rpc_create_client); EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_shutdown_client); EXPORT_SYMBOL(rpc_killall_tasks); -EXPORT_SYMBOL(rpc_do_call); +EXPORT_SYMBOL(rpc_call_sync); +EXPORT_SYMBOL(rpc_call_async); EXPORT_SYMBOL(rpc_call_setup); EXPORT_SYMBOL(rpc_clnt_sigmask); EXPORT_SYMBOL(rpc_clnt_sigunmask); @@ -60,6 +61,7 @@ EXPORT_SYMBOL(rpcauth_init_credcache); EXPORT_SYMBOL(rpcauth_free_credcache); EXPORT_SYMBOL(rpcauth_insert_credcache); EXPORT_SYMBOL(rpcauth_lookupcred); +EXPORT_SYMBOL(rpcauth_bindcred); EXPORT_SYMBOL(rpcauth_matchcred); EXPORT_SYMBOL(rpcauth_releasecred); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 48dd5623d543..06d682223a38 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -68,7 +68,14 @@ /* Following value should be > 32k + RPC overhead */ #define XPRT_MIN_WRITE_SPACE 35000 +extern spinlock_t rpc_queue_lock; + +/* + * Local variables + */ + /* Spinlock for critical sections in the code. */ +spinlock_t xprt_sock_lock = SPIN_LOCK_UNLOCKED; spinlock_t xprt_lock = SPIN_LOCK_UNLOCKED; #ifdef RPC_DEBUG @@ -86,14 +93,12 @@ spinlock_t xprt_lock = SPIN_LOCK_UNLOCKED; */ static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); static void do_xprt_transmit(struct rpc_task *); -static void xprt_transmit_status(struct rpc_task *task); -static void xprt_transmit_timeout(struct rpc_task *task); -static void xprt_receive_status(struct rpc_task *task); static void xprt_reserve_status(struct rpc_task *task); static void xprt_disconnect(struct rpc_xprt *); -static void xprt_reconn_timeout(struct rpc_task *task); -static struct socket *xprt_create_socket(int, struct sockaddr_in *, - struct rpc_timeout *); +static void xprt_reconn_status(struct rpc_task *task); +static struct socket *xprt_create_socket(int, struct rpc_timeout *); +static int xprt_bind_socket(struct rpc_xprt *, struct socket *); +static void xprt_remove_pending(struct rpc_xprt *); #ifdef RPC_DEBUG_DATA /* @@ -140,7 +145,7 @@ xprt_from_sock(struct sock *sk) */ extern inline void -xprt_move_iov(struct msghdr *msg, struct iovec *niv, int amount) +xprt_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount) { struct iovec *iv=msg->msg_iov; int i; @@ -148,7 +153,7 @@ xprt_move_iov(struct msghdr *msg, struct iovec *niv, int amount) /* * Eat any sent iovecs */ - while(iv->iov_len <= amount) { + while (iv->iov_len <= amount) { amount -= iv->iov_len; iv++; msg->msg_iovlen--; @@ -184,14 +189,17 @@ xprt_sendmsg(struct rpc_xprt *xprt, struct rpc_rqst *req) int slen = req->rq_slen - req->rq_bytes_sent; struct iovec niv[MAX_IOVEC]; - if (slen == 0) + if (slen <= 0) return 0; + if (!sock) + return -ENOTCONN; + xprt_pktdump("packet data:", req->rq_svec->iov_base, req->rq_svec->iov_len); - msg.msg_flags = MSG_DONTWAIT; + msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; msg.msg_iov = req->rq_svec; msg.msg_iovlen = req->rq_snr; msg.msg_name = (struct sockaddr *) &xprt->addr; @@ -238,23 +246,30 @@ xprt_sendmsg(struct rpc_xprt *xprt, struct rpc_rqst *req) /* * Read data from socket */ -static inline int -xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len) +static int +xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, unsigned len, unsigned shift) { struct socket *sock = xprt->sock; - struct sockaddr_in sin; struct msghdr msg; mm_segment_t oldfs; + struct iovec niv[MAX_IOVEC]; int result; - msg.msg_flags = MSG_DONTWAIT; + if (!sock) + return -ENOTCONN; + + msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; msg.msg_iov = iov; msg.msg_iovlen = nr; - msg.msg_name = &sin; - msg.msg_namelen = sizeof(sin); + msg.msg_name = NULL; + msg.msg_namelen = 0; msg.msg_control = NULL; msg.msg_controllen = 0; + /* Adjust the iovec if we've already filled it */ + if (shift) + xprt_move_iov(&msg, niv, shift); + oldfs = get_fs(); set_fs(get_ds()); result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT); set_fs(oldfs); @@ -309,21 +324,30 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) int xprt_adjust_timeout(struct rpc_timeout *to) { - if (to->to_exponential) - to->to_current <<= 1; - else - to->to_current += to->to_increment; - if (to->to_maxval && to->to_current >= to->to_maxval) { - to->to_current = to->to_maxval; - to->to_retries = 0; + if (to->to_retries > 0) { + if (to->to_exponential) + to->to_current <<= 1; + else + to->to_current += to->to_increment; + if (to->to_maxval && to->to_current >= to->to_maxval) + to->to_current = to->to_maxval; + } else { + if (to->to_exponential) + to->to_initval <<= 1; + else + to->to_initval += to->to_increment; + if (to->to_maxval && to->to_initval >= to->to_maxval) + to->to_initval = to->to_maxval; + to->to_current = to->to_initval; } + if (!to->to_current) { printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n"); to->to_current = 5 * HZ; } pprintk("RPC: %lu %s\n", jiffies, to->to_retries? "retrans" : "timeout"); - return (to->to_retries)--; + return to->to_retries-- > 0; } /* @@ -332,22 +356,29 @@ xprt_adjust_timeout(struct rpc_timeout *to) static void xprt_close(struct rpc_xprt *xprt) { + struct socket *sock = xprt->sock; struct sock *sk = xprt->inet; - xprt_disconnect(xprt); + if (!sk) + return; + + xprt->inet = NULL; + xprt->sock = NULL; sk->user_data = NULL; sk->data_ready = xprt->old_data_ready; sk->state_change = xprt->old_state_change; sk->write_space = xprt->old_write_space; + + xprt_disconnect(xprt); sk->no_check = 0; - sock_release(xprt->sock); + sock_release(sock); /* * TCP doesnt require the rpciod now - other things may * but rpciod handles that not us. */ - if(xprt->stream && !xprt->connecting) + if(xprt->stream) rpciod_down(); } @@ -360,14 +391,10 @@ xprt_disconnect(struct rpc_xprt *xprt) dprintk("RPC: disconnected transport %p\n", xprt); xprt->connected = 0; xprt->tcp_offset = 0; - xprt->tcp_more = 0; - xprt->tcp_total = 0; - xprt->tcp_reclen = 0; xprt->tcp_copied = 0; - xprt->tcp_rqstp = NULL; - xprt->rx_pending_flag = 0; + xprt->tcp_more = 0; + xprt_remove_pending(xprt); rpc_wake_up_status(&xprt->pending, -ENOTCONN); - rpc_wake_up_status(&xprt->sending, -ENOTCONN); } /* @@ -377,85 +404,87 @@ void xprt_reconnect(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_xprt; - struct socket *sock; - struct sock *inet; + struct socket *sock = xprt->sock; + struct sock *inet = xprt->inet; int status; dprintk("RPC: %4d xprt_reconnect %p connected %d\n", task->tk_pid, xprt, xprt->connected); - task->tk_status = 0; - if (xprt->shutdown) return; if (!xprt->stream) return; - spin_lock_bh(&xprt_lock); - if (xprt->connected) { - spin_unlock_bh(&xprt_lock); + if (!xprt->addr.sin_port) { + task->tk_status = -EIO; return; } + + spin_lock(&xprt_lock); if (xprt->connecting) { - task->tk_timeout = xprt->timeout.to_maxval; + task->tk_timeout = 0; rpc_sleep_on(&xprt->reconn, task, NULL, NULL); - spin_unlock_bh(&xprt_lock); + spin_unlock(&xprt_lock); return; } xprt->connecting = 1; - spin_unlock_bh(&xprt_lock); + spin_unlock(&xprt_lock); - /* Create an unconnected socket */ - if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout))) { - xprt->connecting = 0; - goto defer; + status = -ENOTCONN; + if (!inet) { + /* Create an unconnected socket */ + if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout))) + goto defer; + xprt_bind_socket(xprt, sock); + inet = sock->sk; } - inet = sock->sk; - inet->data_ready = xprt->inet->data_ready; - inet->state_change = xprt->inet->state_change; - inet->write_space = xprt->inet->write_space; - inet->user_data = xprt; - - dprintk("RPC: %4d closing old socket\n", task->tk_pid); - xprt_close(xprt); - - /* Reset to new socket */ - xprt->sock = sock; - xprt->inet = inet; + xprt_disconnect(xprt); /* Now connect it asynchronously. */ dprintk("RPC: %4d connecting new socket\n", task->tk_pid); status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, sizeof(xprt->addr), O_NONBLOCK); - xprt->connecting = 0; if (status < 0) { - if (status != -EINPROGRESS && status != -EALREADY) { + switch (status) { + case -EALREADY: + case -EINPROGRESS: + status = 0; + break; + case -EISCONN: + case -EPIPE: + status = 0; + xprt_close(xprt); + goto defer; + default: printk("RPC: TCP connect error %d!\n", -status); + xprt_close(xprt); goto defer; } dprintk("RPC: %4d connect status %d connected %d\n", task->tk_pid, status, xprt->connected); - task->tk_timeout = 60 * HZ; - spin_lock_bh(&xprt_lock); + spin_lock_bh(&xprt_sock_lock); if (!xprt->connected) { - rpc_sleep_on(&xprt->reconn, task, - NULL, xprt_reconn_timeout); - spin_unlock_bh(&xprt_lock); + task->tk_timeout = xprt->timeout.to_maxval; + rpc_sleep_on(&xprt->reconn, task, xprt_reconn_status, NULL); + spin_unlock_bh(&xprt_sock_lock); return; } - spin_unlock_bh(&xprt_lock); + spin_unlock_bh(&xprt_sock_lock); } - - defer: - spin_lock_bh(&xprt_lock); - if (!xprt->connected) - rpc_wake_up_next(&xprt->reconn); - spin_unlock_bh(&xprt_lock); + spin_lock(&xprt_lock); + xprt->connecting = 0; + if (status < 0) { + rpc_delay(task, 5*HZ); + task->tk_status = -ENOTCONN; + } + rpc_wake_up(&xprt->reconn); + spin_unlock(&xprt_lock); } /* @@ -463,29 +492,21 @@ defer: * process of reconnecting, and leave the rest to the upper layers. */ static void -xprt_reconn_timeout(struct rpc_task *task) +xprt_reconn_status(struct rpc_task *task) { - spin_lock_bh(&xprt_lock); + struct rpc_xprt *xprt = task->tk_xprt; + dprintk("RPC: %4d xprt_reconn_timeout %d\n", task->tk_pid, task->tk_status); - task->tk_status = -ENOTCONN; - if (task->tk_xprt->connecting) - task->tk_xprt->connecting = 0; - if (!task->tk_xprt->connected) - task->tk_status = -ENOTCONN; - else - task->tk_status = -ETIMEDOUT; - task->tk_timeout = 0; - rpc_wake_up_task(task); - spin_unlock_bh(&xprt_lock); + + spin_lock(&xprt_lock); + xprt->connecting = 0; + rpc_wake_up(&xprt->reconn); + spin_unlock(&xprt_lock); } -extern spinlock_t rpc_queue_lock; /* - * Look up the RPC request corresponding to a reply. - * - * RED-PEN: Niiice... Guys, when will we learn finally that locking - * in this manner is NOOP? --ANK + * Look up the RPC request corresponding to a reply, and then lock it. */ static inline struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) @@ -511,6 +532,8 @@ xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid) out_bad: req = NULL; out: + if (req && !rpc_lock_task(req->rq_task)) + req = NULL; spin_unlock_bh(&rpc_queue_lock); return req; } @@ -524,9 +547,6 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) { struct rpc_task *task = req->rq_task; - req->rq_rlen = copied; - req->rq_gotit = 1; - /* Adjust congestion window */ xprt_adjust_cwnd(xprt, copied); @@ -549,12 +569,11 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) } #endif - /* ... and wake up the process. */ dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied); task->tk_status = copied; - if (!RPC_IS_RUNNING(task)) - rpc_wake_up_task(task); + /* ... and wake up the process. */ + rpc_wake_up_task(task); return; } @@ -612,6 +631,7 @@ static int csum_partial_copy_to_page_cache(struct iovec *iov, static inline void udp_data_ready(struct sock *sk, int len) { + struct rpc_task *task; struct rpc_xprt *xprt; struct rpc_rqst *rovr; struct sk_buff *skb; @@ -626,7 +646,10 @@ udp_data_ready(struct sock *sk, int len) dprintk("RPC: udp_data_ready client %p\n", xprt); if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) - goto out_err; + return; + + if (xprt->shutdown) + goto dropit; repsize = skb->len - sizeof(struct udphdr); if (repsize < 4) { @@ -634,14 +657,15 @@ udp_data_ready(struct sock *sk, int len) goto dropit; } - /* Look up the request corresponding to the given XID */ - if (!(rovr = xprt_lookup_rqst(xprt, - *(u32 *) (skb->h.raw + sizeof(struct udphdr))))) + /* Look up and lock the request corresponding to the given XID */ + rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + sizeof(struct udphdr))); + if (!rovr) goto dropit; + task = rovr->rq_task; - dprintk("RPC: %4d received reply\n", rovr->rq_task->tk_pid); + dprintk("RPC: %4d received reply\n", task->tk_pid); xprt_pktdump("packet data:", - (u32 *) (skb->h.raw + sizeof(struct udphdr)), repsize); + (u32 *) (skb->h.raw+sizeof(struct udphdr)), repsize); if ((copied = rovr->rq_rlen) > repsize) copied = repsize; @@ -649,213 +673,287 @@ udp_data_ready(struct sock *sk, int len) rovr->rq_damaged = 1; /* Suck it into the iovec, verify checksum if not done by hw. */ if (csum_partial_copy_to_page_cache(rovr->rq_rvec, skb, copied)) - goto dropit; + goto out_unlock; /* Something worked... */ dst_confirm(skb->dst); xprt_complete_rqst(xprt, rovr, copied); -dropit: + out_unlock: + rpc_unlock_task(task); + + dropit: skb_free_datagram(sk, skb); - return; -out_err: - return; } /* - * TCP record receive routine - * This is not the most efficient code since we call recvfrom twice-- - * first receiving the record marker and XID, then the data. - * - * The optimal solution would be a RPC support in the TCP layer, which - * would gather all data up to the next record marker and then pass us - * the list of all TCP segments ready to be copied. + * TCP read fragment marker */ static inline int -tcp_input_record(struct rpc_xprt *xprt) +tcp_read_fraghdr(struct rpc_xprt *xprt) { - struct rpc_rqst *req; - struct iovec *iov; struct iovec riov; - u32 offset; - int result, maxcpy, reclen, avail, want; + int want, result; - dprintk("RPC: tcp_input_record\n"); + if (xprt->tcp_offset >= xprt->tcp_reclen + sizeof(xprt->tcp_recm)) { + xprt->tcp_offset = 0; + xprt->tcp_reclen = 0; + } + if (xprt->tcp_offset >= sizeof(xprt->tcp_recm)) + goto done; - offset = xprt->tcp_offset; - result = -EAGAIN; - if (offset < 4 || (!xprt->tcp_more && offset < 8)) { - want = (xprt->tcp_more? 4 : 8) - offset; - dprintk("RPC: reading header (%d bytes)\n", want); - riov.iov_base = xprt->tcp_recm.data + offset; + want = sizeof(xprt->tcp_recm) - xprt->tcp_offset; + dprintk("RPC: reading header (%d bytes)\n", want); + do { + riov.iov_base = ((u8*) &xprt->tcp_recm) + xprt->tcp_offset; riov.iov_len = want; - result = xprt_recvmsg(xprt, &riov, 1, want); + result = xprt_recvmsg(xprt, &riov, 1, want, 0); if (result < 0) - goto done; - offset += result; - if (result < want) { - result = -EAGAIN; - goto done; - } + return result; + xprt->tcp_offset += result; + want -= result; + } while (want); - /* Get the record length and mask out the more_fragments bit */ - reclen = ntohl(xprt->tcp_reclen); - dprintk("RPC: reclen %08x\n", reclen); - xprt->tcp_more = (reclen & 0x80000000)? 0 : 1; - reclen &= 0x7fffffff; - xprt->tcp_total += reclen; - xprt->tcp_reclen = reclen; - - dprintk("RPC: got xid %08x reclen %d morefrags %d\n", - xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more); - if (!xprt->tcp_copied - && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) { - iov = xprt->tcp_iovec; - memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0])); -#if 0 -*(u32 *)iov->iov_base = req->rq_xid; -#endif - iov->iov_base += 4; - iov->iov_len -= 4; - xprt->tcp_copied = 4; - xprt->tcp_rqstp = req; - } - } else { - reclen = xprt->tcp_reclen; - } + /* Is this another fragment in the last message */ + if (!xprt->tcp_more) + xprt->tcp_copied = 0; /* No, so we're reading a new message */ + + /* Get the record length and mask out the last fragment bit */ + xprt->tcp_reclen = ntohl(xprt->tcp_recm); + xprt->tcp_more = (xprt->tcp_reclen & 0x80000000) ? 0 : 1; + xprt->tcp_reclen &= 0x7fffffff; + + dprintk("RPC: New record reclen %d morefrags %d\n", + xprt->tcp_reclen, xprt->tcp_more); + done: + return xprt->tcp_reclen + sizeof(xprt->tcp_recm) - xprt->tcp_offset; +} + +/* + * TCP read xid + */ +static inline int +tcp_read_xid(struct rpc_xprt *xprt, int avail) +{ + struct iovec riov; + int want, result; + + if (xprt->tcp_copied >= sizeof(xprt->tcp_xid) || !avail) + goto done; + want = MIN(sizeof(xprt->tcp_xid) - xprt->tcp_copied, avail); + do { + dprintk("RPC: reading xid (%d bytes)\n", want); + riov.iov_base = ((u8*) &xprt->tcp_xid) + xprt->tcp_copied; + riov.iov_len = want; + result = xprt_recvmsg(xprt, &riov, 1, want, 0); + if (result < 0) + return result; + xprt->tcp_copied += result; + xprt->tcp_offset += result; + want -= result; + avail -= result; + } while (want); + done: + return avail; +} - avail = reclen - (offset - 4); - if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid - && req->rq_task->tk_rpcwait == &xprt->pending) { - want = MIN(req->rq_rlen - xprt->tcp_copied, avail); +/* + * TCP read and complete request + */ +static inline int +tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req, int avail) +{ + int want, result; + if (req->rq_rlen <= xprt->tcp_copied || !avail) + goto done; + want = MIN(req->rq_rlen - xprt->tcp_copied, avail); + do { dprintk("RPC: %4d TCP receiving %d bytes\n", - req->rq_task->tk_pid, want); - /* Request must be re-encoded before retransmit */ - req->rq_damaged = 1; - result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want); + req->rq_task->tk_pid, want); + + result = xprt_recvmsg(xprt, req->rq_rvec, req->rq_rnr, want, xprt->tcp_copied); if (result < 0) - goto done; + return result; xprt->tcp_copied += result; - offset += result; + xprt->tcp_offset += result; avail -= result; - if (result < want) { - result = -EAGAIN; - goto done; - } + want -= result; + } while (want); - maxcpy = MIN(req->rq_rlen, xprt->tcp_total); - if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) { - dprintk("RPC: %4d received reply complete\n", - req->rq_task->tk_pid); - xprt_complete_rqst(xprt, req, xprt->tcp_total); - xprt->tcp_copied = 0; - xprt->tcp_rqstp = NULL; - } - } + done: + if (req->rq_rlen > xprt->tcp_copied && xprt->tcp_more) + return avail; + dprintk("RPC: %4d received reply complete\n", req->rq_task->tk_pid); + xprt_complete_rqst(xprt, req, xprt->tcp_copied); - /* Skip over any trailing bytes on short reads */ - while (avail > 0) { - static u8 dummy[64]; + return avail; +} +/* + * TCP discard extra bytes from a short read + */ +static inline int +tcp_read_discard(struct rpc_xprt *xprt, int avail) +{ + struct iovec riov; + static u8 dummy[64]; + int want, result = 0; + + while (avail) { want = MIN(avail, sizeof(dummy)); riov.iov_base = dummy; riov.iov_len = want; dprintk("RPC: TCP skipping %d bytes\n", want); - result = xprt_recvmsg(xprt, &riov, 1, want); + result = xprt_recvmsg(xprt, &riov, 1, want, 0); if (result < 0) - goto done; - offset += result; + return result; + xprt->tcp_offset += result; avail -= result; - if (result < want) { - result = -EAGAIN; - goto done; + } + return avail; +} + +/* + * TCP record receive routine + * This is not the most efficient code since we call recvfrom thrice-- + * first receiving the record marker, then the XID, then the data. + * + * The optimal solution would be a RPC support in the TCP layer, which + * would gather all data up to the next record marker and then pass us + * the list of all TCP segments ready to be copied. + */ +static int +tcp_input_record(struct rpc_xprt *xprt) +{ + struct rpc_rqst *req = NULL; + struct rpc_task *task = NULL; + int avail, result; + + dprintk("RPC: tcp_input_record\n"); + + if (xprt->shutdown) + return -EIO; + if (!xprt->connected) + return -ENOTCONN; + + /* Read in a new fragment marker if necessary */ + /* Can we ever really expect to get completely empty fragments? */ + if ((result = tcp_read_fraghdr(xprt)) <= 0) + return result; + avail = result; + + /* Read in the xid if necessary */ + if ((result = tcp_read_xid(xprt, avail)) <= 0) + return result; + avail = result; + + /* Find and lock the request corresponding to this xid */ + req = xprt_lookup_rqst(xprt, xprt->tcp_xid); + if (req) { + task = req->rq_task; + if (xprt->tcp_copied == sizeof(xprt->tcp_xid) || req->rq_damaged) { + req->rq_damaged = 1; + /* Read in the request data */ + result = tcp_read_request(xprt, req, avail); } + rpc_unlock_task(task); + if (result < 0) + return result; + avail = result; } - if (!xprt->tcp_more) - xprt->tcp_total = 0; - offset = 0; -done: - dprintk("RPC: tcp_input_record done (off %d total %d copied %d)\n", - offset, xprt->tcp_total, xprt->tcp_copied); - xprt->tcp_offset = offset; + /* Skip over any trailing bytes on short reads */ + if ((result = tcp_read_discard(xprt, avail)) < 0) + return result; + + dprintk("RPC: tcp_input_record done (off %d reclen %d copied %d)\n", + xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_copied); + result = xprt->tcp_reclen; return result; } /* * TCP task queue stuff */ - -static struct rpc_xprt *rpc_xprt_pending = NULL; /* Chain by rx_pending of rpc_xprt's */ +LIST_HEAD(rpc_xprt_pending); /* List of xprts having pending tcp requests */ + +static inline +void tcp_rpciod_queue(void) +{ + rpciod_wake_up(); +} + +static inline +void xprt_append_pending(struct rpc_xprt *xprt) +{ + if (!list_empty(&xprt->rx_pending)) + return; + spin_lock_bh(&rpc_queue_lock); + if (list_empty(&xprt->rx_pending)) { + list_add(&xprt->rx_pending, rpc_xprt_pending.prev); + dprintk("RPC: xprt queue %p\n", xprt); + tcp_rpciod_queue(); + } + spin_unlock_bh(&rpc_queue_lock); +} + +static +void xprt_remove_pending(struct rpc_xprt *xprt) +{ + spin_lock_bh(&rpc_queue_lock); + if (!list_empty(&xprt->rx_pending)) { + list_del(&xprt->rx_pending); + INIT_LIST_HEAD(&xprt->rx_pending); + } + spin_unlock_bh(&rpc_queue_lock); +} + +static inline +struct rpc_xprt *xprt_remove_pending_next(void) +{ + struct rpc_xprt *xprt = NULL; + + spin_lock_bh(&rpc_queue_lock); + if (!list_empty(&rpc_xprt_pending)) { + xprt = list_entry(rpc_xprt_pending.next, struct rpc_xprt, rx_pending); + list_del(&xprt->rx_pending); + INIT_LIST_HEAD(&xprt->rx_pending); + } + spin_unlock_bh(&rpc_queue_lock); + return xprt; +} /* * This is protected from tcp_data_ready and the stack as its run * inside of the RPC I/O daemon */ -static void -do_rpciod_tcp_dispatcher(void) +void +__rpciod_tcp_dispatcher(void) { struct rpc_xprt *xprt; - int result = 0; + int safe_retry = 0, result; dprintk("rpciod_tcp_dispatcher: Queue Running\n"); /* * Empty each pending socket */ - - while(1) { - int safe_retry=0; - - if ((xprt = rpc_xprt_pending) == NULL) { - break; - } - xprt->rx_pending_flag = 0; - rpc_xprt_pending=xprt->rx_pending; - xprt->rx_pending = NULL; - + while ((xprt = xprt_remove_pending_next()) != NULL) { dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt); - do - { - if (safe_retry++ > 50) - break; + do { result = tcp_input_record(xprt); - } - while (result >= 0); - - switch (result) { - case -EAGAIN: - case -ENOTCONN: - case -EPIPE: - continue; - default: - printk(KERN_WARNING "RPC: unexpected error %d from tcp_input_record\n", - result); + } while (result >= 0); + + if (safe_retry++ > 200) { + schedule(); + safe_retry = 0; } } } -void rpciod_tcp_dispatcher(void) -{ - /* mama... start_bh_atomic was here... - Calls to sock->ops _are_ _impossible_ with disabled bh. Period. --ANK - */ - do_rpciod_tcp_dispatcher(); -} - -int xprt_tcp_pending(void) -{ - return rpc_xprt_pending != NULL; -} - -extern inline void tcp_rpciod_queue(void) -{ - rpciod_wake_up(); -} - /* * data_ready callback for TCP. We can't just jump into the * tcp recvmsg functions inside of the network receive bh or @@ -874,24 +972,15 @@ static void tcp_data_ready(struct sock *sk, int len) return; } + if (xprt->shutdown) + return; + + xprt_append_pending(xprt); + dprintk("RPC: tcp_data_ready client %p\n", xprt); dprintk("RPC: state %x conn %d dead %d zapped %d\n", sk->state, xprt->connected, sk->dead, sk->zapped); - /* - * If we are not waiting for the RPC bh run then - * we are now - */ - if (!xprt->rx_pending_flag) { - dprintk("RPC: xprt queue %p\n", rpc_xprt_pending); - - xprt->rx_pending=rpc_xprt_pending; - rpc_xprt_pending=xprt; - xprt->rx_pending_flag=1; - } else - dprintk("RPC: xprt queued already %p\n", xprt); - tcp_rpciod_queue(); - } @@ -907,26 +996,20 @@ tcp_state_change(struct sock *sk) sk->state, xprt->connected, sk->dead, sk->zapped); - switch(sk->state) { + spin_lock_bh(&xprt_sock_lock); + switch (sk->state) { case TCP_ESTABLISHED: - if (xprt->connected) - break; xprt->connected = 1; - xprt->connecting = 0; + if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending) + rpc_wake_up_task(xprt->snd_task); rpc_wake_up(&xprt->reconn); - rpc_wake_up_next(&xprt->sending); - tcp_rpciod_queue(); - break; - case TCP_CLOSE: - if (xprt->connecting) - break; - xprt_disconnect(xprt); - rpc_wake_up_status(&xprt->reconn, -ENOTCONN); break; default: + xprt->connected = 0; + rpc_wake_up_status(&xprt->pending, -ENOTCONN); break; } - + spin_unlock_bh(&xprt_sock_lock); } /* @@ -940,20 +1023,23 @@ tcp_write_space(struct sock *sk) if (!(xprt = xprt_from_sock(sk))) return; + if (xprt->shutdown) + return; /* Wait until we have enough socket memory */ if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE)) return; + spin_lock_bh(&xprt_sock_lock); if (xprt->write_space) - return; + goto out_unlock; xprt->write_space = 1; - if (!xprt->snd_task) - rpc_wake_up_next(&xprt->sending); - else if (!RPC_IS_RUNNING(xprt->snd_task)) + if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending) rpc_wake_up_task(xprt->snd_task); + out_unlock: + spin_unlock_bh(&xprt_sock_lock); } static void @@ -963,20 +1049,24 @@ udp_write_space(struct sock *sk) if (!(xprt = xprt_from_sock(sk))) return; + if (xprt->shutdown) + return; /* Wait until we have enough socket memory */ if (sock_wspace(sk) < min(sk->sndbuf,XPRT_MIN_WRITE_SPACE)) return; + spin_lock_bh(&xprt_sock_lock); if (xprt->write_space) - return; + goto out_unlock; xprt->write_space = 1; - if (!xprt->snd_task) - rpc_wake_up_next(&xprt->sending); - else if (!RPC_IS_RUNNING(xprt->snd_task)) + + if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending) rpc_wake_up_task(xprt->snd_task); + out_unlock: + spin_unlock_bh(&xprt_sock_lock); } /* @@ -987,9 +1077,8 @@ xprt_timer(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - if (req) { + if (req) xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT); - } dprintk("RPC: %4d xprt_timer (%s request)\n", task->tk_pid, req ? "pending" : "backlogged"); @@ -1010,12 +1099,13 @@ xprt_down_transmit(struct rpc_task *task) struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt; struct rpc_rqst *req = task->tk_rqstp; - spin_lock_bh(&xprt_lock); + spin_lock(&xprt_lock); if (xprt->snd_task && xprt->snd_task != task) { dprintk("RPC: %4d TCP write queue full (task %d)\n", task->tk_pid, xprt->snd_task->tk_pid); - task->tk_timeout = req->rq_timeout.to_current; - rpc_sleep_on(&xprt->sending, task, xprt_transmit, NULL); + task->tk_timeout = 0; + task->tk_status = -EAGAIN; + rpc_sleep_on(&xprt->sending, task, NULL, NULL); } else if (!xprt->snd_task) { xprt->snd_task = task; #ifdef RPC_PROFILE @@ -1023,23 +1113,23 @@ xprt_down_transmit(struct rpc_task *task) #endif req->rq_bytes_sent = 0; } - spin_unlock_bh(&xprt_lock); + spin_unlock(&xprt_lock); return xprt->snd_task == task; } /* * Releases the socket for use by other requests. */ -static void +static inline void xprt_up_transmit(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt; if (xprt->snd_task && xprt->snd_task == task) { - spin_lock_bh(&xprt_lock); + spin_lock(&xprt_lock); xprt->snd_task = NULL; rpc_wake_up_next(&xprt->sending); - spin_unlock_bh(&xprt_lock); + spin_unlock(&xprt_lock); } } @@ -1050,7 +1140,6 @@ xprt_up_transmit(struct rpc_task *task) void xprt_transmit(struct rpc_task *task) { - struct rpc_timeout *timeo; struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = req->rq_xprt; @@ -1060,26 +1149,21 @@ xprt_transmit(struct rpc_task *task) if (xprt->shutdown) task->tk_status = -EIO; + if (!xprt->connected) + task->tk_status = -ENOTCONN; + if (task->tk_status < 0) return; - /* Reset timeout parameters */ - timeo = &req->rq_timeout; - if (timeo->to_retries < 0) { - dprintk("RPC: %4d xprt_transmit reset timeo\n", - task->tk_pid); - timeo->to_retries = xprt->timeout.to_retries; - timeo->to_current = timeo->to_initval; - } + if (task->tk_rpcwait) + rpc_remove_wait_queue(task); /* set up everything as needed. */ /* Write the record marker */ if (xprt->stream) { - u32 marker; - - marker = htonl(0x80000000|(req->rq_slen-4)); - *((u32 *) req->rq_svec[0].iov_base) = marker; + u32 *marker = req->rq_svec[0].iov_base; + *marker = htonl(0x80000000|(req->rq_slen-sizeof(*marker))); } if (!xprt_down_transmit(task)) @@ -1095,24 +1179,14 @@ do_xprt_transmit(struct rpc_task *task) struct rpc_xprt *xprt = req->rq_xprt; int status, retry = 0; - if (xprt->shutdown) { - task->tk_status = -EIO; - goto out_release; - } /* For fast networks/servers we have to put the request on * the pending list now: + * Note that we don't want the task timing out during the + * call to xprt_sendmsg(), so we initially disable the timeout, + * and then reset it later... */ - req->rq_gotit = 0; - status = rpc_add_wait_queue(&xprt->pending, task); - if (!status) - task->tk_callback = NULL; - - if (status) { - printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); - task->tk_status = status; - goto out_release; - } + xprt_receive(task); /* Continue transmitting the packet/record. We must be careful * to cope with writespace callbacks arriving _after_ we have @@ -1129,80 +1203,67 @@ do_xprt_transmit(struct rpc_task *task) req->rq_bytes_sent += status; if (req->rq_bytes_sent >= req->rq_slen) - goto out_release; - } - - if (status < req->rq_slen) - status = -EAGAIN; - - if (status >= 0 || !xprt->stream) { - dprintk("RPC: %4d xmit complete\n", task->tk_pid); - goto out_release; + goto out_receive; + } else { + if (status >= req->rq_slen) + goto out_receive; + status = -ENOMEM; + break; } dprintk("RPC: %4d xmit incomplete (%d left of %d)\n", task->tk_pid, req->rq_slen - req->rq_bytes_sent, req->rq_slen); + status = -EAGAIN; if (retry++ > 50) break; } + rpc_unlock_task(task); - task->tk_status = (status == -ENOMEM) ? -EAGAIN : status; + task->tk_status = status; - /* We don't care if we got a reply, so don't protect - * against bh. */ - if (task->tk_rpcwait == &xprt->pending) - rpc_remove_wait_queue(task); + /* Note: at this point, task->tk_sleeping has not yet been set, + * hence there is no danger of the waking up task being put on + * schedq, and being picked up by a parallel run of rpciod(). + */ + rpc_wake_up_task(task); + if (!RPC_IS_RUNNING(task)) + goto out_release; - /* Protect against (udp|tcp)_write_space */ - spin_lock_bh(&xprt_lock); - if (status == -ENOMEM || status == -EAGAIN) { + switch (status) { + case -ENOMEM: + /* Protect against (udp|tcp)_write_space */ task->tk_timeout = req->rq_timeout.to_current; + spin_lock_bh(&xprt_sock_lock); if (!xprt->write_space) - rpc_sleep_on(&xprt->sending, task, xprt_transmit_status, - xprt_transmit_timeout); - spin_unlock_bh(&xprt_lock); + rpc_sleep_on(&xprt->sending, task, NULL, NULL); + spin_unlock_bh(&xprt_sock_lock); return; - } - spin_unlock_bh(&xprt_lock); - -out_release: - xprt_up_transmit(task); -} - -/* - * This callback is invoked when the sending task is forced to sleep - * because the TCP write buffers are full - */ -static void -xprt_transmit_status(struct rpc_task *task) -{ - struct rpc_xprt *xprt = task->tk_client->cl_xprt; - - dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status); - if (xprt->snd_task == task) { - task->tk_status = 0; - do_xprt_transmit(task); + case -EAGAIN: + /* Keep holding the socket if it is blocked */ + rpc_delay(task, HZ>>4); return; + case -ECONNREFUSED: + case -ENOTCONN: + if (!xprt->stream) + return; + default: + goto out_release; } -} -/* - * RPC transmit timeout handler. - */ -static void -xprt_transmit_timeout(struct rpc_task *task) -{ - dprintk("RPC: %4d transmit_timeout %d\n", task->tk_pid, task->tk_status); - task->tk_status = -ETIMEDOUT; - task->tk_timeout = 0; - rpc_wake_up_task(task); + out_receive: + dprintk("RPC: %4d xmit complete\n", task->tk_pid); + /* Set the task's receive timeout value */ + task->tk_timeout = req->rq_timeout.to_current; + rpc_add_timer(task, xprt_timer); + rpc_unlock_task(task); + out_release: xprt_up_transmit(task); } /* - * Wait for the reply to our call. + * Queue the task for a reply to our call. * When the callback is invoked, the congestion window should have * been updated already. */ @@ -1214,42 +1275,8 @@ xprt_receive(struct rpc_task *task) dprintk("RPC: %4d xprt_receive\n", task->tk_pid); - /* - * Wait until rq_gotit goes non-null, or timeout elapsed. - */ - task->tk_timeout = req->rq_timeout.to_current; - - spin_lock_bh(&xprt_lock); - if (task->tk_rpcwait) - rpc_remove_wait_queue(task); - - if (task->tk_status < 0 || xprt->shutdown) { - spin_unlock_bh(&xprt_lock); - goto out; - } - - if (!req->rq_gotit) { - rpc_sleep_on(&xprt->pending, task, - xprt_receive_status, xprt_timer); - spin_unlock_bh(&xprt_lock); - return; - } - spin_unlock_bh(&xprt_lock); - - dprintk("RPC: %4d xprt_receive returns %d\n", - task->tk_pid, task->tk_status); - out: - xprt_receive_status(task); -} - -static void -xprt_receive_status(struct rpc_task *task) -{ - struct rpc_xprt *xprt = task->tk_xprt; - - if (xprt->tcp_rqstp == task->tk_rqstp) - xprt->tcp_rqstp = NULL; - + task->tk_timeout = 0; + rpc_sleep_locked(&xprt->pending, task, NULL, NULL); } /* @@ -1335,7 +1362,6 @@ xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid); task->tk_status = 0; - req->rq_gotit = 0; req->rq_timeout = xprt->timeout; req->rq_task = task; req->rq_xprt = xprt; @@ -1353,6 +1379,7 @@ xprt_release(struct rpc_task *task) struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req; + xprt_up_transmit(task); if (!(req = task->tk_rqstp)) return; task->tk_rqstp = NULL; @@ -1363,16 +1390,16 @@ xprt_release(struct rpc_task *task) spin_lock(&xprt_lock); req->rq_next = xprt->free; xprt->free = req; - spin_unlock(&xprt_lock); /* remove slot from queue of pending */ - spin_lock_bh(&xprt_lock); if (task->tk_rpcwait) { printk("RPC: task of released request still queued!\n"); - rpc_del_timer(task); +#ifdef RPC_DEBUG + printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait)); +#endif rpc_remove_wait_queue(task); } - spin_unlock_bh(&xprt_lock); + spin_unlock(&xprt_lock); /* Decrease congestion value. */ xprt->cong -= RPC_CWNDSCALE; @@ -1389,7 +1416,7 @@ xprt_default_timeout(struct rpc_timeout *to, int proto) if (proto == IPPROTO_UDP) xprt_set_timeout(to, 5, 5 * HZ); else - xprt_set_timeout(to, 5, 15 * HZ); + xprt_set_timeout(to, 5, 60 * HZ); } /* @@ -1416,52 +1443,33 @@ xprt_setup(struct socket *sock, int proto, { struct rpc_xprt *xprt; struct rpc_rqst *req; - struct sock *inet; int i; dprintk("RPC: setting up %s transport...\n", proto == IPPROTO_UDP? "UDP" : "TCP"); - inet = sock->sk; - if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) return NULL; memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */ - xprt->file = NULL; - xprt->sock = sock; - xprt->inet = inet; xprt->addr = *ap; xprt->prot = proto; xprt->stream = (proto == IPPROTO_TCP)? 1 : 0; - xprt->congtime = jiffies; - init_waitqueue_head(&xprt->cong_wait); - inet->user_data = xprt; - xprt->old_data_ready = inet->data_ready; - xprt->old_state_change = inet->state_change; - xprt->old_write_space = inet->write_space; - if (proto == IPPROTO_UDP) { - inet->data_ready = udp_data_ready; - inet->write_space = udp_write_space; - inet->no_check = UDP_CSUM_NORCV; - xprt->cwnd = RPC_INITCWND; - } else { - inet->data_ready = tcp_data_ready; - inet->state_change = tcp_state_change; - inet->write_space = tcp_write_space; + if (xprt->stream) { xprt->cwnd = RPC_MAXCWND; xprt->nocong = 1; - } - xprt->connected = 1; + } else + xprt->cwnd = RPC_INITCWND; + xprt->congtime = jiffies; + init_waitqueue_head(&xprt->cong_wait); /* Set timeout parameters */ if (to) { xprt->timeout = *to; xprt->timeout.to_current = to->to_initval; xprt->timeout.to_resrvval = to->to_maxval << 1; - } else { + } else xprt_default_timeout(&xprt->timeout, xprt->prot); - } xprt->pending = RPC_INIT_WAITQ("xprt_pending"); xprt->sending = RPC_INIT_WAITQ("xprt_sending"); @@ -1474,13 +1482,11 @@ xprt_setup(struct socket *sock, int proto, req->rq_next = NULL; xprt->free = xprt->slot; + INIT_LIST_HEAD(&xprt->rx_pending); + dprintk("RPC: created transport %p\n", xprt); - /* - * TCP requires the rpc I/O daemon is present - */ - if(proto==IPPROTO_TCP) - rpciod_up(); + xprt_bind_socket(xprt, sock); return xprt; } @@ -1508,17 +1514,52 @@ xprt_bindresvport(struct socket *sock) return err; } +static int +xprt_bind_socket(struct rpc_xprt *xprt, struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (xprt->inet) + return -EBUSY; + + sk->user_data = xprt; + xprt->old_data_ready = sk->data_ready; + xprt->old_state_change = sk->state_change; + xprt->old_write_space = sk->write_space; + if (xprt->prot == IPPROTO_UDP) { + sk->data_ready = udp_data_ready; + sk->write_space = udp_write_space; + sk->no_check = UDP_CSUM_NORCV; + xprt->connected = 1; + } else { + sk->data_ready = tcp_data_ready; + sk->state_change = tcp_state_change; + sk->write_space = tcp_write_space; + xprt->connected = 0; + } + + /* Reset to new socket */ + xprt->sock = sock; + xprt->inet = sk; + /* + * TCP requires the rpc I/O daemon is present + */ + if(xprt->stream) + rpciod_up(); + + return 0; +} + /* * Create a client socket given the protocol and peer address. */ static struct socket * -xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) +xprt_create_socket(int proto, struct rpc_timeout *to) { struct socket *sock; int type, err; - dprintk("RPC: xprt_create_socket(%08x, %s %d)\n", - sap? ntohl(sap->sin_addr.s_addr) : 0, + dprintk("RPC: xprt_create_socket(%s %d)\n", (proto == IPPROTO_UDP)? "udp" : "tcp", proto); type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM; @@ -1532,15 +1573,6 @@ xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) if (!current->fsuid && xprt_bindresvport(sock) < 0) goto failed; - if (type == SOCK_STREAM && sap) { - err = sock->ops->connect(sock, (struct sockaddr *) sap, - sizeof(*sap), 0); - if (err < 0) { - printk("RPC: TCP connect failed (%d).\n", -err); - goto failed; - } - } - return sock; failed: @@ -1559,7 +1591,7 @@ xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) dprintk("RPC: xprt_create_proto called\n"); - if (!(sock = xprt_create_socket(proto, sap, to))) + if (!(sock = xprt_create_socket(proto, to))) return NULL; if (!(xprt = xprt_setup(sock, proto, sap, to))) @@ -1587,8 +1619,6 @@ xprt_shutdown(struct rpc_xprt *xprt) */ int xprt_clear_backlog(struct rpc_xprt *xprt) { - if (!xprt) - return 0; if (RPCXPRT_CONGESTED(xprt)) return 0; rpc_wake_up_next(&xprt->backlog); @@ -1603,6 +1633,7 @@ int xprt_destroy(struct rpc_xprt *xprt) { dprintk("RPC: destroying transport %p\n", xprt); + xprt_shutdown(xprt); xprt_close(xprt); kfree(xprt); -- 2.39.5