diff options
449 files changed, 23072 insertions, 16826 deletions
diff --git a/Documentation/Configure.help b/Documentation/Configure.help index e13f5a71b..5e4f351e5 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -12811,143 +12811,183 @@ CONFIG_JOYSTICK weapon control system or something like that you can say Y here to enable generic support for these controllers. You will also need to say Y or M to at least one of the hardware specific drivers. This - will make the controllers available as /dev/jsX devices. Please read - the file Documentation/joystick.txt which contains more information - and the location of the joystick package that you'll need. + will make the controllers available as /dev/input/jsX devices. + Please read the file Documentation/joystick.txt which contains more + information and the location of the joystick package that you'll + need. + +ns558 gameports +CONFIG_INPUT_NS558 + Say Y here if you have an ISA, ISAPnP or PCI standard gameport. + For more information on how to use the driver please read + Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joystick.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + The module will be called ns558.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +PDPI Lightning 4 gamecard +CONFIG_INPUT_LIGHTNING + Say Y here if you have a PDPI Lightning 4 gamecard. For more + information on how to use the driver please read + Documentation/joystick.txt + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called lightning.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt. -Classic PC analog -CONFIG_JOY_ANALOG +Aureal Vortex and Trident 4DWave gameports +CONFIG_INPUT_PCIGAME + Say Y here if you have a Trident 4DWave DX/NX or Aureal Vortex 1/2 + card. For more information on how to use the driver please read + Documentation/joystick.txt + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pcigame.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +Classic PC analog joysticks and gamepads +CONFIG_INPUT_ANALOG Say Y here if you have a controller that connects to the PC gameport. This supports many different types, including joysticks with throttle control, with rudders, or with extensions like additional hats and buttons compatible with CH Flightstick Pro, - ThrustMaster FCS or 6 and 8 button gamepads. For more information on - how to use the driver please read Documentation/joystick.txt + ThrustMaster FCS, 6 and 8 button gamepads, or Saitek Cyborg + joysticks. For more information on how to use the driver please read + Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-analog.o. If you want to compile + The module will be called analog.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -FPGaming and MadCatz A3D -CONFIG_JOY_ASSASSIN +Assasin 3D and MadCatz Panther devices +CONFIG_INPUT_A3D Say Y here if you have an FPGaming or MadCatz controller using the A3D protocol over the PC gameport. For more information on how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-assasin.o. If you want to compile + The module will be called a3d.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Gravis GrIP -CONFIG_JOY_GRAVIS - Say Y here if you have a Gravis controller using the GrIP protocol - over the PC gameport. For more information on how to use the driver - please read Documentation/joystick.txt +Logitech ADI digital joysticks and gamepads +CONFIG_INPUT_ADI + Say Y here if you have a Logitech controller using the ADI + protocol over the PC gameport. For more information on how to use + the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-gravis.o. If you want to compile + The module will be called adi.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Logitech ADI -CONFIG_JOY_LOGITECH - Say Y here if you have a Logitech controller using the ADI - protocol over the PC gameport. For more information on how to use - the driver please read Documentation/joystick.txt +Creative Labs Blaster Cobra gamepad +CONFIG_INPUT_COBRA + Say Y here if you have a Creative Labs Blaster Cobra gamepad. + For more information on how to use the driver please read + Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-logitech.o. If you want to compile + The module will be called cobra.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Microsoft SideWinder -CONFIG_JOY_SIDEWINDER - Say Y here if you have a Microsoft controller using the Digital - Overdrive protocol over PC gameport. For more information on how to - use the driver please read Documentation/joystick.txt +Genius Flight2000 Digital joysticks and gamepads +CONFIG_INPUT_GF2K + Say Y here if you have a Genius Flight2000 or MaxFighter + digitally communicating joystick or gamepad. For more information + on how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-sidewinder.o. If you want to compile + The module will be called gf2k.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -ThrustMaster DirectConnect -CONFIG_JOY_THRUSTMASTER - Say Y here if you have a ThrustMaster controller using the - DirectConnect (BSP) protocol over the PC gameport. For more - information on how to use the driver please read - Documentation/joystick.txt +Gravis GrIP joysticks and gamepads +CONFIG_INPUT_GRIP + Say Y here if you have a Gravis controller using the GrIP protocol + over the PC gameport. For more information on how to use the driver + please read Documentation/joystick.txt + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called grip.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +InterAct digital joysticks and gamepads +CONFIG_INPUT_INTERACT + Say Y hereif you have an InterAct gameport or joystick + communicating digitally over the gameport. For more information on + how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-thrustmaster.o. If you want to compile + The module will be called interact.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Creative Labs Blaster -CONFIG_JOY_CREATIVE - Say Y here if you have a Creative Labs controller using the - Blaster protocol over the PC gameport. For more information on how - to use the driver please read Documentation/joystick.txt +ThrustMaster DirectConnect joysticks and gamepads +CONFIG_INPUT_TMDC + Say Y here if you have a ThrustMaster controller using the + DirectConnect (BSP) protocol over the PC gameport. For more + information on how to use the driver please read + Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-creative.o. If you want to compile + The module will be called tmdc.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -PDPI Lightning 4 cards -CONFIG_JOY_LIGHTNING - Say Y here if you have a PDPI Lightning 4 gamecard and an analog - joystick or gamepad connected to it. For more information on how to +Microsoft SideWinder digital joysticks and gamepads +CONFIG_INPUT_SIDEWINDER + Say Y here if you have a Microsoft controller using the Digital + Overdrive protocol over PC gameport. For more information on how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-lightning.o. If you want to compile + The module will be called sidewinder.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Logitech WingMan Warrior -CONFIG_JOY_WARRIOR - Say Y here if you have a Logitech WingMan Warrior controller - connected to your computer's serial port. For more information on - how to use the driver please read Documentation/joystick.txt +Serial port input line discipline +CONFIG_INPUT_SERPORT + Say Y hereif you plan to use a joystick that communicates over the + serial (COM) port. For more information on how to use the driver + please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-warrior.o. If you want to compile + The module will be called serport.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Trident 4DWave and Aureal Vortex gameport -CONFIG_JOY_PCI - Say Y here if you have a Trident 4DWave DX/NX or Aureal Vortex 1/2 - card and want to use its gameport in its enhanced digital mode - with and ordinary analog joystick. For more information on how to - use the driver please read Documentation/joystick.txt +Logitech WingMan Warrior joystick +CONFIG_INPUT_WARRIOR + Say Y here if you have a Logitech WingMan Warrior joystick + connected to your computer's serial port. For more information on + how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-pci.o. If you want to compile + The module will be called warrior.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Magellan and Space Mouse -CONFIG_JOY_MAGELLAN +LogiCad3d Magellan/SpaceMouse 6dof controller +CONFIG_INPUT_MAGELLAN Say Y here if you have a Magellan or Space Mouse 6DOF controller connected to your computer's serial port. For more information on how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-magellan.o. If you want to compile + The module will be called magellan.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -SpaceTec SpaceOrb 360 and SpaceBall Avenger -CONFIG_JOY_SPACEORB +SpaceTec SpaceOrb/Avenger 6dof controller +CONFIG_INPUT_SPACEORB Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF controller connected to your computer's serial port. For more information on how to use the driver please read @@ -12955,37 +12995,39 @@ CONFIG_JOY_SPACEORB This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-spaceorb.o. If you want to compile + The module will be called spaceorb.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -SpaceTec SpaceBall 4000 FLX -CONFIG_JOY_SPACEBALL - Say Y here if you have a SpaceTec SpaceBall 4000 FLX - controller connected to your computer's serial port. For more - information on how to use the driver please read - Documentation/joystick.txt +SpaceTec SpaceBall 4000 FLX 6dof controller +CONFIG_INPUT_SPACEBALL + Say Y here if you have a SpaceTec SpaceBall 4000 FLX controller + connected to your computer's serial port. For more information on + how to use the driver please read Documentation/joystick.txt + +I-Force joysticks/wheels +CONFIG_INPUT_IFORCE_232 + Say Y here if you have an I-Force joystick or steering wheel + connected to your serial (COM) port. For more information on + how to use the driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-spaceball.o. If you want to compile + The module will be called iforce.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. - -NES, SNES, N64, PSX, Multi -CONFIG_JOY_CONSOLE - Say Y here if you have a Nintendo Entertainment System gamepad, - Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad, - Sony PlayStation gamepad or a Multisystem -- Atari, Amiga, - Commodore, Amstrad CPC joystick connected to your parallel port. - For more information on how to use the driver please read - Documentation/joystick.txt and Documentation/joystick-parport.txt + +I-Force joysticks/wheels +CONFIG_INPUT_IFORCE_USB + Say Y here if you have an I-Force joystick or steering wheel + connected to your USB port. For more information on how to use the + driver please read Documentation/joystick.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-console.o. If you want to compile + The module will be called iforce.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. - -Sega, Multi -CONFIG_JOY_DB9 + +Multisystem, Sega Genesis, Saturn joysticks and gamepads +CONFIG_INPUT_DB9 Say Y here if you have a Sega Master System gamepad, Sega Genesis gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga, Commodore, Amstrad CPC joystick connected to your parallel port. @@ -12994,24 +13036,38 @@ CONFIG_JOY_DB9 This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-db9.o. If you want to compile + The module will be called db9.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt. + +Multisystem, NES, SNES, N64, PSX joysticks and gamepads +CONFIG_INPUT_GAMECON + Say Y here if you have a Nintendo Entertainment System gamepad, + Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad, + Sony PlayStation gamepad or a Multisystem -- Atari, Amiga, + Commodore, Amstrad CPC joystick connected to your parallel port. + For more information on how to use the driver please read + Documentation/joystick.txt and Documentation/joystick-parport.txt + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called gamecon.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -TurboGraFX Multisystem interface -CONFIG_JOY_TURBOGRAFX - Say Y here if you have the TurboGraFX interface by Steffen Schwenke, - and want to use it with Multiststem -- Atari, Amiga, Commodore, - Amstrad CPC joystick. For more information on how to use the driver - please read Documentation/joystick.txt and +Multisystem joysticks via TurboGraFX device +CONFIG_INPUT_TURBOGRAFX + Say Y here if you have the TurboGraFX interface by Steffen + Schwenke, and want to use it with Multiststem -- Atari, Amiga, + Commodore, Amstrad CPC joystick. For more information on how to use + the driver please read Documentation/joystick.txt and Documentation/joystick-parport.txt This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called joy-turbografx.o. If you want to compile + The module will be called turbografx.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. Amiga joysticks -CONFIG_JOY_AMIGA +CONFIG_INPUT_AMIJOY Say Y here if you have an Amiga with a digital joystick connected to it. For more information on how to use the driver please read Documentation/joystick.txt @@ -14244,7 +14300,7 @@ CONFIG_OBP_FLASH The OpenBoot PROM on Ultra systems is flashable. If you want to be able to upgrade the OBP firmware, say Y here. -JavaStation OS Flash SIMM (EXPERIMENTAL) +JavaStation OS Flash SIMM CONFIG_SUN_JSFLASH If you say Y here, you will be able to boot from your JavaStation's Flash memory. diff --git a/Documentation/cpqarray.txt b/Documentation/cpqarray.txt index 45a53216f..67db480cb 100644 --- a/Documentation/cpqarray.txt +++ b/Documentation/cpqarray.txt @@ -1,11 +1,5 @@ This driver is for Compaq's SMART2 Intellegent Disk Array Controllers. -WARNING: --------- - -This driver comes with NO WARRANTY. It is not officially supported by -Compaq. Do not call technical support. Use at your own risk. - Supported Cards: ---------------- @@ -22,6 +16,7 @@ This driver is known to work with the following cards: * Integrated Smart Array Controller * SA 4200 * SA 4250ES + * SA 431 It should also work with some really old Disk array adapters, but I am unable to test against these cards: diff --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog index 5728384e6..a96f6f09e 100644 --- a/Documentation/filesystems/devfs/ChangeLog +++ b/Documentation/filesystems/devfs/ChangeLog @@ -1533,3 +1533,46 @@ Work sponsored by SGI - Updated Documentation/filesystems/devfs/README - Updated sample modules.conf +=============================================================================== +Changes for patch v168 + +Work sponsored by SGI + +- Disabled multi-mount capability (use VFS bindings instead) + +- Updated README from master HTML file +=============================================================================== +Changes for patch v169 + +Work sponsored by SGI + +- Removed multi-mount code + +- Removed compatibility macros: VFS has changed too much +=============================================================================== +Changes for patch v170 + +Work sponsored by SGI + +- Updated README from master HTML file + +- Merged devfs inode into devfs entry +=============================================================================== +Changes for patch v171 + +Work sponsored by SGI + +- Updated sample modules.conf + +- Removed dead code in <devfs_register> which used to call + <free_dentries> + +- Ported to kernel 2.4.0-test2-pre3 +=============================================================================== +Changes for patch v172 + +Work sponsored by SGI + +- Changed interface to <devfs_register> + +- Changed interface to <devfs_register_series> diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README index c9b111b9b..b09c8fd9e 100644 --- a/Documentation/filesystems/devfs/README +++ b/Documentation/filesystems/devfs/README @@ -3,7 +3,7 @@ Devfs (Device File System) FAQ Linux Devfs (Device File System) FAQ Richard Gooch -1-MAY-2000 +14-JUN-2000 ----------------------------------------------------------------------------- @@ -797,13 +797,15 @@ which must exist *before* init starts. Once again, you need to mount devfs and then create the named pipe *before* init starts. -The default behaviour now is not to mount devfs onto /dev at boot time. -You can correct this with the "devfs=mount" boot option. This solves -any problems with init, and also prevents the dreaded: +The default behaviour now is not to mount devfs onto /dev at boot time +for 2.3.x and later kernels. You can correct this with the +"devfs=mount" boot option. This solves any problems with init, +and also prevents the dreaded: Cannot open initial console -message. +message. For 2.2.x kernels where you need to apply the devfs patch, +the default is to mount. If you have automatic mounting of devfs onto /dev then you may need to create /dev/initctl in your boot scripts. The following lines should @@ -1460,9 +1462,19 @@ Other resources Douglas Gilbert has written a useful document at http://www.torque.net/sg/devfs_scsi.html which -explores the SCSI subsystem and how it interacts with devfs. +explores the SCSI subsystem and how it interacts with devfs +Douglas Gilbert has written another useful document at + +http://www.torque.net/scsi/scsihosts.html which +discusses the scsihosts= boot option + + +Douglas Gilbert has written yet another useful document at + +http://www.torque.net/scsi/linux_scsi_24 which +discusses the Linux SCSI subsystem in 2.4. diff --git a/Documentation/filesystems/devfs/modules.conf b/Documentation/filesystems/devfs/modules.conf index d925cd28b..a91c99651 100644 --- a/Documentation/filesystems/devfs/modules.conf +++ b/Documentation/filesystems/devfs/modules.conf @@ -1,7 +1,7 @@ # Sample entries for /etc/modules.conf for devfs ############################################################################### -# Configuration section: change to suit +# Configuration section: change to suit your hardware # alias /dev/sound sb alias /dev/v4l bttv diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 7d1f5ca72..7256f5aaf 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -176,7 +176,7 @@ filesystem. As of kernel 2.1.99, the following members are defined: struct super_operations { void (*read_inode) (struct inode *); - void (*write_inode) (struct inode *, int); + void (*write_inode) (struct inode *); void (*put_inode) (struct inode *); void (*delete_inode) (struct inode *); int (*notify_change) (struct dentry *, struct iattr *); @@ -198,8 +198,7 @@ or bottom half). read. Other members are filled in by this method write_inode: this method is called when the VFS needs to write an - inode to disc. The second parameter indicates whether the write - should be synchronous or not, not all filesystems check this flag. + inode to disc put_inode: called when the VFS inode is removed from the inode cache. This method is optional diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 366f2dbd7..0edc635ff 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -103,6 +103,7 @@ Code Seq# Include File Comments 'V' all linux/vt.h 'W' 00-1F linux/watchdog.h conflict! 'W' 00-1F linux/wanrouter.h conflict! +'X' all linux/xfs_fs.h 'Y' all linux/cyclades.h 'a' all ATM on linux <http://lrcwww.epfl.ch/linux-atm/magic.html> diff --git a/Documentation/joystick-parport.txt b/Documentation/joystick-parport.txt index 2217a0489..0576fc07e 100644 --- a/Documentation/joystick-parport.txt +++ b/Documentation/joystick-parport.txt @@ -1,5 +1,5 @@ - Linux Joystick parport drivers v1.2 BETA - (c) 1998-1999 Vojtech Pavlik <vojtech@suse.cz> + Linux Joystick parport drivers v2.0 + (c) 1998-2000 Vojtech Pavlik <vojtech@suse.cz> (c) 1998 Andree Borrmann <a.borrmann@tu-bs.de> Sponsored by SuSE ---------------------------------------------------------------------------- @@ -39,7 +39,7 @@ and/or SNES gamepads connected to the parallel port at once, the output lines of the parallel port are shared, while one of 5 available input lines is assigned to each gamepad. - This protocol is handled by the joy-console.c driver, so that's the one + This protocol is handled by the gamecon.c driver, so that's the one you'll use for NES and SNES gamepads. The main problem with PC parallel ports is that they don't have +5V power @@ -199,9 +199,9 @@ were not compatible with each other: And there were many others. -2.2.1 Multisystem joysticks using joy-db9.c -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - For the Multisystem joysticks, and their derivatives, the joy-db9.c driver +2.2.1 Multisystem joysticks using db9.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + For the Multisystem joysticks, and their derivatives, the db9.c driver was written. It allows only one joystick / gamepad per parallel port, but the interface is easy to build and works with almost anything. @@ -236,25 +236,25 @@ the parallel port. And that's it. On a side note, if you have already built a different adapter for use with -the digital joystick driver 0.8.0.2, this is also supported by the joy-db9.c +the digital joystick driver 0.8.0.2, this is also supported by the db9.c driver, as device type 8. (See section 3.2) -2.2.2 Multisystem joysticks using joy-console.c -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.2.2 Multisystem joysticks using gamecon.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For some people just one joystick per parallel port is not enough, and/or -want to use them on one parallel port together with NES/SNES/PSX pads. This -is possible using the joy-console.c. It supports up to 5 devices of the -above types, including 1 and 2 buttons Multisystem joysticks. +want to use them on one parallel port together with NES/SNES/PSX pads. This is +possible using the gamecon.c. It supports up to 5 devices of the above types, +including 1 and 2 buttons Multisystem joysticks. However, there is nothing for free. To allow more sticks to be used at once, you need the sticks to be purely switch based (that is non-TTL), and not to need power. Just a plain simple six switches inside. If your joystick can do more (eg. turbofire) you'll need to disable it totally first -if you want to use joy-console.c. +if you want to use gamecon.c. Also, the connection is a bit more complex. You'll need a bunch of diodes, and one pullup resistor. First, you connect the Directions and the button -the same as for joy-db9, however with the diodes inbetween. +the same as for db9, however with the diodes inbetween. Diodes (pin 2) -----|<|----> Up @@ -278,17 +278,17 @@ Power --[10kOhm]--+ And that's all, here we go! -2.2.3 Multisystem joysticks using joy-turbografx.c -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.2.3 Multisystem joysticks using turbografx.c +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The TurboGraFX interface, designed by Steffen Schwenke <schwenke@burg-halle.de> allows up to 7 Multisystem joysticks connected to the parallel port. In -Steffen's version, there is support for up to 5 buttons per joystick. -However, since this doesn't work reliably on all parallel ports, the -joy-turbografx.c driver supports only one button per joystick. For more -information on how to build the interface, see +Steffen's version, there is support for up to 5 buttons per joystick. However, +since this doesn't work reliably on all parallel ports, the turbografx.c driver +supports only one button per joystick. For more information on how to build the +interface, see http://www2.burg-halle.de/~schwenke/parport.html @@ -298,7 +298,7 @@ information on how to build the interface, see WARNING: PSX support is experimental, and at the moment doesn't seem to work for most people. If you like adventure, you can try yourself. - The PSX controller is supported by the joy-console.c. + The PSX controller is supported by the gamecon.c. Pinout of the PSX controller (compatible with DirectPadPro): @@ -328,7 +328,7 @@ controllers: ~~~~~~~~ All the Sega controllers are more or less based on the standard 2-button Multisystem joystick. However, since they don't use switches and use TTL -logic, the only driver useable with them is the joy-db9.c driver. +logic, the only driver useable with them is the db9.c driver. 2.4.1 Sega Master System ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -377,13 +377,13 @@ the following schematic: (pin 14) -----> Select - The rest is the same as for Multi2 joysticks using joy-db9.c + The rest is the same as for Multi2 joysticks using db9.c 2.4.3 Sega Saturn ~~~~~~~~~~~~~~~~~ Sega Saturn has eight buttons, and to transfer that, without hacks like Genesis 6 pads use, it needs one more select pin. Anyway, it is still -handled by the joy-db9.c driver. Its pinout is very different from anything +handled by the db9.c driver. Its pinout is very different from anything else. Use this schematic: +-----------> Select 1 @@ -409,7 +409,7 @@ parallel port. (pin 16) -----> Select 2 The other pins (Up, Down, Right, Left, Power, Ground) are the same as for -Multi joysticks using joy-db9.c +Multi joysticks using db9.c 3. The drivers ~~~~~~~~~~~~~~ @@ -417,16 +417,14 @@ Multi joysticks using joy-db9.c described above, allows to connect a different group of joysticks and pads. Here are described their command lines: -3.1 joy-console.c -~~~~~~~~~~~~~~~~~ - Using joy-console.c you can connect up to five devices to one parallel -port. It uses the following kernel/module command line: +3.1 gamecon.c +~~~~~~~~~~~~~ + Using gamecon.c you can connect up to five devices to one parallel port. It +uses the following kernel/module command line: - js_console=port,pad1,pad2,pad3,pad4,pad5 + gc=port,pad1,pad2,pad3,pad4,pad5 - Where 'port' is either the address of the parallel port the joystick/pad -is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ -Linux kernels, the number of the parport interface (eg. 0 for parport0). + Where 'port' the number of the parport interface (eg. 0 for parport0). And 'pad1' to 'pad5' are pad types connected to different data input pins (10,11,12,13,15), as described in section 2.1 of this file. @@ -442,47 +440,22 @@ Linux kernels, the number of the parport interface (eg. 0 for parport0). 5 | Multisystem 2-button joystick 6 | Sony PSX controller 7 | N64 pad - 8 | N64 pad with direction pad as buttons (DirectPadPro style) The exact type of the PSX controller type is autoprobed, so you must have your controller plugged in before initializing. - Should you want to use more than one of parallel ports at once, you can -use js_console_2 and js_console_3 as additional command line parameters for -two more parallel ports. - - Changes: - v0.1 : First version (SNES only) - v0.2 : X/Y directions were exchanged... - v0.3 : Adaptation for kernel 2.1 - v0.4 : Adaptation for joystick-1.2.6 - - added open/close callbacks - v0.5 : Renamed to "joy-console" because I have added - PSX controller support. - v0.6 : NES support - v0.7V : Added "multi system" support - v0.8 : Bugfixed PSX driver... - v0.9V : Changed multi system support - Added Multi2 support - Fixed parport handling - Cleaned up - v0.10 : Fixed PSX buttons 8 and 9 - v0.11V: Switched to EXCL mode - Removed wakeup - v0.12V: Added N64 support - v0.13V: Updated N64 support - v0.14V: Fixed N64 axis/button counts - -3.2 joy-db9.c -~~~~~~~~~~~~~ + Should you want to use more than one of parallel ports at once, you can use +gc_2 and gc_3 as additional command line parameters for two more parallel +ports. + +3.2 db9.c +~~~~~~~~~ Apart from making an interface, there is nothing difficult on using the -joy-db9.c driver. It uses the following kernel/module command line: +db9.c driver. It uses the following kernel/module command line: - js_db9=port,type + db9=port,type - Where 'port' is either the address of the parallel port the joystick/pad -is connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ -Linux kernels, the number of the parport interface (eg. 0 for parport0). + Where 'port' is the number of the parport interface (eg. 0 for parport0). Caveat here: This driver only works on bidirectional parallel ports. If your parallel port is recent enough, you should have no trouble with this. @@ -500,39 +473,26 @@ Old parallel ports may not have this feature. 6 | Genesis pad (6+2 buttons) 7 | Saturn pad (8 buttons) 8 | Multisystem 1-button joystick (v0.8.0.2 pin-out) - 9 | Two Multiststem 1-button joysticks (v0.8.0.2 pin-out) + 9 | Two Multisystem 1-button joysticks (v0.8.0.2 pin-out) + 10 | Amiga CD32 pad Should you want to use more than one of these joysticks/pads at once, you -can use js_db9_2 and js_db9_3 as additional command line parameters for two +can use db9_2 and db9_3 as additional command line parameters for two more joysticks/pads. - Changes: - v0.1 : First version - v0.2 : Changed kernel parameter format - v0.3V: Added Sega Saturn support - Fixed parport and PS/2 mode handling - Cleaned up - v0.4V: Switched to EXCL mode - Removed wakeup - v0.5V: Added 0.8.0.2 HW compatibility for Multi sticks - v0.6V: Better timing for Genesis 6 - v0.7V: Added 0.8.0.2 second joystick support - -3.3 joy-turbografx.c -~~~~~~~~~~~~~~~~~~~~ - The joy-turbografx.c driver uses a very simple kernel/module command line: +3.3 turbografx.c +~~~~~~~~~~~~~~~~ + The turbografx.c driver uses a very simple kernel/module command line: - js_tg=port,js1,js2,js3,js4,js5,js6,js7 + tgfx=port,js1,js2,js3,js4,js5,js6,js7 - Where 'port' is either the address of the parallel port the interface is -connected to (eg. 0x378), or, if you are using the parport driver of 2.1+ -Linux kernels, the number of the parport interface (eg. 0 for parport0). + Where 'port' is the number of the parport interface (eg. 0 for parport0). 'jsX' is the number of buttons the Multisystem joysticks connected to the interface ports 1-7 have. For a standard multisystem joystick, this is 1. Should you want to use more than one of these interfaces at once, you can -use js_tg_2 and js_tg_3 as additional command line parameters for two more +use tgfx_2 and tgfx_3 as additional command line parameters for two more interfaces. 3.4 PC parallel port pinout diff --git a/Documentation/joystick.txt b/Documentation/joystick.txt index 163fece8c..4e53bd281 100644 --- a/Documentation/joystick.txt +++ b/Documentation/joystick.txt @@ -1,5 +1,5 @@ - Linux Joystick driver v1.2.15 - (c) 1996-1999 Vojtech Pavlik <vojtech@suse.cz> + Linux Joystick driver v2.0.0 + (c) 1996-2000 Vojtech Pavlik <vojtech@suse.cz> Sponsored by SuSE ---------------------------------------------------------------------------- @@ -29,45 +29,17 @@ in the package: See the file COPYING. 1. Intro ~~~~~~~~ The joystick driver for Linux provides support for a variety of joysticks -and similar devices. - - These currently include various analog joysticks and gamepads (both -variable resistor based and microswitch+resistor based), following IBM PC -joystick standard, with extensions like additional hats and buttons -compatible with CH Flightstick Pro, ThrustMaster FCS or 6 and 8 button -gamepads. - - In addition to these it also supports some of the new PC joysticks that -use proprietary digital protocols to communicate over the gameport, -currently by FPGaming, Gravis, Logitech, MadCatz, Microsoft, Creative and -ThrustMaster. Saitek protocol support is still to be done. - - The driver also includes support for many gamepads and joysticks that were -used by various non-PC computers and game consoles. These include Multi -system joysticks (Atari, Amiga, Commodore, Amstrad), Sega gamepads (Master -System, Genesis, Saturn), Nintendo gamepads (NES, SNES, N64), Sony gamepads -(PSX). Support for Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap -and others might be added later. - - Last, but not least there is also native Amiga joystick support for the -Amiga Linux port. +and similar devices. It is based on a larger project aiming to support all +input devices in Linux. Should you encounter any problems while using the driver, or joysticks this driver can't make complete use of, I'm very interested in hearing about them. Bug reports and success stories are also welcome. - The joystick package is available at the following FTP sites: - - ftp://ftp.suse.cz/pub/development/joystick/ - ftp://atrey.karlin.mff.cuni.cz/pub/linux/joystick/ - ftp://ftp.gts.cz/pub/linux/joystick/ - - And a homepage of the driver is at: + The input project website is at: - http://www.suse.cz/development/joystick/ - http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/ - http://www.trylinux.com/projects/joystick/ - http://www.linuxgames.com/joystick/ + http://www.suse.cz/development/input/ + http://atrey.karlin.mff.cuni.cz/~vojtech/input/ There is also a mailing list for the driver at: @@ -77,114 +49,70 @@ send "subscribe linux-joystick Your Name" to subscribe to it. 2. Usage ~~~~~~~~ - You could have obtained this driver in two different ways - either in the -joystick package or in the kernel. Because, for successful usage of the -joysticks, the utilities in the package are useful, maybe necessary, and -definitely recommended, I suggest you getting the package at some of the -above mentioned locations. The rest of this file assumes you have it. - -2.1 Compiling the driver package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - To compile the utilities in the joystick package, and the driver itself, -as a standalone module, you first unpack the package, and then edit the -Makefile to meet your needs (namely whether are you using versioned -modules). You will also need an unpacked and configured - - make config - -kernel in - - /usr/src/linux - -Furthermore, if you're using versioned modules, you'll also need - - make dep - -done on the kernel, to create some needed files. - -After that, you compile the joystick driver - - make - - And after that you install it - - make install - - In case you have not used the driver before, you'll need to create the -joystick device files in /dev so that applications can use them: - - make devs - - For manual creation of the joystick devices, check the -Documentation/devices.txt file in the Linux source tree. + For basic usage you just choose the right options in kernel config and +you should be set. - Should you not want to mess with the kernel, and just use the driver -standalone, as modules, skip the next two sections, proceeding right to 2.4, -because all you need is already done. +2.1 inpututils +~~~~~~~~~~~~~~ +For testing and other purposes (for example serial devices), a set of +utilities is available at the abovementioned website. I suggest you download +and install it before going on. -2.2 Patching the kernel -~~~~~~~~~~~~~~~~~~~~~~~ - If you already have the most recent joystick driver in your kernel, skip -this section. If not, you need to patch the kernel, so that it contains the -current driver version. You do that with a command: - - patch -Esp1 < /usr/src/joystick-1.2.x/kernel-2.x.y.diff - -in - - /usr/src/linux - -2.3 Compiling the kernel -~~~~~~~~~~~~~~~~~~~~~~~~ - To compile joystick support into the kernel, use the kernel configuration -scripts, and answer 'Y' to Joystick support and also to at least one of the -hardware specific options. After doing something like - - make bzlilo - - you are done with the driver installation. Just reboot and the driver -should find all the connected joysticks. Read the notes about the hardware -specific drivers later in this file, though. - - You can also compile the driver as modules, answering 'M' to all joystick -support you want to have modules for. It is possible to have the main -joystick driver compiled into the kernel and the hardware dependent drivers -as modules. After you compile the modules - - make modules - - And install them - - make modules_install - - you're set, and can proceed to the next step. +2.2 Device nodes +~~~~~~~~~~~~~~~~ +For applications to be able to use the joysticks, in you don't use devfs, +you'll have to manually create these nodes in /dev: + +cd /dev +rm js* +mkdir input +mknod input/js0 c 13 0 +mknod input/js1 c 13 1 +mknod input/js2 c 13 2 +mknod input/js3 c 13 3 +ln -s input/js0 js0 +ln -s input/js1 js1 +ln -s input/js2 js2 +ln -s input/js3 js3 + +For testing with inpututils it's also convenient to create these: + +mknod input/event0 c 13 64 +mknod input/event1 c 13 65 +mknod input/event2 c 13 66 +mknod input/event3 c 13 67 + +2.4 Modules needed +~~~~~~~~~~~~~~~~~~ + For all joystick drivers to function, you'll need the userland interface +module in kernel, either loaded or compiled in: -2.4 Inserting the modules into the kernel -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - After installing the modules you'll first need to insert the generic -joystick driver module into the kernel + modprobe joydev - insmod joystick + For gameport joysticks, you'll have to load the gameport driver as well; - and then one or more of the hardware specific modules + modprove ns558 - insmod joy-something + And for serial port joysticks, you'll need the serial input line +discipline module loaded and the inputattach utility started: - where 'something' is the type of your joystick. See below for more precise -explanation. + modprobe serport + inputattach -xxx /dev/tts/X & - Alternately you can add the lines + In addition to that, you'll need the joystick driver module itself, most +usually you'll have an analog joystick: - alias char-major-15 joy-something - options joy-something js_xx=x,x,x,x,... + modprobe analog + + For automatic module loading, something like this might work: - to the /etc/conf.modules file, so that the joystick module will be loaded -automatically when the /dev/js devices are accessed. + alias tty-ldisc-2 serport + alias char-major-13 joydev ns558 analog 2.5 Verifying that it works ~~~~~~~~~~~~~~~~~~~~~~~~~~~ For testing the joystick driver functionality, there is the jstest -program. You run it by typing: +program in the utilities package. You run it by typing: jstest /dev/js0 @@ -231,109 +159,95 @@ In this section each of the separate hardware specific drivers is described. 3.1 Analog joysticks ~~~~~~~~~~~~~~~~~~~~ - The joy-analog.c uses the standard analog inputs of the gameport, and thus -supports all standard joysticks and gamepads. It also supports extensions -like additional hats and buttons compatible with CH Flightstick Pro, -ThrustMaster FCS or 6 and 8 button gamepads. + The analog.c uses the standard analog inputs of the gameport, and thus +supports all standard joysticks and gamepads. It uses a very advanced +routine for this, allowing for data precision that can't be found on any +other system. + + It also supports extensions like additional hats and buttons compatible +with CH Flightstick Pro, ThrustMaster FCS or 6 and 8 button gamepads. Saitek +Cyborg 'digital' joysticks are also supportted by this driver, because +they're basically souped up CHF sticks. However the only types that can be autodetected are: * 2-axis, 4-button joystick * 3-axis, 4-button joystick * 4-axis, 4-button joystick +* Saitek Cyborg 'digital' joysticks For other joystick types (more/less axes, hats, and buttons) support you'll need to specify the types either on the kernel command line or on the -module command line, when inserting joy-analog.o into the kernel. The +module command line, when inserting analog.o into the kernel. The parameters are: - js_an=p0,m0,n0,p1,m1,n1 ... - - Where 'p' is the port number, eg. 0x201, which is the standard address. -'m' and 'n' are joystick 0 and joystick 1 bitmasks for the specified -joystick port. The bits in the bitmasks mean: - - Bit | 2^n | Meaning - ---------------------------------- - 0 | 1 | Axis X1 - 1 | 2 | Axis Y1 - 2 | 4 | Axis X2 - 3 | 8 | Axis Y2 - 4 | 16 | Button A - 5 | 32 | Button B - 6 | 64 | Button C - 7 | 128 | Button D - 8 | 256 | CHF Buttons X and Y - 9 | 512 | CHF Hat 1 - 10 | 1024 | CHF Hat 2 - 11 | 2048 | FCS Hat - 12 | 4096 | Pad Button X - 13 | 8192 | Pad Button Y - 14 | 16384 | Pad Button U - 15 | 32768 | Pad Button V - -(CHF = CH Flightstick Pro, FCS = ThrustMaster FCS) - - Following is a table of joysticks for which the 'm' values are known. If -you have any additions/corrections to it, e-mail me. - - Joystick | 'm' value - ---------------------------------------------------- - Simple 2-button 2-axis joystick | 0x0033 - Second simple joystick on Y-cable | 0x00cc - Genius Flight2000 F-12 | 0x00f3 - Genius Flight2000 F-21 | 0x08f7 - Genius Flight2000 F-22 | 0x02ff - Genius GameHunter G-06 | 0xf0f3 - Genius MaxFire G-07 | 0xf0f3 - Genius PowerStation | 0xf0f3 - Laing #1 PC SuperPad | 0xf0f3 - Logitech Wingman | 0x003b - Microsoft SideWinder Standard | 0x003b - QuickShot QS-201 SuperWarrior | 0x00fb - Saitek Megapad XII | 0x30f3 - PC Powerpad Pro | 0x30f3 - - In case you have one of the joystick in the table below, and it doesn't -work with a specific driver in digital mode for some reason, you can use -them in analog mode with the joy-analog driver as well. However, digital -operation is always better. - - Joystick | 'm' value - ---------------------------------------------------- - Gravis GamePad Pro - analog mode | 0x00f3 - Genius Flight2000 F-23 | 0x02ff - Microsoft SideWinder 3D Pro - CHF mode | 0x02ff - Microsoft SideWinder 3D Pro - FCS mode | 0x08f7 - - An example that would configure the driver to use two two axis, two button -joysticks connected to port 0x201, a single four button four axis joystick -connected to port 0x202, a four axis, six button and two hat CHF compatible -joystick on 0x203, and a two axis four button FCS compatible joystick with a -single hat on 0x207: - - js_an=0x201,0x33,0xcc,0x202,0xff,0,0x203,0x7ff,0,0x207,0x8f3,0 - - If you can't sum bits into hex numbers in your head easily, you can simply -sum the values in the 2^n column decimally and use that number instead. -Using this method you'd get a command line: - - js_an=0x201,51,204,0x202,255,0,0x203,2047,0,0x207,2291,0 - - And it would do the same as the above explained command line. Use -whichever way you like best. + js=type,type,type,.... + + 'type' is type of the joystick from the table below, defining joysticks +present on gameports in the system, starting with gameport0, second 'type' +entry defining joystick on gameport1 and so on. + + Type | Meaning + ----------------------------------- + none | No analog joystick on that port + auto | Autodetect joystick + 2btn | 2-button n-axis joystick + y-joy | Two 2-button 2-axis joysticks on an Y-cable + y-pad | Two 2-button 2-axis gamepads on an Y-cable + fcs | Thrustmaster FCS compatible joystick + chf | Joystick with a CH Flightstick compatible hat + fullchf | CH Flightstick compatible with two hats and 6 buttons + gamepad | 4/6-button n-axis gamepad + gamepad8 | 8-button 2-axis gamepad + + In case your joystick doesn't fit in any of the above categories, you can +specify the type as a number by combining the bits in the table below. This +is not recommended unless you really know what are you doing. It's not +dangerous, but not simple either. + + Bit | Meaning + -------------------------- + 0 | Axis X1 + 1 | Axis Y1 + 2 | Axis X2 + 3 | Axis Y2 + 4 | Button A + 5 | Button B + 6 | Button C + 7 | Button D + 8 | CHF Buttons X and Y + 9 | CHF Hat 1 + 10 | CHF Hat 2 + 11 | FCS Hat + 12 | Pad Button X + 13 | Pad Button Y + 14 | Pad Button U + 15 | Pad Button V + 16 | Saitek F1-F4 Buttons + 17 | Saitek Digital Mode + 19 | GamePad + 20 | Joy2 Axis X1 + 21 | Joy2 Axis Y1 + 22 | Joy2 Axis X2 + 23 | Joy2 Axis Y2 + 24 | Joy2 Button A + 25 | Joy2 Button B + 26 | Joy2 Button C + 27 | Joy2 Button D + 31 | Joy2 GamePad 3.2 Microsoft SideWinder joysticks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Microsoft 'Digital Overdrive' protocol is supported by the -joy-sidewinder.c module. All currently supported joysticks: + Microsoft 'Digital Overdrive' protocol is supported by the sidewinder.c +module. All currently supported joysticks: -* SideWinder 3D Pro -* SideWinder Force Feedback Pro -* SideWinder Force Feedback Wheel -* SideWinder FreeStyle Pro -* SideWinder GamePad (up to four, chained together) -* SideWinder Precision Pro +* Microsoft SideWinder 3D Pro +* Microsoft SideWinder Force Feedback Pro +* Microsoft SideWinder Force Feedback Wheel +* Microsoft SideWinder FreeStyle Pro +* Microsoft SideWinder GamePad (up to four, chained) +* Microsoft SideWinder Precision Pro +* Microsoft SideWinder Precision Pro USB are autodetected, and thus no module parameters are needed. @@ -349,9 +263,9 @@ by the analog driver described above. 3.3 Logitech ADI devices ~~~~~~~~~~~~~~~~~~~~~~~~ - Logitech ADI protocol is supported by the joy-logitech.c module. It should -support any Logitech device using this protocol. This includes, but is not -limited to: + Logitech ADI protocol is supported by the adi.c module. It should support +any Logitech device using this protocol. This includes, but is not limited +to: * Logitech CyberMan 2 * Logitech ThunderPad Digital @@ -370,48 +284,48 @@ together. Logitech WingMan Joystick, Logitech WingMan Attack, Logitech WingMan Extreme and Logitech WingMan ThunderPad are not digital joysticks and are handled by the analog driver described above. Logitech WingMan Warrior and -Logitech Magellan are supported by serial drivers described below. Logitech -CyberMan, Logitech WingMan Force and Logitech WingMan Formula Force are not -supported yet. +Logitech Magellan are supported by serial drivers described below. Logitech +WingMan Force and Logitech WingMan Formula Force are supported by the +I-Force driver described below. Logitech CyberMan is not supported yet. 3.4 Gravis GrIP ~~~~~~~~~~~~~~~ - Gravis GrIP protocol is supported by the joy-gravis.c module. It -currently supports: + Gravis GrIP protocol is supported by the grip.c module. It currently +supports: * Gravis GamePad Pro -* Gravis Xterminator * Gravis BlackHawk Digital +* Gravis Xterminator +* Gravis Xterminator DualControl All these devices are autodetected, and you can even use any combination of up to two of these pads either chained together or using an Y-cable on a single gameport. -GrIP MultiPort and Gravis Xterminator DualControl aren't supported yet. -Gravis Stinger is a serial device and hopefully will be supported in the -future. Other Gravis joysticks are supported by the joy-analog driver. +GrIP MultiPort isn't supported yet. Gravis Stinger is a serial device and +hopefully will be supported soon. Other Gravis joysticks are supported by +the analog driver. 3.5 FPGaming A3D and MadCatz A3D ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Assassin 3D protocol created by FPGaming, is used both by FPGaming themselves and is licensed to MadCatz. A3D devices are supported by the -joy-assassin.c module. It currently supports: +a3d.c module. It currently supports: * FPGaming Assassin 3D * MadCatz Panther * MadCatz Panther XL All these devices are autodetected. Because the Assassin 3D and the Panther -allow connecting analog joysticks to them, these are supported in this -driver, too. The driver uses the js_as parameter for the analog joysticks, -which has the same syntax as js_an for the analog driver. +allow connecting analog joysticks to them, you'll need to load the analog +driver as well to handle the attached joysticks. - The trackball support is far from perfect at this stage of development, -but should be well usable. + The trackball should work with USB mousedev module as a normal mouse. See +the USB documentation for how to setup an USB mouse. 3.6 ThrustMaster DirectConnect (BSP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The TM DirectConnect (BSP) protocol is supported by the joy-thrustmaster.c + The TM DirectConnect (BSP) protocol is supported by the tmdc.c module. This includes, but is not limited to: * ThrustMaster Millenium 3D Inceptor @@ -426,96 +340,62 @@ module. This includes, but is not limited to: If you have one of these, contact me. BSP devices are autodetected, and thus no parameters to the module -are needed. +are needed. Up to two TMDC devices can be connected to one gameport, using +an Y-cable. 3.7 Creative Labs Blaster ~~~~~~~~~~~~~~~~~~~~~~~~~ - The Blaster protocol is supported by the joy-creative.c module. It -currently supports only the: + The Blaster protocol is supported by the cobra.c module. It supports only +the: * Creative Blaster GamePad Cobra Up to two of these can be used on a single gameport, using an Y-cable. -3.8 PDPI Lightning 4 gamecards -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - PDPI Lightning 4 gamecards are supported by the joy-lightning.c module. -This driver is only for analog joysticks connected to the card - if you want -to use some of the digital devices, you need to use its specific driver. The -card will work in legacy mode with them, though. - - Since all features of analog joysticks can't be detected, this driver -needs a command line: - - js_l4=p0,m0,n0,p1,m1,n1,.... - - As you can see, it's very similar to the analog drivers command line. -Actually it is the same except for the meaning of p0. p0 in this case is the -port the joystick is attached to: - - p | Port - ---------------------------- - 0 | Primary card, port 1 - 1 | Primary card, port 2 - 2 | Primary card, port 3 - 3 | Primary card, port 4 - 4 | Secondary card, port 1 - 5 | Secondary card, port 2 - 6 | Secondary card, port 3 - 7 | Secondary card, port 4 - - Two cards maximum are allowed in one system, because of the card's design. - - See the description of analog joystick driver for explanations of m0 and -n0 values. - -3.9 Trident 4DWave / Aureal Vortex -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Soundcards with a Trident 4DWave DX/NX or Aureal Vortex chipset provide an -"Enhanced Game Port" mode where the soundcard handles polling the joystick. -This mode is supported by the joy-pci module. - - If no module parameters are given, the joy-pci module will set all the -soundcards it finds to "enhanced" mode, and will try to autodetect the type -of attached joystick. It can only detect the same types of joysticks that -the joy-analog module can. - - This module accepts parameters in the form: - - js_pci=t0,i0,m0,n0,t1,i1,m1,n1,.... - - The "t" value specifies the type of card, as follows: - - t | Card Type - ---------------------------- - 0 | Trident 4DWave DX - 1 | Trident 4DWave NX - 2 | Aureal Vortex1 (Au8820 chipset) - 3 | Aureal Vortex2 (Au8830 chipset) - - If you have more than one card of the same type, the "i" parameter lets -you choose which card to apply the "m" and "n" values to. It counts from -"0". (The driver detects cards in the order listed in the above table.) - - The "m" and "n" values have the same meaning as for the analog module, -with the exception that the value m=0, n=0 indicates that joy-pci should -completely ignore that port. This can be useful to reserve a certain port -for purely MIDI operation. +3.8 Genius Digital joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The Genius digitally communicating joysticks are supported by the gf2k.c +module. This includes: - For example, let's say you have 3 sound cards - a 4Dwave DX, a 4DWave NX, -and a Vortex 2. You have a three-axis, four-button, one-hat CHF- compatible -joystick on the DX. You use the NX to interface to an external MIDI device. -Finally, you have two two-axis, two-button joysticks on the Vortex. Your -command line might look like: +* Genius Flight2000 F-23 joystick +* Genius Flight2000 F-31 joystick +* Genius G-09D gamepad - js_pci=0,0,0x207,0,1,1,0,0,3,0,0x33,0xcc + Other Genius digital joysticks are not supported yet, but support can be +added fairly easily. -3.10 Amiga +3.9 InterAct Digital joysticks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The InterAct digitally communicating joysticks are supported by the +interact.c module. This includes: + +* InterAct HammerHead/FX gamepad +* InterAct ProPad8 gamepad + + Other InterAct digital joysticks are not supported yet, but support can be +added fairly easily. + +3.10 PDPI Lightning 4 gamecards +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + PDPI Lightning 4 gamecards are supported by the lightning.c module. +Once the module is loaded, the analog driver can be used to handle the +joysticks. Digitally communicating joystick will work only on port 0, while +using Y-cables, you can connect up to 8 analog joysticks to a single L4 +card, 16 in case you have two in your system. + +3.11 Trident 4DWave / Aureal Vortex +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Soundcards with a Trident 4DWave DX/NX or Aureal Vortex/Vortex2 chipsets +provide an "Enhanced Game Port" mode where the soundcard handles polling the +joystick. This mode is supported by the pcigame.c module. Once loaded the +analog driver can use the enhanced features of these gameports.. + +3.12 Amiga ~~~~~~~~~~ - Amiga joysticks, connected to an Amiga, are supported by the joy-amiga.c + Amiga joysticks, connected to an Amiga, are supported by the amijoy.c driver. Since they can't be autodetected, the driver has a command line. - js_am=a,b + amijoy=a,b a and b define the joysticks connected to the JOY0DAT and JOY1DAT ports of the Amiga. @@ -528,32 +408,32 @@ the Amiga. No more joystick types are supported now, but that should change in the future if I get an Amiga in the reach of my fingers. -3.11 Game console and 8-bit pads and joysticks +3.13 Game console and 8-bit pads and joysticks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See joystick-parport.txt for more info. -3.12 SpaceTec/LabTec devices +3.14 SpaceTec/LabTec devices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SpaceTec serial devices communicate using the SpaceWare protocol. It is -supported by the joy-spaceorb and joy-spaceball drivers. The devices currently -supported by joy-spaceorb are: +supported by the spaceorb.c and spaceball.c drivers. The devices currently +supported by spaceorb.c are: * SpaceTec SpaceBall Avenger * SpaceTec SpaceOrb 360 -Devices currently supported by joy-spaceball are: +Devices currently supported by spaceball.c are: * SpaceTec SpaceBall 4000 FLX - In addition to having the joy-spaceorb/spaceball module in the kernel, you -also need to attach a serial port to it. to do that, run the jsattach -program: + In addition to having the spaceorb/spaceball and serport modules in the +kernel, you also need to attach a serial port to it. to do that, run the +jsattach program: - jsattach --spaceorb /dev/ttySx & + inputattach --spaceorb /dev/tts/x & or - jsattach --sball4 /dev/ttySx & + jsattach --spaceball /dev/tts/x & -where /dev/ttySx is the serial port which the device is connected to. After +where /dev/tts/x is the serial port which the device is connected to. After doing this, the device will be reported and will start working. There is one caveat with the SpaceOrb. The button #6, the on the bottom @@ -564,9 +444,9 @@ you bind it to some other function. SpaceTec SpaceBall 2003 FLX and 3003 FLX are not supported yet. -3.13 Logitech SWIFT devices +3.15 Logitech SWIFT devices ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The SWIFT serial protocol is supported by the joy-warrior module. It + The SWIFT serial protocol is supported by the warrior.c module. It currently supports only the: * Logitech WingMan Warrior @@ -575,11 +455,11 @@ but in the future, Logitech CyberMan (the original one, not CM2) could be supported as well. To use the module, you need to run jsattach after you insert/compile the module into your kernel: - jsattach --warrior /dev/ttySx & + inputattach --warrior /dev/tts/x & -ttySx is the serial port your Warrior is attached to. +/dev/tts/x is the serial port your Warrior is attached to. -3.14 Magellan / Space Mouse +3.16 Magellan / Space Mouse ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Magellan (or Space Mouse), manufactured by LogiCad3d (formerly Space Systems), for many other companies (Logitech, HP, ...) is supported by the @@ -592,10 +472,29 @@ models, the additional buttons on the 'Plus' versions are not supported yet. To use it, you need to attach the serial port to the driver using the - jsattach --magellan /dev/ttySx & + inputattach --magellan /dev/tts/x & command. After that the Magellan will be detected, initialized, will beep, -and the /dev/jsX device should become useable. +and the /dev/input/jsX device should become useable. + +3.17 I-Force devices +~~~~~~~~~~~~~~~~~~~~ + All I-Force devices ale supported by the iforce.c module. This includes, +but is not limited to: + +* Logitech WingMan Force +* Logitech WingMan Force Wheel +* Guillemot Race Leader wheel + + To use it, you need to attach the serial port to the driver using the + + inputattach --iforce /dev/tts/x & + +command. After that the I-Force device will be detected, and the +/dev/input/jsX device should become useable. + + In case you're using the device via the USB port, the inputattach command +isn't needed. 4. Troubleshooting ~~~~~~~~~~~~~~~~~~ @@ -604,80 +503,20 @@ testing whether the driver works, if in doubt, use the jstest utility in some of its modes. The most useful modes are "normal" - for the 1.x interface, and "old" for the "0.x" interface. You run it by typing: - jstest --normal /dev/js0 - jstest --old /dev/js0 - - If your trouble stems from the fact the drivers can't detect the joystick -attached to your gameport, and you decide you need my help (which I will -gladly provide), please use the joydump utility first. It's created just by -typing - - make joydump.o - - in the directory where you unpacked the joystick package. It is run then -by typing + jstest --normal /dev/input/js0 + jstest --old /dev/input/js0 - insmod joydump.o + Additionally you can do a test with the evtest utility: - in the same directory. It will return a 'device busy' or 'initialization -failed' error. This is perfectly okay. It has already done it's job. The -results can be found in the system log or in the output of the - - dmesg - -command. Please send me the results along with your problem report. + evtest /dev/input/event0 Oh, and read the FAQ! :) 5. FAQ ~~~~~~ -Q: The driver doesn't find any joysticks connected to my soundcard with the - message "joy-something: no joysticks found" and "joy-something.o: - init_module: Device or resource busy." or "Initialization of joy-something - failed" What could be the cause? -A: The most common cause is that the joystick port on your soundcard is - not enabled. If it is an ISA PnP card, you'll need isapnptools to configure - the gameport. Non-PnP cards usually use some option to the sound driver - - see the sound driver docs and source and enable the port. Note that in case - of a PnP card you have to load the joystick driver as a module after running - the isapnp command, it will not work in the opposite order. - -Q: Any access to the joystick devices gives me "Operation not supported by - device". What am I doing wrong? -A: You're running a 2.0 kernel and you forgot to insmod the hardware - specific module. You not only need the joystick.o, but also one of the other - joy-*.o files (most usually joy-analog.o), as described in this document, - section 2. If you are not using modules, then you didn't say 'Y' to any of - the hardware-specific questions. Again, see section 2. If you did select - the specific support, and you still get this message, check that you - selected the right one, and if it still doesn't work, go to the previous - FAQ. - -Q: Everything is fine, except I get "No such device" error when I try to - do anything with /dev/js0. What's the cause? -A: You're running a 2.1 or 2.2. kernel and you want to read the previous FAQ. - -Q: Upon 'insmod joystick.o' I get a LOT of unresolved symbols, including - 'printk' and others. Why? -A: You either don't have your kernel compiled with module support. If - that's the cause, re-compile your kernel with module support switched on. - Or, you use versioned symbols, and don't have -DMODVERSIONS in the joystick - driver Makefile, or vice versa. Correct the situation by either removing or - adding -DMODVERSIONS to the Makefile. - -Q: Upon 'insmod joy-something' I get a bunch of unresolved symbols, like - 'js_register_port, js_unregister device' and others. What's wrong? -A: You need to 'insmod joystick.o' first. - -Q: Running 'jstest 1' or 'jscal 1' doesn't work, and returns with "File - not found" error. What is the problem? -A: The command line interface for these tools is different from what - version 0.8.0 used. You have to specify the whole device name, eg. 'jstest - /dev/js0'. - Q: Running 'jstest /dev/js0' results in "File not found" error. What's the cause? -A: The device files don't exist. Run 'make devs'. +A: The device files don't exist. Create them (see section 2.2). Q: Is it possible to connect my old Atari/Commodore/Amiga/console joystick or pad that uses a 9-pin D-type cannon connector to the serial port of my @@ -741,47 +580,3 @@ to the joystick driver development: If you think you should be in this list and are not, it's possible that I forgot to include you - contact me and I'll correct the error. :) - - Thanks to KYE Systems Europe, who provided me with driver sources for the -Genius Flight2000 Digital F-23, which happens to be identical (in software) -to Microsoft SideWinder 3D Pro. - - Thanks to ThrustMaster Inc. who provided me with docs for their digital -protocol specifications, and to Trystan A Larey-Williams <axe@mail.wsu.edu>, -who wrote an attempt of a driver for them. - - Thanks to Creative Labs Europe, and Ifor Powell <ipowell@cle.creaf.com>, -who provided me with docs for their first generation Blaster GamePad. - - Special thanks go to FP-Gaming, Inc. and James C Barnes <james@fpgaming.com>, -who provided me with help and detailed information about the Assassin 3D -protocol and devices, and even sent me a Panther and Panther XL for testing, -along with cool T-shirts. - - Special thanks to PDPI, Mike Pelkey <pdpi@email.msn.com> and Brand Kvavle -<BrandK@blackdiamond.com>, for providing me with documentation and example -code for their L4 gamecard, and sending me the card to test my driver with -it. - - Thanks to LogiCad3D for their support, for having the specifications -online and for the nice music on their telephone. - - Special thanks to Logitech, Jerry de Raad <Jerry_de_Raad@logitech.com>, -Thomas Burgel <Thomas_Burgel@logitech.com>, Avinash Shinde -<Avinash_Shinde@logitech.com> for providing me with a lot of documentation -for their devices, and also for a big box, containing a CyberMan2, Wingman -Extreme, Magellan, Wingman Warrior, two MouseMan mice, and a NewTouch -keyboard. - - Thanks to everyone else who helped me develop this package of drivers! - - No thanks to Microsoft and Gravis, who don't release a word about their -hardware .... :( - -8. ChangeLog -~~~~~~~~~~~~ - See the ChangeLog file for the log of changes. - -9. To do -~~~~~~~~ - See the TODO file for the list of things planned. diff --git a/MAINTAINERS b/MAINTAINERS index 634e37070..6ce7ccf42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -199,8 +199,9 @@ S: Maintained COMPAQ SMART2 RAID DRIVER P: Charles White M: Charles White <arrays@compaq.com> -L: compaqandlinux@yps.org -S: Maintained +L: compaqandlinux@cpqlin.van-dijk.net +W: ftp.compaq.com/pub/products/drivers/linux +S: Supported COMPUTONE INTELLIPORT MULTIPORT CARD P: Doug McNash @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = -test1-ac22 +EXTRAVERSION = -test2 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/Rules.make b/Rules.make index d3e51bbec..c0263dedb 100644 --- a/Rules.make +++ b/Rules.make @@ -291,9 +291,12 @@ FILES_FLAGS_CHANGED := $(strip \ )) # A kludge: .S files don't get flag dependencies (yet), -# because that will involve changing a lot of Makefiles. +# because that will involve changing a lot of Makefiles. Also +# suppress object files explicitly listed in $(IGNORE_FLAGS_OBJS). +# This allows handling of assembly files that get translated into +# multiple object files (see arch/ia64/lib/idiv.S, for example). FILES_FLAGS_CHANGED := $(strip \ - $(filter-out $(patsubst %.S, %.o, $(wildcard *.S)), \ + $(filter-out $(patsubst %.S, %.o, $(wildcard *.S) $(IGNORE_FLAGS_OBJS)), \ $(FILES_FLAGS_CHANGED))) ifneq ($(FILES_FLAGS_CHANGED),) diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c index 648090d24..f114ab24f 100644 --- a/arch/alpha/kernel/core_mcpcia.c +++ b/arch/alpha/kernel/core_mcpcia.c @@ -45,8 +45,6 @@ #define MCPCIA_MAX_HOSES 4 -static int mcpcia_hose_count; /* Actual number found. */ - /* * Given a bus, device, and function number, compute resulting * configuration space address and setup the MCPCIA_HAXR2 register @@ -412,10 +410,9 @@ mcpcia_startup_hose(struct pci_controler *hose) * Window 2 is direct access 2GB at 2GB * ??? We ought to scale window 1 with memory. */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = iommu_arena_new(hose, 0x40000000, 0x08000000, 0); - /* Make sure to align the arenas. */ - hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 1); - hose->sg_pci = iommu_arena_new(hose, 0x40000000, 0x08000000, 1); __direct_map_base = 0x80000000; __direct_map_size = 0x80000000; @@ -468,20 +465,20 @@ void __init mcpcia_init_hoses(void) { struct pci_controler *hose; + int hose_count; int h; - mcpcia_hose_count = 0; - /* First, find how many hoses we have. */ + hose_count = 0; for (h = 0; h < MCPCIA_MAX_HOSES; ++h) { if (mcpcia_probe_hose(h)) { if (h != 0) mcpcia_new_hose(h); - mcpcia_hose_count++; + hose_count++; } } - printk("mcpcia_init_hoses: found %d hoses\n", mcpcia_hose_count); + printk("mcpcia_init_hoses: found %d hoses\n", hose_count); /* Now do init for each hose. */ for (hose = hose_head; hose; hose = hose->next) @@ -564,7 +561,7 @@ static void mcpcia_print_system_area(unsigned long la_ptr) { struct el_common *frame; - int i; + struct pci_controler *hose; struct IOD_subpacket { unsigned long base; @@ -593,12 +590,12 @@ mcpcia_print_system_area(unsigned long la_ptr) } *iodpp; frame = (struct el_common *)la_ptr; - iodpp = (struct IOD_subpacket *) (la_ptr + frame->sys_offset); - for (i = 0; i < mcpcia_hose_count; i++, iodpp++) { + for (hose = hose_head; hose; hose = hose->next, iodpp++) { + printk("IOD %d Register Subpacket - Bridge Base Address %16lx\n", - i, iodpp->base); + hose->index, iodpp->base); printk(" WHOAMI = %8x\n", iodpp->whoami); printk(" PCI_REV = %8x\n", iodpp->pci_rev); printk(" CAP_CTRL = %8x\n", iodpp->cap_ctrl); diff --git a/arch/alpha/kernel/core_titan.c b/arch/alpha/kernel/core_titan.c index 37dd8b3c3..fac75cab5 100644 --- a/arch/alpha/kernel/core_titan.c +++ b/arch/alpha/kernel/core_titan.c @@ -23,7 +23,6 @@ #include "proto.h" #include "pci_impl.h" -int TITAN_bootcpu; unsigned TITAN_agp = 0; static struct @@ -343,7 +342,11 @@ titan_init_one_pachip_port(titan_pachip_port *port, int index) * address range. */ hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_isa->align_entry = 8; /* 64KB for ISA */ + hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0); + hose->sg_pci->align_entry = 4; /* Titan caches 4 PTEs at a time */ + __direct_map_base = 0x40000000; __direct_map_size = 0x80000000; @@ -401,7 +404,7 @@ titan_init_arch(void) printk("%s: CSR_DREV 0x%lx\n", FN, TITAN_dchip->drev.csr); #endif - TITAN_bootcpu = __hard_smp_processor_id(); + boot_cpuid = __hard_smp_processor_id(); /* With multiple PCI busses, we play with I/O as physical addrs. */ ioport_resource.end = ~0UL; diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index dc665fbfb..ac7ca67f8 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -233,7 +233,7 @@ static unsigned long irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; static void select_smp_affinity(int irq) { - static int last_cpu = 0; + static int last_cpu; int cpu = last_cpu + 1; if (! irq_desc[irq].handler->set_affinity || irq_user_affinity[irq]) diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h index 454950c1b..f29e775db 100644 --- a/arch/alpha/kernel/machvec_impl.h +++ b/arch/alpha/kernel/machvec_impl.h @@ -121,7 +121,7 @@ * all but one of which we want to go away. In a non-GENERIC kernel, * we want only one, ever. * - * Accomplish this in the GENERIC kernel by puting all of the vectors + * Accomplish this in the GENERIC kernel by putting all of the vectors * in the .init.data section where they'll go away. We'll copy the * one we want to the real alpha_mv vector in setup_arch. * diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 39de16465..7a916ad08 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -426,8 +426,8 @@ pcibios_size_bridge(struct pci_bus *bus, struct pbus_set_ranges_data *outer) inner.mem_end -= inner.mem_start; /* Align the sizes up by bridge rules */ - inner.io_end = ROUND_UP(inner.io_end, 4*1024); - inner.mem_end = ROUND_UP(inner.mem_end, 1*1024*1024); + inner.io_end = ROUND_UP(inner.io_end, 4*1024) - 1; + inner.mem_end = ROUND_UP(inner.mem_end, 1*1024*1024) - 1; /* Adjust the bridge's allocation requirements */ bridge->resource[0].end = bridge->resource[0].start + inner.io_end; diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index cadf69b58..ccf0cefbb 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -97,6 +97,10 @@ iommu_arena_alloc(struct pci_iommu_arena *arena, long n) } if (i < n) { + /* Reached the end. Flush the TLB and restart the + search from the beginning. */ + alpha_mv.mv_pci_tbi(arena->hose, 0, -1); + p = 0, i = 0; while (i < n && p+i < nent) { if (ptes[p+i]) @@ -197,7 +201,6 @@ pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size, int direction) DBGA("pci_map_single: [%p,%lx] np %ld -> sg %x from %p\n", cpu_addr, size, npages, ret, __builtin_return_address(0)); - alpha_mv.mv_pci_tbi(hose, ret, ret + size - 1); return ret; } @@ -247,6 +250,13 @@ pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size, npages = calc_npages((dma_addr & ~PAGE_MASK) + size); iommu_arena_free(arena, dma_ofs, npages); + + /* + If we're freeing ptes above the `next_entry' pointer (they + may have snuck back into the TLB since the last wrap flush), + we need to flush the TLB before reallocating the latter. + */ + if (dma_ofs >= arena->next_entry) alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1); DBGA("pci_unmap_single: sg [%x,%lx] np %ld from %p\n", @@ -434,8 +444,6 @@ sg_fill(struct scatterlist *leader, struct scatterlist *end, #endif } while (++sg < end && (int) sg->dma_address < 0); - alpha_mv.mv_pci_tbi(arena->hose, out->dma_address, - out->dma_address+out->dma_length-1); return 1; } @@ -569,6 +577,12 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, if (fend < tend) fend = tend; } + /* + If we're freeing ptes above the `next_entry' pointer (they + may have snuck back into the TLB since the last wrap flush), + we need to flush the TLB before reallocating the latter. + */ + if ((fend - arena->dma_base) >> PAGE_SHIFT >= arena->next_entry) alpha_mv.mv_pci_tbi(hose, fbeg, fend); DBGA("pci_unmap_sg: %d entries\n", nents - (end - sg)); diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 475b477bd..88f4afd68 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -275,11 +275,6 @@ clipper_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) irq = (vector - 0x800) >> 4; -#if 0 - printk("clipper_srm_device_interrupt: vector 0x%lx IRQ %d cpu %d\n", - vector, irq, smp_processor_id()); -#endif - /* * The SRM console reports PCI interrupts with a vector calculated by: * diff --git a/arch/i386/defconfig b/arch/i386/defconfig index d4a47ea4a..638d5df84 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -23,7 +23,8 @@ CONFIG_M686=y # CONFIG_MK6 is not set # CONFIG_MK7 is not set # CONFIG_MCRUSOE is not set -# CONFIG_MWINCHIP is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set # CONFIG_MWINCHIP3D is not set CONFIG_X86_WP_WORKS_OK=y CONFIG_X86_INVLPG=y @@ -114,6 +115,8 @@ CONFIG_BLK_DEV_FD=y # CONFIG_BLK_DEV_MD is not set # CONFIG_MD_LINEAR is not set # CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set @@ -460,6 +463,7 @@ CONFIG_PSMOUSE=y # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set @@ -535,7 +539,6 @@ CONFIG_NFS_FS=y # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index c31838c8e..ce167928b 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -18,7 +18,7 @@ export-objs := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ - pci-dma.o i386_ksyms.o + pci-dma.o i386_ksyms.o i387.o ifdef CONFIG_PCI diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 21d26d3e9..a80d61ebc 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -323,12 +323,10 @@ ENTRY(coprocessor_error) pushl $ SYMBOL_NAME(do_coprocessor_error) jmp error_code -#ifdef CONFIG_X86_XMM ENTRY(simd_coprocessor_error) pushl $0 pushl $ SYMBOL_NAME(do_simd_coprocessor_error) jmp error_code -#endif ENTRY(device_not_available) pushl $-1 # mark this as an int @@ -336,7 +334,7 @@ ENTRY(device_not_available) GET_CURRENT(%ebx) pushl $ret_from_exception movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) + testl $0x4,%eax # EM (math emulation bit) je SYMBOL_NAME(math_state_restore) pushl $0 # temporary storage for ORIG_EIP call SYMBOL_NAME(math_emulate) diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 584caa9c1..824dd7938 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -14,6 +14,7 @@ #include <asm/semaphore.h> #include <asm/processor.h> +#include <asm/i387.h> #include <asm/uaccess.h> #include <asm/checksum.h> #include <asm/io.h> @@ -24,7 +25,6 @@ #include <asm/desc.h> extern void dump_thread(struct pt_regs *, struct user *); -extern int dump_fpu(elf_fpregset_t *); extern spinlock_t rtc_lock; #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) @@ -51,6 +51,7 @@ EXPORT_SYMBOL(MCA_bus); EXPORT_SYMBOL(__verify_write); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(dump_extended_fpu); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(__io_virt_debug); @@ -100,6 +101,10 @@ EXPORT_SYMBOL(strnlen_user); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pcibios_penalize_isa_irq); +#endif + #ifdef CONFIG_X86_USE_3DNOW EXPORT_SYMBOL(_mmx_memcpy); EXPORT_SYMBOL(mmx_clear_page); diff --git a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c new file mode 100644 index 000000000..b264c54a3 --- /dev/null +++ b/arch/i386/kernel/i387.c @@ -0,0 +1,507 @@ +/* + * linux/arch/i386/kernel/i387.c + * + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes <gareth@valinux.com>, May 2000 + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <asm/processor.h> +#include <asm/i387.h> +#include <asm/math_emu.h> +#include <asm/sigcontext.h> +#include <asm/user.h> +#include <asm/ptrace.h> +#include <asm/uaccess.h> + +#if defined(CONFIG_X86_FXSR) +#define HAVE_FXSR 1 +#elif defined(CONFIG_X86_RUNTIME_FXSR) +#define HAVE_FXSR (cpu_has_fxsr) +#else +#define HAVE_FXSR 0 +#endif + +#ifdef CONFIG_MATH_EMULATION +#define HAVE_HWFP (boot_cpu_data.hard_math) +#else +#define HAVE_HWFP 1 +#endif + +/* + * FPU lazy state save handling. + */ + +void save_fpu( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + asm volatile( "fxsave %0 ; fwait" + : "=m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "fnsave %0 ; fwait" + : "=m" (tsk->thread.i387.fsave) ); + } + tsk->flags &= ~PF_USEDFPU; + stts(); +} + +void save_init_fpu( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + asm volatile( "fxsave %0 ; fnclex" + : "=m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "fnsave %0 ; fwait" + : "=m" (tsk->thread.i387.fsave) ); + } + tsk->flags &= ~PF_USEDFPU; + stts(); +} + +void restore_fpu( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + asm volatile( "fxrstor %0" + : : "m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "frstor %0" + : : "m" (tsk->thread.i387.fsave) ); + } +} + +/* + * FPU tag word conversions. + */ + +static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) +{ + unsigned short ret = 0; + int i; + + for ( i = 0 ; i < 8 ; i++ ) { + if ( (twd & 0x3) != 0x3 ) { + ret |= (1 << i); + } + twd = twd >> 2; + } + return ret; +} + +static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) +{ + struct _fpxreg *st = NULL; + unsigned long twd = (unsigned long) fxsave->twd; + unsigned long tag; + unsigned long ret = 0xffff0000; + int i; + +#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16); + + for ( i = 0 ; i < 8 ; i++ ) { + if ( twd & 0x1 ) { + st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); + + switch ( st->exponent ) { + case 0xffff: + tag = 2; /* Special */ + break; + case 0x0000: + if ( !st->significand[0] && + !st->significand[1] && + !st->significand[2] && + !st->significand[3] ) { + tag = 1; /* Zero */ + } else { + tag = 2; /* Special */ + } + break; + default: + if ( st->significand[3] & 0x8000 ) { + tag = 0; /* Valid */ + } else { + tag = 2; /* Special */ + } + break; + } + } else { + tag = 3; /* Empty */ + } + ret |= (tag << (2 * i)); + twd = twd >> 1; + } + return ret; +} + +/* + * FPU state interaction. + */ + +unsigned short get_fpu_cwd( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + return tsk->thread.i387.fxsave.cwd; + } else { + return (unsigned short)tsk->thread.i387.fsave.cwd; + } +} + +unsigned short get_fpu_swd( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + return tsk->thread.i387.fxsave.swd; + } else { + return (unsigned short)tsk->thread.i387.fsave.swd; + } +} + +unsigned short get_fpu_twd( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + return tsk->thread.i387.fxsave.twd; + } else { + return (unsigned short)tsk->thread.i387.fsave.twd; + } +} + +unsigned short get_fpu_mxcsr( struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + return tsk->thread.i387.fxsave.mxcsr; + } else { + return 0x1f80; + } +} + +void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd ) +{ + if ( HAVE_FXSR ) { + tsk->thread.i387.fxsave.cwd = cwd; + } else { + tsk->thread.i387.fsave.cwd = ((long)cwd | 0xffff0000); + } +} + +void set_fpu_swd( struct task_struct *tsk, unsigned short swd ) +{ + if ( HAVE_FXSR ) { + tsk->thread.i387.fxsave.swd = swd; + } else { + tsk->thread.i387.fsave.swd = ((long)swd | 0xffff0000); + } +} + +void set_fpu_twd( struct task_struct *tsk, unsigned short twd ) +{ + if ( HAVE_FXSR ) { + tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd); + } else { + tsk->thread.i387.fsave.twd = ((long)twd | 0xffff0000); + } +} + +void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ) +{ + if ( HAVE_FXSR ) { + tsk->thread.i387.fxsave.mxcsr = mxcsr; + } +} + +/* + * FXSR floating point environment conversions. + */ + +static inline int convert_fxsr_to_user( struct _fpstate *buf, + struct i387_fxsave_struct *fxsave ) +{ + unsigned long env[7]; + struct _fpreg *to; + struct _fpxreg *from; + int i; + + env[0] = (unsigned long)fxsave->cwd | 0xffff0000; + env[1] = (unsigned long)fxsave->swd | 0xffff0000; + env[2] = twd_fxsr_to_i387(fxsave); + env[3] = fxsave->fip; + env[4] = fxsave->fcs | ((unsigned long)fxsave->fop << 16); + env[5] = fxsave->foo; + env[6] = fxsave->fos; + + if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) ) + return 1; + + to = &buf->_st[0]; + from = (struct _fpxreg *) &fxsave->st_space[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_to_user( to, from, sizeof(*to) ) ) + return 1; + } + return 0; +} + +static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, + struct _fpstate *buf ) +{ + unsigned long env[7]; + struct _fpxreg *to; + struct _fpreg *from; + int i; + + if ( __copy_from_user( env, buf, 7 * sizeof(long) ) ) + return 1; + + fxsave->cwd = (unsigned short)(env[0] & 0xffff); + fxsave->swd = (unsigned short)(env[1] & 0xffff); + fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff)); + fxsave->fip = env[3]; + fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16); + fxsave->fcs = (env[4] & 0xffff); + fxsave->foo = env[5]; + fxsave->fos = env[6]; + + to = (struct _fpxreg *) &fxsave->st_space[0]; + from = &buf->_st[0]; + for ( i = 0 ; i < 8 ; i++, to++, from++ ) { + if ( __copy_from_user( to, from, sizeof(*from) ) ) + return 1; + } + return 0; +} + +/* + * Signal frame handlers. + */ + +static inline int save_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + + unlazy_fpu( tsk ); + tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd; + if ( __copy_to_user( buf, &tsk->thread.i387.fsave, + sizeof(struct i387_fsave_struct) ) ) + return -1; + return 1; +} + +static inline int save_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + int err = 0; + + unlazy_fpu( tsk ); + + if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) ) + return -1; + + err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status ); + err |= __put_user( X86_FXSR_MAGIC, &buf->magic ); + if ( err ) + return -1; + + if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave, + sizeof(struct i387_fxsave_struct) ) ) + return -1; + return 1; +} + +int save_i387( struct _fpstate *buf ) +{ + if ( !current->used_math ) + return 0; + + /* This will cause a "finit" to be triggered by the next + * attempted FPU operation by the 'current' process. + */ + current->used_math = 0; + + if ( HAVE_HWFP ) { + if ( HAVE_FXSR ) { + return save_i387_fxsave( buf ); + } else { + return save_i387_fsave( buf ); + } + } else { + return save_i387_soft( ¤t->thread.i387.soft, buf ); + } +} + +static inline int restore_i387_fsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + return __copy_from_user( &tsk->thread.i387.fsave, buf, + sizeof(struct i387_fsave_struct) ); +} + +static inline int restore_i387_fxsave( struct _fpstate *buf ) +{ + struct task_struct *tsk = current; + clear_fpu( tsk ); + if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0], + sizeof(struct i387_fxsave_struct) ) ) + return 1; + return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf ); +} + +int restore_i387( struct _fpstate *buf ) +{ + int err; + + if ( HAVE_HWFP ) { + if ( HAVE_FXSR ) { + err = restore_i387_fxsave( buf ); + } else { + err = restore_i387_fsave( buf ); + } + } else { + err = restore_i387_soft( ¤t->thread.i387.soft, buf ); + } + current->used_math = 1; + return err; +} + +/* + * ptrace request handlers. + */ + +static inline int get_fpregs_fsave( struct user_i387_struct *buf, + struct task_struct *tsk ) +{ + return __copy_to_user( buf, &tsk->thread.i387.fsave, + sizeof(struct user_i387_struct) ); +} + +static inline int get_fpregs_fxsave( struct user_i387_struct *buf, + struct task_struct *tsk ) +{ + return convert_fxsr_to_user( (struct _fpstate *)buf, + &tsk->thread.i387.fxsave ); +} + +int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk ) +{ + if ( HAVE_HWFP ) { + if ( HAVE_FXSR ) { + return get_fpregs_fxsave( buf, tsk ); + } else { + return get_fpregs_fsave( buf, tsk ); + } + } else { + return save_i387_soft( &tsk->thread.i387.soft, + (struct _fpstate *)buf ); + } +} + +static inline int set_fpregs_fsave( struct task_struct *tsk, + struct user_i387_struct *buf ) +{ + return __copy_from_user( &tsk->thread.i387.fsave, buf, + sizeof(struct user_i387_struct) ); +} + +static inline int set_fpregs_fxsave( struct task_struct *tsk, + struct user_i387_struct *buf ) +{ + return convert_fxsr_from_user( &tsk->thread.i387.fxsave, + (struct _fpstate *)buf ); +} + +int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf ) +{ + if ( HAVE_HWFP ) { + if ( HAVE_FXSR ) { + return set_fpregs_fxsave( tsk, buf ); + } else { + return set_fpregs_fsave( tsk, buf ); + } + } else { + return restore_i387_soft( &tsk->thread.i387.soft, + (struct _fpstate *)buf ); + } +} + +int get_fpxregs( struct user_fxsr_struct *buf, struct task_struct *tsk ) +{ + if ( HAVE_FXSR ) { + __copy_to_user( (void *)buf, &tsk->thread.i387.fxsave, + sizeof(struct user_fxsr_struct) ); + return 0; + } else { + return -EIO; + } +} + +int set_fpxregs( struct task_struct *tsk, struct user_fxsr_struct *buf ) +{ + if ( HAVE_FXSR ) { + __copy_from_user( &tsk->thread.i387.fxsave, (void *)buf, + sizeof(struct user_fxsr_struct) ); + return 0; + } else { + return -EIO; + } +} + +/* + * FPU state for core dumps. + */ + +static inline void copy_fpu_fsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + memcpy( fpu, &tsk->thread.i387.fsave, + sizeof(struct user_i387_struct) ); +} + +static inline void copy_fpu_fxsave( struct task_struct *tsk, + struct user_i387_struct *fpu ) +{ + unsigned short *to; + unsigned short *from; + int i; + + memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(long) ); + + to = (unsigned short *)&fpu->st_space[0]; + from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0]; + for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) { + memcpy( to, from, 5 * sizeof(unsigned short) ); + } +} + +int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) +{ + int fpvalid; + struct task_struct *tsk = current; + + fpvalid = tsk->used_math; + if ( fpvalid ) { + unlazy_fpu( tsk ); + if ( HAVE_FXSR ) { + copy_fpu_fxsave( tsk, fpu ); + } else { + copy_fpu_fsave( tsk, fpu ); + } + } + + return fpvalid; +} + +int dump_extended_fpu( struct pt_regs *regs, struct user_fxsr_struct *fpu ) +{ + int fpvalid; + struct task_struct *tsk = current; + + fpvalid = tsk->used_math && HAVE_FXSR; + if ( fpvalid ) { + unlazy_fpu( tsk ); + memcpy( fpu, &tsk->thread.i387.fxsave, + sizeof(struct user_fxsr_struct) ); + } + + return fpvalid; +} diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index ac5de1f2f..735c226b1 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -394,7 +394,11 @@ static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) math_error((void *)regs->eip); } -static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; +/* + * New motherboards sometimes make IRQ 13 be a PCI interrupt, + * so allow interrupt sharing. + */ +static struct irqaction irq13 = { math_error_irq, SA_SHIRQ, 0, "fpu", NULL, NULL }; /* * IRQ2 is cascade interrupt to second interrupt controller diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index 989f70c7e..7d7a58741 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -103,9 +103,9 @@ static int __init microcode_init(void) MICROCODE_MINOR); error = 1; } - devfs_handle = devfs_register(NULL, "cpu/microcode", 0, + devfs_handle = devfs_register(NULL, "cpu/microcode", DEVFS_FL_DEFAULT, 0, 0, S_IFREG | S_IRUSR | S_IWUSR, - 0, 0, µcode_fops, NULL); + µcode_fops, NULL); if (devfs_handle == NULL && error) { printk(KERN_ERR "microcode: failed to devfs_register()\n"); return -EINVAL; diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 758b3ec38..c0a158694 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -1885,8 +1885,8 @@ int __init mtrr_init(void) proc_root_mtrr->proc_fops = &mtrr_fops; #endif #ifdef CONFIG_DEVFS_FS - devfs_handle = devfs_register (NULL, "cpu/mtrr", 0, DEVFS_FL_DEFAULT, 0, 0, - S_IFREG | S_IRUGO | S_IWUSR, 0, 0, + devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, &mtrr_fops, NULL); #endif init_table (); diff --git a/arch/i386/kernel/pci-i386.h b/arch/i386/kernel/pci-i386.h index f4f808e4c..3e86ceecd 100644 --- a/arch/i386/kernel/pci-i386.h +++ b/arch/i386/kernel/pci-i386.h @@ -68,4 +68,4 @@ extern unsigned int pcibios_irq_mask; void pcibios_irq_init(void); void pcibios_fixup_irqs(void); -int pcibios_lookup_irq(struct pci_dev *dev, int assign); +void pcibios_enable_irq(struct pci_dev *dev); diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index cd16c78a5..933d6c3c9 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -14,11 +14,11 @@ #include <linux/irq.h> #include <asm/io.h> +#include <asm/smp.h> +#include <asm/io_apic.h> #include "pci-i386.h" -extern int skip_ioapic_setup; - #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) #define PIRQ_VERSION 0x0100 @@ -27,13 +27,13 @@ static struct irq_routing_table *pirq_table; /* * Never use: 0, 1, 2 (timer, keyboard, and cascade) * Avoid using: 13, 14 and 15 (FP error and IDE). - * Penalize: 3, 4, 7, 12 (known ISA uses: serial, parallel and mouse) + * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) */ unsigned int pcibios_irq_mask = 0xfff8; -static unsigned pirq_penalty[16] = { - 10000, 10000, 10000, 100, 100, 0, 0, 100, - 0, 0, 0, 0, 100, 1000, 1000, 1000 +static int pirq_penalty[16] = { + 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, + 0, 0, 0, 0, 1000, 100000, 100000, 100000 }; struct irq_router { @@ -128,14 +128,13 @@ static void eisa_set_level_irq(unsigned int irq) static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq) { static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + u8 x; + unsigned reg; + pirq--; - if (pirq < 8) { - u8 x; - unsigned reg = 0x48 + (pirq >> 1); - pci_read_config_byte(router, reg, &x); - return irqmap[(pirq & 1) ? (x >> 4) : (x & 0x0f)]; - } - return 0; + reg = 0x48 + (pirq >> 1); + pci_read_config_byte(router, reg, &x); + return irqmap[(pirq & 1) ? (x >> 4) : (x & 0x0f)]; } static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) @@ -143,7 +142,7 @@ static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; unsigned int val = irqmap[irq]; pirq--; - if (val && pirq < 8) { + if (val) { u8 x; unsigned reg = 0x48 + (pirq >> 1); pci_read_config_byte(router, reg, &x); @@ -222,7 +221,7 @@ static struct irq_router pirq_routers[] = { { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82440MX_1, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, @@ -287,7 +286,11 @@ static struct irq_info *pirq_get_info(struct pci_dev *dev, int pin) return NULL; } -int pcibios_lookup_irq(struct pci_dev *dev, int assign) +static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int pcibios_lookup_irq(struct pci_dev *dev, int assign) { struct irq_info *info; int i, pirq, pin, newirq; @@ -323,19 +326,24 @@ int pcibios_lookup_irq(struct pci_dev *dev, int assign) /* Find the best IRQ to assign */ newirq = 0; - for (i = 0; i < 16; i++) { - if (!(mask & (1 << i))) - continue; - if (pirq_penalty[i] < pirq_penalty[newirq]) - newirq = i; + if (assign) { + for (i = 0; i < 16; i++) { + if (!(mask & (1 << i))) + continue; + if (pirq_penalty[i] < pirq_penalty[newirq] && + !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { + free_irq(i, dev); + newirq = i; + } + } + DBG(" -> newirq=%d", newirq); } - DBG(" -> newirq=%d", newirq); /* Try to get current IRQ */ if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) { DBG(" -> got IRQ %d\n", irq); msg = "Found"; - } else if (assign && newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { DBG(" -> assigning IRQ %d", newirq); if (r->set(pirq_router_dev, d, pirq, newirq)) { DBG(" ... OK\n"); @@ -346,7 +354,7 @@ int pcibios_lookup_irq(struct pci_dev *dev, int assign) if (!irq) { DBG(" ... failed\n"); - if (assign && newirq && mask == (1 << newirq)) { + if (newirq && mask == (1 << newirq)) { msg = "Guessed"; irq = newirq; } else @@ -379,6 +387,15 @@ void __init pcibios_irq_init(void) if (pirq_table) { pirq_peer_trick(); pirq_find_router(); + if (pirq_table->exclusive_irqs) { + int i; + for (i=0; i<16; i++) + if (!(pirq_table->exclusive_irqs & (1 << i))) + pirq_penalty[i] += 100; + } + /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ + if (io_apic_assign_pci_irqs) + pirq_table = NULL; } } @@ -397,16 +414,19 @@ void __init pcibios_fixup_irqs(void) DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); dev->irq = 0; } + /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ + if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) + pirq_penalty[dev->irq] = 0; pirq_penalty[dev->irq]++; } pci_for_each_dev(dev) { pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); -#if defined(CONFIG_X86_IO_APIC) +#ifdef CONFIG_X86_IO_APIC /* * Recalculate IRQ numbers if we use the I/O APIC. */ - if (!skip_ioapic_setup) + if (io_apic_assign_pci_irqs) { int irq; @@ -443,3 +463,31 @@ void __init pcibios_fixup_irqs(void) pcibios_lookup_irq(dev, 0); } } + +void __init pcibios_penalize_isa_irq(int irq) +{ + /* + * If any ISAPnP device reports an IRQ in its list of possible + * IRQ's, we try to avoid assigning it to PCI devices. + */ + pirq_penalty[irq] += 100; +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + if (!dev->irq) { + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin && !pcibios_lookup_irq(dev, 1)) { + char *msg; + if (io_apic_assign_pci_irqs) + msg = " Probably buggy MP table."; + else if (pci_probe & PCI_BIOS_IRQ_SCAN) + msg = ""; + else + msg = " Please try using pci=biosirq."; + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", + 'A' + pin - 1, dev->slot_name, msg); + } + } +} diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 404a49868..258e63627 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -14,7 +14,6 @@ #include <asm/segment.h> #include <asm/io.h> -#include <asm/smp.h> #include "pci-i386.h" @@ -1046,13 +1045,6 @@ int pcibios_enable_device(struct pci_dev *dev) if ((err = pcibios_enable_resources(dev)) < 0) return err; - if (!dev->irq) { - u8 pin; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - if (pin && !pcibios_lookup_irq(dev, 1)) - printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", - 'A' + pin - 1, dev->slot_name, - (pci_probe & PCI_BIOS_IRQ_SCAN) ? "" : " Please try using pci=biosirq."); - } + pcibios_enable_irq(dev); return 0; } diff --git a/arch/i386/kernel/pci-visws.c b/arch/i386/kernel/pci-visws.c index 401a4ee13..15e4c584f 100644 --- a/arch/i386/kernel/pci-visws.c +++ b/arch/i386/kernel/pci-visws.c @@ -135,3 +135,7 @@ int pcibios_enable_device(struct pci_dev *dev) { return pcibios_enable_resources(dev); } + +void __init pcibios_penalize_isa_irq(irq) +{ +} diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 4a2c952c7..0da172ad4 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -39,6 +39,7 @@ #include <asm/io.h> #include <asm/ldt.h> #include <asm/processor.h> +#include <asm/i387.h> #include <asm/desc.h> #include <asm/mmu_context.h> #ifdef CONFIG_MATH_EMULATION @@ -343,7 +344,7 @@ void machine_power_off(void) void show_regs(struct pt_regs * regs) { - long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; + long cr0 = 0L, cr2 = 0L, cr3 = 0L; printk("\n"); printk("EIP: %04x:[<%08lx>]",0xffff & regs->xcs,regs->eip); @@ -360,15 +361,7 @@ void show_regs(struct pt_regs * regs) __asm__("movl %%cr0, %0": "=r" (cr0)); __asm__("movl %%cr2, %0": "=r" (cr2)); __asm__("movl %%cr3, %0": "=r" (cr3)); - printk("CR0: %08lx CR2: %08lx CR3: %08lx", cr0, cr2, cr3); - if (current_cpu_data.x86_capability & - (X86_FEATURE_VME | X86_FEATURE_DE | X86_FEATURE_PSE | - X86_FEATURE_TSC | X86_FEATURE_PAE | X86_FEATURE_MCE | - X86_FEATURE_PGE | X86_FEATURE_FXSR | X86_FEATURE_XMM)) { - __asm__("movl %%cr4, %0": "=r" (cr4)); - printk(" CR4: %08lx\n", cr4); - } - printk("\n"); + printk("CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3); } /* @@ -511,38 +504,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, } /* - * fill in the FPU structure for a core dump. - */ -int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu) -{ - int fpvalid; - struct task_struct *tsk = current; -#ifdef CONFIG_X86_FXSR - unsigned short *to; - unsigned short *from; - int i; -#endif - fpvalid = tsk->used_math; - if (fpvalid) { - unlazy_fpu(tsk); -#ifdef CONFIG_X86_FXSR - memcpy(fpu, &tsk->thread.i387.hard, 7 * sizeof(long)); - - to = (unsigned short *)&fpu->st_space[0]; - from = (unsigned short *)&tsk->thread.i387.hard.st_space[0]; - for (i = 0; i < 8; i++, to += 5, from += 8) { - memcpy(to, from, 5 * sizeof(unsigned short)); - } -#else - memcpy(fpu, &tsk->thread.i387.hard, - sizeof(struct user_i387_struct)); -#endif - } - - return fpvalid; -} - -/* * fill in the user structure for a core dump.. */ void dump_thread(struct pt_regs * regs, struct user * dump) @@ -595,8 +556,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump) /* * switch_to(x,yn) should switch tasks from x to y. * - * We f*save/fwait so that an exception goes off at the right time - * (as a call from the f*save or fwait in effect) rather than to + * We fsave/fwait so that an exception goes off at the right time + * (as a call from the fsave or fwait in effect) rather than to * the wrong process. Lazy FP saving no longer makes any sense * with modern CPU's, and this simplifies a lot of things (SMP * and UP become the same). diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 7b8deaf1d..0bafdc7e2 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -5,7 +5,6 @@ * Gareth Hughes <gareth@valinux.com>, May 2000 */ -#include <linux/config.h> /* for CONFIG_MATH_EMULATION */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -19,6 +18,7 @@ #include <asm/pgtable.h> #include <asm/system.h> #include <asm/processor.h> +#include <asm/i387.h> #include <asm/debugreg.h> /* @@ -133,54 +133,6 @@ static unsigned long getreg(struct task_struct *child, return retval; } -static inline int save_i387_user(struct user_i387_struct *buf, - struct i387_hard_struct *hard) -{ -#ifdef CONFIG_X86_FXSR - unsigned short *to; - unsigned short *from; - int i; - - if (__copy_to_user(buf, hard, 7 * sizeof(long))) - return 1; - - to = (unsigned short *)&buf->st_space[0]; - from = (unsigned short *)&hard->st_space[0]; - for (i = 0; i < 8; i++, to += 5, from += 8) { - if (__copy_to_user(to, from, 5 * sizeof(unsigned short))) - return 1; - } -#else - if (__copy_to_user(buf, hard, sizeof(struct user_i387_struct))) - return 1; -#endif - return 0; -} - -static inline int restore_i387_user(struct i387_hard_struct *hard, - struct user_i387_struct *buf) -{ -#ifdef CONFIG_X86_FXSR - unsigned short *to; - unsigned short *from; - int i; - - if (__copy_from_user(hard, buf, 7 * sizeof(long))) - return 1; - - to = (unsigned short *)&hard->st_space[0]; - from = (unsigned short *)&buf->st_space[0]; - for (i = 0; i < 8; i++, to += 8, from += 5) { - if (__copy_from_user(to, from, 5 * sizeof(unsigned short))) - return 1; - } -#else - if (__copy_from_user(hard, buf, sizeof(struct user_i387_struct))) - return 1; -#endif - return 0; -} - asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; @@ -442,90 +394,59 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } case PTRACE_GETFPREGS: { /* Get the child FPU state. */ - if (!access_ok(VERIFY_WRITE, (unsigned *)data, sizeof(struct user_i387_struct))) { + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) { ret = -EIO; break; } ret = 0; if ( !child->used_math ) { /* Simulate an empty FPU. */ - child->thread.i387.hard.cwd = 0xffff037f; - child->thread.i387.hard.swd = 0xffff0000; - child->thread.i387.hard.twd = 0xffffffff; - } -#ifdef CONFIG_MATH_EMULATION - if ( boot_cpu_data.hard_math ) { -#endif - save_i387_user((struct user_i387_struct *)data, &child->thread.i387.hard); -#ifdef CONFIG_MATH_EMULATION - } else { - save_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data); + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); } -#endif + get_fpregs((struct user_i387_struct *)data, child); break; } case PTRACE_SETFPREGS: { /* Set the child FPU state. */ - if (!access_ok(VERIFY_READ, (unsigned *)data, sizeof(struct user_i387_struct))) { + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) { ret = -EIO; break; } child->used_math = 1; -#ifdef CONFIG_MATH_EMULATION - if ( boot_cpu_data.hard_math ) { -#endif - restore_i387_user(&child->thread.i387.hard, (struct user_i387_struct *)data); -#ifdef CONFIG_MATH_EMULATION - } else { - restore_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data); - } -#endif + set_fpregs(child, (struct user_i387_struct *)data); ret = 0; break; } - case PTRACE_GETXFPREGS: { /* Get the child extended FPU state. */ -#ifdef CONFIG_X86_FXSR + case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */ if (!access_ok(VERIFY_WRITE, (unsigned *)data, - sizeof(struct user_xfpregs_struct))) { + sizeof(struct user_fxsr_struct))) { ret = -EIO; break; } - ret = 0; if ( !child->used_math ) { /* Simulate an empty FPU. */ - child->thread.i387.hard.cwd = 0xffff037f; - child->thread.i387.hard.swd = 0xffff0000; - child->thread.i387.hard.twd = 0xffffffff; + set_fpu_cwd(child, 0x037f); + set_fpu_swd(child, 0x0000); + set_fpu_twd(child, 0xffff); + set_fpu_mxcsr(child, 0x1f80); } - printk("PTRACE_GETXFPREGS: copying %d bytes from %p to %p\n", - sizeof(struct user_xfpregs_struct), - &child->thread.i387.hard, data); - __copy_to_user((void *)data, &child->thread.i387.hard, - sizeof(struct user_xfpregs_struct)); -#else - ret = -EIO; -#endif + ret = get_fpxregs((struct user_fxsr_struct *)data, child); break; } - case PTRACE_SETXFPREGS: { /* Set the child extended FPU state. */ -#ifdef CONFIG_X86_FXSR + case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */ if (!access_ok(VERIFY_READ, (unsigned *)data, - sizeof(struct user_xfpregs_struct))) { + sizeof(struct user_fxsr_struct))) { ret = -EIO; break; } child->used_math = 1; - printk("PTRACE_SETXFPREGS: copying %d bytes from %p to %p\n", - sizeof(struct user_xfpregs_struct), - data, &child->thread.i387.hard); - __copy_from_user(&child->thread.i387.hard, (void *)data, - sizeof(struct user_xfpregs_struct)); - ret = 0; -#else - ret = -EIO; -#endif + ret = set_fpxregs(child, (struct user_fxsr_struct *)data); break; } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 6d687ce28..e0425f411 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -39,15 +39,10 @@ * Detection for Celeron coppermine, identify_cpu() overhauled, * and a few other clean ups. * Dave Jones <dave@powertweak.com>, April 2000 - * Pentium-III code by Ingo Molnar and modifications by Goutham Rao - * Updated to: + * * Pentium III FXSR, SSE support + * General FPU state handling cleanups * Gareth Hughes <gareth@valinux.com>, May 2000 - * - * Added proper Cascades CPU and L2 cache detection for Cascades - * and 8-way type cache happy bunch from Intel:^) - * Dragan Stancevic <visitor@valinux.com>, May 2000 - * */ /* diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index 67c260094..cc4843b8a 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -4,11 +4,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson - * 2000-05-25 Pentium III FXSR, SSE support by Gareth Hughes + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes */ -#include <linux/config.h> - #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp.h> @@ -22,6 +20,7 @@ #include <linux/stddef.h> #include <asm/ucontext.h> #include <asm/uaccess.h> +#include <asm/i387.h> #define DEBUG_SIG 0 @@ -186,53 +185,6 @@ struct rt_sigframe char retcode[8]; }; -static inline int restore_i387_hard(struct _fpstate *buf) -{ - struct task_struct *tsk = current; -#ifdef CONFIG_X86_FXSR - struct _fpreg *from; - struct _fpxreg *to; - int i; -#endif - clear_fpu(tsk); - -#ifdef CONFIG_X86_FXSR - if (__copy_from_user(&tsk->thread.i387.hard, buf, 7 * sizeof(long))) - return 1; - - from = &buf->_st[0]; - to = (struct _fpxreg *) &tsk->thread.i387.hard.st_space[0]; - for (i = 0; i < 8; i++, to++, from++) { - if (__copy_from_user(to, from, sizeof(*from))) - return 1; - } - - if (__copy_from_user(&tsk->thread.i387.hard.fxsr_space[0], - &buf->_fxsr_env[0], X86_FXSR_SIZE)) - return 1; -#else - if (__copy_from_user(&tsk->thread.i387.hard, buf, - sizeof(struct i387_hard_struct))) - return 1; -#endif - return 0; -} - -static inline int restore_i387(struct _fpstate *buf) -{ - int err; -#ifndef CONFIG_MATH_EMULATION - err = restore_i387_hard(buf); -#else - if (boot_cpu_data.hard_math) - err = restore_i387_hard(buf); - else - err = restore_i387_soft(¤t->thread.i387.soft, buf); -#endif - current->used_math = 1; - return err; -} - static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *peax) { @@ -363,63 +315,6 @@ badframe: * Set up a signal frame. */ -static inline int save_i387_hard(struct _fpstate * buf) -{ - struct task_struct *tsk = current; -#ifdef CONFIG_X86_FXSR - struct _fpreg *to; - struct _fpxreg *from; - int i; - int err = 0; -#endif - unlazy_fpu(tsk); - tsk->thread.i387.hard.status = tsk->thread.i387.hard.swd; - -#ifdef CONFIG_X86_FXSR - if (__copy_to_user(buf, &tsk->thread.i387.hard, 7 * sizeof(long))) - return -1; - - to = &buf->_st[0]; - from = (struct _fpxreg *) &tsk->thread.i387.hard.st_space[0]; - for (i = 0; i < 8; i++, to++, from++) { - if (__copy_to_user(to, from, sizeof(*to))) - return -1; - } - err |= __put_user(tsk->thread.i387.hard.swd & 0xffff, &buf->status); - err |= __put_user(X86_FXSR_MAGIC, &buf->magic); - if (err) - return -1; - - if (__copy_to_user(&buf->_fxsr_env[0], - &tsk->thread.i387.hard.fxsr_space[0], - X86_FXSR_SIZE)) - return -1; -#else - if (__copy_to_user(buf, &tsk->thread.i387.hard, - sizeof(struct i387_hard_struct))) - return -1; -#endif - return 1; -} - -static int save_i387(struct _fpstate *buf) -{ - if (!current->used_math) - return 0; - - /* This will cause a "finit" to be triggered by the next - attempted FPU operation by the 'current' process. - */ - current->used_math = 0; - -#ifndef CONFIG_MATH_EMULATION - return save_i387_hard(buf); -#else - return boot_cpu_data.hard_math ? save_i387_hard(buf) - : save_i387_soft(¤t->thread.i387.soft, buf); -#endif -} - static int setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate, struct pt_regs *regs, unsigned long mask) @@ -713,7 +608,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) if (!signr) break; - if ((current->ptrace&PT_PTRACED) && signr != SIGKILL) { + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { /* Let the debugger run. */ current->exit_code = signr; current->state = TASK_STOPPED; diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 5467afea9..370c89385 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -9,7 +9,7 @@ /* * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'entry.S'. + * state in 'asm.s'. */ #include <linux/config.h> #include <linux/sched.h> @@ -37,6 +37,7 @@ #include <asm/atomic.h> #include <asm/debugreg.h> #include <asm/desc.h> +#include <asm/i387.h> #include <asm/smp.h> #include <asm/pgalloc.h> @@ -154,9 +155,7 @@ asmlinkage void stack_segment(void); asmlinkage void general_protection(void); asmlinkage void page_fault(void); asmlinkage void coprocessor_error(void); -#ifdef CONFIG_X86_XMM asmlinkage void simd_coprocessor_error(void); -#endif asmlinkage void reserved(void); asmlinkage void alignment_check(void); asmlinkage void spurious_interrupt_bug(void); @@ -587,9 +586,10 @@ void math_error(void *eip) { struct task_struct * task; siginfo_t info; + unsigned short cwd, swd; /* - * Save the info for the exception handler and clear the error + * Save the info for the exception handler and clear the error. */ task = current; save_init_fpu(task); @@ -609,9 +609,9 @@ void math_error(void *eip) * and it will suffer the consequences since we won't be able to * fully reproduce the context of the exception */ - switch(((~task->thread.i387.hard.cwd) & - task->thread.i387.hard.swd & 0x3f) | - (task->thread.i387.hard.swd & 0x240)) { + cwd = get_fpu_cwd(task); + swd = get_fpu_swd(task); + switch (((~cwd) & swd & 0x3f) | (swd & 0x240)) { case 0x000: default: break; @@ -643,18 +643,18 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) math_error((void *)regs->eip); } -#ifdef CONFIG_X86_XMM void simd_math_error(void *eip) { struct task_struct * task; siginfo_t info; + unsigned short mxcsr; /* - * Save the info for the exception handler and clear the error + * Save the info for the exception handler and clear the error. */ task = current; save_init_fpu(task); - set_fpu_mxcsr(XMM_DEFAULT_MXCSR); + load_mxcsr(0x1f80); task->thread.trap_no = 19; task->thread.error_code = 0; info.si_signo = SIGFPE; @@ -667,8 +667,8 @@ void simd_math_error(void *eip) * unmasked exception was caught we must mask the exception mask bits * at 0x1f80, and then use these to mask the exception bits at 0x3f. */ - switch (~((task->thread.i387.hard.mxcsr & 0x1f80) >> 7) & - (task->thread.i387.hard.mxcsr & 0x3f)) { + mxcsr = get_fpu_mxcsr(task); + switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) { case 0x000: default: break; @@ -695,10 +695,26 @@ void simd_math_error(void *eip) asmlinkage void do_simd_coprocessor_error(struct pt_regs * regs, long error_code) { - ignore_irq13 = 1; - simd_math_error((void *)regs->eip); + if (cpu_has_xmm) { + /* Handle SIMD FPU exceptions on PIII+ processors. */ + ignore_irq13 = 1; + simd_math_error((void *)regs->eip); + } else { + /* + * Handle strange cache flush from user space exception + * in all other cases. This is undocumented behaviour. + */ + if (regs->eflags & VM_MASK) { + handle_vm86_fault((struct kernel_vm86_regs *)regs, + error_code); + return; + } + die_if_kernel("cache flush denied", regs, error_code); + current->thread.trap_no = 19; + current->thread.error_code = error_code; + force_sig(SIGSEGV, current); + } } -#endif /* CONFIG_X86_XMM */ asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, long error_code) @@ -718,7 +734,7 @@ asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs, */ asmlinkage void math_state_restore(struct pt_regs regs) { - __asm__ __volatile__("clts"); /* Allow maths ops (or we recurse) */ + __asm__ __volatile__("clts"); /* Allow maths ops (or we recurse) */ if (current->used_math) { restore_fpu(current); @@ -963,10 +979,8 @@ void __init trap_init(void) set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); -#ifdef CONFIG_X86_XMM - if (cpu_has_xmm) - set_trap_gate(19,&simd_coprocessor_error); -#endif + set_trap_gate(19,&simd_coprocessor_error); + set_system_gate(SYSCALL_VECTOR,&system_call); /* diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index 907eade5d..fa803c165 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -443,7 +443,7 @@ int handle_vm86_trap(struct kernel_vm86_regs * regs, long error_code, int trapno } if (trapno !=1) return 1; /* we let this handle by the calling routine */ - if (current->ptrace&PT_PTRACED) { + if (current->ptrace & PT_PTRACED) { unsigned long flags; spin_lock_irqsave(¤t->sigmask_lock, flags); sigdelset(¤t->blocked, SIGTRAP); diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c index 87358602f..2fdffe253 100644 --- a/arch/i386/math-emu/fpu_entry.c +++ b/arch/i386/math-emu/fpu_entry.c @@ -210,7 +210,7 @@ asmlinkage void math_emulate(long arg) } FPU_lookahead = 1; - if (current->ptrace&PT_PTRACED) + if (current->ptrace & PT_PTRACED) FPU_lookahead = 0; if ( !valid_prefix(&byte1, (u_char **)&FPU_EIP, diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile index 897bca8e7..a87cb7c08 100644 --- a/arch/ia64/Makefile +++ b/arch/ia64/Makefile @@ -12,15 +12,11 @@ NM := $(CROSS_COMPILE)nm -B AWK := awk LINKFLAGS = -static -T arch/$(ARCH)/vmlinux.lds -# next line is for HP compiler backend: -#AFLAGS += -DGCC_RETVAL_POINTER_IN_R8 -# The next line is needed when compiling with the July snapshot of the Cygnus compiler: -#EXTRA = -D__GCC_DOESNT_KNOW_IN_REGS__ -# next two lines are for the September snapshot of the Cygnus compiler: -AFLAGS += -D__GCC_MULTIREG_RETVALS__ -Wa,-x -EXTRA = -D__GCC_MULTIREG_RETVALS__ - -CFLAGS := $(CFLAGS) -pipe $(EXTRA) -Wa,-x -ffixed-r13 -mfixed-range=f10-f15,f32-f127 +AFLAGS += -Wa,-x +EXTRA = + +CFLAGS := $(CFLAGS) -pipe $(EXTRA) -Wa,-x -ffixed-r13 -mfixed-range=f10-f15,f32-f127 \ + -mconstant-gp -funwind-tables ifdef CONFIG_IA64_GENERIC CORE_FILES := arch/$(ARCH)/hp/hp.a \ diff --git a/arch/ia64/boot/Makefile b/arch/ia64/boot/Makefile index 5228d6c57..bdeef72ff 100644 --- a/arch/ia64/boot/Makefile +++ b/arch/ia64/boot/Makefile @@ -25,7 +25,8 @@ endif all: $(TARGETS) bootloader: $(OBJECTS) - $(LD) $(LINKFLAGS) $(OBJECTS) $(LIBS) -o bootloader + $(LD) $(LINKFLAGS) $(OBJECTS) $(TOPDIR)/lib/lib.a $(TOPDIR)/arch/$(ARCH)/lib/lib.a \ + -o bootloader clean: rm -f $(TARGETS) diff --git a/arch/ia64/config.in b/arch/ia64/config.in index 3008c6152..b9991cfef 100644 --- a/arch/ia64/config.in +++ b/arch/ia64/config.in @@ -7,7 +7,6 @@ mainmenu_option next_comment comment 'General setup' define_bool CONFIG_IA64 y -define_bool CONFIG_ITANIUM y # easy choice for now... ;-) define_bool CONFIG_ISA n define_bool CONFIG_SBUS n @@ -25,10 +24,13 @@ choice 'Kernel page size' \ 64KB CONFIG_IA64_PAGE_SIZE_64KB" 16KB if [ "$CONFIG_IA64_DIG" = "y" ]; then + define_bool CONFIG_ITANIUM y + define_bool CONFIG_IA64_BRL_EMU y bool ' Enable Itanium A-step specific code' CONFIG_ITANIUM_ASTEP_SPECIFIC + bool ' Enable Itanium A1-step specific code' CONFIG_ITANIUM_A1_SPECIFIC + bool ' Enable use of global TLB purge instruction (ptc.g)' CONFIG_ITANIUM_PTCG bool ' Enable SoftSDV hacks' CONFIG_IA64_SOFTSDV_HACKS - bool ' Enable BigSur hacks' CONFIG_IA64_BIGSUR_HACKS - bool ' Enable Lion hacks' CONFIG_IA64_LION_HACKS + bool ' Enable AzusA hacks' CONFIG_IA64_AZUSA_HACKS bool ' Emulate PAL/SAL/EFI firmware' CONFIG_IA64_FW_EMU bool ' Enable IA64 Machine Check Abort' CONFIG_IA64_MCA fi @@ -46,6 +48,7 @@ define_bool CONFIG_KCORE_ELF y # On IA-64, we always want an ELF /proc/kcore. bool 'SMP support' CONFIG_SMP bool 'Performance monitor support' CONFIG_PERFMON +bool '/proc/palinfo support' CONFIG_IA64_PALINFO bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC @@ -190,5 +193,6 @@ bool 'Early printk support (requires VGA!)' CONFIG_IA64_EARLY_PRINTK bool 'Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG bool 'Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ bool 'Print possible IA64 hazards to console' CONFIG_IA64_PRINT_HAZARDS +bool 'Enable new unwind support' CONFIG_IA64_NEW_UNWIND endmenu diff --git a/arch/ia64/defconfig b/arch/ia64/defconfig index 8dac10752..ce3b4bbe7 100644 --- a/arch/ia64/defconfig +++ b/arch/ia64/defconfig @@ -59,6 +59,10 @@ CONFIG_EXPERIMENTAL=y # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_DAC960 is not set + +# +# Additional Block Devices +# # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_MD is not set # CONFIG_BLK_DEV_RAM is not set @@ -111,8 +115,8 @@ CONFIG_BLK_DEV_IDEDMA=y CONFIG_IDEDMA_PCI_EXPERIMENTAL=y # CONFIG_IDEDMA_PCI_WIP is not set # CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set -# CONFIG_BLK_DEV_AEC62XX is not set -# CONFIG_AEC62XX_TUNING is not set +# CONFIG_BLK_DEV_AEC6210 is not set +# CONFIG_AEC6210_TUNING is not set # CONFIG_BLK_DEV_ALI15X3 is not set # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD7409 is not set diff --git a/arch/ia64/dig/iosapic.c b/arch/ia64/dig/iosapic.c index 9fd01063e..2426a0193 100644 --- a/arch/ia64/dig/iosapic.c +++ b/arch/ia64/dig/iosapic.c @@ -67,6 +67,12 @@ set_rte (unsigned long iosapic_addr, int entry, int pol, int trigger, int delive (delivery << IO_SAPIC_DELIVERY_SHIFT) | vector); +#ifdef CONFIG_IA64_AZUSA_HACKS + /* set Flush Disable bit */ + if (iosapic_addr != 0xc0000000fec00000) + low32 |= (1 << 17); +#endif + /* dest contains both id and eid */ high32 = (dest << IO_SAPIC_DEST_SHIFT); @@ -216,30 +222,33 @@ iosapic_version (unsigned long base_addr) } void -iosapic_init (unsigned long address) +iosapic_init (unsigned long address, int irqbase) { struct hw_interrupt_type *irq_type; struct pci_vector_struct *vectors; int i, irq; - /* - * Map the legacy ISA devices into the IOSAPIC data. Some of - * these may get reprogrammed later on with data from the ACPI - * Interrupt Source Override table. - */ - for (i = 0; i < 16; i++) { - irq = isa_irq_to_vector(i); - iosapic_pin(irq) = i; - iosapic_bus(irq) = BUS_ISA; - iosapic_busdata(irq) = 0; - iosapic_dmode(irq) = IO_SAPIC_LOWEST_PRIORITY; - iosapic_trigger(irq) = IO_SAPIC_EDGE; - iosapic_polarity(irq) = IO_SAPIC_POL_HIGH; + if (irqbase == 0) + /* + * Map the legacy ISA devices into the IOSAPIC data. + * Some of these may get reprogrammed later on with + * data from the ACPI Interrupt Source Override table. + */ + for (i = 0; i < 16; i++) { + irq = isa_irq_to_vector(i); + iosapic_pin(irq) = i; + iosapic_bus(irq) = BUS_ISA; + iosapic_busdata(irq) = 0; + iosapic_dmode(irq) = IO_SAPIC_LOWEST_PRIORITY; + iosapic_trigger(irq) = IO_SAPIC_EDGE; + iosapic_polarity(irq) = IO_SAPIC_POL_HIGH; #ifdef DEBUG_IRQ_ROUTING - printk("ISA: IRQ %02x -> Vector %02x IOSAPIC Pin %d\n", i, irq, iosapic_pin(irq)); + printk("ISA: IRQ %02x -> Vector %02x IOSAPIC Pin %d\n", + i, irq, iosapic_pin(irq)); #endif - } + } +#ifndef CONFIG_IA64_SOFTSDV_HACKS /* * Map the PCI Interrupt data into the ACPI IOSAPIC data using * the info that the bootstrap loader passed to us. @@ -250,6 +259,8 @@ iosapic_init (unsigned long address) irq = vectors[i].irq; if (irq < 16) irq = isa_irq_to_vector(irq); + if (iosapic_baseirq(irq) != irqbase) + continue; iosapic_bustype(irq) = BUS_PCI; iosapic_pin(irq) = irq - iosapic_baseirq(irq); @@ -270,8 +281,12 @@ iosapic_init (unsigned long address) irq, iosapic_pin(irq)); #endif } +#endif /* CONFIG_IA64_SOFTSDV_HACKS */ for (i = 0; i < NR_IRQS; ++i) { + if (iosapic_baseirq(i) != irqbase) + continue; + if (iosapic_pin(i) != -1) { if (iosapic_trigger(i) == IO_SAPIC_LEVEL) irq_type = &irq_type_iosapic_level; diff --git a/arch/ia64/dig/setup.c b/arch/ia64/dig/setup.c index 133f817a1..4d22f46a4 100644 --- a/arch/ia64/dig/setup.c +++ b/arch/ia64/dig/setup.c @@ -53,6 +53,10 @@ dig_setup (char **cmdline_p) */ ROOT_DEV = to_kdev_t(0x0802); /* default to second partition on first drive */ +#ifdef CONFIG_IA64_SOFTSDV_HACKS + ROOT_DEV = to_kdev_t(0x0302); /* 2nd partion on 1st IDE */ +#endif /* CONFIG_IA64_SOFTSDV_HACKS */ + #ifdef CONFIG_SMP init_smp_config(); #endif diff --git a/arch/ia64/hp/hpsim_irq.c b/arch/ia64/hp/hpsim_irq.c index 00f4d1a51..376f664fa 100644 --- a/arch/ia64/hp/hpsim_irq.c +++ b/arch/ia64/hp/hpsim_irq.c @@ -6,6 +6,8 @@ */ #include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> #include <linux/irq.h> static unsigned int diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index 6f702df14..bd29c97e7 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -2,6 +2,8 @@ * IA-32 ELF support. * * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> + * + * 06/16/00 A. Mallick initialize csd/ssd/tssd/cflg for ia32_load_state */ #include <linux/config.h> #include <linux/posix_types.h> @@ -84,6 +86,15 @@ void ia64_elf32_init(struct pt_regs *regs) current->thread.map_base = 0x40000000; + + /* setup ia32 state for ia32_load_state */ + + current->thread.eflag = IA32_EFLAG; + current->thread.csd = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0xBL, 1L, 3L, 1L, 1L, 1L); + current->thread.ssd = IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0x3L, 1L, 3L, 1L, 1L, 1L); + current->thread.tssd = IA64_SEG_DESCRIPTOR(IA32_PAGE_OFFSET + PAGE_SIZE, 0x1FFFL, 0xBL, + 1L, 3L, 1L, 1L, 1L); + /* CS descriptor */ __asm__("mov ar.csd = %0" : /* no outputs */ : "r" IA64_SEG_DESCRIPTOR(0L, 0xFFFFFL, 0xBL, 1L, @@ -96,9 +107,6 @@ void ia64_elf32_init(struct pt_regs *regs) __asm__("mov ar.eflag = %0" : /* no outputs */ : "r" (IA32_EFLAG)); /* Control registers */ - __asm__("mov ar.cflg = %0" - : /* no outputs */ - : "r" (((ulong) IA32_CR4 << 32) | IA32_CR0)); __asm__("mov ar.fsr = %0" : /* no outputs */ : "r" ((ulong)IA32_FSR_DEFAULT)); diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S index ff27a02ce..5bf5ad2c3 100644 --- a/arch/ia64/ia32/ia32_entry.S +++ b/arch/ia64/ia32/ia32_entry.S @@ -1,14 +1,15 @@ +#include <asm/asmmacro.h> #include <asm/offsets.h> #include <asm/signal.h> +#include "../kernel/entry.h" + // // Get possibly unaligned sigmask argument into an aligned // kernel buffer .text - .proc ia32_rt_sigsuspend - .global ia32_rt_sigsuspend -ia32_rt_sigsuspend: +GLOBAL_ENTRY(ia32_rt_sigsuspend) // We'll cheat and not do an alloc here since we are ultimately // going to do a simple branch to the IA64 sys_rt_sigsuspend. // r32 is still the first argument which is the signal mask. @@ -32,24 +33,22 @@ ia32_rt_sigsuspend: st4 [r32]=r2 st4 [r10]=r3 br.cond.sptk.many sys_rt_sigsuspend +END(ia32_rt_sigsuspend) .section __ex_table,"a" data4 @gprel(1b) data4 (2b-1b)|1 .previous +GLOBAL_ENTRY(ia32_ret_from_syscall) + PT_REGS_UNWIND_INFO(0) - .endp ia32_rt_sigsuspend - - .global ia32_ret_from_syscall - .proc ia32_ret_from_syscall -ia32_ret_from_syscall: cmp.ge p6,p7=r8,r0 // syscall executed successfully? adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 ;; st8 [r2]=r8 // store return value in slot for r8 br.cond.sptk.few ia64_leave_kernel - .endp ia32_ret_from_syscall +END(ia32_ret_from_syscall) // // Invoke a system call, but do some tracing before and after the call. @@ -61,9 +60,8 @@ ia32_ret_from_syscall: // r15 = syscall number // b6 = syscall entry point // - .global ia32_trace_syscall - .proc ia32_trace_syscall -ia32_trace_syscall: +GLOBAL_ENTRY(ia32_trace_syscall) + PT_REGS_UNWIND_INFO(0) br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch syscall args .Lret4: br.call.sptk.few rp=b6 // do the syscall .Lret5: cmp.lt p6,p0=r8,r0 // syscall failed? @@ -72,42 +70,38 @@ ia32_trace_syscall: st8.spill [r2]=r8 // store return value in slot for r8 br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch return value .Lret6: br.cond.sptk.many ia64_leave_kernel // rp MUST be != ia64_leave_kernel! +END(ia32_trace_syscall) - .endp ia32_trace_syscall - - .align 16 - .global sys32_vfork - .proc sys32_vfork -sys32_vfork: +GLOBAL_ENTRY(sys32_vfork) alloc r16=ar.pfs,2,2,3,0;; mov out0=IA64_CLONE_VFORK|IA64_CLONE_VM|SIGCHLD // out0 = clone_flags br.cond.sptk.few .fork1 // do the work - .endp sys32_vfork +END(sys32_vfork) - .align 16 - .global sys32_fork - .proc sys32_fork -sys32_fork: - alloc r16=ar.pfs,2,2,3,0;; +GLOBAL_ENTRY(sys32_fork) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)) + alloc r16=ar.pfs,2,2,3,0 mov out0=SIGCHLD // out0 = clone_flags + ;; .fork1: - movl r28=1f - mov loc1=rp - br.cond.sptk.many save_switch_stack -1: - mov loc0=r16 // save ar.pfs across do_fork + mov loc0=rp + mov loc1=r16 // save ar.pfs across do_fork + DO_SAVE_SWITCH_STACK + + UNW(.body) + adds out2=IA64_SWITCH_STACK_SIZE+16,sp adds r2=IA64_SWITCH_STACK_SIZE+IA64_PT_REGS_R12_OFFSET+16,sp ;; ld8 out1=[r2] // fetch usp from pt_regs.r12 br.call.sptk.few rp=do_fork .ret1: - mov ar.pfs=loc0 + mov ar.pfs=loc1 + UNW(.restore sp) adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack - mov rp=loc1 - ;; + mov rp=loc0 br.ret.sptk.many rp - .endp sys32_fork +END(sys32_fork) .rodata .align 8 @@ -304,3 +298,8 @@ ia32_syscall_table: data8 sys_ni_syscall /* streams1 */ data8 sys_ni_syscall /* streams2 */ data8 sys32_vfork /* 190 */ + /* + * CAUTION: If any system calls are added beyond this point + * then the check in `arch/ia64/kernel/ivt.S' will have + * to be modified also. You've been warned. + */ diff --git a/arch/ia64/ia32/ia32_signal.c b/arch/ia64/ia32/ia32_signal.c index e85af6ced..574c1937f 100644 --- a/arch/ia64/ia32/ia32_signal.c +++ b/arch/ia64/ia32/ia32_signal.c @@ -55,7 +55,7 @@ struct rt_sigframe_ia32 }; static int -copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from) +copy_siginfo_to_user32 (siginfo_t32 *to, siginfo_t *from) { int err; @@ -104,6 +104,7 @@ setup_sigcontext_ia32(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate, struct pt_regs *regs, unsigned long mask) { int err = 0; + unsigned long flag; err |= __put_user((regs->r16 >> 32) & 0xffff , (unsigned int *)&sc->fs); err |= __put_user((regs->r16 >> 48) & 0xffff , (unsigned int *)&sc->gs); @@ -124,9 +125,11 @@ setup_sigcontext_ia32(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate, #endif err |= __put_user(regs->cr_iip, &sc->eip); err |= __put_user(regs->r17 & 0xffff, (unsigned int *)&sc->cs); -#if 0 - err |= __put_user(regs->eflags, &sc->eflags); -#endif + /* + * `eflags' is in an ar register for this context + */ + asm volatile ("mov %0=ar.eflag ;;" : "=r"(flag)); + err |= __put_user((unsigned int)flag, &sc->eflags); err |= __put_user(regs->r12, &sc->esp_at_signal); err |= __put_user((regs->r17 >> 16) & 0xffff, (unsigned int *)&sc->ss); @@ -190,15 +193,26 @@ restore_sigcontext_ia32(struct pt_regs *regs, struct sigcontext_ia32 *sc, int *p COPY(cr_iip, eip); COPY_SEG_STRICT(cs); COPY_SEG_STRICT(ss); -#if 0 { - unsigned int tmpflags; - err |= __get_user(tmpflags, &sc->eflags); - /* XXX: Change this to ar.eflags */ - regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); - regs->orig_eax = -1; /* disable syscall checks */ + unsigned int tmpflags; + unsigned long flag; + + /* + * IA32 `eflags' is not part of `pt_regs', it's + * in an ar register which is part of the thread + * context. Fortunately, we are executing in the + * IA32 process's context. + */ + err |= __get_user(tmpflags, &sc->eflags); + asm volatile ("mov %0=ar.eflag ;;" : "=r"(flag)); + flag &= ~0x40DD5; + flag |= (tmpflags & 0x40DD5); + asm volatile ("mov ar.eflag=%0 ;;" :: "r"(flag)); + + regs->r1 = -1; /* disable syscall checks, r1 is orig_eax */ } +#if 0 { struct _fpstate * buf; err |= __get_user(buf, &sc->fpstate); @@ -271,7 +285,7 @@ setup_frame_ia32(int sig, struct k_sigaction *ka, sigset_t *set, /* Set up to return from userspace. If provided, use a stub already in userspace. */ - err |= __put_user(frame->retcode, &frame->pretcode); + err |= __put_user((long)frame->retcode, &frame->pretcode); /* This is popl %eax ; movl $,%eax ; int $0x80 */ err |= __put_user(0xb858, (short *)(frame->retcode+0)); #define __IA32_NR_sigreturn 119 @@ -326,8 +340,8 @@ setup_rt_frame_ia32(int sig, struct k_sigaction *ka, siginfo_t *info, ? current->exec_domain->signal_invmap[sig] : sig), &frame->sig); - err |= __put_user(&frame->info, &frame->pinfo); - err |= __put_user(&frame->uc, &frame->puc); + err |= __put_user((long)&frame->info, &frame->pinfo); + err |= __put_user((long)&frame->uc, &frame->puc); err |= copy_siginfo_to_user32(&frame->info, info); /* Create the ucontext. */ @@ -341,7 +355,7 @@ setup_rt_frame_ia32(int sig, struct k_sigaction *ka, siginfo_t *info, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - err |= __put_user(frame->retcode, &frame->pretcode); + err |= __put_user((long)frame->retcode, &frame->pretcode); /* This is movl $,%eax ; int $0x80 */ err |= __put_user(0xb8, (char *)(frame->retcode+0)); #define __IA32_NR_rt_sigreturn 173 diff --git a/arch/ia64/ia32/ia32_support.c b/arch/ia64/ia32/ia32_support.c index dcf61e8e4..ab5bebfe1 100644 --- a/arch/ia64/ia32/ia32_support.c +++ b/arch/ia64/ia32/ia32_support.c @@ -1,6 +1,9 @@ /* * IA32 helper functions + * + * 06/16/00 A. Mallick added csd/ssd/tssd for ia32 thread context */ + #include <linux/kernel.h> #include <linux/init.h> #include <linux/mm.h> @@ -16,6 +19,57 @@ extern unsigned long *ia32_gdt_table, *ia32_tss; extern void die_if_kernel (char *str, struct pt_regs *regs, long err); +void +ia32_save_state (struct thread_struct *thread) +{ + unsigned long eflag, fsr, fcr, fir, fdr, csd, ssd, tssd; + + asm ("mov %0=ar.eflag;" + "mov %1=ar.fsr;" + "mov %2=ar.fcr;" + "mov %3=ar.fir;" + "mov %4=ar.fdr;" + "mov %5=ar.csd;" + "mov %6=ar.ssd;" + "mov %7=ar.k1" + : "=r"(eflag), "=r"(fsr), "=r"(fcr), "=r"(fir), "=r"(fdr), + "=r"(csd), "=r"(ssd), "=r"(tssd)); + thread->eflag = eflag; + thread->fsr = fsr; + thread->fcr = fcr; + thread->fir = fir; + thread->fdr = fdr; + thread->csd = csd; + thread->ssd = ssd; + thread->tssd = tssd; +} + +void +ia32_load_state (struct thread_struct *thread) +{ + unsigned long eflag, fsr, fcr, fir, fdr, csd, ssd, tssd; + + eflag = thread->eflag; + fsr = thread->fsr; + fcr = thread->fcr; + fir = thread->fir; + fdr = thread->fdr; + csd = thread->csd; + ssd = thread->ssd; + tssd = thread->tssd; + + asm volatile ("mov ar.eflag=%0;" + "mov ar.fsr=%1;" + "mov ar.fcr=%2;" + "mov ar.fir=%3;" + "mov ar.fdr=%4;" + "mov ar.csd=%5;" + "mov ar.ssd=%6;" + "mov ar.k1=%7" + :: "r"(eflag), "r"(fsr), "r"(fcr), "r"(fir), "r"(fdr), + "r"(csd), "r"(ssd), "r"(tssd)); +} + /* * Setup IA32 GDT and TSS */ diff --git a/arch/ia64/ia32/ia32_traps.c b/arch/ia64/ia32/ia32_traps.c index de99a65b3..2cfc9ae02 100644 --- a/arch/ia64/ia32/ia32_traps.c +++ b/arch/ia64/ia32/ia32_traps.c @@ -1,3 +1,9 @@ +/* + * IA32 exceptions handler + * + * 06/16/00 A. Mallick added siginfo for most cases (close to IA32) + */ + #include <linux/kernel.h> #include <linux/sched.h> @@ -9,9 +15,11 @@ ia32_exception (struct pt_regs *regs, unsigned long isr) { struct siginfo siginfo; + siginfo.si_errno = 0; switch ((isr >> 16) & 0xff) { case 1: case 2: + siginfo.si_signo = SIGTRAP; if (isr == 0) siginfo.si_code = TRAP_TRACE; else if (isr & 0x4) @@ -21,27 +29,96 @@ ia32_exception (struct pt_regs *regs, unsigned long isr) break; case 3: + siginfo.si_signo = SIGTRAP; siginfo.si_code = TRAP_BRKPT; break; case 0: /* Divide fault */ + siginfo.si_signo = SIGFPE; + siginfo.si_code = FPE_INTDIV; + break; + case 4: /* Overflow */ case 5: /* Bounds fault */ + siginfo.si_signo = SIGFPE; + siginfo.si_code = 0; + break; + case 6: /* Invalid Op-code */ + siginfo.si_signo = SIGILL; + siginfo.si_code = ILL_ILLOPN; + break; + case 7: /* FP DNA */ case 8: /* Double Fault */ case 9: /* Invalid TSS */ case 11: /* Segment not present */ case 12: /* Stack fault */ case 13: /* General Protection Fault */ + siginfo.si_signo = SIGSEGV; + siginfo.si_code = 0; + break; + case 16: /* Pending FP error */ + { + unsigned long fsr, fcr; + + asm ("mov %0=ar.fsr;" + "mov %1=ar.fcr;" + : "=r"(fsr), "=r"(fcr)); + + siginfo.si_signo = SIGFPE; + /* + * (~cwd & swd) will mask out exceptions that are not set to unmasked + * status. 0x3f is the exception bits in these regs, 0x200 is the + * C1 reg you need in case of a stack fault, 0x040 is the stack + * fault bit. We should only be taking one exception at a time, + * so if this combination doesn't produce any single exception, + * then we have a bad program that isn't syncronizing its FPU usage + * and it will suffer the consequences since we won't be able to + * fully reproduce the context of the exception + */ + switch(((~fcr) & (fsr & 0x3f)) | (fsr & 0x240)) { + case 0x000: + default: + siginfo.si_code = 0; + break; + case 0x001: /* Invalid Op */ + case 0x040: /* Stack Fault */ + case 0x240: /* Stack Fault | Direction */ + siginfo.si_code = FPE_FLTINV; + break; + case 0x002: /* Denormalize */ + case 0x010: /* Underflow */ + siginfo.si_code = FPE_FLTUND; + break; + case 0x004: /* Zero Divide */ + siginfo.si_code = FPE_FLTDIV; + break; + case 0x008: /* Overflow */ + siginfo.si_code = FPE_FLTOVF; + break; + case 0x020: /* Precision */ + siginfo.si_code = FPE_FLTRES; + break; + } + + break; + } + case 17: /* Alignment check */ + siginfo.si_signo = SIGSEGV; + siginfo.si_code = BUS_ADRALN; + break; + case 19: /* SSE Numeric error */ + siginfo.si_signo = SIGFPE; + siginfo.si_code = 0; + break; + default: return -1; } - siginfo.si_signo = SIGTRAP; - siginfo.si_errno = 0; - send_sig_info(SIGTRAP, &siginfo, current); + force_sig_info(SIGTRAP, &siginfo, current); return 0; } diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 06642dcec..bd925c0e4 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -7,6 +7,8 @@ * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> * * These routines maintain argument size conversion between 32bit and 64bit * environment. @@ -55,24 +57,29 @@ #include <net/sock.h> #include <asm/ia32.h> -#define A(__x) ((unsigned long)(__x)) -#define AA(__x) ((unsigned long)(__x)) +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) ((unsigned long)(__x)) +#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) + +extern asmlinkage long sys_execve (char *, char **, char **, struct pt_regs *); +extern asmlinkage long sys_munmap (unsigned long, size_t len); +extern asmlinkage long sys_mprotect (unsigned long, size_t, unsigned long); static int nargs(unsigned int arg, char **ap) { - char *ptr; - int n, err; + int n, err, addr; n = 0; do { - if (err = get_user(ptr, (int *)arg)) + if ((err = get_user(addr, (int *)A(arg))) != 0) return(err); if (ap) - *ap++ = ptr; + *ap++ = (char *)A(addr); arg += sizeof(unsigned int); n++; - } while (ptr); + } while (addr); return(n - 1); } @@ -106,14 +113,14 @@ int stack) down(¤t->mm->mmap_sem); lock_kernel(); - av = do_mmap_pgoff(0, NULL, len, - PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0); + av = (char **) do_mmap_pgoff(0, 0UL, len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0); unlock_kernel(); up(¤t->mm->mmap_sem); if (IS_ERR(av)) - return(av); + return (long)av; ae = av + na + 1; av[na] = (char *)0; ae[ne] = (char *)0; @@ -121,7 +128,7 @@ int stack) (void)nargs(envp, ae); r = sys_execve(filename, av, ae, regs); if (IS_ERR(r)) - sys_munmap(av, len); + sys_munmap((unsigned long) av, len); return(r); } @@ -146,9 +153,9 @@ putstat(struct stat32 *ubuf, struct stat *kbuf) return err; } -extern asmlinkage int sys_newstat(char * filename, struct stat * statbuf); +extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); -asmlinkage int +asmlinkage long sys32_newstat(char * filename, struct stat32 *statbuf) { int ret; @@ -163,9 +170,9 @@ sys32_newstat(char * filename, struct stat32 *statbuf) return ret; } -extern asmlinkage int sys_newlstat(char * filename, struct stat * statbuf); +extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf); -asmlinkage int +asmlinkage long sys32_newlstat(char * filename, struct stat32 *statbuf) { int ret; @@ -180,9 +187,9 @@ sys32_newlstat(char * filename, struct stat32 *statbuf) return ret; } -extern asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf); +extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf); -asmlinkage int +asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 *statbuf) { int ret; @@ -214,34 +221,26 @@ do_mmap_fake(struct file *file, unsigned long addr, unsigned long len, return -EINVAL; if (prot & PROT_WRITE) prot |= PROT_EXEC; -#ifdef DDD -#else // DDD prot |= PROT_WRITE; -#endif // DDD front = NULL; back = NULL; if ((baddr = (addr & PAGE_MASK)) != addr && get_user(c, (char *)baddr) == 0) { front = kmalloc(addr - baddr, GFP_KERNEL); memcpy(front, (void *)baddr, addr - baddr); } -#ifndef DDD - if (addr) -#endif - if (((addr + len) & ~PAGE_MASK) && get_user(c, (char *)(addr + len)) == 0) { + if (addr && ((addr + len) & ~PAGE_MASK) && get_user(c, (char *)(addr + len)) == 0) { back = kmalloc(PAGE_SIZE - ((addr + len) & ~PAGE_MASK), GFP_KERNEL); - memcpy(back, addr + len, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); + memcpy(back, (char *)addr + len, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); } down(¤t->mm->mmap_sem); r = do_mmap(0, baddr, len + (addr - baddr), prot, flags | MAP_ANONYMOUS, 0); up(¤t->mm->mmap_sem); if (r < 0) return(r); -#ifndef DDD if (addr == 0) addr = r; -#endif // DDD if (back) { - memcpy(addr + len, back, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); + memcpy((char *)addr + len, back, PAGE_SIZE - ((addr + len) & ~PAGE_MASK)); kfree(back); } if (front) { @@ -249,7 +248,7 @@ do_mmap_fake(struct file *file, unsigned long addr, unsigned long len, kfree(front); } if (flags & MAP_ANONYMOUS) { - memset(addr, 0, len); + memset((char *)addr, 0, len); return(addr); } if (!file) @@ -283,7 +282,7 @@ struct mmap_arg_struct { unsigned int offset; }; -asmlinkage int +asmlinkage long sys32_mmap(struct mmap_arg_struct *arg) { int error = -EFAULT; @@ -302,11 +301,7 @@ sys32_mmap(struct mmap_arg_struct *arg) } a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); -#ifdef DDD if ((a.flags & MAP_FIXED) && ((a.addr & ~PAGE_MASK) || (a.offset & ~PAGE_MASK))) { -#else // DDD - if (1) { -#endif // DDD unlock_kernel(); error = do_mmap_fake(file, a.addr, a.len, a.prot, a.flags, a.offset); lock_kernel(); @@ -351,7 +346,7 @@ sys32_mprotect(unsigned long start, size_t len, unsigned long prot) return(sys_mprotect(start & PAGE_MASK, len & PAGE_MASK, prot)); } -asmlinkage int +asmlinkage long sys32_rt_sigaction(int sig, struct sigaction32 *act, struct sigaction32 *oact, unsigned int sigsetsize) { @@ -410,10 +405,10 @@ sys32_rt_sigaction(int sig, struct sigaction32 *act, } -extern asmlinkage int sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, - size_t sigsetsize); +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); -asmlinkage int +asmlinkage long sys32_rt_sigprocmask(int how, sigset32_t *set, sigset32_t *oset, unsigned int sigsetsize) { @@ -468,9 +463,9 @@ put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) return err; } -extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); +extern asmlinkage long sys_statfs(const char * path, struct statfs * buf); -asmlinkage int +asmlinkage long sys32_statfs(const char * path, struct statfs32 *buf) { int ret; @@ -485,9 +480,9 @@ sys32_statfs(const char * path, struct statfs32 *buf) return ret; } -extern asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf); +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); -asmlinkage int +asmlinkage long sys32_fstatfs(unsigned int fd, struct statfs32 *buf) { int ret; @@ -554,7 +549,7 @@ put_it32(struct itimerval32 *o, struct itimerval *i) extern int do_getitimer(int which, struct itimerval *value); -asmlinkage int +asmlinkage long sys32_getitimer(int which, struct itimerval32 *it) { struct itimerval kit; @@ -569,7 +564,7 @@ sys32_getitimer(int which, struct itimerval32 *it) extern int do_setitimer(int which, struct itimerval *, struct itimerval *); -asmlinkage int +asmlinkage long sys32_setitimer(int which, struct itimerval32 *in, struct itimerval32 *out) { struct itimerval kin, kout; @@ -614,7 +609,7 @@ sys32_alarm(unsigned int seconds) extern struct timezone sys_tz; extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); -asmlinkage int +asmlinkage long sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) { if (tv) { @@ -630,7 +625,7 @@ sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) return 0; } -asmlinkage int +asmlinkage long sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) { struct timeval ktv; @@ -648,56 +643,135 @@ sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); } -struct dirent32 { - unsigned int d_ino; - unsigned int d_off; - unsigned short d_reclen; - char d_name[NAME_MAX + 1]; +struct linux32_dirent { + u32 d_ino; + u32 d_off; + u16 d_reclen; + char d_name[1]; }; -static void -xlate_dirent(void *dirent64, void *dirent32, long n) -{ - long off; - struct dirent *dirp; - struct dirent32 *dirp32; - - off = 0; - while (off < n) { - dirp = (struct dirent *)(dirent64 + off); - dirp32 = (struct dirent32 *)(dirent32 + off); - off += dirp->d_reclen; - dirp32->d_ino = dirp->d_ino; - dirp32->d_off = (unsigned int)dirp->d_off; - dirp32->d_reclen = dirp->d_reclen; - strncpy(dirp32->d_name, dirp->d_name, dirp->d_reclen - ((3 * 4) + 2)); - } - return; +struct old_linux32_dirent { + u32 d_ino; + u32 d_offset; + u16 d_namlen; + char d_name[1]; +}; + +struct getdents32_callback { + struct linux32_dirent * current_dir; + struct linux32_dirent * previous; + int count; + int error; +}; + +struct readdir32_callback { + struct old_linux32_dirent * dirent; + int count; +}; + +static int +filldir32 (void *__buf, const char *name, int namlen, off_t offset, ino_t ino) +{ + struct linux32_dirent * dirent; + struct getdents32_callback * buf = (struct getdents32_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1, 4); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; } asmlinkage long -sys32_getdents(unsigned int fd, void * dirent32, unsigned int count) +sys32_getdents (unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux32_dirent * lastdirent; + struct getdents32_callback buf; + int error; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct linux32_dirent *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + lock_kernel(); + error = vfs_readdir(file, filldir32, &buf); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + +out_putf: + unlock_kernel(); + fput(file); +out: + return error; +} + +static int +fillonedir32 (void * __buf, const char * name, int namlen, off_t offset, ino_t ino) { - long n; - void *dirent64; + struct readdir32_callback * buf = (struct readdir32_callback *) __buf; + struct old_linux32_dirent * dirent; - dirent64 = (unsigned long)(dirent32 + (sizeof(long) - 1)) & ~(sizeof(long) - 1); - if ((n = sys_getdents(fd, dirent64, count - (dirent64 - dirent32))) < 0) - return(n); - xlate_dirent(dirent64, dirent32, n); - return(n); + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; } -asmlinkage int -sys32_readdir(unsigned int fd, void * dirent32, unsigned int count) +asmlinkage long +sys32_readdir (unsigned int fd, void * dirent, unsigned int count) { - int n; - struct dirent dirent64; + int error; + struct file * file; + struct readdir32_callback buf; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + lock_kernel(); + error = vfs_readdir(file, fillonedir32, &buf); + if (error >= 0) + error = buf.count; + unlock_kernel(); - if ((n = old_readdir(fd, &dirent64, count)) < 0) - return(n); - xlate_dirent(&dirent64, dirent32, dirent64.d_reclen); - return(n); + fput(file); +out: + return error; } /* @@ -710,9 +784,9 @@ sys32_readdir(unsigned int fd, void * dirent32, unsigned int count) */ #define MAX_SELECT_SECONDS \ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) -#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) +#define ROUND_UP_TIME(x,y) (((x)+(y)-1)/(y)) -asmlinkage int +asmlinkage long sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32) { fd_set_bits fds; @@ -732,7 +806,7 @@ sys32_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tv goto out_nofds; if ((unsigned long) sec < MAX_SELECT_SECONDS) { - timeout = ROUND_UP(usec, 1000000/HZ); + timeout = ROUND_UP_TIME(usec, 1000000/HZ); timeout += sec * (unsigned long) HZ; } } @@ -809,13 +883,15 @@ struct sel_arg_struct { unsigned int tvp; }; -asmlinkage int old_select(struct sel_arg_struct *arg) +asmlinkage long +old_select(struct sel_arg_struct *arg) { struct sel_arg_struct a; if (copy_from_user(&a, arg, sizeof(a))) return -EFAULT; - return sys32_select(a.n, a.inp, a.outp, a.exp, a.tvp); + return sys32_select(a.n, (fd_set *)A(a.inp), (fd_set *)A(a.outp), (fd_set *)A(a.exp), + (struct timeval32 *)A(a.tvp)); } struct timespec32 { @@ -823,10 +899,9 @@ struct timespec32 { int tv_nsec; }; -extern asmlinkage int sys_nanosleep(struct timespec *rqtp, - struct timespec *rmtp); +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); -asmlinkage int +asmlinkage long sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) { struct timespec t; @@ -1007,9 +1082,9 @@ struct rlimit32 { int rlim_max; }; -extern asmlinkage int sys_getrlimit(unsigned int resource, struct rlimit *rlim); +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); -asmlinkage int +asmlinkage long sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) { struct rlimit r; @@ -1026,9 +1101,9 @@ sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) return ret; } -extern asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim); +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); -asmlinkage int +asmlinkage long sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) { struct rlimit r; @@ -1049,118 +1124,6 @@ sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) return ret; } -/* Argument list sizes for sys_socketcall */ -#define AL(x) ((x) * sizeof(u32)) -static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), - AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), - AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; -#undef AL - -extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); -extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, - int addrlen); -extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, - int *upeer_addrlen); -extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); -extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); -extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags); -extern asmlinkage int sys_sendto(int fd, u32 buff, __kernel_size_t32 len, - unsigned flags, u32 addr, int addr_len); -extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags); -extern asmlinkage int sys_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, - unsigned flags, u32 addr, u32 addr_len); -extern asmlinkage int sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen); -extern asmlinkage int sys_getsockopt(int fd, int level, int optname, - u32 optval, u32 optlen); - -extern asmlinkage int sys_socket(int family, int type, int protocol); -extern asmlinkage int sys_socketpair(int family, int type, int protocol, - int usockvec[2]); -extern asmlinkage int sys_shutdown(int fd, int how); -extern asmlinkage int sys_listen(int fd, int backlog); - -asmlinkage int sys32_socketcall(int call, u32 *args) -{ - int i, ret; - u32 a[6]; - u32 a0,a1; - - if (call<SYS_SOCKET||call>SYS_RECVMSG) - return -EINVAL; - if (copy_from_user(a, args, nas[call])) - return -EFAULT; - a0=a[0]; - a1=a[1]; - - switch(call) - { - case SYS_SOCKET: - ret = sys_socket(a0, a1, a[2]); - break; - case SYS_BIND: - ret = sys_bind(a0, (struct sockaddr *)A(a1), a[2]); - break; - case SYS_CONNECT: - ret = sys_connect(a0, (struct sockaddr *)A(a1), a[2]); - break; - case SYS_LISTEN: - ret = sys_listen(a0, a1); - break; - case SYS_ACCEPT: - ret = sys_accept(a0, (struct sockaddr *)A(a1), - (int *)A(a[2])); - break; - case SYS_GETSOCKNAME: - ret = sys_getsockname(a0, (struct sockaddr *)A(a1), - (int *)A(a[2])); - break; - case SYS_GETPEERNAME: - ret = sys_getpeername(a0, (struct sockaddr *)A(a1), - (int *)A(a[2])); - break; - case SYS_SOCKETPAIR: - ret = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); - break; - case SYS_SEND: - ret = sys_send(a0, (void *)A(a1), a[2], a[3]); - break; - case SYS_SENDTO: - ret = sys_sendto(a0, a1, a[2], a[3], a[4], a[5]); - break; - case SYS_RECV: - ret = sys_recv(a0, (void *)A(a1), a[2], a[3]); - break; - case SYS_RECVFROM: - ret = sys_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); - break; - case SYS_SHUTDOWN: - ret = sys_shutdown(a0,a1); - break; - case SYS_SETSOCKOPT: - ret = sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), - a[4]); - break; - case SYS_GETSOCKOPT: - ret = sys_getsockopt(a0, a1, a[2], a[3], a[4]); - break; - case SYS_SENDMSG: - ret = sys32_sendmsg(a0, (struct msghdr32 *)A(a1), - a[2]); - break; - case SYS_RECVMSG: - ret = sys32_recvmsg(a0, (struct msghdr32 *)A(a1), - a[2]); - break; - default: - ret = EINVAL; - break; - } - return ret; -} - /* * Declare the IA32 version of the msghdr */ @@ -1183,13 +1146,13 @@ shape_msg(struct msghdr *mp, struct msghdr32 *mp32) if (!access_ok(VERIFY_READ, mp32, sizeof(*mp32))) return(-EFAULT); __get_user(i, &mp32->msg_name); - mp->msg_name = (void *)i; + mp->msg_name = (void *)A(i); __get_user(mp->msg_namelen, &mp32->msg_namelen); __get_user(i, &mp32->msg_iov); - mp->msg_iov = (struct iov *)i; + mp->msg_iov = (struct iovec *)A(i); __get_user(mp->msg_iovlen, &mp32->msg_iovlen); __get_user(i, &mp32->msg_control); - mp->msg_control = (void *)i; + mp->msg_control = (void *)A(i); __get_user(mp->msg_controllen, &mp32->msg_controllen); __get_user(mp->msg_flags, &mp32->msg_flags); return(0); @@ -1235,7 +1198,7 @@ verify_iovec32(struct msghdr *m, struct iovec *iov, char *address, int mode) iov32 = (struct iovec32 *)iov; for (ct = m->msg_iovlen; ct-- > 0; ) { iov[ct].iov_len = (__kernel_size_t)iov32[ct].iov_len; - iov[ct].iov_base = (void *)iov32[ct].iov_base; + iov[ct].iov_base = (void *) A(iov32[ct].iov_base); err += iov[ct].iov_len; } out: @@ -1260,7 +1223,7 @@ extern struct socket *sockfd_lookup(int fd, int *err); * BSD sendmsg interface */ -asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags) +int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags) { struct socket *sock; char address[MAX_SOCK_ADDR]; @@ -1339,7 +1302,8 @@ out: * BSD recvmsg interface */ -asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *msg, unsigned int flags) +int +sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags) { struct socket *sock; struct iovec iovstack[UIO_FASTIOV]; @@ -1421,6 +1385,118 @@ out: return err; } +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +extern asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, + int addrlen); +extern asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, + int *upeer_addrlen); +extern asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, + int *usockaddr_len); +extern asmlinkage long sys_send(int fd, void *buff, size_t len, unsigned flags); +extern asmlinkage long sys_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage long sys_recv(int fd, void *ubuf, size_t size, unsigned flags); +extern asmlinkage long sys_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage long sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); +extern asmlinkage long sys_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +extern asmlinkage long sys_socket(int family, int type, int protocol); +extern asmlinkage long sys_socketpair(int family, int type, int protocol, + int usockvec[2]); +extern asmlinkage long sys_shutdown(int fd, int how); +extern asmlinkage long sys_listen(int fd, int backlog); + +asmlinkage long sys32_socketcall(int call, u32 *args) +{ + int ret; + u32 a[6]; + u32 a0,a1; + + if (call<SYS_SOCKET||call>SYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, args, nas[call])) + return -EFAULT; + a0=a[0]; + a1=a[1]; + + switch(call) + { + case SYS_SOCKET: + ret = sys_socket(a0, a1, a[2]); + break; + case SYS_BIND: + ret = sys_bind(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_CONNECT: + ret = sys_connect(a0, (struct sockaddr *)A(a1), a[2]); + break; + case SYS_LISTEN: + ret = sys_listen(a0, a1); + break; + case SYS_ACCEPT: + ret = sys_accept(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETSOCKNAME: + ret = sys_getsockname(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_GETPEERNAME: + ret = sys_getpeername(a0, (struct sockaddr *)A(a1), + (int *)A(a[2])); + break; + case SYS_SOCKETPAIR: + ret = sys_socketpair(a0, a1, a[2], (int *)A(a[3])); + break; + case SYS_SEND: + ret = sys_send(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_SENDTO: + ret = sys_sendto(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_RECV: + ret = sys_recv(a0, (void *)A(a1), a[2], a[3]); + break; + case SYS_RECVFROM: + ret = sys_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); + break; + case SYS_SHUTDOWN: + ret = sys_shutdown(a0,a1); + break; + case SYS_SETSOCKOPT: + ret = sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), + a[4]); + break; + case SYS_GETSOCKOPT: + ret = sys_getsockopt(a0, a1, a[2], a[3], a[4]); + break; + case SYS_SENDMSG: + ret = sys32_sendmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + case SYS_RECVMSG: + ret = sys32_recvmsg(a0, (struct msghdr32 *)A(a1), + a[2]); + break; + default: + ret = EINVAL; + break; + } + return ret; +} + /* * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. * @@ -1615,7 +1691,7 @@ out: static int do_sys32_msgctl (int first, int second, void *uptr) { - int err, err2; + int err = -EINVAL, err2; struct msqid_ds m; struct msqid64_ds m64; struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; @@ -1646,7 +1722,7 @@ do_sys32_msgctl (int first, int second, void *uptr) case MSG_STAT: old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_msgctl (first, second, &m64); + err = sys_msgctl (first, second, (void *) &m64); set_fs (old_fs); err2 = put_user (m64.msg_perm.key, &up->msg_perm.key); err2 |= __put_user(m64.msg_perm.uid, &up->msg_perm.uid); @@ -1727,7 +1803,7 @@ do_sys32_shmctl (int first, int second, void *uptr) case SHM_STAT: old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_shmctl (first, second, &s64); + err = sys_shmctl (first, second, (void *) &s64); set_fs (old_fs); if (err < 0) break; @@ -1755,7 +1831,7 @@ do_sys32_shmctl (int first, int second, void *uptr) case SHM_INFO: old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_shmctl (first, second, &si); + err = sys_shmctl (first, second, (void *)&si); set_fs (old_fs); if (err < 0) break; @@ -1775,7 +1851,7 @@ do_sys32_shmctl (int first, int second, void *uptr) return err; } -asmlinkage int +asmlinkage long sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) { int version, err; @@ -1900,10 +1976,10 @@ put_rusage (struct rusage32 *ru, struct rusage *r) return err; } -extern asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); -asmlinkage int +asmlinkage long sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, struct rusage32 *ru) { @@ -1925,17 +2001,17 @@ sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, } } -asmlinkage int +asmlinkage long sys32_waitpid(__kernel_pid_t32 pid, unsigned int *stat_addr, int options) { return sys32_wait4(pid, stat_addr, options, NULL); } -extern asmlinkage int +extern asmlinkage long sys_getrusage(int who, struct rusage *ru); -asmlinkage int +asmlinkage long sys32_getrusage(int who, struct rusage32 *ru) { struct rusage r; @@ -2431,9 +2507,9 @@ getname32(const char *filename) /* 32-bit timeval and related flotsam. */ -extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); +extern asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); -asmlinkage int +asmlinkage long sys32_ioperm(u32 from, u32 num, int on) { return sys_ioperm((unsigned long)from, (unsigned long)num, on); @@ -2505,10 +2581,10 @@ struct dqblk32 { __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr); -asmlinkage int +asmlinkage long sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; @@ -2552,13 +2628,13 @@ sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) return err; } -extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); struct utimbuf32 { __kernel_time_t32 actime, modtime; }; -asmlinkage int +asmlinkage long sys32_utime(char * filename, struct utimbuf32 *times) { struct utimbuf t; @@ -2642,10 +2718,10 @@ set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) __put_user(*fdset, ufdset); } -extern asmlinkage int sys_sysfs(int option, unsigned long arg1, +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2); -asmlinkage int +asmlinkage long sys32_sysfs(int option, u32 arg1, u32 arg2) { return sys_sysfs(option, arg1, arg2); @@ -2741,7 +2817,7 @@ extern asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, #define SMBFS_NAME "smbfs" #define NCPFS_NAME "ncpfs" -asmlinkage int +asmlinkage long sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data) { @@ -2815,9 +2891,9 @@ struct sysinfo32 { char _f[22]; }; -extern asmlinkage int sys_sysinfo(struct sysinfo *info); +extern asmlinkage long sys_sysinfo(struct sysinfo *info); -asmlinkage int +asmlinkage long sys32_sysinfo(struct sysinfo32 *info) { struct sysinfo s; @@ -2843,10 +2919,10 @@ sys32_sysinfo(struct sysinfo32 *info) return ret; } -extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, +extern asmlinkage long sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); -asmlinkage int +asmlinkage long sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) { struct timespec t; @@ -2862,10 +2938,10 @@ sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) return ret; } -extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set, +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); -asmlinkage int +asmlinkage long sys32_sigprocmask(int how, old_sigset_t32 *set, old_sigset_t32 *oset) { old_sigset_t s; @@ -2881,9 +2957,9 @@ sys32_sigprocmask(int how, old_sigset_t32 *set, old_sigset_t32 *oset) return 0; } -extern asmlinkage int sys_sigpending(old_sigset_t *set); +extern asmlinkage long sys_sigpending(old_sigset_t *set); -asmlinkage int +asmlinkage long sys32_sigpending(old_sigset_t32 *set) { old_sigset_t s; @@ -2897,9 +2973,9 @@ sys32_sigpending(old_sigset_t32 *set) return ret; } -extern asmlinkage int sys_rt_sigpending(sigset_t *set, size_t sigsetsize); +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); -asmlinkage int +asmlinkage long sys32_rt_sigpending(sigset_t32 *set, __kernel_size_t32 sigsetsize) { sigset_t s; @@ -3002,11 +3078,11 @@ siginfo32to64(siginfo_t *d, siginfo_t32 *s) return d; } -extern asmlinkage int +extern asmlinkage long sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, const struct timespec *uts, size_t sigsetsize); -asmlinkage int +asmlinkage long sys32_rt_sigtimedwait(sigset_t32 *uthese, siginfo_t32 *uinfo, struct timespec32 *uts, __kernel_size_t32 sigsetsize) { @@ -3043,10 +3119,10 @@ sys32_rt_sigtimedwait(sigset_t32 *uthese, siginfo_t32 *uinfo, return ret; } -extern asmlinkage int +extern asmlinkage long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); -asmlinkage int +asmlinkage long sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) { siginfo_t info; @@ -3064,9 +3140,9 @@ sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) return ret; } -extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid); +extern asmlinkage long sys_setreuid(uid_t ruid, uid_t euid); -asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) +asmlinkage long sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) { uid_t sruid, seuid; @@ -3075,9 +3151,9 @@ asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) return sys_setreuid(sruid, seuid); } -extern asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); +extern asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); -asmlinkage int +asmlinkage long sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid, __kernel_uid_t32 suid) { @@ -3089,9 +3165,9 @@ sys32_setresuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid, return sys_setresuid(sruid, seuid, ssuid); } -extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); +extern asmlinkage long sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); -asmlinkage int +asmlinkage long sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid, __kernel_uid_t32 *suid) { @@ -3107,9 +3183,9 @@ sys32_getresuid(__kernel_uid_t32 *ruid, __kernel_uid_t32 *euid, return ret; } -extern asmlinkage int sys_setregid(gid_t rgid, gid_t egid); +extern asmlinkage long sys_setregid(gid_t rgid, gid_t egid); -asmlinkage int +asmlinkage long sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) { gid_t srgid, segid; @@ -3119,9 +3195,9 @@ sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) return sys_setregid(srgid, segid); } -extern asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); +extern asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); -asmlinkage int +asmlinkage long sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid, __kernel_gid_t32 sgid) { @@ -3133,9 +3209,9 @@ sys32_setresgid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid, return sys_setresgid(srgid, segid, ssgid); } -extern asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); +extern asmlinkage long sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); -asmlinkage int +asmlinkage long sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid, __kernel_gid_t32 *sgid) { @@ -3154,9 +3230,9 @@ sys32_getresgid(__kernel_gid_t32 *rgid, __kernel_gid_t32 *egid, return ret; } -extern asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist); +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); -asmlinkage int +asmlinkage long sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist) { gid_t gl[NGROUPS]; @@ -3173,9 +3249,9 @@ sys32_getgroups(int gidsetsize, __kernel_gid_t32 *grouplist) return ret; } -extern asmlinkage int sys_setgroups(int gidsetsize, gid_t *grouplist); +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); -asmlinkage int +asmlinkage long sys32_setgroups(int gidsetsize, __kernel_gid_t32 *grouplist) { gid_t gl[NGROUPS]; @@ -3619,7 +3695,7 @@ fail: kmsg->msg_control = (void *) orig_cmsg_uptr; } -asmlinkage int +asmlinkage long sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) { struct socket *sock; @@ -3667,7 +3743,7 @@ out: return err; } -asmlinkage int +asmlinkage long sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags) { struct iovec iovstack[UIO_FASTIOV]; @@ -3758,7 +3834,7 @@ out: extern void check_pending(int signum); -asmlinkage int +asmlinkage long sys32_sigaction (int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) { @@ -3803,21 +3879,21 @@ sys32_create_module(const char *name_user, __kernel_size_t32 size) return sys_create_module(name_user, (size_t)size); } -extern asmlinkage int sys_init_module(const char *name_user, +extern asmlinkage long sys_init_module(const char *name_user, struct module *mod_user); /* Hey, when you're trying to init module, take time and prepare us a nice 64bit * module structure, even if from 32bit modutils... Why to pollute kernel... :)) */ -asmlinkage int +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) { return sys_init_module(name_user, mod_user); } -extern asmlinkage int sys_delete_module(const char *name_user); +extern asmlinkage long sys_delete_module(const char *name_user); -asmlinkage int +asmlinkage long sys32_delete_module(const char *name_user) { return sys_delete_module(name_user); @@ -4092,7 +4168,7 @@ qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) return error; } -asmlinkage int +asmlinkage long sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret) { @@ -4160,9 +4236,9 @@ struct kernel_sym32 { char name[60]; }; -extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); +extern asmlinkage long sys_get_kernel_syms(struct kernel_sym *table); -asmlinkage int +asmlinkage long sys32_get_kernel_syms(struct kernel_sym32 *table) { int len, i; @@ -4194,19 +4270,19 @@ sys32_create_module(const char *name_user, size_t size) return -ENOSYS; } -asmlinkage int +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) { return -ENOSYS; } -asmlinkage int +asmlinkage long sys32_delete_module(const char *name_user) { return -ENOSYS; } -asmlinkage int +asmlinkage long sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret) { @@ -4218,7 +4294,7 @@ sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, return -ENOSYS; } -asmlinkage int +asmlinkage long sys32_get_kernel_syms(struct kernel_sym *table) { return -ENOSYS; @@ -4434,7 +4510,7 @@ nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) return err; } -extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp); +extern asmlinkage long sys_nfsservctl(int cmd, void *arg, void *resp); int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) @@ -4505,9 +4581,9 @@ done: return err; } -asmlinkage int sys_utimes(char *, struct timeval *); +asmlinkage long sys_utimes(char *, struct timeval *); -asmlinkage int +asmlinkage long sys32_utimes(char *filename, struct timeval32 *tvs) { char *kfilename; @@ -4535,7 +4611,7 @@ sys32_utimes(char *filename, struct timeval32 *tvs) } /* These are here just in case some old ia32 binary calls it. */ -asmlinkage int +asmlinkage long sys32_pause(void) { current->state = TASK_INTERRUPTIBLE; @@ -4544,19 +4620,19 @@ sys32_pause(void) } /* PCI config space poking. */ -extern asmlinkage int sys_pciconfig_read(unsigned long bus, +extern asmlinkage long sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, unsigned char *buf); -extern asmlinkage int sys_pciconfig_write(unsigned long bus, +extern asmlinkage long sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, unsigned char *buf); -asmlinkage int +asmlinkage long sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) { return sys_pciconfig_read((unsigned long) bus, @@ -4566,7 +4642,7 @@ sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) (unsigned char *)AA(ubuf)); } -asmlinkage int +asmlinkage long sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) { return sys_pciconfig_write((unsigned long) bus, @@ -4576,11 +4652,11 @@ sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) (unsigned char *)AA(ubuf)); } -extern asmlinkage int sys_prctl(int option, unsigned long arg2, +extern asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); -asmlinkage int +asmlinkage long sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) { return sys_prctl(option, @@ -4591,9 +4667,9 @@ sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) } -extern asmlinkage int sys_newuname(struct new_utsname * name); +extern asmlinkage long sys_newuname(struct new_utsname * name); -asmlinkage int +asmlinkage long sys32_newuname(struct new_utsname * name) { int ret = sys_newuname(name); @@ -4629,9 +4705,9 @@ sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count, } -extern asmlinkage int sys_personality(unsigned long); +extern asmlinkage long sys_personality(unsigned long); -asmlinkage int +asmlinkage long sys32_personality(unsigned long personality) { int ret; @@ -4648,7 +4724,7 @@ sys32_personality(unsigned long personality) extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count); -asmlinkage int +asmlinkage long sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count) { mm_segment_t old_fs = get_fs(); @@ -4685,7 +4761,7 @@ struct timex32 { extern int do_adjtimex(struct timex *); -asmlinkage int +asmlinkage long sys32_adjtimex(struct timex32 *utp) { struct timex txc; diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 225cbec5d..3fb62560d 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -1,11 +1,6 @@ # # Makefile for the linux kernel. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now in the main makefile... .S.s: $(CPP) $(AFLAGS) -o $*.s $< @@ -15,16 +10,19 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_ia64.o irq_sapic.o ivt.o \ - pal.o pci-dma.o process.o perfmon.o ptrace.o sal.o sal_stub.o semaphore.o setup.o \ +O_OBJS := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_ia64.o irq_sapic.o ivt.o \ + pal.o pci-dma.o process.o perfmon.o ptrace.o sal.o semaphore.o setup.o \ signal.o sys_ia64.o traps.o time.o unaligned.o unwind.o -#O_OBJS := fpreg.o -#OX_OBJS := ia64_ksyms.o +OX_OBJS := ia64_ksyms.o ifdef CONFIG_IA64_GENERIC O_OBJS += machvec.o endif +ifdef CONFIG_IA64_PALINFO +O_OBJS += palinfo.o +endif + ifdef CONFIG_PCI O_OBJS += pci.o endif @@ -37,6 +35,10 @@ ifdef CONFIG_IA64_MCA O_OBJS += mca.o mca_asm.o endif +ifdef CONFIG_IA64_BRL_EMU +O_OBJS += brl_emu.o +endif + clean:: include $(TOPDIR)/Rules.make diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 72e10a683..20521da36 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -89,16 +89,16 @@ acpi_iosapic(char *p) #ifdef CONFIG_IA64_DIG acpi_entry_iosapic_t *iosapic = (acpi_entry_iosapic_t *) p; unsigned int ver, v; - int l, pins; + int l, max_pin; ver = iosapic_version(iosapic->address); - pins = (ver >> 16) & 0xff; + max_pin = (ver >> 16) & 0xff; printk("IOSAPIC Version %x.%x: address 0x%lx IRQs 0x%x - 0x%x\n", (ver & 0xf0) >> 4, (ver & 0x0f), iosapic->address, - iosapic->irq_base, iosapic->irq_base + pins); + iosapic->irq_base, iosapic->irq_base + max_pin); - for (l = 0; l < pins; l++) { + for (l = 0; l <= max_pin; l++) { v = iosapic->irq_base + l; if (v < 16) v = isa_irq_to_vector(v); @@ -110,7 +110,7 @@ acpi_iosapic(char *p) iosapic_addr(v) = (unsigned long) ioremap(iosapic->address, 0); iosapic_baseirq(v) = iosapic->irq_base; } - iosapic_init(iosapic->address); + iosapic_init(iosapic->address, iosapic->irq_base); #endif } diff --git a/arch/ia64/kernel/brl_emu.c b/arch/ia64/kernel/brl_emu.c new file mode 100644 index 000000000..8948b7bb2 --- /dev/null +++ b/arch/ia64/kernel/brl_emu.c @@ -0,0 +1,220 @@ +/* + * Emulation of the "brl" instruction for IA64 processors that + * don't support it in hardware. + * Author: Stephan Zeisset, Intel Corp. <Stephan.Zeisset@intel.com> + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/uaccess.h> +#include <asm/processor.h> + +extern char ia64_set_b1, ia64_set_b2, ia64_set_b3, ia64_set_b4, ia64_set_b5; + +struct illegal_op_return { + unsigned long fkt, arg1, arg2, arg3; +}; + +/* + * The unimplemented bits of a virtual address must be set + * to the value of the most significant implemented bit. + * unimpl_va_mask includes all unimplemented bits and + * the most significant implemented bit, so the result + * of an and operation with the mask must be all 0's + * or all 1's for the address to be valid. + */ +#define unimplemented_virtual_address(va) ( \ + ((va) & my_cpu_data.unimpl_va_mask) != 0 && \ + ((va) & my_cpu_data.unimpl_va_mask) != my_cpu_data.unimpl_va_mask \ +) + +/* + * The unimplemented bits of a physical address must be 0. + * unimpl_pa_mask includes all unimplemented bits, so the result + * of an and operation with the mask must be all 0's for the + * address to be valid. + */ +#define unimplemented_physical_address(pa) ( \ + ((pa) & my_cpu_data.unimpl_pa_mask) != 0 \ +) + +/* + * Handle an illegal operation fault that was caused by an + * unimplemented "brl" instruction. + * If we are not successful (e.g because the illegal operation + * wasn't caused by a "brl" after all), we return -1. + * If we are successful, we return either 0 or the address + * of a "fixup" function for manipulating preserved register + * state. + */ + +struct illegal_op_return +ia64_emulate_brl (struct pt_regs *regs, unsigned long ar_ec) +{ + unsigned long bundle[2]; + unsigned long opcode, btype, qp, offset; + unsigned long next_ip; + struct siginfo siginfo; + struct illegal_op_return rv; + int tmp_taken, unimplemented_address; + + rv.fkt = (unsigned long) -1; + + /* + * Decode the instruction bundle. + */ + + if (copy_from_user(bundle, (void *) (regs->cr_iip), sizeof(bundle))) + return rv; + + next_ip = (unsigned long) regs->cr_iip + 16; + + /* "brl" must be in slot 2. */ + if (ia64_psr(regs)->ri != 1) return rv; + + /* Must be "mlx" template */ + if ((bundle[0] & 0x1e) != 0x4) return rv; + + opcode = (bundle[1] >> 60); + btype = ((bundle[1] >> 29) & 0x7); + qp = ((bundle[1] >> 23) & 0x3f); + offset = ((bundle[1] & 0x0800000000000000L) << 4) + | ((bundle[1] & 0x00fffff000000000L) >> 32) + | ((bundle[1] & 0x00000000007fffffL) << 40) + | ((bundle[0] & 0xffff000000000000L) >> 24); + + tmp_taken = regs->pr & (1L << qp); + + switch(opcode) { + + case 0xC: + /* + * Long Branch. + */ + if (btype != 0) return rv; + rv.fkt = 0; + if (!(tmp_taken)) { + /* + * Qualifying predicate is 0. + * Skip instruction. + */ + regs->cr_iip = next_ip; + ia64_psr(regs)->ri = 0; + return rv; + } + break; + + case 0xD: + /* + * Long Call. + */ + rv.fkt = 0; + if (!(tmp_taken)) { + /* + * Qualifying predicate is 0. + * Skip instruction. + */ + regs->cr_iip = next_ip; + ia64_psr(regs)->ri = 0; + return rv; + } + + /* + * BR[btype] = IP+16 + */ + switch(btype) { + case 0: + regs->b0 = next_ip; + break; + case 1: + rv.fkt = (unsigned long) &ia64_set_b1; + break; + case 2: + rv.fkt = (unsigned long) &ia64_set_b2; + break; + case 3: + rv.fkt = (unsigned long) &ia64_set_b3; + break; + case 4: + rv.fkt = (unsigned long) &ia64_set_b4; + break; + case 5: + rv.fkt = (unsigned long) &ia64_set_b5; + break; + case 6: + regs->b6 = next_ip; + break; + case 7: + regs->b7 = next_ip; + break; + } + rv.arg1 = next_ip; + + /* + * AR[PFS].pfm = CFM + * AR[PFS].pec = AR[EC] + * AR[PFS].ppl = PSR.cpl + */ + regs->ar_pfs = ((regs->cr_ifs & 0x3fffffffff) + | (ar_ec << 52) + | ((unsigned long) ia64_psr(regs)->cpl << 62)); + + /* + * CFM.sof -= CFM.sol + * CFM.sol = 0 + * CFM.sor = 0 + * CFM.rrb.gr = 0 + * CFM.rrb.fr = 0 + * CFM.rrb.pr = 0 + */ + regs->cr_ifs = ((regs->cr_ifs & 0xffffffc00000007f) + - ((regs->cr_ifs >> 7) & 0x7f)); + + break; + + default: + /* + * Unknown opcode. + */ + return rv; + + } + + regs->cr_iip += offset; + ia64_psr(regs)->ri = 0; + + if (ia64_psr(regs)->it == 0) + unimplemented_address = unimplemented_physical_address(regs->cr_iip); + else + unimplemented_address = unimplemented_virtual_address(regs->cr_iip); + + if (unimplemented_address) { + /* + * The target address contains unimplemented bits. + */ + printk("Woah! Unimplemented Instruction Address Trap!\n"); + siginfo.si_signo = SIGILL; + siginfo.si_errno = 0; + siginfo.si_code = ILL_BADIADDR; + force_sig_info(SIGILL, &siginfo, current); + } else if (ia64_psr(regs)->tb) { + /* + * Branch Tracing is enabled. + * Force a taken branch signal. + */ + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = 0; + siginfo.si_code = TRAP_BRANCH; + force_sig_info(SIGTRAP, &siginfo, current); + } else if (ia64_psr(regs)->ss) { + /* + * Single Step is enabled. + * Force a trace signal. + */ + siginfo.si_signo = SIGTRAP; + siginfo.si_errno = 0; + siginfo.si_code = TRAP_TRACE; + force_sig_info(SIGTRAP, &siginfo, current); + } + return rv; +} diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 0ce1db504..c4383b97f 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -5,15 +5,18 @@ * * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> - * Copyright (C) 1999 Hewlett-Packard Co. + * Copyright (C) 1999-2000 Hewlett-Packard Co. * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> - * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com> * * All EFI Runtime Services are not implemented yet as EFI only * supports physical mode addressing on SoftSDV. This is to be fixed * in a future version. --drummond 1999-07-20 * * Implemented EFI runtime services and virtual mode calls. --davidm + * + * Goutham Rao: <goutham.rao@intel.com> + * Skip non-WB memory and ignore empty memory ranges. */ #include <linux/kernel.h> #include <linux/init.h> @@ -22,6 +25,7 @@ #include <asm/efi.h> #include <asm/io.h> +#include <asm/pgtable.h> #include <asm/processor.h> #define EFI_DEBUG 0 @@ -172,6 +176,14 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg) continue; } + if (!(md->attribute & EFI_MEMORY_WB)) + continue; + if (md->num_pages == 0) { + printk("efi_memmap_walk: ignoring empty region at 0x%lx", + md->phys_addr); + continue; + } + curr.start = PAGE_OFFSET + md->phys_addr; curr.end = curr.start + (md->num_pages << 12); @@ -207,6 +219,61 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg) } } +/* + * Look for the PAL_CODE region reported by EFI and maps it using an + * ITR to enable safe PAL calls in virtual mode. See IA-64 Processor + * Abstraction Layer chapter 11 in ADAG + */ +static void +map_pal_code (void) +{ + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + u64 efi_desc_size; + int pal_code_count=0; + u64 mask, flags; + u64 vaddr; + + efi_map_start = __va(ia64_boot_param.efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param.efi_memmap_size; + efi_desc_size = ia64_boot_param.efi_memdesc_size; + + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + if (md->type != EFI_PAL_CODE) continue; + + if (++pal_code_count > 1) { + printk(KERN_ERR "Too many EFI Pal Code memory ranges, dropped @ %lx\n", + md->phys_addr); + continue; + } + mask = ~((1 << _PAGE_SIZE_4M)-1); /* XXX should be dynamic? */ + vaddr = PAGE_OFFSET + md->phys_addr; + + printk(__FUNCTION__": mapping PAL code [0x%lx-0x%lx) into [0x%lx-0x%lx)\n", + md->phys_addr, md->phys_addr + (md->num_pages << 12), + vaddr & mask, (vaddr & mask) + 4*1024*1024); + + /* + * Cannot write to CRx with PSR.ic=1 + */ + ia64_clear_ic(flags); + + /* + * ITR0/DTR0: used for kernel code/data + * ITR1/DTR1: used by HP simulator + * ITR2/DTR2: map PAL code + * ITR3/DTR3: used to map PAL calls buffer + */ + ia64_itr(0x1, 2, vaddr & mask, + pte_val(mk_pte_phys(md->phys_addr, + __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RX))), + _PAGE_SIZE_4M); + local_irq_restore(flags); + ia64_srlz_i (); + } +} + void __init efi_init (void) { @@ -291,6 +358,8 @@ efi_init (void) } } #endif + + map_pal_code(); } void diff --git a/arch/ia64/kernel/efi_stub.S b/arch/ia64/kernel/efi_stub.S index 4e6f1fc63..2bb45c790 100644 --- a/arch/ia64/kernel/efi_stub.S +++ b/arch/ia64/kernel/efi_stub.S @@ -1,7 +1,8 @@ /* * EFI call stub. * - * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger <davidm@hpl.hp.com> * * This stub allows us to make EFI calls in physical mode with interrupts * turned off. We need this because we can't call SetVirtualMap() until @@ -30,6 +31,7 @@ (IA64_PSR_BN) #include <asm/processor.h> +#include <asm/asmmacro.h> .text .psr abi64 @@ -39,53 +41,6 @@ .text /* - * Switch execution mode from virtual to physical or vice versa. - * - * Inputs: - * r16 = new psr to establish - */ - .proc switch_mode -switch_mode: - { - alloc r2=ar.pfs,0,0,0,0 - rsm psr.i | psr.ic // disable interrupts and interrupt collection - mov r15=ip - } - ;; - { - flushrs // must be first insn in group - srlz.i - shr.u r19=r15,61 // r19 <- top 3 bits of current IP - } - ;; - mov cr.ipsr=r16 // set new PSR - add r3=1f-switch_mode,r15 - xor r15=0x7,r19 // flip the region bits - - mov r17=ar.bsp - mov r14=rp // get return address into a general register - - // switch RSE backing store: - ;; - dep r17=r15,r17,61,3 // make ar.bsp physical or virtual - mov r18=ar.rnat // save ar.rnat - ;; - mov ar.bspstore=r17 // this steps on ar.rnat - dep r3=r15,r3,61,3 // make rfi return address physical or virtual - ;; - mov cr.iip=r3 - mov cr.ifs=r0 - dep sp=r15,sp,61,3 // make stack pointer physical or virtual - ;; - mov ar.rnat=r18 // restore ar.rnat - dep r14=r15,r14,61,3 // make function return address physical or virtual - rfi // must be last insn in group - ;; -1: mov rp=r14 - br.ret.sptk.few rp - .endp switch_mode - -/* * Inputs: * in0 = address of function descriptor of EFI routine to call * in1..in7 = arguments to routine @@ -94,13 +49,12 @@ switch_mode: * r8 = EFI_STATUS returned by called function */ - .global efi_call_phys - .proc efi_call_phys -efi_call_phys: - - alloc loc0=ar.pfs,8,5,7,0 +GLOBAL_ENTRY(efi_call_phys) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,5,7,0 ld8 r2=[in0],8 // load EFI function's entry point - mov loc1=rp + mov loc0=rp + UNW(.body) ;; mov loc2=gp // save global pointer mov loc4=ar.rsc // save RSE configuration @@ -121,7 +75,7 @@ efi_call_phys: ;; andcm r16=loc3,r16 // get psr with IT, DT, and RT bits cleared mov out3=in4 - br.call.sptk.few rp=switch_mode + br.call.sptk.few rp=ia64_switch_mode .ret0: mov out4=in5 mov out5=in6 @@ -130,12 +84,11 @@ efi_call_phys: .ret1: mov ar.rsc=r0 // put RSE in enforced lazy, LE mode mov r16=loc3 - br.call.sptk.few rp=switch_mode // return to virtual mode + br.call.sptk.few rp=ia64_switch_mode // return to virtual mode .ret2: mov ar.rsc=loc4 // restore RSE configuration - mov ar.pfs=loc0 - mov rp=loc1 + mov ar.pfs=loc1 + mov rp=loc0 mov gp=loc2 br.ret.sptk.few rp - - .endp efi_call_phys +END(efi_call_phys) diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 755e3a0c1..e56e3fc8e 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -13,8 +13,6 @@ /* * Global (preserved) predicate usage on syscall entry/exit path: * - * - * pEOI: See entry.h. * pKern: See entry.h. * pSys: See entry.h. * pNonSys: !pSys @@ -30,6 +28,7 @@ #include <asm/offsets.h> #include <asm/processor.h> #include <asm/unistd.h> +#include <asm/asmmacro.h> #include "entry.h" @@ -42,11 +41,11 @@ * execve() is special because in case of success, we need to * setup a null register window frame. */ - .align 16 - .proc ia64_execve -ia64_execve: - alloc loc0=ar.pfs,3,2,4,0 - mov loc1=rp +ENTRY(ia64_execve) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(3)) + alloc loc1=ar.pfs,3,2,4,0 + mov loc0=rp + UNW(.body) mov out0=in0 // filename ;; // stop bit between alloc and call mov out1=in1 // argv @@ -54,25 +53,22 @@ ia64_execve: add out3=16,sp // regs br.call.sptk.few rp=sys_execve .ret0: cmp4.ge p6,p0=r8,r0 - mov ar.pfs=loc0 // restore ar.pfs + mov ar.pfs=loc1 // restore ar.pfs ;; (p6) mov ar.pfs=r0 // clear ar.pfs in case of success sxt4 r8=r8 // return 64-bit result - mov rp=loc1 + mov rp=loc0 br.ret.sptk.few rp - .endp ia64_execve +END(ia64_execve) - .align 16 - .global sys_clone - .proc sys_clone -sys_clone: +GLOBAL_ENTRY(sys_clone) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)) alloc r16=ar.pfs,2,2,3,0;; - movl r28=1f - mov loc1=rp - br.cond.sptk.many save_switch_stack -1: - mov loc0=r16 // save ar.pfs across do_fork + mov loc0=rp + DO_SAVE_SWITCH_STACK + mov loc1=r16 // save ar.pfs across do_fork + UNW(.body) adds out2=IA64_SWITCH_STACK_SIZE+16,sp adds r2=IA64_SWITCH_STACK_SIZE+IA64_PT_REGS_R12_OFFSET+16,sp cmp.eq p8,p9=in1,r0 // usp == 0? @@ -82,24 +78,22 @@ sys_clone: (p9) mov out1=in1 br.call.sptk.few rp=do_fork .ret1: - mov ar.pfs=loc0 + mov ar.pfs=loc1 + UNW(.restore sp) adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack - mov rp=loc1 + mov rp=loc0 ;; br.ret.sptk.many rp - .endp sys_clone +END(sys_clone) /* - * prev_task <- switch_to(struct task_struct *next) + * prev_task <- ia64_switch_to(struct task_struct *next) */ - .align 16 - .global ia64_switch_to - .proc ia64_switch_to -ia64_switch_to: +GLOBAL_ENTRY(ia64_switch_to) + UNW(.prologue) alloc r16=ar.pfs,1,0,0,0 - movl r28=1f - br.cond.sptk.many save_switch_stack -1: + DO_SAVE_SWITCH_STACK + UNW(.body) // disable interrupts to ensure atomicity for next few instructions: mov r17=psr // M-unit ;; @@ -123,66 +117,60 @@ ia64_switch_to: mov psr.l=r17 ;; srlz.d - - movl r28=1f - br.cond.sptk.many load_switch_stack -1: + DO_LOAD_SWITCH_STACK( ) br.ret.sptk.few rp - .endp ia64_switch_to +END(ia64_switch_to) +#ifndef CONFIG_IA64_NEW_UNWIND /* * Like save_switch_stack, but also save the stack frame that is active * at the time this function is called. */ - .align 16 - .proc save_switch_stack_with_current_frame -save_switch_stack_with_current_frame: -1: { - alloc r16=ar.pfs,0,0,0,0 // pass ar.pfs to save_switch_stack - mov r28=ip - } - ;; - adds r28=1f-1b,r28 - br.cond.sptk.many save_switch_stack -1: br.ret.sptk.few rp - .endp save_switch_stack_with_current_frame +ENTRY(save_switch_stack_with_current_frame) + UNW(.prologue) + alloc r16=ar.pfs,0,0,0,0 // pass ar.pfs to save_switch_stack + DO_SAVE_SWITCH_STACK + br.ret.sptk.few rp +END(save_switch_stack_with_current_frame) +#endif /* !CONFIG_IA64_NEW_UNWIND */ + /* * Note that interrupts are enabled during save_switch_stack and * load_switch_stack. This means that we may get an interrupt with * "sp" pointing to the new kernel stack while ar.bspstore is still * pointing to the old kernel backing store area. Since ar.rsc, * ar.rnat, ar.bsp, and ar.bspstore are all preserved by interrupts, - * this is not a problem. + * this is not a problem. Also, we don't need to specify unwind + * information for preserved registers that are not modified in + * save_switch_stack as the right unwind information is already + * specified at the call-site of save_switch_stack. */ /* * save_switch_stack: * - r16 holds ar.pfs - * - r28 holds address to return to + * - b7 holds address to return to * - rp (b0) holds return address to save */ - .align 16 - .global save_switch_stack - .proc save_switch_stack -save_switch_stack: +GLOBAL_ENTRY(save_switch_stack) + UNW(.prologue) + UNW(.altrp b7) flushrs // flush dirty regs to backing store (must be first in insn group) mov r17=ar.unat // preserve caller's - adds r2=-IA64_SWITCH_STACK_SIZE+16,sp // r2 = &sw->caller_unat + adds r2=16,sp // r2 = &sw->caller_unat ;; mov r18=ar.fpsr // preserve fpsr mov ar.rsc=r0 // put RSE in mode: enforced lazy, little endian, pl 0 ;; mov r19=ar.rnat - adds r3=-IA64_SWITCH_STACK_SIZE+24,sp // r3 = &sw->ar_fpsr - - // Note: the instruction ordering is important here: we can't - // store anything to the switch stack before sp is updated - // as otherwise an interrupt might overwrite the memory! - adds sp=-IA64_SWITCH_STACK_SIZE,sp + adds r3=24,sp // r3 = &sw->ar_fpsr ;; + .savesp ar.unat,SW(CALLER_UNAT) st8 [r2]=r17,16 + .savesp ar.fpsr,SW(AR_FPSR) st8 [r3]=r18,24 ;; + UNW(.body) stf.spill [r2]=f2,32 stf.spill [r3]=f3,32 mov r21=b0 @@ -259,16 +247,17 @@ save_switch_stack: st8 [r3]=r21 // save predicate registers mov ar.rsc=3 // put RSE back into eager mode, pl 0 br.cond.sptk.few b7 - .endp save_switch_stack +END(save_switch_stack) /* * load_switch_stack: - * - r28 holds address to return to + * - b7 holds address to return to */ - .align 16 - .proc load_switch_stack -load_switch_stack: +ENTRY(load_switch_stack) + UNW(.prologue) + UNW(.altrp b7) invala // invalidate ALAT + UNW(.body) adds r2=IA64_SWITCH_STACK_B0_OFFSET+16,sp // get pointer to switch_stack.b0 mov ar.rsc=r0 // put RSE into enforced lazy mode adds r3=IA64_SWITCH_STACK_B0_OFFSET+24,sp // get pointer to switch_stack.b1 @@ -353,21 +342,16 @@ load_switch_stack: ;; ld8.fill r4=[r2],16 ld8.fill r5=[r3],16 - mov b7=r28 ;; ld8.fill r6=[r2],16 ld8.fill r7=[r3],16 mov ar.unat=r18 // restore caller's unat mov ar.fpsr=r19 // restore fpsr mov ar.rsc=3 // put RSE back into eager mode, pl 0 - adds sp=IA64_SWITCH_STACK_SIZE,sp // pop switch_stack br.cond.sptk.few b7 - .endp load_switch_stack +END(load_switch_stack) - .align 16 - .global __ia64_syscall - .proc __ia64_syscall -__ia64_syscall: +GLOBAL_ENTRY(__ia64_syscall) .regstk 6,0,0,0 mov r15=in5 // put syscall number in place break __BREAK_SYSCALL @@ -377,30 +361,42 @@ __ia64_syscall: (p6) st4 [r2]=r8 (p6) mov r8=-1 br.ret.sptk.few rp - .endp __ia64_syscall +END(__ia64_syscall) // // We invoke syscall_trace through this intermediate function to // ensure that the syscall input arguments are not clobbered. We // also use it to preserve b6, which contains the syscall entry point. // - .align 16 - .global invoke_syscall_trace - .proc invoke_syscall_trace -invoke_syscall_trace: - alloc loc0=ar.pfs,8,3,0,0 +GLOBAL_ENTRY(invoke_syscall_trace) +#ifdef CONFIG_IA64_NEW_UNWIND + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,3,0,0 + mov loc0=rp + UNW(.body) + mov loc2=b6 + ;; + br.call.sptk.few rp=syscall_trace +.ret3: mov rp=loc0 + mov ar.pfs=loc1 + mov b6=loc2 + br.ret.sptk.few rp +#else /* !CONFIG_IA64_NEW_SYSCALL */ + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,3,0,0 ;; // WAW on CFM at the br.call - mov loc1=rp + mov loc0=rp br.call.sptk.many rp=save_switch_stack_with_current_frame // must preserve b6!! .ret2: mov loc2=b6 br.call.sptk.few rp=syscall_trace .ret3: adds sp=IA64_SWITCH_STACK_SIZE,sp // drop switch_stack frame - mov rp=loc1 - mov ar.pfs=loc0 + mov rp=loc0 + mov ar.pfs=loc1 mov b6=loc2 ;; br.ret.sptk.few rp - .endp invoke_syscall_trace +#endif /* !CONFIG_IA64_NEW_SYSCALL */ +END(invoke_syscall_trace) // // Invoke a system call, but do some tracing before and after the call. @@ -414,19 +410,19 @@ invoke_syscall_trace: // .global ia64_trace_syscall .global ia64_strace_leave_kernel - .global ia64_strace_clear_r8 - .proc ia64_strace_clear_r8 -ia64_strace_clear_r8: // this is where we return after cloning when PF_TRACESYS is on +GLOBAL_ENTRY(ia64_strace_clear_r8) + // this is where we return after cloning when PF_TRACESYS is on + PT_REGS_UNWIND_INFO(0) # ifdef CONFIG_SMP br.call.sptk.few rp=invoke_schedule_tail # endif mov r8=0 br strace_check_retval - .endp ia64_strace_clear_r8 +END(ia64_strace_clear_r8) - .proc ia64_trace_syscall -ia64_trace_syscall: +ENTRY(ia64_trace_syscall) + PT_REGS_UNWIND_INFO(0) br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch syscall args .ret4: br.call.sptk.few rp=b6 // do the syscall strace_check_retval: @@ -454,7 +450,7 @@ strace_error: (p6) mov r10=-1 (p6) mov r8=r9 br.cond.sptk.few strace_save_retval - .endp ia64_trace_syscall +END(ia64_trace_syscall) /* * A couple of convenience macros to help implement/understand the state @@ -472,12 +468,8 @@ strace_error: #define rKRBS r22 #define rB6 r21 - .align 16 - .global ia64_ret_from_syscall - .global ia64_ret_from_syscall_clear_r8 - .global ia64_leave_kernel - .proc ia64_ret_from_syscall -ia64_ret_from_syscall_clear_r8: +GLOBAL_ENTRY(ia64_ret_from_syscall_clear_r8) + PT_REGS_UNWIND_INFO(0) #ifdef CONFIG_SMP // In SMP mode, we need to call schedule_tail to complete the scheduling process. // Called by ia64_switch_to after do_fork()->copy_thread(). r8 contains the @@ -487,7 +479,10 @@ ia64_ret_from_syscall_clear_r8: #endif mov r8=0 ;; // added stop bits to prevent r8 dependency -ia64_ret_from_syscall: +END(ia64_ret_from_syscall_clear_r8) + // fall through +GLOBAL_ENTRY(ia64_ret_from_syscall) + PT_REGS_UNWIND_INFO(0) cmp.ge p6,p7=r8,r0 // syscall executed successfully? adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8 adds r3=IA64_PT_REGS_R8_OFFSET+32,sp // r3 = &pt_regs.r10 @@ -497,19 +492,21 @@ ia64_ret_from_syscall: .mem.offset 8,0 (p6) st8.spill [r3]=r0 // clear error indication in slot for r10 and set unat bit (p7) br.cond.spnt.few handle_syscall_error // handle potential syscall failure - -ia64_leave_kernel: +END(ia64_ret_from_syscall) + // fall through +GLOBAL_ENTRY(ia64_leave_kernel) // check & deliver software interrupts: + PT_REGS_UNWIND_INFO(0) #ifdef CONFIG_SMP - adds r2=IA64_TASK_PROCESSOR_OFFSET,r13 - movl r3=softirq_state + adds r2=IA64_TASK_PROCESSOR_OFFSET,r13 + movl r3=softirq_state ;; - ld4 r2=[r2] + ld4 r2=[r2] ;; - shl r2=r2,SMP_LOG_CACHE_BYTES // can't use shladd here... + shl r2=r2,SMP_LOG_CACHE_BYTES // can't use shladd here... ;; - add r3=r2,r3 + add r3=r2,r3 #else movl r3=softirq_state #endif @@ -538,32 +535,28 @@ back_from_resched: ld4 r14=[r14] mov rp=r3 // arrange for schedule() to return to back_from_resched ;; - /* - * If pEOI is set, we need to write the cr.eoi now and then - * clear pEOI because both invoke_schedule() and - * handle_signal_delivery() may call the scheduler. Since - * we're returning to user-level, we get at most one nested - * interrupt of the same priority level, which doesn't tax the - * kernel stack too much. - */ -(pEOI) mov cr.eoi=r0 cmp.ne p6,p0=r2,r0 cmp.ne p2,p0=r14,r0 // NOTE: pKern is an alias for p2!! -(pEOI) cmp.ne pEOI,p0=r0,r0 // clear pEOI before calling schedule() srlz.d (p6) br.call.spnt.many b6=invoke_schedule // ignore return value 2: // check & deliver pending signals: (p2) br.call.spnt.few rp=handle_signal_delivery -#if defined(CONFIG_SMP) || defined(CONFIG_IA64_SOFTSDV_HACKS) +#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS) // Check for lost ticks + rsm psr.i mov r2 = ar.itc + movl r14 = 1000 // latency tolerance mov r3 = cr.itm ;; sub r2 = r2, r3 ;; + sub r2 = r2, r14 + ;; cmp.ge p6,p7 = r2, r0 (p6) br.call.spnt.few rp=invoke_ia64_reset_itm + ;; + ssm psr.i #endif restore_all: @@ -692,18 +685,6 @@ restore_all: ;; add r18=r16,r18 // adjust the loadrs value ;; -#ifdef CONFIG_IA64_SOFTSDV_HACKS - // Reset ITM if we've missed a timer tick. Workaround for SoftSDV bug - mov r16 = r2 - mov r2 = ar.itc - mov r17 = cr.itm - ;; - cmp.gt p6,p7 = r2, r17 -(p6) addl r17 = 100, r2 - ;; - mov cr.itm = r17 - mov r2 = r16 -#endif dont_preserve_current_frame: alloc r16=ar.pfs,0,0,0,0 // drop the current call frame (noop for syscalls) ;; @@ -724,14 +705,14 @@ skip_rbs_switch: mov ar.rsc=rARRSC mov ar.unat=rARUNAT mov cr.ifs=rCRIFS // restore cr.ifs only if not a (synchronous) syscall -(pEOI) mov cr.eoi=r0 mov pr=rARPR,-1 mov cr.iip=rCRIIP mov cr.ipsr=rCRIPSR ;; rfi;; // must be last instruction in an insn group +END(ia64_leave_kernel) -handle_syscall_error: +ENTRY(handle_syscall_error) /* * Some system calls (e.g., ptrace, mmap) can return arbitrary * values which could lead us to mistake a negative return @@ -740,6 +721,7 @@ handle_syscall_error: * If pt_regs.r8 is zero, we assume that the call completed * successfully. */ + PT_REGS_UNWIND_INFO(0) ld8 r3=[r2] // load pt_regs.r8 sub r9=0,r8 // negate return value to get errno ;; @@ -753,205 +735,283 @@ handle_syscall_error: .mem.offset 0,0; st8.spill [r2]=r9 // store errno in pt_regs.r8 and set unat bit .mem.offset 8,0; st8.spill [r3]=r10 // store error indication in pt_regs.r10 and set unat bit br.cond.sptk.many ia64_leave_kernel - .endp handle_syscall_error +END(handle_syscall_error) #ifdef CONFIG_SMP /* * Invoke schedule_tail(task) while preserving in0-in7, which may be needed * in case a system call gets restarted. */ - .proc invoke_schedule_tail -invoke_schedule_tail: - alloc loc0=ar.pfs,8,2,1,0 - mov loc1=rp +ENTRY(invoke_schedule_tail) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,2,1,0 + mov loc0=rp mov out0=r8 // Address of previous task ;; br.call.sptk.few rp=schedule_tail .ret8: - mov ar.pfs=loc0 - mov rp=loc1 + mov ar.pfs=loc1 + mov rp=loc0 br.ret.sptk.many rp - .endp invoke_schedule_tail +END(invoke_schedule_tail) + +#endif /* CONFIG_SMP */ + +#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS) - .proc invoke_ia64_reset_itm -invoke_ia64_reset_itm: - alloc loc0=ar.pfs,8,2,0,0 - mov loc1=rp +ENTRY(invoke_ia64_reset_itm) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,2,0,0 + mov loc0=rp ;; + UNW(.body) br.call.sptk.many rp=ia64_reset_itm ;; - mov ar.pfs=loc0 - mov rp=loc1 + mov ar.pfs=loc1 + mov rp=loc0 br.ret.sptk.many rp - .endp invoke_ia64_reset_itm +END(invoke_ia64_reset_itm) -#endif /* CONFIG_SMP */ +#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC || CONFIG_IA64_SOFTSDV_HACKS */ /* * Invoke do_softirq() while preserving in0-in7, which may be needed * in case a system call gets restarted. */ - .proc invoke_do_softirq -invoke_do_softirq: - alloc loc0=ar.pfs,8,2,0,0 - mov loc1=rp -(pEOI) mov cr.eoi=r0 +ENTRY(invoke_do_softirq) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,2,0,0 + mov loc0=rp ;; -(pEOI) cmp.ne pEOI,p0=r0,r0 + UNW(.body) br.call.sptk.few rp=do_softirq .ret9: - mov ar.pfs=loc0 - mov rp=loc1 + mov ar.pfs=loc1 + mov rp=loc0 br.ret.sptk.many rp - .endp invoke_do_softirq +END(invoke_do_softirq) /* * Invoke schedule() while preserving in0-in7, which may be needed * in case a system call gets restarted. */ - .proc invoke_schedule -invoke_schedule: - alloc loc0=ar.pfs,8,2,0,0 - mov loc1=rp +ENTRY(invoke_schedule) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)) + alloc loc1=ar.pfs,8,2,0,0 + mov loc0=rp ;; + UNW(.body) br.call.sptk.few rp=schedule .ret10: - mov ar.pfs=loc0 - mov rp=loc1 + mov ar.pfs=loc1 + mov rp=loc0 br.ret.sptk.many rp - .endp invoke_schedule +END(invoke_schedule) // // Setup stack and call ia64_do_signal. Note that pSys and pNonSys need to // be set up by the caller. We declare 8 input registers so the system call // args get preserved, in case we need to restart a system call. // - .align 16 - .proc handle_signal_delivery -handle_signal_delivery: - alloc loc0=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! +ENTRY(handle_signal_delivery) +#ifdef CONFIG_IA64_NEW_UNWIND + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) + alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! mov r9=ar.unat - - // If the process is being ptraced, the signal may not actually be delivered to - // the process. Instead, SIGCHLD will be sent to the parent. We need to - // setup a switch_stack so ptrace can inspect the processes state if necessary. - adds r2=IA64_TASK_FLAGS_OFFSET,r13 - ;; - ld8 r2=[r2] + mov loc0=rp // save return address mov out0=0 // there is no "oldset" - adds out1=16,sp // out1=&pt_regs - ;; + adds out1=0,sp // out1=&sigscratch (pSys) mov out2=1 // out2==1 => we're in a syscall - tbit.nz p16,p17=r2,PF_PTRACED_BIT -(p16) br.cond.spnt.many setup_switch_stack ;; -back_from_setup_switch_stack: (pNonSys) mov out2=0 // out2==0 => not a syscall - adds r3=-IA64_SWITCH_STACK_SIZE+IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp -(p17) adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for (dummy) switch_stack - ;; -(p17) st8 [r3]=r9 // save ar.unat in sw->caller_unat - mov loc1=rp // save return address + .fframe 16 + .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!) + st8 [sp]=r9,-16 // allocate space for ar.unat and save it + .body br.call.sptk.few rp=ia64_do_signal .ret11: - adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp + .restore sp + adds sp=16,sp // pop scratch stack space ;; - ld8 r9=[r3] // load new unat from sw->caller_unat - mov rp=loc1 + ld8 r9=[sp] // load new unat from sw->caller_unat + mov rp=loc0 ;; -(p17) adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch_stack -(p17) mov ar.unat=r9 -(p17) mov ar.pfs=loc0 -(p17) br.ret.sptk.many rp - - // restore the switch stack (ptrace may have modified it): - movl r28=1f - br.cond.sptk.many load_switch_stack -1: br.ret.sptk.many rp - // NOT REACHED - -setup_switch_stack: - movl r28=back_from_setup_switch_stack - mov r16=loc0 - br.cond.sptk.many save_switch_stack - // NOT REACHED - - .endp handle_signal_delivery + mov ar.unat=r9 + mov ar.pfs=loc1 + br.ret.sptk.many rp +#else /* !CONFIG_IA64_NEW_UNWIND */ + .prologue + alloc r16=ar.pfs,8,0,3,0 // preserve all eight input regs in case of syscall restart! + DO_SAVE_SWITCH_STACK + UNW(.body) - .align 16 - .proc sys_rt_sigsuspend - .global sys_rt_sigsuspend -sys_rt_sigsuspend: - alloc loc0=ar.pfs,2,2,3,0 + mov out0=0 // there is no "oldset" + adds out1=16,sp // out1=&sigscratch + .pred.rel.mutex pSys, pNonSys +(pSys) mov out2=1 // out2==1 => we're in a syscall +(pNonSys) mov out2=0 // out2==0 => not a syscall + br.call.sptk.few rp=ia64_do_signal +.ret11: + // restore the switch stack (ptrace may have modified it) + DO_LOAD_SWITCH_STACK( ) + br.ret.sptk.many rp +#endif /* !CONFIG_IA64_NEW_UNWIND */ +END(handle_signal_delivery) - // If the process is being ptraced, the signal may not actually be delivered to - // the process. Instead, SIGCHLD will be sent to the parent. We need to - // setup a switch_stack so ptrace can inspect the processes state if necessary. - // Also, the process might not ptraced until stopped in sigsuspend, so this - // isn't something that we can do conditionally based upon the value of - // PF_PTRACED_BIT. +GLOBAL_ENTRY(sys_rt_sigsuspend) +#ifdef CONFIG_IA64_NEW_UNWIND + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) + alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! + mov r9=ar.unat + mov loc0=rp // save return address mov out0=in0 // mask mov out1=in1 // sigsetsize + adds out2=0,sp // out2=&sigscratch ;; - adds out2=16,sp // out1=&pt_regs - movl r28=back_from_sigsuspend_setup_switch_stack - mov r16=loc0 - br.cond.sptk.many save_switch_stack - ;; -back_from_sigsuspend_setup_switch_stack: - mov loc1=rp // save return address - br.call.sptk.many rp=ia64_rt_sigsuspend + .fframe 16 + .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!) + st8 [sp]=r9,-16 // allocate space for ar.unat and save it + .body + br.call.sptk.few rp=ia64_rt_sigsuspend .ret12: - adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp + .restore sp + adds sp=16,sp // pop scratch stack space ;; - ld8 r9=[r3] // load new unat from sw->caller_unat - mov rp=loc1 + ld8 r9=[sp] // load new unat from sw->caller_unat + mov rp=loc0 ;; + mov ar.unat=r9 + mov ar.pfs=loc1 + br.ret.sptk.many rp +#else /* !CONFIG_IA64_NEW_UNWIND */ + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)) + alloc r16=ar.pfs,2,0,3,0 + DO_SAVE_SWITCH_STACK + UNW(.body) - // restore the switch stack (ptrace may have modified it): - movl r28=1f - br.cond.sptk.many load_switch_stack -1: br.ret.sptk.many rp - // NOT REACHED - .endp sys_rt_sigsuspend + mov out0=in0 // mask + mov out1=in1 // sigsetsize + adds out2=16,sp // out1=&sigscratch + br.call.sptk.many rp=ia64_rt_sigsuspend +.ret12: + // restore the switch stack (ptrace may have modified it) + DO_LOAD_SWITCH_STACK( ) + br.ret.sptk.many rp +#endif /* !CONFIG_IA64_NEW_UNWIND */ +END(sys_rt_sigsuspend) - .align 16 - .proc sys_rt_sigreturn -sys_rt_sigreturn: +ENTRY(sys_rt_sigreturn) +#ifdef CONFIG_IA64_NEW_UNWIND .regstk 0,0,3,0 // inherited from gate.s:invoke_sighandler() - adds out0=16,sp // out0 = &pt_regs - adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for unat and padding + PT_REGS_UNWIND_INFO(0) + .prologue + PT_REGS_SAVES(16) + adds sp=-16,sp + .body + cmp.eq pNonSys,p0=r0,r0 // sigreturn isn't a normal syscall... + ;; + adds out0=16,sp // out0 = &sigscratch + br.call.sptk.few rp=ia64_rt_sigreturn +.ret13: + adds sp=16,sp // doesn't drop pt_regs, so don't mark it as restoring sp! + PT_REGS_UNWIND_INFO(0) // instead, create a new body section with the smaller frame ;; + ld8 r9=[sp] // load new ar.unat + mov b7=r8 + ;; + mov ar.unat=r9 + br b7 +#else /* !CONFIG_IA64_NEW_UNWIND */ + .regstk 0,0,3,0 // inherited from gate.s:invoke_sighandler() + PT_REGS_UNWIND_INFO(0) + UNW(.prologue) + UNW(.fframe IA64_PT_REGS_SIZE+IA64_SWITCH_STACK_SIZE) + UNW(.spillsp rp, PT(CR_IIP)+IA64_SWITCH_STACK_SIZE) + UNW(.spillsp ar.pfs, PT(CR_IFS)+IA64_SWITCH_STACK_SIZE) + UNW(.spillsp ar.unat, PT(AR_UNAT)+IA64_SWITCH_STACK_SIZE) + UNW(.spillsp pr, PT(PR)+IA64_SWITCH_STACK_SIZE) + adds sp=-IA64_SWITCH_STACK_SIZE,sp cmp.eq pNonSys,p0=r0,r0 // sigreturn isn't a normal syscall... + ;; + UNW(.body) + + adds out0=16,sp // out0 = &sigscratch br.call.sptk.few rp=ia64_rt_sigreturn .ret13: adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp ;; ld8 r9=[r3] // load new ar.unat - mov rp=r8 + mov b7=r8 ;; + PT_REGS_UNWIND_INFO(0) adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch-stack frame mov ar.unat=r9 - br rp - .endp sys_rt_sigreturn + br b7 +#endif /* !CONFIG_IA64_NEW_UNWIND */ +END(sys_rt_sigreturn) - .align 16 - .global ia64_prepare_handle_unaligned - .proc ia64_prepare_handle_unaligned -ia64_prepare_handle_unaligned: - movl r28=1f +GLOBAL_ENTRY(ia64_prepare_handle_unaligned) // // r16 = fake ar.pfs, we simply need to make sure // privilege is still 0 // + PT_REGS_UNWIND_INFO(0) mov r16=r0 - br.cond.sptk.few save_switch_stack -1: br.call.sptk.few rp=ia64_handle_unaligned // stack frame setup in ivt + DO_SAVE_SWITCH_STACK + br.call.sptk.few rp=ia64_handle_unaligned // stack frame setup in ivt .ret14: - movl r28=2f - br.cond.sptk.many load_switch_stack -2: br.cond.sptk.many rp // goes to ia64_leave_kernel - .endp ia64_prepare_handle_unaligned + DO_LOAD_SWITCH_STACK(PT_REGS_UNWIND_INFO(0)) + br.cond.sptk.many rp // goes to ia64_leave_kernel +END(ia64_prepare_handle_unaligned) + +#ifdef CONFIG_IA64_NEW_UNWIND + + // + // unw_init_running(void (*callback)(info, arg), void *arg) + // +# define EXTRA_FRAME_SIZE ((UNW_FRAME_INFO_SIZE+15)&~15) + +GLOBAL_ENTRY(unw_init_running) + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) + alloc loc1=ar.pfs,2,3,3,0 + ;; + ld8 loc2=[in0],8 + mov loc0=rp + mov r16=loc1 + DO_SAVE_SWITCH_STACK + .body + + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) + .fframe IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE + SWITCH_STACK_SAVES(EXTRA_FRAME_SIZE) + adds sp=-EXTRA_FRAME_SIZE,sp + .body + ;; + adds out0=16,sp // &info + mov out1=r13 // current + adds out2=16+EXTRA_FRAME_SIZE,sp // &switch_stack + br.call.sptk.few rp=unw_init_frame_info +1: adds out0=16,sp // &info + mov b6=loc2 + mov loc2=gp // save gp across indirect function call + ;; + ld8 gp=[in0] + mov out1=in1 // arg + br.call.sptk.few rp=b6 // invoke the callback function +1: mov gp=loc2 // restore gp + + // For now, we don't allow changing registers from within + // unw_init_running; if we ever want to allow that, we'd + // have to do a load_switch_stack here: + .restore sp + adds sp=IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE,sp + + mov ar.pfs=loc1 + mov rp=loc0 + br.ret.sptk.many rp +END(unw_init_running) + +#endif .rodata .align 8 @@ -1053,9 +1113,9 @@ sys_call_table: data8 sys_syslog data8 sys_setitimer data8 sys_getitimer - data8 sys_newstat // 1120 - data8 sys_newlstat - data8 sys_newfstat + data8 ia64_oldstat // 1120 + data8 ia64_oldlstat + data8 ia64_oldfstat data8 sys_vhangup data8 sys_lchown data8 sys_vm86 // 1125 @@ -1065,7 +1125,7 @@ sys_call_table: data8 sys_setdomainname data8 sys_newuname // 1130 data8 sys_adjtimex - data8 sys_create_module + data8 ia64_create_module data8 sys_init_module data8 sys_delete_module data8 sys_get_kernel_syms // 1135 @@ -1143,9 +1203,9 @@ sys_call_table: data8 sys_pivot_root data8 sys_mincore data8 sys_madvise - data8 ia64_ni_syscall // 1210 - data8 ia64_ni_syscall - data8 ia64_ni_syscall + data8 sys_newstat // 1210 + data8 sys_newlstat + data8 sys_newfstat data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1215 @@ -1212,4 +1272,3 @@ sys_call_table: data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall - diff --git a/arch/ia64/kernel/entry.h b/arch/ia64/kernel/entry.h index ecef44f60..41307f1b0 100644 --- a/arch/ia64/kernel/entry.h +++ b/arch/ia64/kernel/entry.h @@ -2,7 +2,64 @@ * Preserved registers that are shared between code in ivt.S and entry.S. Be * careful not to step on these! */ -#define pEOI p1 /* should leave_kernel write EOI? */ #define pKern p2 /* will leave_kernel return to kernel-mode? */ #define pSys p4 /* are we processing a (synchronous) system call? */ #define pNonSys p5 /* complement of pSys */ + +#define PT(f) (IA64_PT_REGS_##f##_OFFSET + 16) +#define SW(f) (IA64_SWITCH_STACK_##f##_OFFSET + 16) + +#define PT_REGS_SAVES(off) \ + UNW(.unwabi @svr4, 'i'); \ + UNW(.fframe IA64_PT_REGS_SIZE+16+(off)); \ + UNW(.spillsp rp, PT(CR_IIP)+(off)); \ + UNW(.spillsp ar.pfs, PT(CR_IFS)+(off)); \ + UNW(.spillsp ar.unat, PT(AR_UNAT)+(off)); \ + UNW(.spillsp ar.fpsr, PT(AR_FPSR)+(off)); \ + UNW(.spillsp pr, PT(PR)+(off)); + +#define PT_REGS_UNWIND_INFO(off) \ + UNW(.prologue); \ + PT_REGS_SAVES(off); \ + UNW(.body) + +#define SWITCH_STACK_SAVES(off) \ + UNW(.savesp ar.unat,SW(CALLER_UNAT)+(off)); UNW(.savesp ar.fpsr,SW(AR_FPSR)+(off)); \ + UNW(.spillsp f2,SW(F2)+(off)); UNW(.spillsp f3,SW(F3)+(off)); \ + UNW(.spillsp f4,SW(F4)+(off)); UNW(.spillsp f5,SW(F5)+(off)); \ + UNW(.spillsp f16,SW(F16)+(off)); UNW(.spillsp f17,SW(F17)+(off)); \ + UNW(.spillsp f18,SW(F18)+(off)); UNW(.spillsp f19,SW(F19)+(off)); \ + UNW(.spillsp f20,SW(F20)+(off)); UNW(.spillsp f21,SW(F21)+(off)); \ + UNW(.spillsp f22,SW(F22)+(off)); UNW(.spillsp f23,SW(F23)+(off)); \ + UNW(.spillsp f24,SW(F24)+(off)); UNW(.spillsp f25,SW(F25)+(off)); \ + UNW(.spillsp f26,SW(F26)+(off)); UNW(.spillsp f27,SW(F27)+(off)); \ + UNW(.spillsp f28,SW(F28)+(off)); UNW(.spillsp f29,SW(F29)+(off)); \ + UNW(.spillsp f30,SW(F30)+(off)); UNW(.spillsp f31,SW(F31)+(off)); \ + UNW(.spillsp r4,SW(R4)+(off)); UNW(.spillsp r5,SW(R5)+(off)); \ + UNW(.spillsp r6,SW(R6)+(off)); UNW(.spillsp r7,SW(R7)+(off)); \ + UNW(.spillsp b0,SW(B0)+(off)); UNW(.spillsp b1,SW(B1)+(off)); \ + UNW(.spillsp b2,SW(B2)+(off)); UNW(.spillsp b3,SW(B3)+(off)); \ + UNW(.spillsp b4,SW(B4)+(off)); UNW(.spillsp b5,SW(B5)+(off)); \ + UNW(.spillsp ar.pfs,SW(AR_PFS)+(off)); UNW(.spillsp ar.lc,SW(AR_LC)+(off)); \ + UNW(.spillsp @priunat,SW(AR_UNAT)+(off)); \ + UNW(.spillsp ar.rnat,SW(AR_RNAT)+(off)); UNW(.spillsp ar.bspstore,SW(AR_BSPSTORE)+(off)); \ + UNW(.spillsp pr,SW(PR)+(off)) + +#define DO_SAVE_SWITCH_STACK \ + movl r28=1f; \ + ;; \ + .fframe IA64_SWITCH_STACK_SIZE; \ + adds sp=-IA64_SWITCH_STACK_SIZE,sp; \ + mov b7=r28; \ + SWITCH_STACK_SAVES(0); \ + br.cond.sptk.many save_switch_stack; \ +1: + +#define DO_LOAD_SWITCH_STACK(extra) \ + movl r28=1f; \ + ;; \ + mov b7=r28; \ + br.cond.sptk.many load_switch_stack; \ +1: UNW(.restore sp); \ + extra; \ + adds sp=IA64_SWITCH_STACK_SIZE,sp diff --git a/arch/ia64/kernel/fw-emu.c b/arch/ia64/kernel/fw-emu.c index 23ded0730..9e5ec1668 100644 --- a/arch/ia64/kernel/fw-emu.c +++ b/arch/ia64/kernel/fw-emu.c @@ -124,7 +124,18 @@ asm (" .proc pal_emulator_static pal_emulator_static: mov r8=-1 - cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */ + + mov r9=256 + ;; + cmp.gtu p6,p7=r9,r28 /* r28 <= 255? */ +(p6) br.cond.sptk.few static + ;; + mov r9=512 + ;; + cmp.gtu p6,p7=r9,r28 +(p6) br.cond.sptk.few stacked + ;; +static: cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */ (p7) br.cond.sptk.few 1f ;; mov r8=0 /* status = 0 */ @@ -157,7 +168,12 @@ pal_emulator_static: ;; mov ar.lc=r9 mov r8=r0 -1: br.cond.sptk.few rp +1: + br.cond.sptk.few rp + +stacked: + br.ret.sptk.few rp + .endp pal_emulator_static\n"); /* Macro to emulate SAL call using legacy IN and OUT calls to CF8, CFC etc.. */ diff --git a/arch/ia64/kernel/gate.S b/arch/ia64/kernel/gate.S index 8eabe53d1..f7f8d02ae 100644 --- a/arch/ia64/kernel/gate.S +++ b/arch/ia64/kernel/gate.S @@ -3,10 +3,11 @@ * each task's text region. For now, it contains the signal * trampoline code only. * - * Copyright (C) 1999 Hewlett-Packard Co - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ +#include <asm/asmmacro.h> #include <asm/offsets.h> #include <asm/sigcontext.h> #include <asm/system.h> @@ -75,15 +76,12 @@ * [sp+16] = sigframe */ - .global ia64_sigtramp - .proc ia64_sigtramp -ia64_sigtramp: +GLOBAL_ENTRY(ia64_sigtramp) ld8 r10=[r3],8 // get signal handler entry point br.call.sptk.many rp=invoke_sighandler - .endp ia64_sigtramp +END(ia64_sigtramp) - .proc invoke_sighandler -invoke_sighandler: +ENTRY(invoke_sighandler) ld8 gp=[r3] // get signal handler's global pointer mov b6=r10 cover // push args in interrupted frame onto backing store @@ -152,10 +150,9 @@ back_from_restore_rbs: ldf.fill f15=[base1],32 mov r15=__NR_rt_sigreturn break __BREAK_SYSCALL - .endp invoke_sighandler +END(invoke_sighandler) - .proc setup_rbs -setup_rbs: +ENTRY(setup_rbs) flushrs // must be first in insn mov ar.rsc=r0 // put RSE into enforced lazy mode adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp @@ -167,9 +164,9 @@ setup_rbs: mov ar.rsc=0xf // set RSE into eager mode, pl 3 invala // invalidate ALAT br.cond.sptk.many back_from_setup_rbs +END(setup_rbs) - .proc restore_rbs -restore_rbs: +ENTRY(restore_rbs) flushrs mov ar.rsc=r0 // put RSE into enforced lazy mode adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp @@ -181,5 +178,4 @@ restore_rbs: mov ar.rsc=0xf // (will be restored later on from sc_ar_rsc) // invala not necessary as that will happen when returning to user-mode br.cond.sptk.many back_from_restore_rbs - - .endp restore_rbs +END(restore_rbs) diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S index 35a52628a..d0bc7687f 100644 --- a/arch/ia64/kernel/head.S +++ b/arch/ia64/kernel/head.S @@ -16,6 +16,7 @@ #include <linux/config.h> +#include <asm/asmmacro.h> #include <asm/fpu.h> #include <asm/pal.h> #include <asm/offsets.h> @@ -54,10 +55,12 @@ halt_msg: stringz "Halting kernel\n" .text - .align 16 - .global _start - .proc _start -_start: + +GLOBAL_ENTRY(_start) + UNW(.prologue) + UNW(.save rp, r4) // terminate unwind chain with a NULL rp + UNW(mov r4=r0) + UNW(.body) // set IVT entry point---can't access I/O ports without it movl r3=ia64_ivt ;; @@ -156,12 +159,9 @@ alive_msg: ld8 out0=[r2] br.call.sptk.few b0=console_print self: br.sptk.few self // endless loop - .endp _start +END(_start) - .align 16 - .global ia64_save_debug_regs - .proc ia64_save_debug_regs -ia64_save_debug_regs: +GLOBAL_ENTRY(ia64_save_debug_regs) alloc r16=ar.pfs,1,0,0,0 mov r20=ar.lc // preserve ar.lc mov ar.lc=IA64_NUM_DBG_REGS-1 @@ -177,13 +177,10 @@ ia64_save_debug_regs: br.cloop.sptk.few 1b ;; mov ar.lc=r20 // restore ar.lc - br.ret.sptk.few b0 - .endp ia64_save_debug_regs + br.ret.sptk.few rp +END(ia64_save_debug_regs) - .align 16 - .global ia64_load_debug_regs - .proc ia64_load_debug_regs -ia64_load_debug_regs: +GLOBAL_ENTRY(ia64_load_debug_regs) alloc r16=ar.pfs,1,0,0,0 lfetch.nta [in0] mov r20=ar.lc // preserve ar.lc @@ -200,13 +197,10 @@ ia64_load_debug_regs: br.cloop.sptk.few 1b ;; mov ar.lc=r20 // restore ar.lc - br.ret.sptk.few b0 - .endp ia64_load_debug_regs + br.ret.sptk.few rp +END(ia64_load_debug_regs) - .align 16 - .global __ia64_save_fpu - .proc __ia64_save_fpu -__ia64_save_fpu: +GLOBAL_ENTRY(__ia64_save_fpu) alloc r2=ar.pfs,1,0,0,0 adds r3=16,in0 ;; @@ -354,12 +348,9 @@ __ia64_save_fpu: stf.spill.nta [in0]=f126,32 stf.spill.nta [ r3]=f127,32 br.ret.sptk.few rp - .endp __ia64_save_fpu +END(__ia64_save_fpu) - .align 16 - .global __ia64_load_fpu - .proc __ia64_load_fpu -__ia64_load_fpu: +GLOBAL_ENTRY(__ia64_load_fpu) alloc r2=ar.pfs,1,0,0,0 adds r3=16,in0 ;; @@ -507,12 +498,9 @@ __ia64_load_fpu: ldf.fill.nta f126=[in0],32 ldf.fill.nta f127=[ r3],32 br.ret.sptk.few rp - .endp __ia64_load_fpu +END(__ia64_load_fpu) - .align 16 - .global __ia64_init_fpu - .proc __ia64_init_fpu -__ia64_init_fpu: +GLOBAL_ENTRY(__ia64_init_fpu) alloc r2=ar.pfs,0,0,0,0 stf.spill [sp]=f0 mov f32=f0 @@ -644,4 +632,74 @@ __ia64_init_fpu: ldf.fill f126=[sp] mov f127=f0 br.ret.sptk.few rp - .endp __ia64_init_fpu +END(__ia64_init_fpu) + +/* + * Switch execution mode from virtual to physical or vice versa. + * + * Inputs: + * r16 = new psr to establish + * + * Note: RSE must already be in enforced lazy mode + */ +GLOBAL_ENTRY(ia64_switch_mode) + { + alloc r2=ar.pfs,0,0,0,0 + rsm psr.i | psr.ic // disable interrupts and interrupt collection + mov r15=ip + } + ;; + { + flushrs // must be first insn in group + srlz.i + shr.u r19=r15,61 // r19 <- top 3 bits of current IP + } + ;; + mov cr.ipsr=r16 // set new PSR + add r3=1f-ia64_switch_mode,r15 + xor r15=0x7,r19 // flip the region bits + + mov r17=ar.bsp + mov r14=rp // get return address into a general register + + // switch RSE backing store: + ;; + dep r17=r15,r17,61,3 // make ar.bsp physical or virtual + mov r18=ar.rnat // save ar.rnat + ;; + mov ar.bspstore=r17 // this steps on ar.rnat + dep r3=r15,r3,61,3 // make rfi return address physical or virtual + ;; + mov cr.iip=r3 + mov cr.ifs=r0 + dep sp=r15,sp,61,3 // make stack pointer physical or virtual + ;; + mov ar.rnat=r18 // restore ar.rnat + dep r14=r15,r14,61,3 // make function return address physical or virtual + rfi // must be last insn in group + ;; +1: mov rp=r14 + br.ret.sptk.few rp +END(ia64_switch_mode) + +#ifdef CONFIG_IA64_BRL_EMU + +/* + * Assembly routines used by brl_emu.c to set preserved register state. + */ + +#define SET_REG(reg) \ + GLOBAL_ENTRY(ia64_set_##reg); \ + alloc r16=ar.pfs,1,0,0,0; \ + mov reg=r32; \ + ;; \ + br.ret.sptk rp; \ + END(ia64_set_##reg) + +SET_REG(b1); +SET_REG(b2); +SET_REG(b3); +SET_REG(b4); +SET_REG(b5); + +#endif /* CONFIG_IA64_BRL_EMU */ diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c new file mode 100644 index 000000000..7f01b667c --- /dev/null +++ b/arch/ia64/kernel/ia64_ksyms.c @@ -0,0 +1,72 @@ +/* + * Architecture-specific kernel symbols + */ + +#include <linux/config.h> +#include <linux/module.h> + +#include <linux/string.h> +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strtok); + +#include <linux/pci.h> +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); + +#include <linux/in6.h> +#include <asm/checksum.h> +EXPORT_SYMBOL(csum_partial_copy_nocheck); + +#include <asm/irq.h> +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); + +#include <asm/current.h> +#include <asm/hardirq.h> +EXPORT_SYMBOL(irq_stat); + +#include <asm/processor.h> +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(kernel_thread); + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(synchronize_irq); + +#include <asm/smplock.h> +EXPORT_SYMBOL(kernel_flag); + +#include <asm/system.h> +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); + +#endif + +#include <asm/uaccess.h> +EXPORT_SYMBOL(__copy_user); + +#include <asm/unistd.h> +EXPORT_SYMBOL(__ia64_syscall); + +/* from arch/ia64/lib */ +extern void __divdi3(void); +extern void __udivdi3(void); +extern void __moddi3(void); +extern void __umoddi3(void); + +EXPORT_SYMBOL_NOVERS(__divdi3); +EXPORT_SYMBOL_NOVERS(__udivdi3); +EXPORT_SYMBOL_NOVERS(__moddi3); +EXPORT_SYMBOL_NOVERS(__umoddi3); diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index a01432a60..279befd3b 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -201,10 +201,14 @@ static void show(char * str) printk(" %d",local_bh_count(i)); printk(" ]\nStack dumps:"); -#ifdef __ia64__ - printk(" ]\nStack dumps: <unimplemented on IA-64---please fix me>"); - /* for now we don't have stack dumping support... */ -#elif __i386__ +#if defined(__ia64__) + /* + * We can't unwind the stack of another CPU without access to + * the registers of that CPU. And sending an IPI when we're + * in a potentially wedged state doesn't sound like a smart + * idea. + */ +#elif defined(__i386__) for(i=0;i< smp_num_cpus;i++) { unsigned long esp; if(i==cpu) @@ -227,9 +231,7 @@ static void show(char * str) You lose... #endif printk("\nCPU %d:",cpu); -#ifdef __i386__ show_stack(NULL); -#endif printk("\n"); } @@ -582,7 +584,8 @@ unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs) if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ - status |= IRQ_INPROGRESS; /* we are handling it */ + if (!(status & IRQ_PER_CPU)) + status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index 1a8398f85..1ee2974b5 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c @@ -33,7 +33,9 @@ #include <asm/pgtable.h> #include <asm/system.h> -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#define IRQ_DEBUG 0 + +#ifdef CONFIG_ITANIUM_A1_SPECIFIC spinlock_t ivr_read_lock; #endif @@ -49,7 +51,7 @@ __u8 isa_irq_to_vector_map[16] = { 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x40, 0x41 }; -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC int usbfix; @@ -63,7 +65,7 @@ usbfix_option (char *str) __setup("usbfix", usbfix_option); -#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ +#endif /* CONFIG_ITANIUM_A1_SPECIFIC */ /* * That's where the IVT branches when we get an external @@ -73,13 +75,8 @@ __setup("usbfix", usbfix_option); void ia64_handle_irq (unsigned long vector, struct pt_regs *regs) { - unsigned long bsp, sp, saved_tpr; - -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC -# ifndef CONFIG_SMP - static unsigned int max_prio = 0; - unsigned int prev_prio; -# endif + unsigned long saved_tpr; +#ifdef CONFIG_ITANIUM_A1_SPECIFIC unsigned long eoi_ptr; # ifdef CONFIG_USB @@ -95,18 +92,14 @@ ia64_handle_irq (unsigned long vector, struct pt_regs *regs) spin_lock(&ivr_read_lock); { unsigned int tmp; - /* * Disable PCI writes */ outl(0x80ff81c0, 0xcf8); tmp = inl(0xcfc); outl(tmp | 0x400, 0xcfc); - eoi_ptr = inl(0xcfc); - vector = ia64_get_ivr(); - /* * Enable PCI writes */ @@ -118,75 +111,61 @@ ia64_handle_irq (unsigned long vector, struct pt_regs *regs) if (usbfix) reenable_usb(); # endif +#endif /* CONFIG_ITANIUM_A1_SPECIFIC */ -# ifndef CONFIG_SMP - prev_prio = max_prio; - if (vector < max_prio) { - printk ("ia64_handle_irq: got vector %lu while %u was in progress!\n", - vector, max_prio); - - } else - max_prio = vector; -# endif /* !CONFIG_SMP */ -#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ +#if IRQ_DEBUG + { + unsigned long bsp, sp; + + asm ("mov %0=ar.bsp" : "=r"(bsp)); + asm ("mov %0=sp" : "=r"(sp)); + + if ((sp - bsp) < 1024) { + static unsigned char count; + static long last_time; + + if (count > 5 && jiffies - last_time > 5*HZ) + count = 0; + if (++count < 5) { + last_time = jiffies; + printk("ia64_handle_irq: DANGER: less than " + "1KB of free stack space!!\n" + "(bsp=0x%lx, sp=%lx)\n", bsp, sp); + } + } + } +#endif /* IRQ_DEBUG */ /* * Always set TPR to limit maximum interrupt nesting depth to * 16 (without this, it would be ~240, which could easily lead - * to kernel stack overflows. + * to kernel stack overflows). */ saved_tpr = ia64_get_tpr(); ia64_srlz_d(); - ia64_set_tpr(vector); - ia64_srlz_d(); - - asm ("mov %0=ar.bsp" : "=r"(bsp)); - asm ("mov %0=sp" : "=r"(sp)); - - if ((sp - bsp) < 1024) { - static long last_time; - static unsigned char count; - - if (count > 5 && jiffies - last_time > 5*HZ) - count = 0; - if (++count < 5) { - last_time = jiffies; - printk("ia64_handle_irq: DANGER: less than 1KB of free stack space!!\n" - "(bsp=0x%lx, sp=%lx)\n", bsp, sp); + do { + if (vector >= NR_IRQS) { + printk("handle_irq: invalid vector %lu\n", vector); + ia64_set_tpr(saved_tpr); + ia64_srlz_d(); + return; } - } + ia64_set_tpr(vector); + ia64_srlz_d(); - /* - * The interrupt is now said to be in service - */ - if (vector >= NR_IRQS) { - printk("handle_irq: invalid vector %lu\n", vector); - goto out; - } - - do_IRQ(vector, regs); - out: -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC - { - long pEOI; - - asm ("mov %0=0;; (p1) mov %0=1" : "=r"(pEOI)); - if (!pEOI) { - printk("Yikes: ia64_handle_irq() without pEOI!!\n"); - asm volatile ("cmp.eq p1,p0=r0,r0" : "=r"(pEOI)); - } - } - - local_irq_disable(); -# ifndef CONFIG_SMP - if (max_prio == vector) - max_prio = prev_prio; -# endif /* !CONFIG_SMP */ -#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ + do_IRQ(vector, regs); - ia64_srlz_d(); - ia64_set_tpr(saved_tpr); - ia64_srlz_d(); + /* + * Disable interrupts and send EOI: + */ + local_irq_disable(); + ia64_set_tpr(saved_tpr); + ia64_eoi(); +#ifdef CONFIG_ITANIUM_A1_SPECIFIC + break; +#endif + vector = ia64_get_ivr(); + } while (vector != IA64_SPURIOUS_INT); } #ifdef CONFIG_SMP @@ -210,12 +189,12 @@ init_IRQ (void) ia64_set_lrr0(0, 1); ia64_set_lrr1(0, 1); - irq_desc[TIMER_IRQ].handler = &irq_type_ia64_sapic; irq_desc[IA64_SPURIOUS_INT].handler = &irq_type_ia64_sapic; #ifdef CONFIG_SMP /* * Configure the IPI vector and handler */ + irq_desc[IPI_IRQ].status |= IRQ_PER_CPU; irq_desc[IPI_IRQ].handler = &irq_type_ia64_sapic; setup_irq(IPI_IRQ, &ipi_irqaction); #endif @@ -234,7 +213,7 @@ ipi_send (int cpu, int vector, int delivery_mode, int redirect) { unsigned long ipi_addr; unsigned long ipi_data; -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC unsigned long flags; #endif # define EID 0 @@ -242,13 +221,13 @@ ipi_send (int cpu, int vector, int delivery_mode, int redirect) ipi_data = (delivery_mode << 8) | (vector & 0xff); ipi_addr = ipi_base_addr | ((cpu << 8 | EID) << 4) | ((redirect & 1) << 3); -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC spin_lock_irqsave(&ivr_read_lock, flags); -#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ +#endif writeq(ipi_data, ipi_addr); -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC spin_unlock_irqrestore(&ivr_read_lock, flags); #endif } diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S index 56dd2a333..d58cd494e 100644 --- a/arch/ia64/kernel/ivt.S +++ b/arch/ia64/kernel/ivt.S @@ -170,9 +170,31 @@ ia64_ivt: * The ITLB basically does the same as the VHPT handler except * that we always insert exactly one instruction TLB entry. */ +#if 0 + /* + * This code works, but I don't want to enable it until I have numbers + * that prove this to be a win. + */ + mov r31=pr // save predicates + ;; + thash r17=r16 // compute virtual address of L3 PTE + ;; + ld8.s r18=[r17] // try to read L3 PTE + ;; + tnat.nz p6,p0=r18 // did read succeed? +(p6) br.cond.spnt.many 1f + ;; + itc.i r18 + ;; + mov pr=r31,-1 + rfi + +1: rsm psr.dt // use physical addressing for data +#else mov r16=cr.ifa // get address that caused the TLB miss ;; rsm psr.dt // use physical addressing for data +#endif mov r31=pr // save the predicate registers mov r19=ar.k7 // get page table base address shl r21=r16,3 // shift bit 60 into sign bit @@ -222,9 +244,31 @@ ia64_ivt: * that we always insert exactly one data TLB entry. */ mov r16=cr.ifa // get address that caused the TLB miss +#if 0 + /* + * This code works, but I don't want to enable it until I have numbers + * that prove this to be a win. + */ + mov r31=pr // save predicates + ;; + thash r17=r16 // compute virtual address of L3 PTE + ;; + ld8.s r18=[r17] // try to read L3 PTE + ;; + tnat.nz p6,p0=r18 // did read succeed? +(p6) br.cond.spnt.many 1f + ;; + itc.d r18 ;; + mov pr=r31,-1 + rfi + +1: rsm psr.dt // use physical addressing for data +#else rsm psr.dt // use physical addressing for data mov r31=pr // save the predicate registers + ;; +#endif mov r19=ar.k7 // get page table base address shl r21=r16,3 // shift bit 60 into sign bit shr.u r17=r16,61 // get the region number into r17 @@ -265,37 +309,6 @@ ia64_ivt: mov pr=r31,-1 // restore predicate registers rfi - //----------------------------------------------------------------------------------- - // call do_page_fault (predicates are in r31, psr.dt is off, r16 is faulting address) -page_fault: - SAVE_MIN_WITH_COVER - // - // Copy control registers to temporary registers, then turn on psr bits, - // then copy the temporary regs to the output regs. We have to do this - // because the "alloc" can cause a mandatory store which could lead to - // an "Alt DTLB" fault which we can handle only if psr.ic is on. - // - mov r8=cr.ifa - mov r9=cr.isr - adds r3=8,r2 // set up second base pointer - ;; - ssm psr.ic | psr.dt - ;; - srlz.i // guarantee that interrupt collection is enabled - ;; -(p15) ssm psr.i // restore psr.i - movl r14=ia64_leave_kernel - ;; - alloc r15=ar.pfs,0,0,3,0 // must be first in insn group - mov out0=r8 - mov out1=r9 - ;; - SAVE_REST - mov rp=r14 - ;; - adds out2=16,r12 // out2 = pointer to pt_regs - br.call.sptk.few b6=ia64_do_page_fault // ignore return address - .align 1024 ///////////////////////////////////////////////////////////////////////////////////////// // 0x0c00 Entry 3 (size 64 bundles) Alt ITLB (19) @@ -303,7 +316,7 @@ page_fault: movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RX ;; shr.u r18=r16,57 // move address bit 61 to bit 4 - dep r16=0,r16,IA64_PHYS_BITS,(64-IA64_PHYS_BITS) // clear ed, resvd, and unimpl. phys bits + dep r16=0,r16,IA64_MAX_PHYS_BITS,(64-IA64_MAX_PHYS_BITS) // clear ed & reserved bits ;; andcm r18=0x10,r18 // bit 4=~address-bit(61) dep r16=r17,r16,0,12 // insert PTE control bits into r16 @@ -318,18 +331,58 @@ page_fault: // 0x1000 Entry 4 (size 64 bundles) Alt DTLB (7,46) mov r16=cr.ifa // get address that caused the TLB miss movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW + mov r20=cr.isr + mov r21=cr.ipsr + mov r19=pr ;; + tbit.nz p6,p7=r20,IA64_ISR_SP_BIT // is speculation bit on? shr.u r18=r16,57 // move address bit 61 to bit 4 - dep r16=0,r16,IA64_PHYS_BITS,(64-IA64_PHYS_BITS) // clear ed, resvd, and unimpl. phys bits + dep r16=0,r16,IA64_MAX_PHYS_BITS,(64-IA64_MAX_PHYS_BITS) // clear ed & reserved bits ;; + dep r21=-1,r21,IA64_PSR_ED_BIT,1 andcm r18=0x10,r18 // bit 4=~address-bit(61) dep r16=r17,r16,0,12 // insert PTE control bits into r16 ;; or r16=r16,r18 // set bit 4 (uncached) if the access was to region 6 +(p6) mov cr.ipsr=r21 ;; - itc.d r16 // insert the TLB entry +(p7) itc.d r16 // insert the TLB entry + mov pr=r19,-1 rfi + ;; + + //----------------------------------------------------------------------------------- + // call do_page_fault (predicates are in r31, psr.dt is off, r16 is faulting address) +page_fault: + SAVE_MIN_WITH_COVER + // + // Copy control registers to temporary registers, then turn on psr bits, + // then copy the temporary regs to the output regs. We have to do this + // because the "alloc" can cause a mandatory store which could lead to + // an "Alt DTLB" fault which we can handle only if psr.ic is on. + // + mov r8=cr.ifa + mov r9=cr.isr + adds r3=8,r2 // set up second base pointer + ;; + ssm psr.ic | psr.dt + ;; + srlz.i // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i // restore psr.i + movl r14=ia64_leave_kernel + ;; + alloc r15=ar.pfs,0,0,3,0 // must be first in insn group + mov out0=r8 + mov out1=r9 + ;; + SAVE_REST + mov rp=r14 + ;; + adds out2=16,r12 // out2 = pointer to pt_regs + br.call.sptk.few b6=ia64_do_page_fault // ignore return address + .align 1024 ///////////////////////////////////////////////////////////////////////////////////////// // 0x1400 Entry 5 (size 64 bundles) Data nested TLB (6,45) @@ -338,7 +391,7 @@ page_fault: // Access-bit, or Data Access-bit faults cause a nested fault because the // dTLB entry for the virtual page table isn't present. In such a case, // we lookup the pte for the faulting address by walking the page table - // and return to the contination point passed in register r30. + // and return to the continuation point passed in register r30. // In accessing the page tables, we don't need to check for NULL entries // because if the page tables didn't map the faulting address, it would not // be possible to receive one of the above faults. @@ -441,9 +494,6 @@ page_fault: tbit.z p6,p0=r17,IA64_PSR_IS_BIT // IA64 instruction set? ;; (p6) mov r16=r18 // if so, use cr.iip instead of cr.ifa -#if 0 - ;; -#endif mov pr=r31,-1 #endif /* CONFIG_ITANIUM */ movl r30=1f // load continuation point in case of nested fault @@ -489,7 +539,6 @@ page_fault: ;; srlz.d // ensure everyone knows psr.dt is off... cmp.eq p0,p7=r16,r17 // is this a system call? (p7 <- false, if so) - #if 1 // Allow syscalls via the old system call number for the time being. This is // so we can transition to the new syscall number in a relatively smooth @@ -498,7 +547,6 @@ page_fault: ;; (p7) cmp.eq.or.andcm p0,p7=r16,r17 // is this the old syscall number? #endif - (p7) br.cond.spnt.many non_syscall SAVE_MIN // uses r31; defines r2: @@ -575,13 +623,12 @@ page_fault: ssm psr.ic | psr.dt // turn interrupt collection and data translation back on ;; adds r3=8,r2 // set up second base pointer for SAVE_REST - cmp.eq pEOI,p0=r0,r0 // set pEOI flag so that ia64_leave_kernel writes cr.eoi srlz.i // ensure everybody knows psr.ic and psr.dt are back on ;; SAVE_REST ;; alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC mov out0=r0 // defer reading of cr.ivr to handle_irq... #else mov out0=cr.ivr // pass cr.ivr as first arg @@ -609,6 +656,50 @@ page_fault: // 0x3c00 Entry 15 (size 64 bundles) Reserved FAULT(15) +// +// Squatting in this space ... +// +// This special case dispatcher for illegal operation faults +// allows preserved registers to be modified through a +// callback function (asm only) that is handed back from +// the fault handler in r8. Up to three arguments can be +// passed to the callback function by returning an aggregate +// with the callback as its first element, followed by the +// arguments. +// +dispatch_illegal_op_fault: + SAVE_MIN_WITH_COVER + // + // The "alloc" can cause a mandatory store which could lead to + // an "Alt DTLB" fault which we can handle only if psr.ic is on. + // + ssm psr.ic | psr.dt + ;; + srlz.i // guarantee that interrupt collection is enabled + ;; +(p15) ssm psr.i // restore psr.i + adds r3=8,r2 // set up second base pointer for SAVE_REST + ;; + alloc r14=ar.pfs,0,0,1,0 // must be first in insn group + mov out0=ar.ec + ;; + SAVE_REST + ;; + br.call.sptk.few rp=ia64_illegal_op_fault + ;; + alloc r14=ar.pfs,0,0,3,0 // must be first in insn group + mov out0=r9 + mov out1=r10 + mov out2=r11 + movl r15=ia64_leave_kernel + ;; + mov rp=r15 + mov b6=r8 + ;; + cmp.ne p6,p0=0,r8 +(p6) br.call.dpnt b6=b6 // call returns to ia64_leave_kernel + br.sptk ia64_leave_kernel + .align 1024 ///////////////////////////////////////////////////////////////////////////////////////// // 0x4000 Entry 16 (size 64 bundles) Reserved @@ -643,14 +734,17 @@ dispatch_to_ia32_handler: (p6) br.call.dpnt.few b6=non_ia32_syscall adds r14=IA64_PT_REGS_R8_OFFSET + 16,sp // 16 byte hole per SW conventions - + adds r15=IA64_PT_REGS_R1_OFFSET + 16,sp + ;; + cmp.eq pSys,pNonSys=r0,r0 // set pSys=1, pNonSys=0 + st8 [r15]=r8 // save orignal EAX in r1 (IA32 procs don't use the GP) ;; alloc r15=ar.pfs,0,0,6,0 // must first in an insn group ;; ld4 r8=[r14],8 // r8 == EAX (syscall number) - mov r15=0xff + mov r15=190 // sys_vfork - last implemented system call ;; - cmp.ltu.unc p6,p7=r8,r15 + cmp.leu.unc p6,p7=r8,r15 ld4 out1=[r14],8 // r9 == ecx ;; ld4 out2=[r14],8 // r10 == edx @@ -868,7 +962,16 @@ dispatch_to_fault_handler: .align 256 ///////////////////////////////////////////////////////////////////////////////////////// // 0x5400 Entry 24 (size 16 bundles) General Exception (5,32,34,36,38,39) - FAULT(24) + mov r16=cr.isr + mov r31=pr + rsm psr.dt // avoid nested faults due to TLB misses... + ;; + srlz.d // ensure everyone knows psr.dt is off... + cmp4.eq p6,p0=0,r16 +(p6) br.sptk dispatch_illegal_op_fault + ;; + mov r19=24 // fault number + br.cond.sptk.many dispatch_to_fault_handler .align 256 ///////////////////////////////////////////////////////////////////////////////////////// @@ -939,7 +1042,6 @@ dispatch_to_fault_handler: mov r31=pr // prepare to save predicates ;; srlz.d // ensure everyone knows psr.dt is off - mov r19=30 // error vector for fault_handler (when kernel) br.cond.sptk.many dispatch_unaligned_handler .align 256 diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 150feac03..003b8dd69 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -9,15 +9,16 @@ * Copyright (C) 1999 Silicon Graphics, Inc. * Copyright (C) Vijay Chander(vijay@engr.sgi.com) * - * 00/03/29 C. Fleckenstein Fixed PAL/SAL update issues, began MCA bug fixes, logging issues, + * 00/03/29 C. Fleckenstein Fixed PAL/SAL update issues, began MCA bug fixes, + * logging issues, * added min save state dump, added INIT handler. */ +#include <linux/config.h> #include <linux/types.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/irq.h> #include <linux/smp_lock.h> -#include <linux/config.h> #include <asm/page.h> #include <asm/ptrace.h> diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S index 81966bb99..e4a9f0530 100644 --- a/arch/ia64/kernel/mca_asm.S +++ b/arch/ia64/kernel/mca_asm.S @@ -6,7 +6,6 @@ // 00/03/29 cfleck Added code to save INIT handoff state in pt_regs format, switch to temp kstack, // switch modes, jump to C INIT handler // -#include <asm/offsets.h> #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/mca_asm.h> diff --git a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h index bcfe1659c..24be2f53d 100644 --- a/arch/ia64/kernel/minstate.h +++ b/arch/ia64/kernel/minstate.h @@ -101,7 +101,6 @@ ;; \ st8 [r16]=r18,16; /* save ar.rsc value for "loadrs" */ \ st8.spill [r17]=rR1,16; /* save original r1 */ \ - cmp.ne pEOI,p0=r0,r0 /* clear pEOI by default */ \ ;; \ .mem.offset 0,0; st8.spill [r16]=r2,16; \ .mem.offset 8,0; st8.spill [r17]=r3,16; \ diff --git a/arch/ia64/kernel/pal.S b/arch/ia64/kernel/pal.S index 1506bacc2..e6f44cfb6 100644 --- a/arch/ia64/kernel/pal.S +++ b/arch/ia64/kernel/pal.S @@ -4,9 +4,16 @@ * * Copyright (C) 1999 Don Dugger <don.dugger@intel.com> * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> - * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 David Mosberger <davidm@hpl.hp.com> + * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com> + * + * 05/22/2000 eranian Added support for stacked register calls + * 05/24/2000 eranian Added support for physical mode static calls */ +#include <asm/asmmacro.h> +#include <asm/processor.h> + .text .psr abi64 .psr lsb @@ -24,29 +31,23 @@ pal_entry_point: * * in0 Address of the PAL entry point (text address, NOT a function descriptor). */ - .align 16 - .global ia64_pal_handler_init - .proc ia64_pal_handler_init -ia64_pal_handler_init: +GLOBAL_ENTRY(ia64_pal_handler_init) alloc r3=ar.pfs,1,0,0,0 movl r2=pal_entry_point ;; st8 [r2]=in0 br.ret.sptk.few rp - - .endp ia64_pal_handler_init +END(ia64_pal_handler_init) /* * Default PAL call handler. This needs to be coded in assembly because it uses * the static calling convention, i.e., the RSE may not be used and calls are * done via "br.cond" (not "br.call"). */ - .align 16 - .global ia64_pal_default_handler - .proc ia64_pal_default_handler -ia64_pal_default_handler: +GLOBAL_ENTRY(ia64_pal_default_handler) mov r8=-1 br.cond.sptk.few rp +END(ia64_pal_default_handler) /* * Make a PAL call using the static calling convention. @@ -56,64 +57,139 @@ ia64_pal_default_handler: * in2 - in4 Remaning PAL arguments * */ +GLOBAL_ENTRY(ia64_pal_call_static) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(6)) + alloc loc1 = ar.pfs,6,90,0,0 + movl loc2 = pal_entry_point +1: { + mov r28 = in0 + mov r29 = in1 + mov r8 = ip + } + ;; + ld8 loc2 = [loc2] // loc2 <- entry point + mov r30 = in2 + mov r31 = in3 + ;; + mov loc3 = psr + mov loc0 = rp + UNW(.body) + adds r8 = .ret0-1b,r8 + ;; + rsm psr.i + mov b7 = loc2 + mov rp = r8 + ;; + br.cond.sptk.few b7 +.ret0: mov psr.l = loc3 + mov ar.pfs = loc1 + mov rp = loc0 + ;; + srlz.d // seralize restoration of psr.l + br.ret.sptk.few b0 +END(ia64_pal_call_static) -#ifdef __GCC_MULTIREG_RETVALS__ -# define arg0 in0 -# define arg1 in1 -# define arg2 in2 -# define arg3 in3 -# define arg4 in4 -#else -# define arg0 in1 -# define arg1 in2 -# define arg2 in3 -# define arg3 in4 -# define arg4 in5 -#endif +/* + * Make a PAL call using the stacked registers calling convention. + * + * Inputs: + * in0 Index of PAL service + * in2 - in3 Remaning PAL arguments + */ +GLOBAL_ENTRY(ia64_pal_call_stacked) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5)) + alloc loc1 = ar.pfs,5,4,87,0 + movl loc2 = pal_entry_point - .text - .psr abi64 - .psr lsb - .lsb + mov r28 = in0 // Index MUST be copied to r28 + mov out0 = in0 // AND in0 of PAL function + mov loc0 = rp + UNW(.body) + ;; + ld8 loc2 = [loc2] // loc2 <- entry point + mov out1 = in1 + mov out2 = in2 + mov out3 = in3 + mov loc3 = psr + ;; + rsm psr.i + mov b7 = loc2 + ;; + br.call.sptk.many rp=b7 // now make the call +.ret2: + mov psr.l = loc3 + mov ar.pfs = loc1 + mov rp = loc0 + ;; + srlz.d // serialize restoration of psr.l + br.ret.sptk.few b0 +END(ia64_pal_call_stacked) + +/* + * Make a physical mode PAL call using the static registers calling convention. + * + * Inputs: + * in0 Index of PAL service + * in2 - in3 Remaning PAL arguments + * + * PSR_DB, PSR_LP, PSR_TB, PSR_ID, PSR_DA are never set by the kernel. + * So we don't need to clear them. + */ +#define PAL_PSR_BITS_TO_CLEAR \ + (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | \ + IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \ + IA64_PSR_DFL | IA64_PSR_DFH) + +#define PAL_PSR_BITS_TO_SET \ + (IA64_PSR_BN) - .align 16 - .global ia64_pal_call_static - .proc ia64_pal_call_static -ia64_pal_call_static: - alloc loc0 = ar.pfs,6,90,0,0 - movl loc2 = pal_entry_point + +GLOBAL_ENTRY(ia64_pal_call_phys_static) + UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(6)) + alloc loc1 = ar.pfs,6,90,0,0 + movl loc2 = pal_entry_point 1: { - mov r28 = arg0 - mov r29 = arg1 - mov r8 = ip + mov r28 = in0 // copy procedure index + mov r8 = ip // save ip to compute branch + mov loc0 = rp // save rp } + UNW(.body) ;; - ld8 loc2 = [loc2] // loc2 <- entry point - mov r30 = arg2 - mov r31 = arg3 + ld8 loc2 = [loc2] // loc2 <- entry point + mov r29 = in1 // first argument + mov r30 = in2 // copy arg2 + mov r31 = in3 // copy arg3 ;; - mov loc3 = psr - mov loc1 = rp - adds r8 = .ret0-1b,r8 - ;; - rsm psr.i - mov b7 = loc2 - mov rp = r8 + mov loc3 = psr // save psr + adds r8 = .ret4-1b,r8 // calculate return address for call ;; - br.cond.sptk.few b7 -.ret0: mov psr.l = loc3 -#ifndef __GCC_MULTIREG_RETVALS__ - st8 [in0] = r8, 8 + mov loc4=ar.rsc // save RSE configuration + dep.z loc2=loc2,0,61 // convert pal entry point to physical + dep.z r8=r8,0,61 // convert rp to physical ;; - st8 [in0] = r9, 8 + mov b7 = loc2 // install target to branch reg + mov ar.rsc=r0 // put RSE in enforced lazy, LE mode + movl r16=PAL_PSR_BITS_TO_CLEAR + movl r17=PAL_PSR_BITS_TO_SET ;; - st8 [in0] = r10, 8 + or loc3=loc3,r17 // add in psr the bits to set ;; - st8 [in0] = r11, 8 -#endif - mov ar.pfs = loc0 - mov rp = loc1 + andcm r16=loc3,r16 // removes bits to clear from psr + br.call.sptk.few rp=ia64_switch_mode +.ret3: + mov rp = r8 // install return address (physical) + br.cond.sptk.few b7 +.ret4: + mov ar.rsc=r0 // put RSE in enforced lazy, LE mode + mov r16=loc3 // r16= original psr + br.call.sptk.few rp=ia64_switch_mode // return to virtual mode + +.ret5: mov psr.l = loc3 // restore init PSR + + mov ar.pfs = loc1 + mov rp = loc0 ;; + mov ar.rsc=loc4 // restore RSE configuration srlz.d // seralize restoration of psr.l br.ret.sptk.few b0 - .endp ia64_pal_call_static +END(ia64_pal_call_phys_static) diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c new file mode 100644 index 000000000..ad40e911e --- /dev/null +++ b/arch/ia64/kernel/palinfo.c @@ -0,0 +1,780 @@ +/* + * palinfo.c + * + * Prints processor specific information reported by PAL. + * This code is based on specification of PAL as of the + * Intel IA-64 Architecture Software Developer's Manual v1.0. + * + * + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com> + * + * 05/26/2000 S.Eranian initial release + * + * ISSUES: + * - because of some PAL bugs, some calls return invalid results or + * are empty for now. + * - remove hack to avoid problem with <= 256M RAM for itr. + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> + +#include <asm/pal.h> +#include <asm/sal.h> +#include <asm/efi.h> +#include <asm/page.h> +#include <asm/processor.h> + +/* + * Hope to get rid of these in a near future +*/ +#define IA64_PAL_VERSION_BUG 1 + +#define PALINFO_VERSION "0.1" + +typedef int (*palinfo_func_t)(char*); + +typedef struct { + const char *name; /* name of the proc entry */ + palinfo_func_t proc_read; /* function to call for reading */ + struct proc_dir_entry *entry; /* registered entry (removal) */ +} palinfo_entry_t; + +static struct proc_dir_entry *palinfo_dir; + +/* + * A bunch of string array to get pretty printing + */ + +static char *cache_types[] = { + "", /* not used */ + "Instruction", + "Data", + "Data/Instruction" /* unified */ +}; + +static const char *cache_mattrib[]={ + "WriteThrough", + "WriteBack", + "", /* reserved */ + "" /* reserved */ +}; + +static const char *cache_st_hints[]={ + "Temporal, level 1", + "Reserved", + "Reserved", + "Non-temporal, all levels", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +static const char *cache_ld_hints[]={ + "Temporal, level 1", + "Non-temporal, level 1", + "Reserved", + "Non-temporal, all levels", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +static const char *rse_hints[]={ + "enforced lazy", + "eager stores", + "eager loads", + "eager loads and stores" +}; + +#define RSE_HINTS_COUNT (sizeof(rse_hints)/sizeof(const char *)) + +/* + * The current resvision of the Volume 2 of + * IA-64 Architecture Software Developer's Manual is wrong. + * Table 4-10 has invalid information concerning the ma field: + * Correct table is: + * bit 0 - 001 - UC + * bit 4 - 100 - UC + * bit 5 - 101 - UCE + * bit 6 - 110 - WC + * bit 7 - 111 - NatPage + */ +static const char *mem_attrib[]={ + "Write Back (WB)", /* 000 */ + "Uncacheable (UC)", /* 001 */ + "Reserved", /* 010 */ + "Reserved", /* 011 */ + "Uncacheable (UC)", /* 100 */ + "Uncacheable Exported (UCE)", /* 101 */ + "Write Coalescing (WC)", /* 110 */ + "NaTPage" /* 111 */ +}; + + + +/* + * Allocate a buffer suitable for calling PAL code in Virtual mode + * + * The documentation (PAL2.6) requires thius buffer to have a pinned + * translation to avoid any DTLB faults. For this reason we allocate + * a page (large enough to hold any possible reply) and use a DTC + * to hold the translation during the call. A call the free_palbuffer() + * is required to release ALL resources (page + translation). + * + * The size of the page allocated is based on the PAGE_SIZE defined + * at compile time for the kernel, i.e. >= 4Kb. + * + * Return: a pointer to the newly allocated page (virtual address) + */ +static void * +get_palcall_buffer(void) +{ + void *tmp; + + tmp = (void *)__get_free_page(GFP_KERNEL); + if (tmp == 0) { + printk(KERN_ERR "%s: can't get a buffer page\n", __FUNCTION__); + } else if ( ((u64)tmp - PAGE_OFFSET) > (1<<_PAGE_SIZE_256M) ) { /* XXX: temporary hack */ + unsigned long flags; + + /* PSR.ic must be zero to insert new DTR */ + ia64_clear_ic(flags); + + /* + * we only insert of DTR + * + * XXX: we need to figure out a way to "allocate" TR(s) to avoid + * conflicts. Maybe something in an include file like pgtable.h + * page.h or processor.h + * + * ITR0/DTR0: used for kernel code/data + * ITR1/DTR1: used by HP simulator + * ITR2/DTR2: used to map PAL code + */ + ia64_itr(0x2, 3, (u64)tmp, + pte_val(mk_pte_phys(__pa(tmp), __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW))), PAGE_SHIFT); + + ia64_srlz_d (); + + __restore_flags(flags); + } + + return tmp; +} + +/* + * Free a palcall buffer allocated with the previous call + * + * The translation is also purged. + */ +static void +free_palcall_buffer(void *addr) +{ + __free_page(addr); + ia64_ptr(0x2, (u64)addr, PAGE_SHIFT); + ia64_srlz_d (); +} + +/* + * Take a 64bit vector and produces a string such that + * if bit n is set then 2^n in clear text is generated. The adjustment + * to the right unit is also done. + * + * Input: + * - a pointer to a buffer to hold the string + * - a 64-bit vector + * Ouput: + * - a pointer to the end of the buffer + * + */ +static char * +bitvector_process(char *p, u64 vector) +{ + int i,j; + const char *units[]={ "", "K", "M", "G", "T" }; + + for (i=0, j=0; i < 64; i++ , j=i/10) { + if (vector & 0x1) { + p += sprintf(p, "%d%s ", 1 << (i-j*10), units[j]); + } + vector >>= 1; + } + return p; +} + +/* + * Take a 64bit vector and produces a string such that + * if bit n is set then register n is present. The function + * takes into account consecutive registers and prints out ranges. + * + * Input: + * - a pointer to a buffer to hold the string + * - a 64-bit vector + * Ouput: + * - a pointer to the end of the buffer + * + */ +static char * +bitregister_process(char *p, u64 *reg_info, int max) +{ + int i, begin, skip = 0; + u64 value = reg_info[0]; + + value >>= i = begin = ffs(value) - 1; + + for(; i < max; i++ ) { + + if (i != 0 && (i%64) == 0) value = *++reg_info; + + if ((value & 0x1) == 0 && skip == 0) { + if (begin <= i - 2) + p += sprintf(p, "%d-%d ", begin, i-1); + else + p += sprintf(p, "%d ", i-1); + skip = 1; + begin = -1; + } else if ((value & 0x1) && skip == 1) { + skip = 0; + begin = i; + } + value >>=1; + } + if (begin > -1) { + if (begin < 127) + p += sprintf(p, "%d-127", begin); + else + p += sprintf(p, "127"); + } + + return p; +} + +static int +power_info(char *page) +{ + s64 status; + char *p = page; + pal_power_mgmt_info_u_t *halt_info; + int i; + + halt_info = get_palcall_buffer(); + if (halt_info == 0) return 0; + + status = ia64_pal_halt_info(halt_info); + if (status != 0) { + free_palcall_buffer(halt_info); + return 0; + } + + for (i=0; i < 8 ; i++ ) { + if (halt_info[i].pal_power_mgmt_info_s.im == 1) { + p += sprintf(p, "Power level %d:\n" \ + "\tentry_latency : %d cycles\n" \ + "\texit_latency : %d cycles\n" \ + "\tpower consumption : %d mW\n" \ + "\tCache+TLB coherency : %s\n", i, + halt_info[i].pal_power_mgmt_info_s.entry_latency, + halt_info[i].pal_power_mgmt_info_s.exit_latency, + halt_info[i].pal_power_mgmt_info_s.power_consumption, + halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No"); + } else { + p += sprintf(p,"Power level %d: not implemented\n",i); + } + } + + free_palcall_buffer(halt_info); + + return p - page; +} + +static int +cache_info(char *page) +{ + char *p = page; + u64 levels, unique_caches; + pal_cache_config_info_t cci; + int i,j, k; + s64 status; + + if ((status=ia64_pal_cache_summary(&levels, &unique_caches)) != 0) { + printk("ia64_pal_cache_summary=%ld\n", status); + return 0; + } + + p += sprintf(p, "Cache levels : %ld\n" \ + "Unique caches : %ld\n\n", + levels, + unique_caches); + + for (i=0; i < levels; i++) { + + for (j=2; j >0 ; j--) { + + /* even without unification some level may not be present */ + if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0) { + continue; + } + p += sprintf(p, "%s Cache level %d:\n" \ + "\tSize : %ld bytes\n" \ + "\tAttributes : ", + cache_types[j+cci.pcci_unified], i+1, + cci.pcci_cache_size); + + if (cci.pcci_unified) p += sprintf(p, "Unified "); + + p += sprintf(p, "%s\n", cache_mattrib[cci.pcci_cache_attr]); + + p += sprintf(p, "\tAssociativity : %d\n" \ + "\tLine size : %d bytes\n" \ + "\tStride : %d bytes\n", + cci.pcci_assoc, + 1<<cci.pcci_line_size, + 1<<cci.pcci_stride); + if (j == 1) + p += sprintf(p, "\tStore latency : N/A\n"); + else + p += sprintf(p, "\tStore latency : %d cycle(s)\n", + cci.pcci_st_latency); + + p += sprintf(p, "\tLoad latency : %d cycle(s)\n" \ + "\tStore hints : ", + cci.pcci_ld_latency); + + for(k=0; k < 8; k++ ) { + if ( cci.pcci_st_hints & 0x1) p += sprintf(p, "[%s]", cache_st_hints[k]); + cci.pcci_st_hints >>=1; + } + p += sprintf(p, "\n\tLoad hints : "); + + for(k=0; k < 8; k++ ) { + if ( cci.pcci_ld_hints & 0x1) p += sprintf(p, "[%s]", cache_ld_hints[k]); + cci.pcci_ld_hints >>=1; + } + p += sprintf(p, "\n\tAlias boundary : %d byte(s)\n" \ + "\tTag LSB : %d\n" \ + "\tTag MSB : %d\n", + 1<<cci.pcci_alias_boundary, + cci.pcci_tag_lsb, + cci.pcci_tag_msb); + + /* when unified, data(j=2) is enough */ + if (cci.pcci_unified) break; + } + } + return p - page; +} + + +static int +vm_info(char *page) +{ + char *p = page; + u64 tr_pages =0, vw_pages=0, tc_pages; + u64 attrib; + pal_vm_info_1_u_t vm_info_1; + pal_vm_info_2_u_t vm_info_2; + pal_tc_info_u_t tc_info; + ia64_ptce_info_t ptce; + int i, j; + s64 status; + + if ((status=ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) { + printk("ia64_pal_vm_summary=%ld\n", status); + return 0; + } + + + p += sprintf(p, "Physical Address Space : %d bits\n" \ + "Virtual Address Space : %d bits\n" \ + "Protection Key Registers(PKR) : %d\n" \ + "Implemented bits in PKR.key : %d\n" \ + "Hash Tag ID : 0x%x\n" \ + "Size of RR.rid : %d\n", + vm_info_1.pal_vm_info_1_s.phys_add_size, + vm_info_2.pal_vm_info_2_s.impl_va_msb+1, + vm_info_1.pal_vm_info_1_s.max_pkr+1, + vm_info_1.pal_vm_info_1_s.key_size, + vm_info_1.pal_vm_info_1_s.hash_tag_id, + vm_info_2.pal_vm_info_2_s.rid_size); + + if (ia64_pal_mem_attrib(&attrib) != 0) return 0; + + p += sprintf(p, "Supported memory attributes : %s\n", mem_attrib[attrib&0x7]); + + if ((status=ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) { + printk("ia64_pal_vm_page_size=%ld\n", status); + return 0; + } + + p += sprintf(p, "\nTLB walker : %s implemented\n" \ + "Number of DTR : %d\n" \ + "Number of ITR : %d\n" \ + "TLB insertable page sizes : ", + vm_info_1.pal_vm_info_1_s.vw ? "\b":"not", + vm_info_1.pal_vm_info_1_s.max_dtr_entry+1, + vm_info_1.pal_vm_info_1_s.max_itr_entry+1); + + + p = bitvector_process(p, tr_pages); + + p += sprintf(p, "\nTLB purgeable page sizes : "); + + p = bitvector_process(p, vw_pages); + + if ((status=ia64_get_ptce(&ptce)) != 0) { + printk("ia64_get_ptce=%ld\n",status); + return 0; + } + + p += sprintf(p, "\nPurge base address : 0x%016lx\n" \ + "Purge outer loop count : %d\n" \ + "Purge inner loop count : %d\n" \ + "Purge outer loop stride : %d\n" \ + "Purge inner loop stride : %d\n", + ptce.base, + ptce.count[0], + ptce.count[1], + ptce.stride[0], + ptce.stride[1]); + + p += sprintf(p, "TC Levels : %d\n" \ + "Unique TC(s) : %d\n", + vm_info_1.pal_vm_info_1_s.num_tc_levels, + vm_info_1.pal_vm_info_1_s.max_unique_tcs); + + for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) { + for (j=2; j>0 ; j--) { + tc_pages = 0; /* just in case */ + + + /* even without unification, some levels may not be present */ + if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) { + continue; + } + + p += sprintf(p, "\n%s Translation Cache Level %d:\n" \ + "\tHash sets : %d\n" \ + "\tAssociativity : %d\n" \ + "\tNumber of entries : %d\n" \ + "\tFlags : ", + cache_types[j+tc_info.tc_unified], i+1, + tc_info.tc_num_sets, + tc_info.tc_associativity, + tc_info.tc_num_entries); + + if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized "); + if (tc_info.tc_unified) p += sprintf(p, "Unified "); + if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction"); + + p += sprintf(p, "\n\tSupported page sizes: "); + + p = bitvector_process(p, tc_pages); + + /* when unified date (j=2) is enough */ + if (tc_info.tc_unified) break; + } + } + p += sprintf(p, "\n"); + + return p - page; +} + + +static int +register_info(char *page) +{ + char *p = page; + u64 reg_info[2]; + u64 info; + u64 phys_stacked; + pal_hints_u_t hints; + u64 iregs, dregs; + char *info_type[]={ + "Implemented AR(s)", + "AR(s) with read side-effects", + "Implemented CR(s)", + "CR(s) with read side-effects", + }; + + for(info=0; info < 4; info++) { + + if (ia64_pal_register_info(info, ®_info[0], ®_info[1]) != 0) return 0; + + p += sprintf(p, "%-32s : ", info_type[info]); + + p = bitregister_process(p, reg_info, 128); + + p += sprintf(p, "\n"); + } + + if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0; + + p += sprintf(p, "RSE stacked physical registers : %ld\n" \ + "RSE load/store hints : %ld (%s)\n", + phys_stacked, + hints.ph_data, + hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)"); + + if (ia64_pal_debug_info(&iregs, &dregs)) return 0; + + p += sprintf(p, "Instruction debug register pairs : %ld\n" \ + "Data debug register pairs : %ld\n", + iregs, dregs); + + return p - page; +} + +static const char *proc_features[]={ + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL, + NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL, + NULL,NULL,NULL,NULL,NULL, + "XIP,XPSR,XFS implemented", + "XR1-XR3 implemented", + "Disable dynamic predicate prediction", + "Disable processor physical number", + "Disable dynamic data cache prefetch", + "Disable dynamic inst cache prefetch", + "Disable dynamic branch prediction", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "Disable BINIT on processor time-out", + "Disable dynamic power management (DPM)", + "Disable coherency", + "Disable cache", + "Enable CMCI promotion", + "Enable MCA to BINIT promotion", + "Enable MCA promotion", + "Enable BEER promotion" +}; + + +static int +processor_info(char *page) +{ + char *p = page; + const char **v = proc_features; + u64 avail=1, status=1, control=1; + int i; + s64 ret; + + /* must be in physical mode */ + if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0; + + for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) { + if ( ! *v ) continue; + p += sprintf(p, "%-40s : %s%s %s\n", *v, + avail & 0x1 ? "" : "NotImpl", + avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "", + avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): ""); + } + return p - page; +} + +/* + * physical mode call for PAL_VERSION is working fine. + * This function is meant to go away once PAL get fixed. + */ +static inline s64 +ia64_pal_version_phys(pal_version_u_t *pal_min_version, pal_version_u_t *pal_cur_version) +{ + struct ia64_pal_retval iprv; + PAL_CALL_PHYS(iprv, PAL_VERSION, 0, 0, 0); + if (pal_min_version) + pal_min_version->pal_version_val = iprv.v0; + if (pal_cur_version) + pal_cur_version->pal_version_val = iprv.v1; + return iprv.status; +} + +static int +version_info(char *page) +{ + s64 status; + pal_version_u_t min_ver, cur_ver; + char *p = page; + +#ifdef IA64_PAL_VERSION_BUG + /* The virtual mode call is buggy. But the physical mode call seems + * to be ok. Until they fix virtual mode, we do physical. + */ + status = ia64_pal_version_phys(&min_ver, &cur_ver); +#else + /* The system crashes if you enable this code with the wrong PAL + * code + */ + status = ia64_pal_version(&min_ver, &cur_ver); +#endif + if (status != 0) return 0; + + p += sprintf(p, "PAL_vendor : 0x%x (min=0x%x)\n" \ + "PAL_A revision : 0x%x (min=0x%x)\n" \ + "PAL_A model : 0x%x (min=0x%x)\n" \ + "PAL_B mode : 0x%x (min=0x%x)\n" \ + "PAL_B revision : 0x%x (min=0x%x)\n", + cur_ver.pal_version_s.pv_pal_vendor, + min_ver.pal_version_s.pv_pal_vendor, + cur_ver.pal_version_s.pv_pal_a_rev, + cur_ver.pal_version_s.pv_pal_a_rev, + cur_ver.pal_version_s.pv_pal_a_model, + min_ver.pal_version_s.pv_pal_a_model, + cur_ver.pal_version_s.pv_pal_b_rev, + min_ver.pal_version_s.pv_pal_b_rev, + cur_ver.pal_version_s.pv_pal_b_model, + min_ver.pal_version_s.pv_pal_b_model); + + return p - page; +} + +static int +perfmon_info(char *page) +{ + char *p = page; + u64 *pm_buffer; + pal_perf_mon_info_u_t pm_info; + + pm_buffer = (u64 *)get_palcall_buffer(); + if (pm_buffer == 0) return 0; + + if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) { + free_palcall_buffer(pm_buffer); + return 0; + } + +#ifdef IA64_PAL_PERF_MON_INFO_BUG + pm_buffer[5]=0x3; + pm_info.pal_perf_mon_info_s.cycles = 0x12; + pm_info.pal_perf_mon_info_s.retired = 0x08; +#endif + + p += sprintf(p, "PMC/PMD pairs : %d\n" \ + "Counter width : %d bits\n" \ + "Cycle event number : %d\n" \ + "Retired event number : %d\n" \ + "Implemented PMC : ", + pm_info.pal_perf_mon_info_s.generic, + pm_info.pal_perf_mon_info_s.width, + pm_info.pal_perf_mon_info_s.cycles, + pm_info.pal_perf_mon_info_s.retired); + + p = bitregister_process(p, pm_buffer, 256); + + p += sprintf(p, "\nImplemented PMD : "); + + p = bitregister_process(p, pm_buffer+4, 256); + + p += sprintf(p, "\nCycles count capable : "); + + p = bitregister_process(p, pm_buffer+8, 256); + + p += sprintf(p, "\nRetired bundles count capable : "); + + p = bitregister_process(p, pm_buffer+12, 256); + + p += sprintf(p, "\n"); + + free_palcall_buffer(pm_buffer); + + return p - page; +} + +static int +frequency_info(char *page) +{ + char *p = page; + struct pal_freq_ratio proc, itc, bus; + u64 base; + + if (ia64_pal_freq_base(&base) == -1) + p += sprintf(p, "Output clock : not implemented\n"); + else + p += sprintf(p, "Output clock : %ld ticks/s\n", base); + + if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0; + + p += sprintf(p, "Processor/Clock ratio : %ld/%ld\n" \ + "Bus/Clock ratio : %ld/%ld\n" \ + "ITC/Clock ratio : %ld/%ld\n", + proc.num, proc.den, + bus.num, bus.den, + itc.num, itc.den); + + return p - page; +} + + +/* + * Entry point routine: all calls go trhough this function + */ +static int +palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + palinfo_func_t info = (palinfo_func_t)data; + int len = info(page); + + if (len <= off+count) *eof = 1; + + *start = page + off; + len -= off; + + if (len>count) len = count; + if (len<0) len = 0; + + return len; +} + +/* + * List names,function pairs for every entry in /proc/palinfo + * Must be terminated with the NULL,NULL entry. + */ +static palinfo_entry_t palinfo_entries[]={ + { "version_info", version_info, }, + { "vm_info", vm_info, }, + { "cache_info", cache_info, }, + { "power_info", power_info, }, + { "register_info", register_info, }, + { "processor_info", processor_info, }, + { "perfmon_info", perfmon_info, }, + { "frequency_info", frequency_info, }, + { NULL, NULL,} +}; + + +static int __init +palinfo_init(void) +{ + palinfo_entry_t *p; + + printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION); + + palinfo_dir = create_proc_entry("palinfo", S_IFDIR | S_IRUGO | S_IXUGO, NULL); + + for (p = palinfo_entries; p->name ; p++){ + p->entry = create_proc_read_entry (p->name, 0, palinfo_dir, + palinfo_read_entry, p->proc_read); + } + + return 0; +} + +static int __exit +palinfo_exit(void) +{ + palinfo_entry_t *p; + + for (p = palinfo_entries; p->name ; p++){ + remove_proc_entry (p->name, palinfo_dir); + } + remove_proc_entry ("palinfo", 0); + + return 0; +} + +module_init(palinfo_init); +module_exit(palinfo_exit); diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c index 0bc110510..ab86e69b3 100644 --- a/arch/ia64/kernel/pci-dma.c +++ b/arch/ia64/kernel/pci-dma.c @@ -23,8 +23,8 @@ pci_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle void *ret; int gfp = GFP_ATOMIC; - if (!hwdev || hwdev->dma_mask != 0xffffffff) - gfp |= GFP_DMA; + if (!hwdev || hwdev->dma_mask == 0xffffffff) + gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */ ret = (void *)__get_free_pages(gfp, get_order(size)); if (ret) { diff --git a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c index 767cfa5ce..2d814b443 100644 --- a/arch/ia64/kernel/pci.c +++ b/arch/ia64/kernel/pci.c @@ -133,7 +133,7 @@ pci_find_bios(void) * Initialization. Uses the SAL interface */ -#define PCI_BUSSES_TO_SCAN 2 /* On "real" ;) hardware this will be 255 */ +#define PCI_BUSES_TO_SCAN 255 void __init pcibios_init(void) @@ -147,7 +147,7 @@ pcibios_init(void) } printk("PCI: Probing PCI hardware\n"); - for (i = 0; i < PCI_BUSSES_TO_SCAN; i++) + for (i = 0; i < PCI_BUSES_TO_SCAN; i++) pci_scan_bus(i, ops, NULL); platform_pci_fixup(); return; @@ -197,7 +197,7 @@ pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * r ranges->mem_end -= bus->resource[1]->start; } -int __init +int pcibios_enable_device (struct pci_dev *dev) { /* Not needed, since we enable all devices at startup. */ diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index a8c217b9a..58ad3c21c 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -23,8 +23,40 @@ #include <asm/processor.h> #include <asm/sal.h> #include <asm/uaccess.h> +#include <asm/unwind.h> #include <asm/user.h> +static void +do_show_stack (struct unw_frame_info *info, void *arg) +{ + unsigned long ip, sp, bsp; + + printk("\nCall Trace: "); + do { + unw_get_ip(info, &ip); + if (ip == 0) + break; + + unw_get_sp(info, &sp); + unw_get_bsp(info, &bsp); + printk("[<%016lx>] sp=0x%016lx bsp=0x%016lx\n", ip, sp, bsp); + } while (unw_unwind(info) >= 0); +} + +void +show_stack (struct task_struct *task) +{ +#ifdef CONFIG_IA64_NEW_UNWIND + if (!task) + unw_init_running(do_show_stack, 0); + else { + struct unw_frame_info info; + + unw_init_from_blocked_task(&info, task); + do_show_stack(&info, 0); + } +#endif +} void show_regs (struct pt_regs *regs) @@ -71,6 +103,10 @@ show_regs (struct pt_regs *regs) ((i == sof - 1) || (i % 3) == 2) ? "\n" : " "); } } +#ifdef CONFIG_IA64_NEW_UNWIND + if (!user_mode(regs)) + show_stack(0); +#endif } void __attribute__((noreturn)) @@ -98,16 +134,49 @@ cpu_idle (void *unused) if (pm_idle) (*pm_idle)(); #ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC - if (ia64_get_itm() < ia64_get_itc()) { - extern void ia64_reset_itm (void); - - printk("cpu_idle: ITM in past, resetting it...\n"); - ia64_reset_itm(); + local_irq_disable(); + { + u64 itc, itm; + + itc = ia64_get_itc(); + itm = ia64_get_itm(); + if (time_after(itc, itm + 1000)) { + extern void ia64_reset_itm (void); + + printk("cpu_idle: ITM in past (itc=%lx,itm=%lx:%lums)\n", + itc, itm, (itc - itm)/500000); + ia64_reset_itm(); + } } + local_irq_enable(); #endif } } +void +ia64_save_extra (struct task_struct *task) +{ + extern void ia64_save_debug_regs (unsigned long *save_area); + extern void ia32_save_state (struct thread_struct *thread); + + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) + ia64_save_debug_regs(&task->thread.dbr[0]); + if (IS_IA32_PROCESS(ia64_task_regs(task))) + ia32_save_state(&task->thread); +} + +void +ia64_load_extra (struct task_struct *task) +{ + extern void ia64_load_debug_regs (unsigned long *save_area); + extern void ia32_load_state (struct thread_struct *thread); + + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) + ia64_load_debug_regs(&task->thread.dbr[0]); + if (IS_IA32_PROCESS(ia64_task_regs(task))) + ia32_load_state(&task->thread); +} + /* * Copy the state of an ia-64 thread. * @@ -234,9 +303,103 @@ copy_thread (int nr, unsigned long clone_flags, unsigned long usp, return 0; } +#ifdef CONFIG_IA64_NEW_UNWIND + +void +do_copy_regs (struct unw_frame_info *info, void *arg) +{ + unsigned long ar_bsp, ndirty, *krbs, addr, mask, sp, nat_bits = 0, ip; + elf_greg_t *dst = arg; + struct pt_regs *pt; + char nat; + long val; + int i; + + memset(dst, 0, sizeof(elf_gregset_t)); /* don't leak any kernel bits to user-level */ + + if (unw_unwind_to_user(info) < 0) + return; + + unw_get_sp(info, &sp); + pt = (struct pt_regs *) (sp + 16); + + krbs = (unsigned long *) current + IA64_RBS_OFFSET/8; + ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19)); + ar_bsp = (unsigned long) ia64_rse_skip_regs((long *) pt->ar_bspstore, ndirty); + + /* + * Write portion of RSE backing store living on the kernel + * stack to the VM of the process. + */ + for (addr = pt->ar_bspstore; addr < ar_bsp; addr += 8) + if (ia64_peek(pt, current, addr, &val) == 0) + access_process_vm(current, addr, &val, sizeof(val), 1); + + /* r0 is zero */ + for (i = 1, mask = (1UL << i); i < 32; ++i) { + unw_get_gr(info, i, &dst[i], &nat); + if (nat) + nat_bits |= mask; + mask <<= 1; + } + dst[32] = nat_bits; + unw_get_pr(info, &dst[33]); + + for (i = 0; i < 8; ++i) + unw_get_br(info, i, &dst[34 + i]); + + unw_get_rp(info, &ip); + dst[42] = ip + ia64_psr(pt)->ri; + dst[43] = pt->cr_ifs & 0x3fffffffff; + dst[44] = pt->cr_ipsr & IA64_PSR_UM; + + unw_get_ar(info, UNW_AR_RSC, &dst[45]); + /* + * For bsp and bspstore, unw_get_ar() would return the kernel + * addresses, but we need the user-level addresses instead: + */ + dst[46] = ar_bsp; + dst[47] = pt->ar_bspstore; + unw_get_ar(info, UNW_AR_RNAT, &dst[48]); + unw_get_ar(info, UNW_AR_CCV, &dst[49]); + unw_get_ar(info, UNW_AR_UNAT, &dst[50]); + unw_get_ar(info, UNW_AR_FPSR, &dst[51]); + dst[52] = pt->ar_pfs; /* UNW_AR_PFS is == to pt->cr_ifs for interrupt frames */ + unw_get_ar(info, UNW_AR_LC, &dst[53]); + unw_get_ar(info, UNW_AR_EC, &dst[54]); +} + +void +do_dump_fpu (struct unw_frame_info *info, void *arg) +{ + struct task_struct *fpu_owner = ia64_get_fpu_owner(); + elf_fpreg_t *dst = arg; + int i; + + memset(dst, 0, sizeof(elf_fpregset_t)); /* don't leak any "random" bits */ + + if (unw_unwind_to_user(info) < 0) + return; + + /* f0 is 0.0, f1 is 1.0 */ + + for (i = 2; i < 32; ++i) + unw_get_fr(info, i, dst + i); + + if ((fpu_owner == current) || (current->thread.flags & IA64_THREAD_FPH_VALID)) { + ia64_sync_fph(current); + memcpy(dst + 32, current->thread.fph, 96*16); + } +} + +#endif /* CONFIG_IA64_NEW_UNWIND */ + void ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) { +#ifdef CONFIG_IA64_NEW_UNWIND + unw_init_running(do_copy_regs, dst); +#else struct switch_stack *sw = ((struct switch_stack *) pt) - 1; unsigned long ar_ec, cfm, ar_bsp, ndirty, *krbs, addr; @@ -270,7 +433,7 @@ ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) * ar.rsc ar.bsp ar.bspstore ar.rnat * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec */ - memset(dst, 0, sizeof (dst)); /* don't leak any "random" bits */ + memset(dst, 0, sizeof(dst)); /* don't leak any "random" bits */ /* r0 is zero */ dst[ 1] = pt->r1; dst[ 2] = pt->r2; dst[ 3] = pt->r3; dst[ 4] = sw->r4; dst[ 5] = sw->r5; dst[ 6] = sw->r6; dst[ 7] = sw->r7; @@ -285,17 +448,22 @@ ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) dst[34] = pt->b0; dst[35] = sw->b1; dst[36] = sw->b2; dst[37] = sw->b3; dst[38] = sw->b4; dst[39] = sw->b5; dst[40] = pt->b6; dst[41] = pt->b7; - dst[42] = pt->cr_iip; dst[43] = pt->cr_ifs; - dst[44] = pt->cr_ipsr; /* XXX perhaps we should filter out some bits here? --davidm */ + dst[42] = pt->cr_iip + ia64_psr(pt)->ri; + dst[43] = pt->cr_ifs; + dst[44] = pt->cr_ipsr & IA64_PSR_UM; dst[45] = pt->ar_rsc; dst[46] = ar_bsp; dst[47] = pt->ar_bspstore; dst[48] = pt->ar_rnat; dst[49] = pt->ar_ccv; dst[50] = pt->ar_unat; dst[51] = sw->ar_fpsr; dst[52] = pt->ar_pfs; dst[53] = sw->ar_lc; dst[54] = (sw->ar_pfs >> 52) & 0x3f; +#endif /* !CONFIG_IA64_NEW_UNWIND */ } int dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) { +#ifdef CONFIG_IA64_NEW_UNWIND + unw_init_running(do_dump_fpu, dst); +#else struct switch_stack *sw = ((struct switch_stack *) pt) - 1; struct task_struct *fpu_owner = ia64_get_fpu_owner(); @@ -312,6 +480,7 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) } memcpy(dst + 32, current->thread.fph, 96*16); } +#endif return 1; /* f0-f31 are always valid so we always return 1 */ } @@ -384,7 +553,7 @@ release_thread (struct task_struct *dead_task) unsigned long get_wchan (struct task_struct *p) { - struct ia64_frame_info info; + struct unw_frame_info info; unsigned long ip; int count = 0; /* @@ -403,11 +572,11 @@ get_wchan (struct task_struct *p) * gracefully if the process wasn't really blocked after all. * --davidm 99/12/15 */ - ia64_unwind_init_from_blocked_task(&info, p); + unw_init_from_blocked_task(&info, p); do { - if (ia64_unwind_to_previous_frame(&info) < 0) + if (unw_unwind(&info) < 0) return 0; - ip = ia64_unwind_get_ip(&info); + unw_get_ip(&info, &ip); if (ip < first_sched || ip >= last_sched) return ip; } while (count++ < 16); diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 22ed4f569..0efd42bb8 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -7,6 +7,7 @@ * Derived from the x86 and Alpha versions. Most of the code in here * could actually be factored into a common set of routines. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -29,8 +30,74 @@ * id (instruction debug fault disable; one bit) * dd (data debug fault disable; one bit) * ri (restart instruction; two bits) + * is (instruction set; one bit) */ -#define CR_IPSR_CHANGE_MASK 0x06a00100003eUL +#define IPSR_WRITE_MASK \ + (IA64_PSR_UM | IA64_PSR_DB | IA64_PSR_IS | IA64_PSR_ID | IA64_PSR_DD | IA64_PSR_RI) +#define IPSR_READ_MASK IPSR_WRITE_MASK + +#ifdef CONFIG_IA64_NEW_UNWIND + +#define PTRACE_DEBUG 1 + +#if PTRACE_DEBUG +# define dprintk(format...) printk(format) +# define inline +#else +# define dprintk(format...) +#endif + +/* + * Collect the NaT bits for r1-r31 from scratch_unat and return a NaT + * bitset where bit i is set iff the NaT bit of register i is set. + */ +unsigned long +ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat) +{ +# define GET_BITS(first, last, unat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&pt->r##first); \ + unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \ + (ia64_rotl(unat, first) >> bit) & mask; \ + }) + unsigned long val; + + val = GET_BITS( 1, 3, scratch_unat); + val |= GET_BITS(12, 15, scratch_unat); + val |= GET_BITS( 8, 11, scratch_unat); + val |= GET_BITS(16, 31, scratch_unat); + return val; + +# undef GET_BITS +} + +/* + * Set the NaT bits for the scratch registers according to NAT and + * return the resulting unat (assuming the scratch registers are + * stored in PT). + */ +unsigned long +ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat) +{ + unsigned long scratch_unat; + +# define PUT_BITS(first, last, nat) \ + ({ \ + unsigned long bit = ia64_unat_pos(&pt->r##first); \ + unsigned long mask = ((1UL << (last - first + 1)) - 1) << bit; \ + (ia64_rotr(nat, first) << bit) & mask; \ + }) + scratch_unat = PUT_BITS( 1, 3, nat); + scratch_unat |= PUT_BITS(12, 15, nat); + scratch_unat |= PUT_BITS( 8, 11, nat); + scratch_unat |= PUT_BITS(16, 31, nat); + + return scratch_unat; + +# undef PUT_BITS +} + +#else /* !CONFIG_IA64_NEW_UNWIND */ /* * Collect the NaT bits for r1-r31 from sw->caller_unat and @@ -79,28 +146,26 @@ ia64_put_nat_bits (struct pt_regs *pt, struct switch_stack *sw, unsigned long na # undef PUT_BITS } -#define IA64_MLI_TEMPLATE 0x2 +#endif /* !CONFIG_IA64_NEW_UNWIND */ + +#define IA64_MLX_TEMPLATE 0x2 #define IA64_MOVL_OPCODE 6 void ia64_increment_ip (struct pt_regs *regs) { - unsigned long w0, w1, ri = ia64_psr(regs)->ri + 1; + unsigned long w0, ri = ia64_psr(regs)->ri + 1; if (ri > 2) { ri = 0; regs->cr_iip += 16; } else if (ri == 2) { get_user(w0, (char *) regs->cr_iip + 0); - get_user(w1, (char *) regs->cr_iip + 8); - if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) { + if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) { /* - * rfi'ing to slot 2 of an MLI bundle causes + * rfi'ing to slot 2 of an MLX bundle causes * an illegal operation fault. We don't want - * that to happen... Note that we check the - * opcode only. "movl" has a vc bit of 0, but - * since a vc bit of 1 is currently reserved, - * we might just as well treat it like a movl. + * that to happen... */ ri = 0; regs->cr_iip += 16; @@ -112,21 +177,17 @@ ia64_increment_ip (struct pt_regs *regs) void ia64_decrement_ip (struct pt_regs *regs) { - unsigned long w0, w1, ri = ia64_psr(regs)->ri - 1; + unsigned long w0, ri = ia64_psr(regs)->ri - 1; if (ia64_psr(regs)->ri == 0) { regs->cr_iip -= 16; ri = 2; get_user(w0, (char *) regs->cr_iip + 0); - get_user(w1, (char *) regs->cr_iip + 8); - if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) { + if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) { /* - * rfi'ing to slot 2 of an MLI bundle causes + * rfi'ing to slot 2 of an MLX bundle causes * an illegal operation fault. We don't want - * that to happen... Note that we check the - * opcode only. "movl" has a vc bit of 0, but - * since a vc bit of 1 is currently reserved, - * we might just as well treat it like a movl. + * that to happen... */ ri = 1; } @@ -291,7 +352,11 @@ ia64_peek (struct pt_regs *regs, struct task_struct *child, unsigned long addr, laddr = (unsigned long *) addr; child_regs = ia64_task_regs(child); +#ifdef CONFIG_IA64_NEW_UNWIND + child_stack = (struct switch_stack *) (child->thread.ksp + 16); +#else child_stack = (struct switch_stack *) child_regs - 1; +#endif bspstore = (unsigned long *) child_regs->ar_bspstore; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore); @@ -335,7 +400,11 @@ ia64_poke (struct pt_regs *regs, struct task_struct *child, unsigned long addr, laddr = (unsigned long *) addr; child_regs = ia64_task_regs(child); +#ifdef CONFIG_IA64_NEW_UNWIND + child_stack = (struct switch_stack *) (child->thread.ksp + 16); +#else child_stack = (struct switch_stack *) child_regs - 1; +#endif bspstore = (unsigned long *) child_regs->ar_bspstore; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore); @@ -394,21 +463,43 @@ sync_kernel_register_backing_store (struct task_struct *child, long new_bsp, int force_loadrs_to_zero) { - unsigned long *krbs, bspstore, bsp, krbs_num_regs, rbs_end, addr, val; - long ndirty, ret; - struct pt_regs *child_regs; + unsigned long *krbs, bspstore, *kbspstore, bsp, rbs_end, addr, val; + long ndirty, ret = 0; + struct pt_regs *child_regs = ia64_task_regs(child); + +#ifdef CONFIG_IA64_NEW_UNWIND + struct unw_frame_info info; + unsigned long cfm, sof; + + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return -1; + + unw_get_bsp(&info, (unsigned long *) &kbspstore); + + krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + ndirty = ia64_rse_num_regs(krbs, krbs + (child_regs->loadrs >> 19)); + bspstore = child_regs->ar_bspstore; + bsp = (long) ia64_rse_skip_regs((long *)bspstore, ndirty); + + cfm = child_regs->cr_ifs; + if (!(cfm & (1UL << 63))) + unw_get_cfm(&info, &cfm); + sof = (cfm & 0x7f); + rbs_end = (long) ia64_rse_skip_regs((long *)bspstore, sof); +#else struct switch_stack *child_stack; + unsigned long krbs_num_regs; - ret = 0; - child_regs = ia64_task_regs(child); child_stack = (struct switch_stack *) child_regs - 1; - + kbspstore = (unsigned long *) child_stack->ar_bspstore; krbs = (unsigned long *) child + IA64_RBS_OFFSET/8; ndirty = ia64_rse_num_regs(krbs, krbs + (child_regs->loadrs >> 19)); bspstore = child_regs->ar_bspstore; bsp = (long) ia64_rse_skip_regs((long *)bspstore, ndirty); - krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore); + krbs_num_regs = ia64_rse_num_regs(krbs, kbspstore); rbs_end = (long) ia64_rse_skip_regs((long *)bspstore, krbs_num_regs); +#endif /* Return early if nothing to do */ if (bsp == new_bsp) @@ -437,13 +528,15 @@ sync_kernel_register_backing_store (struct task_struct *child, } static void -sync_thread_rbs (struct task_struct *child, int make_writable) +sync_thread_rbs (struct task_struct *child, struct mm_struct *mm, int make_writable) { struct task_struct *p; read_lock(&tasklist_lock); - for_each_task(p) { - if (p->mm == child->mm && p->state != TASK_RUNNING) - sync_kernel_register_backing_store(p, 0, make_writable); + { + for_each_task(p) { + if (p->mm == mm && p->state != TASK_RUNNING) + sync_kernel_register_backing_store(p, 0, make_writable); + } } read_unlock(&tasklist_lock); child->thread.flags |= IA64_THREAD_KRBS_SYNCED; @@ -452,10 +545,11 @@ sync_thread_rbs (struct task_struct *child, int make_writable) /* * Ensure the state in child->thread.fph is up-to-date. */ -static void -sync_fph (struct task_struct *child) +void +ia64_sync_fph (struct task_struct *child) { if (ia64_psr(ia64_task_regs(child))->mfh && ia64_get_fpu_owner() == child) { + ia64_set_fpu_owner(0); ia64_save_fpu(&child->thread.fph[0]); child->thread.flags |= IA64_THREAD_FPH_VALID; } @@ -465,15 +559,383 @@ sync_fph (struct task_struct *child) } } +#ifdef CONFIG_IA64_NEW_UNWIND + +#include <asm/unwind.h> + +static int +access_fr (struct unw_frame_info *info, int regnum, int hi, unsigned long *data, int write_access) +{ + struct ia64_fpreg fpval; + int ret; + + ret = unw_get_fr(info, regnum, &fpval); + if (ret < 0) + return ret; + + if (write_access) { + fpval.u.bits[hi] = *data; + ret = unw_set_fr(info, regnum, fpval); + } else + *data = fpval.u.bits[hi]; + return ret; +} + +static int +access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access) +{ + unsigned long *ptr, *rbs, *bspstore, ndirty, regnum; + struct switch_stack *sw; + struct unw_frame_info info; + struct pt_regs *pt; + + pt = ia64_task_regs(child); + sw = (struct switch_stack *) (child->thread.ksp + 16); + + if ((addr & 0x7) != 0) { + dprintk("ptrace: unaligned register address 0x%lx\n", addr); + return -1; + } + + if (addr < PT_F127 + 16) { + /* accessing fph */ + ia64_sync_fph(child); + ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr); + } else if (addr >= PT_F10 && addr < PT_F15 + 16) { + /* scratch registers untouched by kernel (saved in switch_stack) */ + ptr = (unsigned long *) ((long) sw + addr - PT_NAT_BITS); + } else if (addr < PT_AR_LC + 8) { + /* preserved state: */ + unsigned long nat_bits, scratch_unat, dummy = 0; + struct unw_frame_info info; + char nat = 0; + int ret; + + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return -1; + + switch (addr) { + case PT_NAT_BITS: + if (write_access) { + nat_bits = *data; + scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits); + if (unw_set_ar(&info, UNW_AR_UNAT, scratch_unat) < 0) { + dprintk("ptrace: failed to set ar.unat\n"); + return -1; + } + for (regnum = 4; regnum <= 7; ++regnum) { + unw_get_gr(&info, regnum, &dummy, &nat); + unw_set_gr(&info, regnum, dummy, (nat_bits >> regnum) & 1); + } + } else { + if (unw_get_ar(&info, UNW_AR_UNAT, &scratch_unat) < 0) { + dprintk("ptrace: failed to read ar.unat\n"); + return -1; + } + nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat); + for (regnum = 4; regnum <= 7; ++regnum) { + unw_get_gr(&info, regnum, &dummy, &nat); + nat_bits |= (nat != 0) << regnum; + } + *data = nat_bits; + } + return 0; + + case PT_R4: case PT_R5: case PT_R6: case PT_R7: + if (write_access) { + /* read NaT bit first: */ + ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, data, &nat); + if (ret < 0) + return ret; + } + return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, &nat, + write_access); + + case PT_B1: case PT_B2: case PT_B3: case PT_B4: case PT_B5: + return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, write_access); + + case PT_AR_LC: + return unw_access_ar(&info, UNW_AR_LC, data, write_access); + + default: + if (addr >= PT_F2 && addr < PT_F5 + 16) + return access_fr(&info, (addr - PT_F2)/16 + 2, (addr & 8) != 0, + data, write_access); + else if (addr >= PT_F16 && addr < PT_F31 + 16) + return access_fr(&info, (addr - PT_F16)/16 + 16, (addr & 8) != 0, + data, write_access); + else { + dprintk("ptrace: rejecting access to register address 0x%lx\n", + addr); + return -1; + } + } + } else if (addr < PT_F9+16) { + /* scratch state */ + switch (addr) { + case PT_AR_BSP: + if (write_access) + /* FIXME? Account for lack of ``cover'' in the syscall case */ + return sync_kernel_register_backing_store(child, *data, 1); + else { + rbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + bspstore = (unsigned long *) pt->ar_bspstore; + ndirty = ia64_rse_num_regs(rbs, rbs + (pt->loadrs >> 19)); + + /* + * If we're in a system call, no ``cover'' was done. So to + * make things uniform, we'll add the appropriate displacement + * onto bsp if we're in a system call. + */ + if (!(pt->cr_ifs & (1UL << 63))) { + struct unw_frame_info info; + unsigned long cfm; + + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return -1; + + unw_get_cfm(&info, &cfm); + ndirty += cfm & 0x7f; + } + *data = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty); + return 0; + } + + case PT_CFM: + if (pt->cr_ifs & (1UL << 63)) { + if (write_access) + pt->cr_ifs = ((pt->cr_ifs & ~0x3fffffffffUL) + | (*data & 0x3fffffffffUL)); + else + *data = pt->cr_ifs & 0x3fffffffffUL; + } else { + /* kernel was entered through a system call */ + unsigned long cfm; + + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return -1; + + unw_get_cfm(&info, &cfm); + if (write_access) + unw_set_cfm(&info, ((cfm & ~0x3fffffffffU) + | (*data & 0x3fffffffffUL))); + else + *data = cfm; + } + return 0; + + case PT_CR_IPSR: + if (write_access) + pt->cr_ipsr = ((*data & IPSR_WRITE_MASK) + | (pt->cr_ipsr & ~IPSR_WRITE_MASK)); + else + *data = (pt->cr_ipsr & IPSR_READ_MASK); + return 0; + + case PT_R1: case PT_R2: case PT_R3: + case PT_R8: case PT_R9: case PT_R10: case PT_R11: + case PT_R12: case PT_R13: case PT_R14: case PT_R15: + case PT_R16: case PT_R17: case PT_R18: case PT_R19: + case PT_R20: case PT_R21: case PT_R22: case PT_R23: + case PT_R24: case PT_R25: case PT_R26: case PT_R27: + case PT_R28: case PT_R29: case PT_R30: case PT_R31: + case PT_B0: case PT_B6: case PT_B7: + case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: + case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: + case PT_AR_BSPSTORE: + case PT_AR_RSC: case PT_AR_UNAT: case PT_AR_PFS: case PT_AR_RNAT: + case PT_AR_CCV: case PT_AR_FPSR: case PT_CR_IIP: case PT_PR: + /* scratch register */ + ptr = (unsigned long *) ((long) pt + addr - PT_CR_IPSR); + break; + + default: + /* disallow accessing anything else... */ + dprintk("ptrace: rejecting access to register address 0x%lx\n", + addr); + return -1; + } + } else { + /* access debug registers */ + + if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { + child->thread.flags |= IA64_THREAD_DBG_VALID; + memset(child->thread.dbr, 0, sizeof(child->thread.dbr)); + memset(child->thread.ibr, 0, sizeof( child->thread.ibr)); + } + if (addr >= PT_IBR) { + regnum = (addr - PT_IBR) >> 3; + ptr = &child->thread.ibr[0]; + } else { + regnum = (addr - PT_DBR) >> 3; + ptr = &child->thread.dbr[0]; + } + + if (regnum >= 8) { + dprintk("ptrace: rejecting access to register address 0x%lx\n", addr); + return -1; + } + + ptr += regnum; + } + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; +} + +#else /* !CONFIG_IA64_NEW_UNWIND */ + +static int +access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access) +{ + unsigned long *ptr, *rbs, *bspstore, ndirty, regnum; + struct switch_stack *sw; + struct pt_regs *pt; + + if ((addr & 0x7) != 0) + return -1; + + if (addr < PT_F127+16) { + /* accessing fph */ + ia64_sync_fph(child); + ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr); + } else if (addr < PT_F9+16) { + /* accessing switch_stack or pt_regs: */ + pt = ia64_task_regs(child); + sw = (struct switch_stack *) pt - 1; + + switch (addr) { + case PT_NAT_BITS: + if (write_access) + ia64_put_nat_bits(pt, sw, *data); + else + *data = ia64_get_nat_bits(pt, sw); + return 0; + + case PT_AR_BSP: + if (write_access) + /* FIXME? Account for lack of ``cover'' in the syscall case */ + return sync_kernel_register_backing_store(child, *data, 1); + else { + rbs = (unsigned long *) child + IA64_RBS_OFFSET/8; + bspstore = (unsigned long *) pt->ar_bspstore; + ndirty = ia64_rse_num_regs(rbs, rbs + (pt->loadrs >> 19)); + + /* + * If we're in a system call, no ``cover'' was done. So to + * make things uniform, we'll add the appropriate displacement + * onto bsp if we're in a system call. + */ + if (!(pt->cr_ifs & (1UL << 63))) + ndirty += sw->ar_pfs & 0x7f; + *data = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty); + return 0; + } + + case PT_CFM: + if (write_access) { + if (pt->cr_ifs & (1UL << 63)) + pt->cr_ifs = ((pt->cr_ifs & ~0x3fffffffffUL) + | (*data & 0x3fffffffffUL)); + else + sw->ar_pfs = ((sw->ar_pfs & ~0x3fffffffffUL) + | (*data & 0x3fffffffffUL)); + return 0; + } else { + if ((pt->cr_ifs & (1UL << 63)) == 0) + *data = sw->ar_pfs; + else + /* return only the CFM */ + *data = pt->cr_ifs & 0x3fffffffffUL; + return 0; + } + + case PT_CR_IPSR: + if (write_access) + pt->cr_ipsr = ((*data & IPSR_WRITE_MASK) + | (pt->cr_ipsr & ~IPSR_WRITE_MASK)); + else + *data = (pt->cr_ipsr & IPSR_READ_MASK); + return 0; + + case PT_R1: case PT_R2: case PT_R3: + case PT_R4: case PT_R5: case PT_R6: case PT_R7: + case PT_R8: case PT_R9: case PT_R10: case PT_R11: + case PT_R12: case PT_R13: case PT_R14: case PT_R15: + case PT_R16: case PT_R17: case PT_R18: case PT_R19: + case PT_R20: case PT_R21: case PT_R22: case PT_R23: + case PT_R24: case PT_R25: case PT_R26: case PT_R27: + case PT_R28: case PT_R29: case PT_R30: case PT_R31: + case PT_B0: case PT_B1: case PT_B2: case PT_B3: + case PT_B4: case PT_B5: case PT_B6: case PT_B7: + case PT_F2: case PT_F2+8: case PT_F3: case PT_F3+8: + case PT_F4: case PT_F4+8: case PT_F5: case PT_F5+8: + case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: + case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: + case PT_F10: case PT_F10+8: case PT_F11: case PT_F11+8: + case PT_F12: case PT_F12+8: case PT_F13: case PT_F13+8: + case PT_F14: case PT_F14+8: case PT_F15: case PT_F15+8: + case PT_F16: case PT_F16+8: case PT_F17: case PT_F17+8: + case PT_F18: case PT_F18+8: case PT_F19: case PT_F19+8: + case PT_F20: case PT_F20+8: case PT_F21: case PT_F21+8: + case PT_F22: case PT_F22+8: case PT_F23: case PT_F23+8: + case PT_F24: case PT_F24+8: case PT_F25: case PT_F25+8: + case PT_F26: case PT_F26+8: case PT_F27: case PT_F27+8: + case PT_F28: case PT_F28+8: case PT_F29: case PT_F29+8: + case PT_F30: case PT_F30+8: case PT_F31: case PT_F31+8: + case PT_AR_BSPSTORE: + case PT_AR_RSC: case PT_AR_UNAT: case PT_AR_PFS: case PT_AR_RNAT: + case PT_AR_CCV: case PT_AR_FPSR: case PT_CR_IIP: case PT_PR: + case PT_AR_LC: + ptr = (unsigned long *) ((long) sw + addr - PT_NAT_BITS); + break; + + default: + /* disallow accessing anything else... */ + return -1; + } + } else { + /* access debug registers */ + + if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { + child->thread.flags |= IA64_THREAD_DBG_VALID; + memset(child->thread.dbr, 0, sizeof child->thread.dbr); + memset(child->thread.ibr, 0, sizeof child->thread.ibr); + } + if (addr >= PT_IBR) { + regnum = (addr - PT_IBR) >> 3; + ptr = &child->thread.ibr[0]; + } else { + regnum = (addr - PT_DBR) >> 3; + ptr = &child->thread.dbr[0]; + } + + if (regnum >= 8) + return -1; + + ptr += regnum; + } + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; +} + +#endif /* !CONFIG_IA64_NEW_UNWIND */ + asmlinkage long sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, long arg4, long arg5, long arg6, long arg7, long stack) { struct pt_regs *regs = (struct pt_regs *) &stack; - struct switch_stack *child_stack; - struct pt_regs *child_regs; struct task_struct *child; - unsigned long flags, regnum, *base; + unsigned long flags; long ret; lock_kernel(); @@ -489,17 +951,21 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, ret = -ESRCH; read_lock(&tasklist_lock); - child = find_task_by_pid(pid); + { + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + } read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* no messing around with init! */ - goto out; + goto out_tsk; if (request == PTRACE_ATTACH) { if (child == current) - goto out; + goto out_tsk; if ((!child->dumpable || (current->uid != child->euid) || (current->uid != child->suid) || @@ -508,10 +974,10 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, (current->gid != child->sgid) || (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) - goto out; + goto out_tsk; /* the same process cannot be attached many times */ if (child->flags & PF_PTRACED) - goto out; + goto out_tsk; child->flags |= PF_PTRACED; if (child->p_pptr != current) { unsigned long flags; @@ -524,199 +990,98 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, } send_sig(SIGSTOP, child, 1); ret = 0; - goto out; + goto out_tsk; } ret = -ESRCH; if (!(child->flags & PF_PTRACED)) - goto out; + goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) - goto out; + goto out_tsk; } if (child->p_pptr != current) - goto out; + goto out_tsk; switch (request) { case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: /* read word at location addr */ - if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED) - && atomic_read(&child->mm->mm_users) > 1) - sync_thread_rbs(child, 0); + if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) { + struct mm_struct *mm; + long do_sync; + + task_lock(child); + { + mm = child->mm; + do_sync = mm && (atomic_read(&mm->mm_users) > 1); + } + task_unlock(child); + if (do_sync) + sync_thread_rbs(child, mm, 0); + } ret = ia64_peek(regs, child, addr, &data); if (ret == 0) { ret = data; regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ } - goto out; + goto out_tsk; case PTRACE_POKETEXT: case PTRACE_POKEDATA: /* write the word at location addr */ - if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED) - && atomic_read(&child->mm->mm_users) > 1) - sync_thread_rbs(child, 1); + if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) { + struct mm_struct *mm; + long do_sync; + + task_lock(child); + { + mm = child->mm; + do_sync = mm && (atomic_read(&child->mm->mm_users) > 1); + } + task_unlock(child); + if (do_sync) + sync_thread_rbs(child, mm, 1); + } ret = ia64_poke(regs, child, addr, data); - goto out; + goto out_tsk; case PTRACE_PEEKUSR: /* read the word at addr in the USER area */ - ret = -EIO; - if ((addr & 0x7) != 0) - goto out; - - if (addr < PT_CALLER_UNAT) { - /* accessing fph */ - sync_fph(child); - addr += (unsigned long) &child->thread.fph; - ret = *(unsigned long *) addr; - } else if (addr < PT_F9+16) { - /* accessing switch_stack or pt_regs: */ - child_regs = ia64_task_regs(child); - child_stack = (struct switch_stack *) child_regs - 1; - ret = *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT); - - if (addr == PT_AR_BSP) { - /* ret currently contains pt_regs.loadrs */ - unsigned long *rbs, *bspstore, ndirty; - - rbs = (unsigned long *) child + IA64_RBS_OFFSET/8; - bspstore = (unsigned long *) child_regs->ar_bspstore; - ndirty = ia64_rse_num_regs(rbs, rbs + (ret >> 19)); - ret = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty); - - /* - * If we're in a system call, no ``cover'' was done. So - * to make things uniform, we'll add the appropriate - * displacement onto bsp if we're in a system call. - * - * Note: It may be better to leave the system call case - * alone and subtract the amount of the cover for the - * non-syscall case. That way the reported bsp value - * would actually be the correct bsp for the child - * process. - */ - if (!(child_regs->cr_ifs & (1UL << 63))) { - ret = (unsigned long) - ia64_rse_skip_regs((unsigned long *) ret, - child_stack->ar_pfs & 0x7f); - } - } else if (addr == PT_CFM) { - /* ret currently contains pt_regs.cr_ifs */ - if ((ret & (1UL << 63)) == 0) - ret = child_stack->ar_pfs; - ret &= 0x3fffffffffUL; /* return only the CFM */ - } - } else { - if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { - child->thread.flags |= IA64_THREAD_DBG_VALID; - memset(child->thread.dbr, 0, sizeof child->thread.dbr); - memset(child->thread.ibr, 0, sizeof child->thread.ibr); - } - if (addr >= PT_IBR) { - regnum = (addr - PT_IBR) >> 3; - base = &child->thread.ibr[0]; - } else { - regnum = (addr - PT_DBR) >> 3; - base = &child->thread.dbr[0]; - } - if (regnum >= 8) - goto out; - ret = base[regnum]; + if (access_uarea(child, addr, &data, 0) < 0) { + ret = -EIO; + goto out_tsk; } + ret = data; regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */ - goto out; + goto out_tsk; case PTRACE_POKEUSR: /* write the word at addr in the USER area */ - ret = -EIO; - if ((addr & 0x7) != 0) - goto out; - - if (addr < PT_CALLER_UNAT) { - /* accessing fph */ - sync_fph(child); - addr += (unsigned long) &child->thread.fph; - *(unsigned long *) addr = data; - } else if (addr == PT_AR_BSPSTORE || addr == PT_CALLER_UNAT - || addr == PT_KERNEL_FPSR || addr == PT_K_B0 || addr == PT_K_AR_PFS - || (PT_K_AR_UNAT <= addr && addr <= PT_K_PR)) { - /* - * Don't permit changes to certain registers. - * - * We don't allow bspstore to be modified because doing - * so would mess up any modifications to bsp. (See - * sync_kernel_register_backing_store for the details.) - */ - goto out; - } else if (addr == PT_AR_BSP) { - /* FIXME? Account for lack of ``cover'' in the syscall case */ - ret = sync_kernel_register_backing_store(child, data, 1); - goto out; - } else if (addr == PT_CFM) { - child_regs = ia64_task_regs(child); - child_stack = (struct switch_stack *) child_regs - 1; - - if (child_regs->cr_ifs & (1UL << 63)) { - child_regs->cr_ifs = (child_regs->cr_ifs & ~0x3fffffffffUL) - | (data & 0x3fffffffffUL); - } else { - child_stack->ar_pfs = (child_stack->ar_pfs & ~0x3fffffffffUL) - | (data & 0x3fffffffffUL); - } - } else if (addr < PT_F9+16) { - /* accessing switch_stack or pt_regs */ - child_regs = ia64_task_regs(child); - child_stack = (struct switch_stack *) child_regs - 1; - - if (addr == PT_CR_IPSR) - data = (data & CR_IPSR_CHANGE_MASK) - | (child_regs->cr_ipsr & ~CR_IPSR_CHANGE_MASK); - - *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT) = data; - } else { - if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { - child->thread.flags |= IA64_THREAD_DBG_VALID; - memset(child->thread.dbr, 0, sizeof child->thread.dbr); - memset(child->thread.ibr, 0, sizeof child->thread.ibr); - } - - if (addr >= PT_IBR) { - regnum = (addr - PT_IBR) >> 3; - base = &child->thread.ibr[0]; - } else { - regnum = (addr - PT_DBR) >> 3; - base = &child->thread.dbr[0]; - } - if (regnum >= 8) - goto out; - if (regnum & 1) { - /* force breakpoint to be effective only for user-level: */ - data &= ~(0x7UL << 56); - } - base[regnum] = data; + if (access_uarea(child, addr, &data, 1) < 0) { + ret = -EIO; + goto out_tsk; } ret = 0; - goto out; + goto out_tsk; case PTRACE_GETSIGINFO: ret = -EIO; if (!access_ok(VERIFY_WRITE, data, sizeof (siginfo_t)) || child->thread.siginfo == 0) - goto out; + goto out_tsk; copy_to_user((siginfo_t *) data, child->thread.siginfo, sizeof (siginfo_t)); ret = 0; - goto out; + goto out_tsk; break; case PTRACE_SETSIGINFO: ret = -EIO; if (!access_ok(VERIFY_READ, data, sizeof (siginfo_t)) || child->thread.siginfo == 0) - goto out; + goto out_tsk; copy_from_user(child->thread.siginfo, (siginfo_t *) data, sizeof (siginfo_t)); ret = 0; - goto out; + goto out_tsk; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: /* restart after signal. */ ret = -EIO; if (data > _NSIG) - goto out; + goto out_tsk; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; else @@ -732,7 +1097,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, wake_up_process(child); ret = 0; - goto out; + goto out_tsk; case PTRACE_KILL: /* @@ -741,7 +1106,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, * that it wants to exit. */ if (child->state == TASK_ZOMBIE) /* already dead */ - goto out; + goto out_tsk; child->exit_code = SIGKILL; /* make sure the single step/take-branch tra bits are not set: */ @@ -753,13 +1118,13 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, wake_up_process(child); ret = 0; - goto out; + goto out_tsk; case PTRACE_SINGLESTEP: /* let child execute for one instruction */ case PTRACE_SINGLEBLOCK: ret = -EIO; if (data > _NSIG) - goto out; + goto out_tsk; child->flags &= ~PF_TRACESYS; if (request == PTRACE_SINGLESTEP) { @@ -775,12 +1140,12 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, /* give it a chance to run. */ wake_up_process(child); ret = 0; - goto out; + goto out_tsk; case PTRACE_DETACH: /* detach a process that was attached. */ ret = -EIO; if (data > _NSIG) - goto out; + goto out_tsk; child->flags &= ~(PF_PTRACED|PF_TRACESYS); child->exit_code = data; @@ -799,12 +1164,14 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data, wake_up_process(child); ret = 0; - goto out; + goto out_tsk; default: ret = -EIO; - goto out; + goto out_tsk; } + out_tsk: + free_task_struct(child); out: unlock_kernel(); return ret; diff --git a/arch/ia64/kernel/sal_stub.S b/arch/ia64/kernel/sal_stub.S deleted file mode 100644 index d73851810..000000000 --- a/arch/ia64/kernel/sal_stub.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 1998-2000 Hewlett-Packard Co - * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> - */ -#ifndef __GCC_MULTIREG_RETVALS__ - /* - * gcc currently does not conform to the ia-64 calling - * convention as far as returning function values are - * concerned. Instead of returning values up to 32 bytes in - * size in r8-r11, gcc returns any value bigger than a - * doubleword via a structure that's allocated by the caller - * and whose address is passed into the function. Since - * SAL_PROC returns values according to the calling - * convention, this stub takes care of copying r8-r11 to the - * place where gcc expects them. - */ - .text - .psr abi64 - .psr lsb - .lsb - - .align 16 - .global ia64_sal_stub -ia64_sal_stub: - /* - * Sheesh, the Cygnus backend passes the pointer to a return value structure in - * in0 whereas the HP backend passes it in r8. Don't you hate those little - * differences... - */ -#ifdef GCC_RETVAL_POINTER_IN_R8 - adds r2=-24,sp - adds sp=-48,sp - mov r14=rp - ;; - st8 [r2]=r8,8 // save pointer to return value - addl r3=@ltoff(ia64_sal),gp - ;; - ld8 r3=[r3] - st8 [r2]=gp,8 // save global pointer - ;; - ld8 r3=[r3] // fetch the value of ia64_sal - st8 [r2]=r14 // save return pointer - ;; - ld8 r2=[r3],8 // load function's entry point - ;; - ld8 gp=[r3] // load function's global pointer - ;; - mov b6=r2 - br.call.sptk.few rp=b6 -.ret0: adds r2=24,sp - ;; - ld8 r3=[r2],8 // restore pointer to return value - ;; - ld8 gp=[r2],8 // restore global pointer - st8 [r3]=r8,8 - ;; - ld8 r14=[r2] // restore return pointer - st8 [r3]=r9,8 - ;; - mov rp=r14 - st8 [r3]=r10,8 - ;; - st8 [r3]=r11,8 - adds sp=48,sp - br.sptk.few rp -#else - /* - * On input: - * in0 = pointer to return value structure - * in1 = index of SAL function to call - * in2..inN = remaining args to SAL call - */ - /* - * We allocate one input and eight output register such that the br.call instruction - * will rename in1-in7 to in0-in6---exactly what we want because SAL doesn't want to - * see the pointer to the return value structure. - */ - alloc r15=ar.pfs,1,0,8,0 - - adds r2=-24,sp - adds sp=-48,sp - mov r14=rp - ;; - st8 [r2]=r15,8 // save ar.pfs - addl r3=@ltoff(ia64_sal),gp - ;; - ld8 r3=[r3] // get address of ia64_sal - st8 [r2]=gp,8 // save global pointer - ;; - ld8 r3=[r3] // get value of ia64_sal - st8 [r2]=r14,8 // save return address (rp) - ;; - ld8 r2=[r3],8 // load function's entry point - ;; - ld8 gp=[r3] // load function's global pointer - mov b6=r2 - br.call.sptk.few rp=b6 // make SAL call -.ret0: adds r2=24,sp - ;; - ld8 r15=[r2],8 // restore ar.pfs - ;; - ld8 gp=[r2],8 // restore global pointer - st8 [in0]=r8,8 // store 1. dword of return value - ;; - ld8 r14=[r2] // restore return address (rp) - st8 [in0]=r9,8 // store 2. dword of return value - ;; - mov rp=r14 - st8 [in0]=r10,8 // store 3. dword of return value - ;; - st8 [in0]=r11,8 - adds sp=48,sp // pop stack frame - mov ar.pfs=r15 - br.ret.sptk.few rp -#endif - - .endp ia64_sal_stub -#endif /* __GCC_MULTIREG_RETVALS__ */ diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 80838f990..09850fdd8 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -28,6 +28,7 @@ #include <linux/console.h> #include <asm/acpi-ext.h> +#include <asm/ia32.h> #include <asm/page.h> #include <asm/machvec.h> #include <asm/processor.h> @@ -36,6 +37,10 @@ #include <asm/efi.h> #include <asm/mca.h> +#ifdef CONFIG_BLK_DEV_RAM +# include <linux/blk.h> +#endif + extern char _end; /* cpu_data[bootstrap_processor] is data for the bootstrap processor: */ @@ -108,6 +113,8 @@ setup_arch (char **cmdline_p) { unsigned long max_pfn, bootmap_start, bootmap_size; + unw_init(); + /* * The secondary bootstrap loader passes us the boot * parameters at the beginning of the ZERO_PAGE, so let's @@ -125,11 +132,22 @@ setup_arch (char **cmdline_p) * change APIs, they'd do things for the better. Grumble... */ bootmap_start = PAGE_ALIGN(__pa(&_end)); + if (ia64_boot_param.initrd_size) + bootmap_start = PAGE_ALIGN(bootmap_start + ia64_boot_param.initrd_size); bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn); efi_memmap_walk(free_available_memory, 0); reserve_bootmem(bootmap_start, bootmap_size); +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = ia64_boot_param.initrd_start; + if (initrd_start) { + initrd_end = initrd_start+ia64_boot_param.initrd_size; + printk("Initial ramdisk at: 0x%p (%lu bytes)\n", + (void *) initrd_start, ia64_boot_param.initrd_size); + reserve_bootmem(virt_to_phys(initrd_start), ia64_boot_param.initrd_size); + } +#endif #if 0 /* XXX fix me */ init_mm.start_code = (unsigned long) &_stext; @@ -155,10 +173,8 @@ setup_arch (char **cmdline_p) #ifdef CONFIG_SMP bootstrap_processor = hard_smp_processor_id(); current->processor = bootstrap_processor; -#else - cpu_init(); - identify_cpu(&cpu_data[0]); #endif + cpu_init(); /* initialize the bootstrap CPU */ if (efi.acpi) { /* Parse the ACPI tables */ @@ -270,35 +286,18 @@ identify_cpu (struct cpuinfo_ia64 *c) u64 features; } field; } cpuid; + pal_vm_info_1_u_t vm1; + pal_vm_info_2_u_t vm2; + pal_status_t status; + unsigned long impl_va_msb = 50, phys_addr_size = 44; /* Itanium defaults */ int i; - for (i = 0; i < 5; ++i) { + for (i = 0; i < 5; ++i) cpuid.bits[i] = ia64_get_cpuid(i); - } -#ifdef CONFIG_SMP - /* - * XXX Instead of copying the ITC info from the bootstrap - * processor, ia64_init_itm() should be done per CPU. That - * should get you the right info. --davidm 1/24/00 - */ - if (c != &cpu_data[bootstrap_processor]) { - memset(c, 0, sizeof(struct cpuinfo_ia64)); - c->proc_freq = cpu_data[bootstrap_processor].proc_freq; - c->itc_freq = cpu_data[bootstrap_processor].itc_freq; - c->cyc_per_usec = cpu_data[bootstrap_processor].cyc_per_usec; - c->usec_per_cyc = cpu_data[bootstrap_processor].usec_per_cyc; - } -#else memset(c, 0, sizeof(struct cpuinfo_ia64)); -#endif memcpy(c->vendor, cpuid.field.vendor, 16); -#ifdef CONFIG_IA64_SOFTSDV_HACKS - /* BUG: SoftSDV doesn't support the cpuid registers. */ - if (c->vendor[0] == '\0') - memcpy(c->vendor, "Intel", 6); -#endif c->ppn = cpuid.field.ppn; c->number = cpuid.field.number; c->revision = cpuid.field.revision; @@ -306,8 +305,29 @@ identify_cpu (struct cpuinfo_ia64 *c) c->family = cpuid.field.family; c->archrev = cpuid.field.archrev; c->features = cpuid.field.features; -#ifdef CONFIG_SMP - c->loops_per_sec = loops_per_sec; + + status = ia64_pal_vm_summary(&vm1, &vm2); + if (status == PAL_STATUS_SUCCESS) { +#if 1 + /* + * XXX the current PAL code returns IMPL_VA_MSB==60, which is dead-wrong. + * --davidm 00/05/26 + s*/ + impl_va_msb = 50; +#else + impl_va_msb = vm2.pal_vm_info_2_s.impl_va_msb; +#endif + phys_addr_size = vm1.pal_vm_info_1_s.phys_add_size; + } + printk("processor implements %lu virtual and %lu physical address bits\n", + impl_va_msb + 1, phys_addr_size); + c->unimpl_va_mask = ~((7L<<61) | ((1L << (impl_va_msb + 1)) - 1)); + c->unimpl_pa_mask = ~((1L<<63) | ((1L << phys_addr_size) - 1)); + +#ifdef CONFIG_IA64_SOFTSDV_HACKS + /* BUG: SoftSDV doesn't support the cpuid registers. */ + if (c->vendor[0] == '\0') + memcpy(c->vendor, "Intel", 6); #endif } @@ -318,6 +338,11 @@ identify_cpu (struct cpuinfo_ia64 *c) void cpu_init (void) { + extern void __init ia64_rid_init (void); + extern void __init ia64_tlb_init (void); + + identify_cpu(&my_cpu_data); + /* Clear the stack memory reserved for pt_regs: */ memset(ia64_task_regs(current), 0, sizeof(struct pt_regs)); @@ -331,6 +356,21 @@ cpu_init (void) */ ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP); ia64_set_fpu_owner(0); /* initialize ar.k5 */ + atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + + ia64_rid_init(); + ia64_tlb_init(); + +#ifdef CONFIG_IA32_SUPPORT + /* initialize global ia32 state - CR0 and CR4 */ + __asm__("mov ar.cflg = %0" + : /* no outputs */ + : "r" (((ulong) IA32_CR4 << 32) | IA32_CR0)); +#endif + +#ifdef CONFIG_SMP + normal_xtp(); +#endif } diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c index 25197c1d4..8a46377c9 100644 --- a/arch/ia64/kernel/signal.c +++ b/arch/ia64/kernel/signal.c @@ -37,16 +37,26 @@ # define GET_SIGSET(k,u) __get_user((k)->sig[0], &(u)->sig[0]) #endif +struct sigscratch { +#ifdef CONFIG_IA64_NEW_UNWIND + unsigned long scratch_unat; /* ar.unat for the general registers saved in pt */ + unsigned long pad; +#else + struct switch_stack sw; +#endif + struct pt_regs pt; +}; + struct sigframe { struct siginfo info; struct sigcontext sc; }; extern long sys_wait4 (int, int *, int, struct rusage *); -extern long ia64_do_signal (sigset_t *, struct pt_regs *, long); /* forward decl */ +extern long ia64_do_signal (sigset_t *, struct sigscratch *, long); /* forward decl */ long -ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct pt_regs *pt) +ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct sigscratch *scr) { sigset_t oldset, set; @@ -71,12 +81,19 @@ ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct pt_regs *pt) * pre-set the correct error code here to ensure that the right values * get saved in sigcontext by ia64_do_signal. */ - pt->r8 = EINTR; - pt->r10 = -1; +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(&scr->pt)) { + scr->pt.r8 = -EINTR; + } else +#endif + { + scr->pt.r8 = EINTR; + scr->pt.r10 = -1; + } while (1) { set_current_state(TASK_INTERRUPTIBLE); schedule(); - if (ia64_do_signal(&oldset, pt, 1)) + if (ia64_do_signal(&oldset, scr, 1)) return -EINTR; } } @@ -91,9 +108,8 @@ sys_sigaltstack (const stack_t *uss, stack_t *uoss, long arg2, long arg3, long a } static long -restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt) +restore_sigcontext (struct sigcontext *sc, struct sigscratch *scr) { - struct switch_stack *sw = (struct switch_stack *) pt - 1; unsigned long ip, flags, nat, um, cfm; long err; @@ -104,28 +120,32 @@ restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt) err |= __get_user(ip, &sc->sc_ip); /* instruction pointer */ err |= __get_user(cfm, &sc->sc_cfm); err |= __get_user(um, &sc->sc_um); /* user mask */ - err |= __get_user(pt->ar_rsc, &sc->sc_ar_rsc); - err |= __get_user(pt->ar_ccv, &sc->sc_ar_ccv); - err |= __get_user(pt->ar_unat, &sc->sc_ar_unat); - err |= __get_user(pt->ar_fpsr, &sc->sc_ar_fpsr); - err |= __get_user(pt->ar_pfs, &sc->sc_ar_pfs); - err |= __get_user(pt->pr, &sc->sc_pr); /* predicates */ - err |= __get_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */ - err |= __get_user(pt->b6, &sc->sc_br[6]); /* b6 */ - err |= __get_user(pt->b7, &sc->sc_br[7]); /* b7 */ - err |= __copy_from_user(&pt->r1, &sc->sc_gr[1], 3*8); /* r1-r3 */ - err |= __copy_from_user(&pt->r8, &sc->sc_gr[8], 4*8); /* r8-r11 */ - err |= __copy_from_user(&pt->r12, &sc->sc_gr[12], 4*8); /* r12-r15 */ - err |= __copy_from_user(&pt->r16, &sc->sc_gr[16], 16*8); /* r16-r31 */ - - pt->cr_ifs = cfm | (1UL << 63); + err |= __get_user(scr->pt.ar_rsc, &sc->sc_ar_rsc); + err |= __get_user(scr->pt.ar_ccv, &sc->sc_ar_ccv); + err |= __get_user(scr->pt.ar_unat, &sc->sc_ar_unat); + err |= __get_user(scr->pt.ar_fpsr, &sc->sc_ar_fpsr); + err |= __get_user(scr->pt.ar_pfs, &sc->sc_ar_pfs); + err |= __get_user(scr->pt.pr, &sc->sc_pr); /* predicates */ + err |= __get_user(scr->pt.b0, &sc->sc_br[0]); /* b0 (rp) */ + err |= __get_user(scr->pt.b6, &sc->sc_br[6]); /* b6 */ + err |= __get_user(scr->pt.b7, &sc->sc_br[7]); /* b7 */ + err |= __copy_from_user(&scr->pt.r1, &sc->sc_gr[1], 3*8); /* r1-r3 */ + err |= __copy_from_user(&scr->pt.r8, &sc->sc_gr[8], 4*8); /* r8-r11 */ + err |= __copy_from_user(&scr->pt.r12, &sc->sc_gr[12], 4*8); /* r12-r15 */ + err |= __copy_from_user(&scr->pt.r16, &sc->sc_gr[16], 16*8); /* r16-r31 */ + + scr->pt.cr_ifs = cfm | (1UL << 63); /* establish new instruction pointer: */ - pt->cr_iip = ip & ~0x3UL; - ia64_psr(pt)->ri = ip & 0x3; - pt->cr_ipsr = (pt->cr_ipsr & ~IA64_PSR_UM) | (um & IA64_PSR_UM); + scr->pt.cr_iip = ip & ~0x3UL; + ia64_psr(&scr->pt)->ri = ip & 0x3; + scr->pt.cr_ipsr = (scr->pt.cr_ipsr & ~IA64_PSR_UM) | (um & IA64_PSR_UM); - ia64_put_nat_bits (pt, sw, nat); /* restore the original scratch NaT bits */ +#ifdef CONFIG_IA64_NEW_UNWIND + scr->scratch_unat = ia64_put_scratch_nat_bits(&scr->pt, nat); +#else + ia64_put_nat_bits(&scr->pt, &scr->sw, nat); /* restore the original scratch NaT bits */ +#endif if (flags & IA64_SC_FLAG_FPH_VALID) { struct task_struct *fpu_owner = ia64_get_fpu_owner(); @@ -138,7 +158,8 @@ restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt) return err; } -int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +int +copy_siginfo_to_user (siginfo_t *to, siginfo_t *from) { if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) return -EFAULT; @@ -147,43 +168,39 @@ int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) else { int err; - /* If you change siginfo_t structure, please be sure - this code is fixed accordingly. - It should never copy any pad contained in the structure - to avoid security leaks, but must copy the generic - 3 ints plus the relevant union member. */ + /* + * If you change siginfo_t structure, please be sure + * this code is fixed accordingly. It should never + * copy any pad contained in the structure to avoid + * security leaks, but must copy the generic 3 ints + * plus the relevant union member. + */ err = __put_user(from->si_signo, &to->si_signo); err |= __put_user(from->si_errno, &to->si_errno); err |= __put_user((short)from->si_code, &to->si_code); switch (from->si_code >> 16) { - case __SI_FAULT >> 16: - case __SI_POLL >> 16: + case __SI_FAULT >> 16: + err |= __put_user(from->si_isr, &to->si_isr); + case __SI_POLL >> 16: err |= __put_user(from->si_addr, &to->si_addr); err |= __put_user(from->si_imm, &to->si_imm); break; - case __SI_CHLD >> 16: + case __SI_CHLD >> 16: err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_status, &to->si_status); - default: + default: err |= __put_user(from->si_uid, &to->si_uid); err |= __put_user(from->si_pid, &to->si_pid); break; - /* case __SI_RT: This is not generated by the kernel as of now. */ + /* case __SI_RT: This is not generated by the kernel as of now. */ } return err; } } -/* - * When we get here, ((struct switch_stack *) pt - 1) is a - * switch_stack frame that has no defined value. Upon return, we - * expect sw->caller_unat to contain the new unat value. The reason - * we use a full switch_stack frame is so everything is symmetric - * with ia64_do_signal(). - */ long -ia64_rt_sigreturn (struct pt_regs *pt) +ia64_rt_sigreturn (struct sigscratch *scr) { extern char ia64_strace_leave_kernel, ia64_leave_kernel; struct sigcontext *sc; @@ -191,7 +208,7 @@ ia64_rt_sigreturn (struct pt_regs *pt) sigset_t set; long retval; - sc = &((struct sigframe *) (pt->r12 + 16))->sc; + sc = &((struct sigframe *) (scr->pt.r12 + 16))->sc; /* * When we return to the previously executing context, r8 and @@ -200,9 +217,15 @@ ia64_rt_sigreturn (struct pt_regs *pt) * must not touch r8 or r10 as otherwise user-level stat could * be corrupted. */ - retval = (long) &ia64_leave_kernel | 1; - if ((current->flags & PF_TRACESYS) - && (sc->sc_flags & IA64_SC_FLAG_IN_SYSCALL)) + retval = (long) &ia64_leave_kernel; + if (current->flags & PF_TRACESYS) + /* + * strace expects to be notified after sigreturn + * returns even though the context to which we return + * may not be in the middle of a syscall. Thus, the + * return-value that strace displays for sigreturn is + * meaningless. + */ retval = (long) &ia64_strace_leave_kernel; if (!access_ok(VERIFY_READ, sc, sizeof(*sc))) @@ -217,18 +240,18 @@ ia64_rt_sigreturn (struct pt_regs *pt) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - if (restore_sigcontext(sc, pt)) + if (restore_sigcontext(sc, scr)) goto give_sigsegv; #if DEBUG_SIG printk("SIG return (%s:%d): sp=%lx ip=%lx\n", - current->comm, current->pid, pt->r12, pt->cr_iip); + current->comm, current->pid, scr->pt.r12, scr->pt.cr_iip); #endif /* * It is more difficult to avoid calling this function than to * call it and ignore errors. */ - do_sigaltstack(&sc->sc_stack, 0, pt->r12); + do_sigaltstack(&sc->sc_stack, 0, scr->pt.r12); return retval; give_sigsegv: @@ -249,14 +272,13 @@ ia64_rt_sigreturn (struct pt_regs *pt) * trampoline starts. Everything else is done at the user-level. */ static long -setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct pt_regs *pt) +setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct sigscratch *scr) { - struct switch_stack *sw = (struct switch_stack *) pt - 1; struct task_struct *fpu_owner = ia64_get_fpu_owner(); unsigned long flags = 0, ifs, nat; long err; - ifs = pt->cr_ifs; + ifs = scr->pt.cr_ifs; if (on_sig_stack((unsigned long) sc)) flags |= IA64_SC_FLAG_ONSTACK; @@ -276,46 +298,49 @@ setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct pt_regs *pt) * Note: sw->ar_unat is UNDEFINED unless the process is being * PTRACED. However, this is OK because the NaT bits of the * preserved registers (r4-r7) are never being looked at by - * the signal handler (register r4-r7 are used instead). + * the signal handler (registers r4-r7 are used instead). */ - nat = ia64_get_nat_bits(pt, sw); +#ifdef CONFIG_IA64_NEW_UNWIND + nat = ia64_get_scratch_nat_bits(&scr->pt, scr->scratch_unat); +#else + nat = ia64_get_nat_bits(&scr->pt, &scr->sw); +#endif err = __put_user(flags, &sc->sc_flags); err |= __put_user(nat, &sc->sc_nat); err |= PUT_SIGSET(mask, &sc->sc_mask); - err |= __put_user(pt->cr_ipsr & IA64_PSR_UM, &sc->sc_um); - err |= __put_user(pt->ar_rsc, &sc->sc_ar_rsc); - err |= __put_user(pt->ar_ccv, &sc->sc_ar_ccv); - err |= __put_user(pt->ar_unat, &sc->sc_ar_unat); /* ar.unat */ - err |= __put_user(pt->ar_fpsr, &sc->sc_ar_fpsr); /* ar.fpsr */ - err |= __put_user(pt->ar_pfs, &sc->sc_ar_pfs); - err |= __put_user(pt->pr, &sc->sc_pr); /* predicates */ - err |= __put_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */ - err |= __put_user(pt->b6, &sc->sc_br[6]); /* b6 */ - err |= __put_user(pt->b7, &sc->sc_br[7]); /* b7 */ - - err |= __copy_to_user(&sc->sc_gr[1], &pt->r1, 3*8); /* r1-r3 */ - err |= __copy_to_user(&sc->sc_gr[8], &pt->r8, 4*8); /* r8-r11 */ - err |= __copy_to_user(&sc->sc_gr[12], &pt->r12, 4*8); /* r12-r15 */ - err |= __copy_to_user(&sc->sc_gr[16], &pt->r16, 16*8); /* r16-r31 */ - - err |= __put_user(pt->cr_iip + ia64_psr(pt)->ri, &sc->sc_ip); - err |= __put_user(pt->r12, &sc->sc_gr[12]); /* r12 */ + err |= __put_user(scr->pt.cr_ipsr & IA64_PSR_UM, &sc->sc_um); + err |= __put_user(scr->pt.ar_rsc, &sc->sc_ar_rsc); + err |= __put_user(scr->pt.ar_ccv, &sc->sc_ar_ccv); + err |= __put_user(scr->pt.ar_unat, &sc->sc_ar_unat); /* ar.unat */ + err |= __put_user(scr->pt.ar_fpsr, &sc->sc_ar_fpsr); /* ar.fpsr */ + err |= __put_user(scr->pt.ar_pfs, &sc->sc_ar_pfs); + err |= __put_user(scr->pt.pr, &sc->sc_pr); /* predicates */ + err |= __put_user(scr->pt.b0, &sc->sc_br[0]); /* b0 (rp) */ + err |= __put_user(scr->pt.b6, &sc->sc_br[6]); /* b6 */ + err |= __put_user(scr->pt.b7, &sc->sc_br[7]); /* b7 */ + + err |= __copy_to_user(&sc->sc_gr[1], &scr->pt.r1, 3*8); /* r1-r3 */ + err |= __copy_to_user(&sc->sc_gr[8], &scr->pt.r8, 4*8); /* r8-r11 */ + err |= __copy_to_user(&sc->sc_gr[12], &scr->pt.r12, 4*8); /* r12-r15 */ + err |= __copy_to_user(&sc->sc_gr[16], &scr->pt.r16, 16*8); /* r16-r31 */ + + err |= __put_user(scr->pt.cr_iip + ia64_psr(&scr->pt)->ri, &sc->sc_ip); return err; } static long -setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *pt) +setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, + struct sigscratch *scr) { - struct switch_stack *sw = (struct switch_stack *) pt - 1; extern char ia64_sigtramp[], __start_gate_section[]; unsigned long tramp_addr, new_rbs = 0; struct sigframe *frame; struct siginfo si; long err; - frame = (void *) pt->r12; + frame = (void *) scr->pt.r12; tramp_addr = GATE_ADDR + (ia64_sigtramp - __start_gate_section); if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && !on_sig_stack((unsigned long) frame)) { new_rbs = (current->sas_ss_sp + sizeof(long) - 1) & ~(sizeof(long) - 1); @@ -331,31 +356,39 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, st err |= __put_user(current->sas_ss_sp, &frame->sc.sc_stack.ss_sp); err |= __put_user(current->sas_ss_size, &frame->sc.sc_stack.ss_size); - err |= __put_user(sas_ss_flags(pt->r12), &frame->sc.sc_stack.ss_flags); - err |= setup_sigcontext(&frame->sc, set, pt); + err |= __put_user(sas_ss_flags(scr->pt.r12), &frame->sc.sc_stack.ss_flags); + err |= setup_sigcontext(&frame->sc, set, scr); if (err) goto give_sigsegv; - pt->r12 = (unsigned long) frame - 16; /* new stack pointer */ - pt->r2 = sig; /* signal number */ - pt->r3 = (unsigned long) ka->sa.sa_handler; /* addr. of handler's proc. descriptor */ - pt->r15 = new_rbs; - pt->ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */ - pt->cr_iip = tramp_addr; - ia64_psr(pt)->ri = 0; /* start executing in first slot */ + scr->pt.r12 = (unsigned long) frame - 16; /* new stack pointer */ + scr->pt.r2 = sig; /* signal number */ + scr->pt.r3 = (unsigned long) ka->sa.sa_handler; /* addr. of handler's proc desc */ + scr->pt.r15 = new_rbs; + scr->pt.ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */ + scr->pt.cr_iip = tramp_addr; + ia64_psr(&scr->pt)->ri = 0; /* start executing in first slot */ +#ifdef CONFIG_IA64_NEW_UNWIND + /* + * Note: this affects only the NaT bits of the scratch regs + * (the ones saved in pt_regs), which is exactly what we want. + */ + scr->scratch_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */ +#else /* * Note: this affects only the NaT bits of the scratch regs - * (the ones saved in pt_regs, which is exactly what we want. + * (the ones saved in pt_regs), which is exactly what we want. * The NaT bits for the preserved regs (r4-r7) are in * sw->ar_unat iff this process is being PTRACED. */ - sw->caller_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */ + scr->sw.caller_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */ +#endif #if DEBUG_SIG printk("SIG deliver (%s:%d): sig=%d sp=%lx ip=%lx handler=%lx\n", - current->comm, current->pid, sig, pt->r12, pt->cr_iip, pt->r3); + current->comm, current->pid, sig, scr->pt.r12, scr->pt.cr_iip, scr->pt.r3); #endif return 1; @@ -374,17 +407,17 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, st static long handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, - struct pt_regs *pt) + struct sigscratch *scr) { #ifdef CONFIG_IA32_SUPPORT - if (IS_IA32_PROCESS(pt)) { + if (IS_IA32_PROCESS(&scr->pt)) { /* send signal to IA-32 process */ - if (!ia32_setup_frame1(sig, ka, info, oldset, pt)) + if (!ia32_setup_frame1(sig, ka, info, oldset, &scr->pt)) return 0; } else #endif /* send signal to IA-64 process */ - if (!setup_frame(sig, ka, info, oldset, pt)) + if (!setup_frame(sig, ka, info, oldset, scr)) return 0; if (ka->sa.sa_flags & SA_ONESHOT) @@ -401,12 +434,6 @@ handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigse } /* - * When we get here, `pt' points to struct pt_regs and ((struct - * switch_stack *) pt - 1) points to a switch stack structure. - * HOWEVER, in the normal case, the ONLY value valid in the - * switch_stack is the caller_unat field. The entire switch_stack is - * valid ONLY if current->flags has PF_PTRACED set. - * * Note that `init' is a special process: it doesn't get signals it * doesn't want to handle. Thus you cannot kill init even with a * SIGKILL even by mistake. @@ -416,24 +443,35 @@ handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigse * user-level signal handling stack-frames in one go after that. */ long -ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall) +ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) { struct k_sigaction *ka; siginfo_t info; long restart = in_syscall; + long errno = scr->pt.r8; /* * In the ia64_leave_kernel code path, we want the common case * to go fast, which is why we may in certain cases get here * from kernel mode. Just return without doing anything if so. */ - if (!user_mode(pt)) + if (!user_mode(&scr->pt)) return 0; if (!oldset) oldset = ¤t->blocked; - if (pt->r10 != -1) { +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(&scr->pt)) { + if (in_syscall) { + if (errno >= 0) + restart = 0; + else + errno = -errno; + } + } else +#endif + if (scr->pt.r10 != -1) { /* * A system calls has to be restarted only if one of * the error codes ERESTARTNOHAND, ERESTARTSYS, or @@ -527,7 +565,7 @@ ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall) case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: - if (do_coredump(signr, pt)) + if (do_coredump(signr, &scr->pt)) exit_code |= 0x80; /* FALLTHRU */ @@ -542,39 +580,54 @@ ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall) } if (restart) { - switch (pt->r8) { + switch (errno) { case ERESTARTSYS: if ((ka->sa.sa_flags & SA_RESTART) == 0) { case ERESTARTNOHAND: - pt->r8 = EINTR; - /* note: pt->r10 is already -1 */ +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(&scr->pt)) + scr->pt.r8 = -EINTR; + else +#endif + scr->pt.r8 = EINTR; + /* note: scr->pt.r10 is already -1 */ break; } case ERESTARTNOINTR: - ia64_decrement_ip(pt); +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(&scr->pt)) { + scr->pt.r8 = scr->pt.r1; + scr->pt.cr_iip -= 2; + } else +#endif + ia64_decrement_ip(&scr->pt); } } /* Whee! Actually deliver the signal. If the delivery failed, we need to continue to iterate in this loop so we can deliver the SIGSEGV... */ - if (handle_signal(signr, ka, &info, oldset, pt)) + if (handle_signal(signr, ka, &info, oldset, scr)) return 1; } /* Did we come from a system call? */ if (restart) { /* Restart the system call - no handlers present */ - if (pt->r8 == ERESTARTNOHAND || - pt->r8 == ERESTARTSYS || - pt->r8 == ERESTARTNOINTR) { + if (errno == ERESTARTNOHAND || errno == ERESTARTSYS || errno == ERESTARTNOINTR) { +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(&scr->pt)) { + scr->pt.r8 = scr->pt.r1; + scr->pt.cr_iip -= 2; + } else +#endif /* * Note: the syscall number is in r15 which is * saved in pt_regs so all we need to do here * is adjust ip so that the "break" * instruction gets re-executed. */ - ia64_decrement_ip(pt); + ia64_decrement_ip(&scr->pt); } } return 0; diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c index 43d9f2dde..e6f0f36fe 100644 --- a/arch/ia64/kernel/smp.c +++ b/arch/ia64/kernel/smp.c @@ -21,11 +21,13 @@ #include <linux/smp.h> #include <linux/kernel_stat.h> #include <linux/mm.h> +#include <linux/delay.h> #include <asm/atomic.h> #include <asm/bitops.h> #include <asm/current.h> #include <asm/delay.h> + #include <asm/io.h> #include <asm/irq.h> #include <asm/page.h> @@ -39,6 +41,7 @@ extern int cpu_idle(void * unused); extern void _start(void); +extern void machine_halt(void); extern int cpu_now_booting; /* Used by head.S to find idle task */ extern volatile unsigned long cpu_online_map; /* Bitmap of available cpu's */ @@ -66,15 +69,18 @@ struct smp_call_struct { atomic_t unstarted_count; atomic_t unfinished_count; }; -static struct smp_call_struct *smp_call_function_data; +static volatile struct smp_call_struct *smp_call_function_data; -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#ifdef CONFIG_ITANIUM_A1_SPECIFIC extern spinlock_t ivr_read_lock; #endif #define IPI_RESCHEDULE 0 #define IPI_CALL_FUNC 1 #define IPI_CPU_STOP 2 +#ifndef CONFIG_ITANIUM_PTCG +# define IPI_FLUSH_TLB 3 +#endif /*!CONFIG_ITANIUM_PTCG */ /* * Setup routine for controlling SMP activation @@ -126,6 +132,22 @@ halt_processor(void) } +static inline int +pointer_lock(void *lock, void *data, int retry) +{ + again: + if (cmpxchg_acq((void **) lock, 0, data) == 0) + return 0; + + if (!retry) + return -EBUSY; + + while (*(void **) lock) + ; + + goto again; +} + void handle_IPI(int irq, void *dev_id, struct pt_regs *regs) { @@ -160,13 +182,14 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs) void *info; int wait; + /* release the 'pointer lock' */ data = smp_call_function_data; func = data->func; info = data->info; wait = data->wait; mb(); - atomic_dec (&data->unstarted_count); + atomic_dec(&data->unstarted_count); /* At this point the structure may be gone unless wait is true. */ (*func)(info); @@ -174,7 +197,7 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs) /* Notify the sending CPU that the task is done. */ mb(); if (wait) - atomic_dec (&data->unfinished_count); + atomic_dec(&data->unfinished_count); } break; @@ -182,6 +205,51 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs) halt_processor(); break; +#ifndef CONFIG_ITANIUM_PTCG + case IPI_FLUSH_TLB: + { + extern unsigned long flush_start, flush_end, flush_nbits, flush_rid; + extern atomic_t flush_cpu_count; + unsigned long saved_rid = ia64_get_rr(flush_start); + unsigned long end = flush_end; + unsigned long start = flush_start; + unsigned long nbits = flush_nbits; + + /* + * Current CPU may be running with different + * RID so we need to reload the RID of flushed + * address. Purging the translation also + * needs ALAT invalidation; we do not need + * "invala" here since it is done in + * ia64_leave_kernel. + */ + ia64_srlz_d(); + if (saved_rid != flush_rid) { + ia64_set_rr(flush_start, flush_rid); + ia64_srlz_d(); + } + + do { + /* + * Purge local TLB entries. + */ + __asm__ __volatile__ ("ptc.l %0,%1" :: + "r"(start), "r"(nbits<<2) : "memory"); + start += (1UL << nbits); + } while (start < end); + + ia64_insn_group_barrier(); + ia64_srlz_i(); /* srlz.i implies srlz.d */ + + if (saved_rid != flush_rid) { + ia64_set_rr(flush_start, saved_rid); + ia64_srlz_d(); + } + atomic_dec(&flush_cpu_count); + break; + } +#endif /* !CONFIG_ITANIUM_PTCG */ + default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); break; @@ -199,7 +267,7 @@ send_IPI_single(int dest_cpu, int op) if (dest_cpu == -1) return; - ipi_op[dest_cpu] |= (1 << op); + set_bit(op, &ipi_op[dest_cpu]); ipi_send(dest_cpu, IPI_IRQ, IA64_IPI_DM_INT, 0); } @@ -243,6 +311,14 @@ smp_send_stop(void) send_IPI_allbutself(IPI_CPU_STOP); } +#ifndef CONFIG_ITANIUM_PTCG +void +smp_send_flush_tlb(void) +{ + send_IPI_allbutself(IPI_FLUSH_TLB); +} +#endif /* !CONFIG_ITANIUM_PTCG */ + /* * Run a function on all other CPUs. * <func> The function to run. This must be fast and non-blocking. @@ -260,63 +336,35 @@ smp_call_function (void (*func) (void *info), void *info, int retry, int wait) { struct smp_call_struct data; long timeout; - static spinlock_t lock = SPIN_LOCK_UNLOCKED; + int cpus = smp_num_cpus - 1; + + if (cpus == 0) + return 0; data.func = func; data.info = info; data.wait = wait; - atomic_set(&data.unstarted_count, smp_num_cpus - 1); - atomic_set(&data.unfinished_count, smp_num_cpus - 1); + atomic_set(&data.unstarted_count, cpus); + atomic_set(&data.unfinished_count, cpus); - if (retry) { - while (1) { - if (smp_call_function_data) { - schedule (); /* Give a mate a go */ - continue; - } - spin_lock (&lock); - if (smp_call_function_data) { - spin_unlock (&lock); /* Bad luck */ - continue; - } - /* Mine, all mine! */ - break; - } - } - else { - if (smp_call_function_data) - return -EBUSY; - spin_lock (&lock); - if (smp_call_function_data) { - spin_unlock (&lock); - return -EBUSY; - } - } + if (pointer_lock(&smp_call_function_data, &data, retry)) + return -EBUSY; - smp_call_function_data = &data; - spin_unlock (&lock); - data.func = func; - data.info = info; - atomic_set (&data.unstarted_count, smp_num_cpus - 1); - data.wait = wait; - if (wait) - atomic_set (&data.unfinished_count, smp_num_cpus - 1); - /* Send a message to all other CPUs and wait for them to respond */ send_IPI_allbutself(IPI_CALL_FUNC); /* Wait for response */ timeout = jiffies + HZ; - while ( (atomic_read (&data.unstarted_count) > 0) && - time_before (jiffies, timeout) ) - barrier (); - if (atomic_read (&data.unstarted_count) > 0) { + while ((atomic_read(&data.unstarted_count) > 0) && time_before(jiffies, timeout)) + barrier(); + if (atomic_read(&data.unstarted_count) > 0) { smp_call_function_data = NULL; return -ETIMEDOUT; } if (wait) - while (atomic_read (&data.unfinished_count) > 0) - barrier (); + while (atomic_read(&data.unfinished_count) > 0) + barrier(); + /* unlock pointer */ smp_call_function_data = NULL; return 0; } @@ -382,17 +430,21 @@ smp_do_timer(struct pt_regs *regs) } } - -/* - * Called by both boot and secondaries to move global data into - * per-processor storage. - */ static inline void __init -smp_store_cpu_info(int cpuid) +smp_calibrate_delay(int cpuid) { struct cpuinfo_ia64 *c = &cpu_data[cpuid]; - - identify_cpu(c); +#if 0 + unsigned long old = loops_per_sec; + extern void calibrate_delay(void); + + loops_per_sec = 0; + calibrate_delay(); + c->loops_per_sec = loops_per_sec; + loops_per_sec = old; +#else + c->loops_per_sec = loops_per_sec; +#endif } /* @@ -446,34 +498,26 @@ smp_callin(void) extern void ia64_init_itm(void); extern void ia64_cpu_local_tick(void); - ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP); - ia64_set_fpu_owner(0); - ia64_rid_init(); /* initialize region ids */ - cpu_init(); - __flush_tlb_all(); - smp_store_cpu_info(smp_processor_id()); smp_setup_percpu_timer(smp_processor_id()); - if (test_and_set_bit(smp_processor_id(), &cpu_online_map)) { - printk("CPU#%d already initialized!\n", smp_processor_id()); - machine_halt(); - } - while (!smp_threads_ready) - mb(); - - normal_xtp(); - /* setup the CPU local timer tick */ - ia64_cpu_local_tick(); + ia64_init_itm(); /* Disable all local interrupts */ ia64_set_lrr0(0, 1); ia64_set_lrr1(0, 1); - __sti(); /* Interrupts have been off till now. */ + if (test_and_set_bit(smp_processor_id(), &cpu_online_map)) { + printk("CPU#%d already initialized!\n", smp_processor_id()); + machine_halt(); + } + while (!smp_threads_ready) + mb(); + local_irq_enable(); /* Interrupts have been off until now */ + smp_calibrate_delay(smp_processor_id()); printk("SMP: CPU %d starting idle loop\n", smp_processor_id()); cpu_idle(NULL); @@ -583,16 +627,8 @@ smp_boot_cpus(void) /* Setup BSP mappings */ __cpu_number_map[bootstrap_processor] = 0; __cpu_logical_map[0] = bootstrap_processor; - current->processor = bootstrap_processor; - - /* Mark BSP booted and get active_mm context */ - cpu_init(); - - /* reset XTP for interrupt routing */ - normal_xtp(); - /* And generate an entry in cpu_data */ - smp_store_cpu_info(bootstrap_processor); + smp_calibrate_delay(smp_processor_id()); #if 0 smp_tune_scheduling(); #endif diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c index f06d3bea8..cd9d64fce 100644 --- a/arch/ia64/kernel/sys_ia64.c +++ b/arch/ia64/kernel/sys_ia64.c @@ -14,6 +14,9 @@ #include <linux/file.h> /* doh, must come after sched.h... */ #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/highuid.h> + +#include <asm/uaccess.h> asmlinkage long ia64_getpriority (int which, int who, long arg2, long arg3, long arg4, long arg5, long arg6, @@ -94,7 +97,11 @@ sys_pipe (long arg0, long arg1, long arg2, long arg3, static inline unsigned long do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) { + long start_low, end_low, starting_region, ending_region; + unsigned long loff, hoff; struct file *file = 0; + /* the virtual address space that is mappable in each region: */ +# define OCTANT_SIZE ((PTRS_PER_PGD<<PGDIR_SHIFT)/8) /* * A zero mmap always succeeds in Linux, independent of @@ -103,15 +110,19 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un if (PAGE_ALIGN(len) == 0) return addr; -#ifdef notyet - /* Don't permit mappings that would cross a region boundary: */ - region_start = IA64_GET_REGION(addr); - region_end = IA64_GET_REGION(addr + len); - if (region_start != region_end) + /* Don't permit mappings into or across the address hole in a region: */ + loff = REGION_OFFSET(addr); + hoff = loff - (REGION_SIZE - OCTANT_SIZE/2); + if ((len | loff | (loff + len)) >= OCTANT_SIZE/2 + && (len | hoff | (hoff + len)) >= OCTANT_SIZE/2) return -EINVAL; - <<x??x>> -#endif + /* Don't permit mappings that would cross a region boundary: */ + + starting_region = REGION_NUMBER(addr); + ending_region = REGION_NUMBER(addr + len); + if (starting_region != ending_region) + return -EINVAL; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { @@ -156,6 +167,9 @@ sys_mmap (unsigned long addr, unsigned long len, int prot, int flags, { struct pt_regs *regs = (struct pt_regs *) &stack; + if ((off & ~PAGE_MASK) != 0) + return -EINVAL; + addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT); if (!IS_ERR(addr)) regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */ @@ -196,6 +210,150 @@ sys_modify_ldt (long arg0, long arg1, long arg2, long arg3) return -ENOSYS; } +asmlinkage unsigned long +ia64_create_module (const char *name_user, size_t size, long arg2, long arg3, + long arg4, long arg5, long arg6, long arg7, long stack) +{ + extern unsigned long sys_create_module (const char *, size_t); + struct pt_regs *regs = (struct pt_regs *) &stack; + unsigned long addr; + + addr = sys_create_module (name_user, size); + if (!IS_ERR(addr)) + regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */ + return addr; +} + +#if 1 +/* + * This is here for a while to keep compatibillity with the old stat() + * call - it will be removed later once everybody migrates to the new + * kernel stat structure that matches the glibc one - Jes + */ +static __inline__ int +do_revalidate (struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +static int +cp_ia64_old_stat (struct inode *inode, struct ia64_oldstat *statbuf) +{ + struct ia64_oldstat tmp; + unsigned int blocks, indirect; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + SET_STAT_UID(tmp, inode->i_uid); + SET_STAT_GID(tmp, inode->i_gid); + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; +/* + * st_blocks and st_blksize are approximated with a simple algorithm if + * they aren't supported directly by the filesystem. The minix and msdos + * filesystems don't keep track of blocks, so they would either have to + * be counted explicitly (by delving into the file itself), or by using + * this simple algorithm to get a reasonable (although not 100% accurate) + * value. + */ + +/* + * Use minix fs values for the number of direct and indirect blocks. The + * count is now exact for the minix fs except that it counts zero blocks. + * Everything is in units of BLOCK_SIZE until the assignment to + * tmp.st_blksize. + */ +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + + if (!inode->i_blksize) { + blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (blocks > D_B) { + indirect = (blocks - D_B + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) + blocks++; + } + } + tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; + tmp.st_blksize = BLOCK_SIZE; + } else { + tmp.st_blocks = inode->i_blocks; + tmp.st_blksize = inode->i_blksize; + } + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long +ia64_oldstat (char *filename, struct ia64_oldstat *statbuf) +{ + struct nameidata nd; + int error; + + lock_kernel(); + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + unlock_kernel(); + return error; +} + + +asmlinkage long +ia64_oldlstat (char *filename, struct ia64_oldstat *statbuf) { + struct nameidata nd; + int error; + + lock_kernel(); + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + unlock_kernel(); + return error; +} + +asmlinkage long +ia64_oldfstat (unsigned int fd, struct ia64_oldstat *statbuf) +{ + struct file * f; + int err = -EBADF; + + lock_kernel(); + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_ia64_old_stat(dentry->d_inode, statbuf); + fput(f); + } + unlock_kernel(); + return err; +} + +#endif + #ifndef CONFIG_PCI asmlinkage long @@ -212,5 +370,4 @@ sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, un return -ENOSYS; } - #endif /* CONFIG_PCI */ diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index b88855ce4..d14ba0031 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -34,7 +34,10 @@ unsigned long last_cli_ip; static struct { unsigned long delta; - unsigned long next[NR_CPUS]; + union { + unsigned long count; + unsigned char pad[SMP_CACHE_BYTES]; + } next[NR_CPUS]; } itm; static void @@ -69,16 +72,27 @@ do_profile (unsigned long ip) static inline unsigned long gettimeoffset (void) { - unsigned long now = ia64_get_itc(); - unsigned long elapsed_cycles, lost; - - elapsed_cycles = now - (itm.next[smp_processor_id()] - itm.delta); - - lost = lost_ticks; - if (lost) - elapsed_cycles += lost*itm.delta; - +#ifdef CONFIG_SMP + /* + * The code below doesn't work for SMP because only CPU 0 + * keeps track of the time. + */ + return 0; +#else + unsigned long now = ia64_get_itc(), last_tick; + unsigned long elapsed_cycles, lost = lost_ticks; + + last_tick = (itm.next[smp_processor_id()].count - (lost+1)*itm.delta); +# if 1 + if ((long) (now - last_tick) < 0) { + printk("Yikes: now < last_tick (now=0x%lx,last_tick=%lx)! No can do.\n", + now, last_tick); + return 0; + } +# endif + elapsed_cycles = now - last_tick; return (elapsed_cycles*my_cpu_data.usec_per_cyc) >> IA64_USEC_PER_CYC_SHIFT; +#endif } void @@ -137,6 +151,7 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) static unsigned long last_time; static unsigned char count; int cpu = smp_processor_id(); + unsigned long new_itm; int printed = 0; /* @@ -146,6 +161,12 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * xtime_lock. */ write_lock(&xtime_lock); + new_itm = itm.next[cpu].count; + + if (!time_after(ia64_get_itc(), new_itm)) + printk("Oops: timer tick before it's due (itc=%lx,itm=%lx)\n", + ia64_get_itc(), new_itm); + while (1) { /* * Do kernel PC profiling here. We multiply the @@ -164,18 +185,10 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) do_timer(regs); #endif - itm.next[cpu] += itm.delta; - /* - * There is a race condition here: to be on the "safe" - * side, we process timer ticks until itm.next is - * ahead of the itc by at least half the timer - * interval. This should give us enough time to set - * the new itm value without losing a timer tick. - */ - if (time_after(itm.next[cpu], ia64_get_itc() + itm.delta/2)) { - ia64_set_itm(itm.next[cpu]); + new_itm += itm.delta; + itm.next[cpu].count = new_itm; + if (time_after(new_itm, ia64_get_itc())) break; - } #if !(defined(CONFIG_IA64_SOFTSDV_HACKS) && defined(CONFIG_SMP)) /* @@ -188,28 +201,39 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) last_time = jiffies; if (!printed) { printk("Lost clock tick on CPU %d (now=%lx, next=%lx)!!\n", - cpu, ia64_get_itc(), itm.next[cpu]); + cpu, ia64_get_itc(), itm.next[cpu].count); printed = 1; - } # ifdef CONFIG_IA64_DEBUG_IRQ - printk("last_cli_ip=%lx\n", last_cli_ip); + printk("last_cli_ip=%lx\n", last_cli_ip); # endif + } } #endif } write_unlock(&xtime_lock); + + /* + * If we're too close to the next clock tick for comfort, we + * increase the saftey margin by intentionally dropping the + * next tick(s). We do NOT update itm.next accordingly + * because that would force us to call do_timer() which in + * turn would let our clock run too fast (with the potentially + * devastating effect of losing monotony of time). + */ + while (!time_after(new_itm, ia64_get_itc() + itm.delta/2)) + new_itm += itm.delta; + ia64_set_itm(new_itm); } -#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC +#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS) -void +/* + * Interrupts must be disabled before calling this routine. + */ +void ia64_reset_itm (void) { - unsigned long flags; - - local_irq_save(flags); timer_interrupt(0, 0, ia64_task_regs(current)); - local_irq_restore(flags); } #endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ @@ -220,11 +244,14 @@ ia64_reset_itm (void) void __init ia64_cpu_local_tick(void) { +#ifdef CONFIG_IA64_SOFTSDV_HACKS + ia64_set_itc(0); +#endif + /* arrange for the cycle counter to generate a timer interrupt: */ ia64_set_itv(TIMER_IRQ, 0); - ia64_set_itc(0); - itm.next[smp_processor_id()] = ia64_get_itc() + itm.delta; - ia64_set_itm(itm.next[smp_processor_id()]); + itm.next[smp_processor_id()].count = ia64_get_itc() + itm.delta; + ia64_set_itm(itm.next[smp_processor_id()].count); } void __init @@ -254,25 +281,7 @@ ia64_init_itm (void) itc_ratio.num = 3; itc_ratio.den = 1; } -#if defined(CONFIG_IA64_LION_HACKS) - /* Our Lion currently returns base freq 104.857MHz, which - ain't right (it really is 100MHz). */ - printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n", - platform_base_freq, itc_ratio.num, itc_ratio.den, - proc_ratio.num, proc_ratio.den); - platform_base_freq = 100000000; -#elif 0 && defined(CONFIG_IA64_BIGSUR_HACKS) - /* BigSur with 991020 firmware returned itc-ratio=9/2 and base - freq 75MHz, which wasn't right. The 991119 firmware seems - to return the right values, so this isn't necessary - anymore... */ - printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n", - platform_base_freq, itc_ratio.num, itc_ratio.den, - proc_ratio.num, proc_ratio.den); - platform_base_freq = 100000000; - proc_ratio.num = 5; proc_ratio.den = 1; - itc_ratio.num = 5; itc_ratio.den = 1; -#elif defined(CONFIG_IA64_SOFTSDV_HACKS) +#ifdef CONFIG_IA64_SOFTSDV_HACKS platform_base_freq = 10000000; proc_ratio.num = 4; proc_ratio.den = 1; itc_ratio.num = 4; itc_ratio.den = 1; @@ -290,8 +299,9 @@ ia64_init_itm (void) itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den; itm.delta = itc_freq / HZ; - printk("timer: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n", - platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000, + printk("timer: CPU %d base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n", + smp_processor_id(), + platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000, itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000); my_cpu_data.proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den; @@ -313,6 +323,8 @@ void __init time_init (void) { /* we can't do request_irq() here because the kmalloc() would fail... */ + irq_desc[TIMER_IRQ].status |= IRQ_PER_CPU; + irq_desc[TIMER_IRQ].handler = &irq_type_ia64_sapic; setup_irq(TIMER_IRQ, &timer_irqaction); efi_gettimeofday(&xtime); diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 3a7706a27..4003b20f1 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -3,8 +3,12 @@ * * Copyright (C) 1998-2000 Hewlett-Packard Co * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE */ +#define FPSWA_DEBUG 1 + /* * The fpu_fault() handler needs to be able to access and update all * floating point registers. Those saved in pt_regs can be accessed @@ -168,7 +172,7 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs) siginfo.si_signo = sig; siginfo.si_errno = 0; siginfo.si_code = code; - send_sig_info(sig, &siginfo, current); + force_sig_info(sig, &siginfo, current); } /* @@ -300,6 +304,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) if (copy_from_user(bundle, (void *) fault_ip, sizeof(bundle))) return -1; +#ifdef FPSWA_DEBUG if (fpu_swa_count > 5 && jiffies - last_time > 5*HZ) fpu_swa_count = 0; if (++fpu_swa_count < 5) { @@ -307,7 +312,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) printk("%s(%d): floating-point assist fault at ip %016lx\n", current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri); } - +#endif exception = fp_emulate(fp_fault, bundle, ®s->cr_ipsr, ®s->ar_fpsr, &isr, ®s->pr, ®s->cr_ifs, regs); if (fp_fault) { @@ -331,7 +336,8 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) } else if (isr & 0x44) { siginfo.si_code = FPE_FLTDIV; } - send_sig_info(SIGFPE, &siginfo, current); + siginfo.si_isr = isr; + force_sig_info(SIGFPE, &siginfo, current); } } else { if (exception == -1) { @@ -350,12 +356,49 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) } else if (isr & 0x2200) { siginfo.si_code = FPE_FLTRES; } - send_sig_info(SIGFPE, &siginfo, current); + siginfo.si_isr = isr; + force_sig_info(SIGFPE, &siginfo, current); } } return 0; } +struct illegal_op_return { + unsigned long fkt, arg1, arg2, arg3; +}; + +struct illegal_op_return +ia64_illegal_op_fault (unsigned long ec, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, unsigned long arg5, + unsigned long arg6, unsigned long arg7, unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + struct illegal_op_return rv; + struct siginfo si; + char buf[128]; + +#ifdef CONFIG_IA64_BRL_EMU + { + extern struct illegal_op_return ia64_emulate_brl (struct pt_regs *, unsigned long); + + rv = ia64_emulate_brl(regs, ec); + if (rv.fkt != (unsigned long) -1) + return rv; + } +#endif + + sprintf(buf, "IA-64 Illegal operation fault"); + die_if_kernel(buf, regs, 0); + + memset(&si, 0, sizeof(si)); + si.si_signo = SIGILL; + si.si_code = ILL_ILLOPC; + si.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + force_sig_info(SIGILL, &si, current); + rv.fkt = 0; + return rv; +} + void ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, unsigned long iim, unsigned long itir, unsigned long arg5, @@ -450,11 +493,6 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, force_sig_info(SIGTRAP, &siginfo, current); return; - case 30: /* Unaligned fault */ - sprintf(buf, "Kernel unaligned trap accessing %016lx (ip=%016lx)!", - ifa, regs->cr_iip + ia64_psr(regs)->ri); - break; - case 32: /* fp fault */ case 33: /* fp trap */ result = handle_fpu_swa((vector == 32) ? 1 : 0, regs, isr); diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c index 35e8cb846..a95b78f64 100644 --- a/arch/ia64/kernel/unaligned.c +++ b/arch/ia64/kernel/unaligned.c @@ -1,8 +1,8 @@ /* * Architecture-specific unaligned trap handling. * - * Copyright (C) 1999 Hewlett-Packard Co - * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com> */ #include <linux/kernel.h> #include <linux/sched.h> @@ -460,32 +460,15 @@ setfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) * enabled. * * The registers [32-127] are ususally saved in the tss. When get here, - * they are NECESSARY live because they are only saved explicitely. + * they are NECESSARILY live because they are only saved explicitely. * We have 3 ways of updating the values: force a save of the range * in tss, use a gigantic switch/case statement or generate code on the * fly to store to the right register. * For now, we are using the (slow) save/restore way. */ if (regnum >= IA64_FIRST_ROTATING_FR) { - /* - * force a save of [32-127] to tss - * we use the __() form to avoid fiddling with the dfh bit - */ - __ia64_save_fpu(¤t->thread.fph[0]); - + ia64_sync_fph(current); current->thread.fph[IA64_FPH_OFFS(regnum)] = *fpval; - - __ia64_load_fpu(¤t->thread.fph[0]); - - /* - * mark the high partition as being used now - * - * This is REQUIRED because the disabled_fph_fault() does - * not set it, it's relying on the faulting instruction to - * do it. In our case the faulty instruction never gets executed - * completely, so we need to toggle the bit. - */ - regs->cr_ipsr |= IA64_PSR_MFH; } else { /* * pt_regs or switch_stack ? @@ -544,15 +527,8 @@ getfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs) * See discussion in setfpreg() for reasons and other ways of doing this. */ if (regnum >= IA64_FIRST_ROTATING_FR) { - - /* - * force a save of [32-127] to tss - * we use the__ia64_save_fpu() form to avoid fiddling with - * the dfh bit. - */ - __ia64_save_fpu(¤t->thread.fph[0]); - - *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)]; + ia64_sync_fph(current); + *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)]; } else { /* * f0 = 0.0, f1= 1.0. Those registers are constant and are thus @@ -1410,6 +1386,25 @@ ia64_handle_unaligned(unsigned long ifa, struct pt_regs *regs) die_if_kernel("Unaligned reference while in kernel\n", regs, 30); /* NOT_REACHED */ } + /* + * For now, we don't support user processes running big-endian + * which do unaligned accesses + */ + if (ia64_psr(regs)->be) { + struct siginfo si; + + printk(KERN_ERR "%s(%d): big-endian unaligned access %016lx (ip=%016lx) not " + "yet supported\n", + current->comm, current->pid, ifa, regs->cr_iip + ipsr->ri); + + si.si_signo = SIGBUS; + si.si_errno = 0; + si.si_code = BUS_ADRALN; + si.si_addr = (void *) ifa; + force_sig_info(SIGBUS, &si, current); + return; + } + if (current->thread.flags & IA64_THREAD_UAC_SIGBUS) { struct siginfo si; @@ -1417,7 +1412,7 @@ ia64_handle_unaligned(unsigned long ifa, struct pt_regs *regs) si.si_errno = 0; si.si_code = BUS_ADRALN; si.si_addr = (void *) ifa; - send_sig_info (SIGBUS, &si, current); + force_sig_info(SIGBUS, &si, current); return; } diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c index c2b772e68..7f3c203ad 100644 --- a/arch/ia64/kernel/unwind.c +++ b/arch/ia64/kernel/unwind.c @@ -1,16 +1,1796 @@ /* - * Copyright (C) 1999 Hewlett-Packard Co - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ +/* + * This file implements call frame unwind support for the Linux + * kernel. Parsing and processing the unwind information is + * time-consuming, so this implementation translates the the unwind + * descriptors into unwind scripts. These scripts are very simple + * (basically a sequence of assignments) and efficient to execute. + * They are cached for later re-use. Each script is specific for a + * given instruction pointer address and the set of predicate values + * that the script depends on (most unwind descriptors are + * unconditional and scripts often do not depend on predicates at + * all). This code is based on the unwind conventions described in + * the "IA-64 Software Conventions and Runtime Architecture" manual. + * + * SMP conventions: + * o updates to the global unwind data (in structure "unw") are serialized + * by the unw.lock spinlock + * o each unwind script has its own read-write lock; a thread must acquire + * a read lock before executing a script and must acquire a write lock + * before modifying a script + * o if both the unw.lock spinlock and a script's read-write lock must be + * acquired, then the read-write lock must be acquired first. + */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/slab.h> #include <asm/unwind.h> +#ifdef CONFIG_IA64_NEW_UNWIND + +#include <asm/delay.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/ptrace_offsets.h> +#include <asm/rse.h> +#include <asm/system.h> + +#include "entry.h" +#include "unwind_i.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define p5 5 + +/* + * The unwind tables are supposed to be sorted, but the GNU toolchain + * currently fails to produce a sorted table in the presence of + * functions that go into sections other than .text. For example, the + * kernel likes to put initialization code into .text.init, which + * messes up the sort order. Hopefully, this will get fixed sometime + * soon. --davidm 00/05/23 + */ +#define UNWIND_TABLE_SORT_BUG + +#define UNW_LOG_CACHE_SIZE 7 /* each unw_script is ~256 bytes in size */ +#define UNW_CACHE_SIZE (1 << UNW_LOG_CACHE_SIZE) + +#define UNW_LOG_HASH_SIZE (UNW_LOG_CACHE_SIZE + 1) +#define UNW_HASH_SIZE (1 << UNW_LOG_HASH_SIZE) + +#define UNW_DEBUG 1 +#define UNW_STATS 0 /* WARNING: this disabled interrupts for long time-spans!! */ + +#if UNW_DEBUG +# define dprintk(format...) printk(format) +# define inline +#else +# define dprintk(format...) +#endif + +#if UNW_STATS +# define STAT(x...) x +#else +# define STAT(x...) +#endif + +#define alloc_reg_state() kmalloc(sizeof(struct unw_state_record), GFP_ATOMIC) +#define free_reg_state(usr) kfree(usr) + +typedef unsigned long unw_word; +typedef unsigned char unw_hash_index_t; + +#define struct_offset(str,fld) ((char *)&((str *)NULL)->fld - (char *) 0) + +static struct { + spinlock_t lock; /* spinlock for unwind data */ + + /* list of unwind tables (one per load-module) */ + struct unw_table *tables; + + /* table of registers that prologues can save (and order in which they're saved): */ + const unsigned char save_order[8]; + + /* maps a preserved register index (preg_index) to corresponding switch_stack offset: */ + unsigned short sw_off[sizeof(struct unw_frame_info) / 8]; + + unsigned short lru_head; /* index of lead-recently used script */ + unsigned short lru_tail; /* index of most-recently used script */ + + /* index into unw_frame_info for preserved register i */ + unsigned short preg_index[UNW_NUM_REGS]; + + /* unwind table for the kernel: */ + struct unw_table kernel_table; + + /* hash table that maps instruction pointer to script index: */ + unw_hash_index_t hash[UNW_HASH_SIZE]; + + /* script cache: */ + struct unw_script cache[UNW_CACHE_SIZE]; + +# if UNW_DEBUG + const char *preg_name[UNW_NUM_REGS]; +# endif +# if UNW_STATS + struct { + struct { + int lookups; + int hinted_hits; + int normal_hits; + int collision_chain_traversals; + } cache; + struct { + unsigned long build_time; + unsigned long run_time; + unsigned long parse_time; + int builds; + int news; + int collisions; + int runs; + } script; + struct { + unsigned long init_time; + unsigned long unwind_time; + int inits; + int unwinds; + } api; + } stat; +# endif +} unw = { + tables: &unw.kernel_table, + lock: SPIN_LOCK_UNLOCKED, + save_order: { + UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR, + UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR + }, + preg_index: { + struct_offset(struct unw_frame_info, pri_unat)/8, /* PRI_UNAT_GR */ + struct_offset(struct unw_frame_info, pri_unat)/8, /* PRI_UNAT_MEM */ + struct_offset(struct unw_frame_info, pbsp)/8, + struct_offset(struct unw_frame_info, bspstore)/8, + struct_offset(struct unw_frame_info, pfs)/8, + struct_offset(struct unw_frame_info, rnat)/8, + struct_offset(struct unw_frame_info, psp)/8, + struct_offset(struct unw_frame_info, rp)/8, + struct_offset(struct unw_frame_info, r4)/8, + struct_offset(struct unw_frame_info, r5)/8, + struct_offset(struct unw_frame_info, r6)/8, + struct_offset(struct unw_frame_info, r7)/8, + struct_offset(struct unw_frame_info, unat)/8, + struct_offset(struct unw_frame_info, pr)/8, + struct_offset(struct unw_frame_info, lc)/8, + struct_offset(struct unw_frame_info, fpsr)/8, + struct_offset(struct unw_frame_info, b1)/8, + struct_offset(struct unw_frame_info, b2)/8, + struct_offset(struct unw_frame_info, b3)/8, + struct_offset(struct unw_frame_info, b4)/8, + struct_offset(struct unw_frame_info, b5)/8, + struct_offset(struct unw_frame_info, f2)/8, + struct_offset(struct unw_frame_info, f3)/8, + struct_offset(struct unw_frame_info, f4)/8, + struct_offset(struct unw_frame_info, f5)/8, + struct_offset(struct unw_frame_info, fr[16 - 16])/8, + struct_offset(struct unw_frame_info, fr[17 - 16])/8, + struct_offset(struct unw_frame_info, fr[18 - 16])/8, + struct_offset(struct unw_frame_info, fr[19 - 16])/8, + struct_offset(struct unw_frame_info, fr[20 - 16])/8, + struct_offset(struct unw_frame_info, fr[21 - 16])/8, + struct_offset(struct unw_frame_info, fr[22 - 16])/8, + struct_offset(struct unw_frame_info, fr[23 - 16])/8, + struct_offset(struct unw_frame_info, fr[24 - 16])/8, + struct_offset(struct unw_frame_info, fr[25 - 16])/8, + struct_offset(struct unw_frame_info, fr[26 - 16])/8, + struct_offset(struct unw_frame_info, fr[27 - 16])/8, + struct_offset(struct unw_frame_info, fr[28 - 16])/8, + struct_offset(struct unw_frame_info, fr[29 - 16])/8, + struct_offset(struct unw_frame_info, fr[30 - 16])/8, + struct_offset(struct unw_frame_info, fr[31 - 16])/8, + }, + hash : { [0 ... UNW_HASH_SIZE - 1] = -1 }, +#if UNW_DEBUG + preg_name: { + "pri_unat_gr", "pri_unat_mem", "bsp", "bspstore", "ar.pfs", "ar.rnat", "psp", "rp", + "r4", "r5", "r6", "r7", + "ar.unat", "pr", "ar.lc", "ar.fpsr", + "b1", "b2", "b3", "b4", "b5", + "f2", "f3", "f4", "f5", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" + } +#endif +}; + + +/* Unwind accessors. */ + +int +unw_access_gr (struct unw_frame_info *info, int regnum, unsigned long *val, char *nat, int write) +{ + unsigned long *addr, *nat_addr, nat_mask = 0, dummy_nat; + struct unw_ireg *ireg; + struct pt_regs *pt; + + if ((unsigned) regnum - 1 >= 127) { + dprintk("unwind: trying to access non-existent r%u\n", regnum); + return -1; + } + + if (regnum < 32) { + if (regnum >= 4 && regnum <= 7) { + /* access a preserved register */ + ireg = &info->r4 + (regnum - 4); + addr = ireg->loc; + if (addr) { + nat_addr = addr + ireg->nat.off; + switch (ireg->nat.type) { + case UNW_NAT_VAL: + /* simulate getf.sig/setf.sig */ + if (write) { + if (*nat) { + /* write NaTVal and be done with it */ + addr[0] = 0; + addr[1] = 0x1fffe; + return 0; + } + addr[1] = 0x1003e; + } else { + if (addr[0] == 0 && addr[1] == 0x1ffe) { + /* return NaT and be done with it */ + *val = 0; + *nat = 1; + return 0; + } + } + /* fall through */ + case UNW_NAT_NONE: + nat_addr = &dummy_nat; + break; + + case UNW_NAT_SCRATCH: + if (info->pri_unat) + nat_addr = info->pri_unat; + else + nat_addr = &info->sw->caller_unat; + case UNW_NAT_PRI_UNAT: + nat_mask = (1UL << ((long) addr & 0x1f8)/8); + break; + + case UNW_NAT_STACKED: + nat_addr = ia64_rse_rnat_addr(addr); + if ((unsigned long) addr < info->regstk.limit + || (unsigned long) addr >= info->regstk.top) + { + dprintk("unwind: 0x%p outside of regstk " + "[0x%lx-0x%lx)\n", addr, + info->regstk.limit, info->regstk.top); + return -1; + } + if ((unsigned long) nat_addr >= info->regstk.top) + nat_addr = &info->sw->ar_rnat; + nat_mask = (1UL << ia64_rse_slot_num(addr)); + break; + } + } else { + addr = &info->sw->r4 + (regnum - 4); + nat_addr = &info->sw->ar_unat; + nat_mask = (1UL << ((long) addr & 0x1f8)/8); + } + } else { + /* access a scratch register */ + if (info->flags & UNW_FLAG_INTERRUPT_FRAME) + pt = (struct pt_regs *) info->psp - 1; + else + pt = (struct pt_regs *) info->sp - 1; + if (regnum <= 3) + addr = &pt->r1 + (regnum - 1); + else if (regnum <= 11) + addr = &pt->r8 + (regnum - 8); + else if (regnum <= 15) + addr = &pt->r12 + (regnum - 12); + else + addr = &pt->r16 + (regnum - 16); + if (info->pri_unat) + nat_addr = info->pri_unat; + else + nat_addr = &info->sw->caller_unat; + nat_mask = (1UL << ((long) addr & 0x1f8)/8); + } + } else { + /* access a stacked register */ + addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum); + nat_addr = ia64_rse_rnat_addr(addr); + if ((unsigned long) addr < info->regstk.limit + || (unsigned long) addr >= info->regstk.top) + { + dprintk("unwind: ignoring attempt to access register outside of rbs\n"); + return -1; + } + if ((unsigned long) nat_addr >= info->regstk.top) + nat_addr = &info->sw->ar_rnat; + nat_mask = (1UL << ia64_rse_slot_num(addr)); + } + + if (write) { + *addr = *val; + *nat_addr = (*nat_addr & ~nat_mask) | nat_mask; + } else { + *val = *addr; + *nat = (*nat_addr & nat_mask) != 0; + } + return 0; +} + +int +unw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int write) +{ + unsigned long *addr; + struct pt_regs *pt; + + if (info->flags & UNW_FLAG_INTERRUPT_FRAME) + pt = (struct pt_regs *) info->psp - 1; + else + pt = (struct pt_regs *) info->sp - 1; + switch (regnum) { + /* scratch: */ + case 0: addr = &pt->b0; break; + case 6: addr = &pt->b6; break; + case 7: addr = &pt->b7; break; + + /* preserved: */ + case 1: case 2: case 3: case 4: case 5: + addr = *(&info->b1 + (regnum - 1)); + if (!addr) + addr = &info->sw->b1 + (regnum - 1); + break; + + default: + dprintk("unwind: trying to access non-existent b%u\n", regnum); + return -1; + } + if (write) + *addr = *val; + else + *val = *addr; + return 0; +} + +int +unw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write) +{ + struct ia64_fpreg *addr = 0; + struct pt_regs *pt; + + if ((unsigned) (regnum - 2) >= 126) { + dprintk("unwind: trying to access non-existent f%u\n", regnum); + return -1; + } + + if (info->flags & UNW_FLAG_INTERRUPT_FRAME) + pt = (struct pt_regs *) info->psp - 1; + else + pt = (struct pt_regs *) info->sp - 1; + + if (regnum <= 5) { + addr = *(&info->f2 + (regnum - 2)); + if (!addr) + addr = &info->sw->f2 + (regnum - 2); + } else if (regnum <= 15) { + if (regnum <= 9) + addr = &pt->f6 + (regnum - 6); + else + addr = &info->sw->f10 + (regnum - 10); + } else if (regnum <= 31) { + addr = info->fr[regnum - 16]; + if (!addr) + addr = &info->sw->f16 + (regnum - 16); + } else { + struct task_struct *t = info->task; + + ia64_sync_fph(t); + addr = t->thread.fph + (regnum - 32); + } + + if (write) + *addr = *val; + else + *val = *addr; + return 0; +} + +int +unw_access_ar (struct unw_frame_info *info, int regnum, unsigned long *val, int write) +{ + unsigned long *addr; + struct pt_regs *pt; + + if (info->flags & UNW_FLAG_INTERRUPT_FRAME) + pt = (struct pt_regs *) info->psp - 1; + else + pt = (struct pt_regs *) info->sp - 1; + + switch (regnum) { + case UNW_AR_BSP: + addr = info->pbsp; + if (!addr) + addr = &info->sw->ar_bspstore; + break; + + case UNW_AR_BSPSTORE: + addr = info->bspstore; + if (!addr) + addr = &info->sw->ar_bspstore; + break; + + case UNW_AR_PFS: + addr = info->pfs; + if (!addr) + addr = &info->sw->ar_pfs; + break; + + case UNW_AR_RNAT: + addr = info->rnat; + if (!addr) + addr = &info->sw->ar_rnat; + break; + + case UNW_AR_UNAT: + addr = info->unat; + if (!addr) + addr = &info->sw->ar_unat; + break; + + case UNW_AR_LC: + addr = info->lc; + if (!addr) + addr = &info->sw->ar_lc; + break; + + case UNW_AR_EC: + if (!info->cfm) + return -1; + if (write) + *info->cfm = (*info->cfm & ~(0x3fUL << 52)) | ((*val & 0x3f) << 52); + else + *val = (*info->cfm >> 52) & 0x3f; + return 0; + + case UNW_AR_FPSR: + addr = info->fpsr; + if (!addr) + addr = &info->sw->ar_fpsr; + break; + + case UNW_AR_RSC: + addr = &pt->ar_rsc; + break; + + case UNW_AR_CCV: + addr = &pt->ar_ccv; + break; + + default: + dprintk("unwind: trying to access non-existent ar%u\n", regnum); + return -1; + } + + if (write) + *addr = *val; + else + *val = *addr; + return 0; +} + +inline int +unw_access_pr (struct unw_frame_info *info, unsigned long *val, int write) +{ + unsigned long *addr; + + addr = info->pr; + if (!addr) + addr = &info->sw->pr; + + if (write) + *addr = *val; + else + *val = *addr; + return 0; +} + + +/* Unwind decoder routines */ + +static inline void +push (struct unw_state_record *sr) +{ + struct unw_reg_state *rs; + + rs = alloc_reg_state(); + memcpy(rs, &sr->curr, sizeof(*rs)); + rs->next = sr->stack; + sr->stack = rs; +} + +static void +pop (struct unw_state_record *sr) +{ + struct unw_reg_state *rs; + + if (!sr->stack) { + printk ("unwind: stack underflow!\n"); + return; + } + rs = sr->stack; + sr->stack = rs->next; + free_reg_state(rs); +} + +static enum unw_register_index __attribute__((const)) +decode_abreg (unsigned char abreg, int memory) +{ + switch (abreg) { + case 0x04 ... 0x07: return UNW_REG_R4 + (abreg - 0x04); + case 0x22 ... 0x25: return UNW_REG_F2 + (abreg - 0x22); + case 0x30 ... 0x3f: return UNW_REG_F16 + (abreg - 0x30); + case 0x41 ... 0x45: return UNW_REG_B1 + (abreg - 0x41); + case 0x60: return UNW_REG_PR; + case 0x61: return UNW_REG_PSP; + case 0x62: return memory ? UNW_REG_PRI_UNAT_MEM : UNW_REG_PRI_UNAT_GR; + case 0x63: return UNW_REG_RP; + case 0x64: return UNW_REG_BSP; + case 0x65: return UNW_REG_BSPSTORE; + case 0x66: return UNW_REG_RNAT; + case 0x67: return UNW_REG_UNAT; + case 0x68: return UNW_REG_FPSR; + case 0x69: return UNW_REG_PFS; + case 0x6a: return UNW_REG_LC; + default: + break; + } + dprintk("unwind: bad abreg=0x%x\n", abreg); + return UNW_REG_LC; +} + +static void +set_reg (struct unw_reg_info *reg, enum unw_where where, int when, unsigned long val) +{ + reg->val = val; + reg->where = where; + if (reg->when == UNW_WHEN_NEVER) + reg->when = when; +} + +static void +alloc_spill_area (unsigned long *offp, unsigned long regsize, + struct unw_reg_info *lo, struct unw_reg_info *hi) +{ + struct unw_reg_info *reg; + + for (reg = hi; reg >= lo; --reg) { + if (reg->where == UNW_WHERE_SPILL_HOME) { + reg->where = UNW_WHERE_PSPREL; + reg->val = 0x10 - *offp; + *offp += regsize; + } + } +} + +static inline void +spill_next_when (struct unw_reg_info **regp, struct unw_reg_info *lim, unw_word t) +{ + struct unw_reg_info *reg; + + for (reg = *regp; reg <= lim; ++reg) { + if (reg->where == UNW_WHERE_SPILL_HOME) { + reg->when = t; + *regp = reg + 1; + return; + } + } + dprintk("unwind: excess spill!\n"); +} + +static inline void +finish_prologue (struct unw_state_record *sr) +{ + struct unw_reg_info *reg; + unsigned long off; + int i; + + /* + * First, resolve implicit register save locations + * (see Section "11.4.2.3 Rules for Using Unwind + * Descriptors", rule 3): + */ + for (i = 0; i < (int) sizeof(unw.save_order)/sizeof(unw.save_order[0]); ++i) { + reg = sr->curr.reg + unw.save_order[i]; + if (reg->where == UNW_WHERE_GR_SAVE) { + reg->where = UNW_WHERE_GR; + reg->val = sr->gr_save_loc++; + } + } + + /* + * Next, compute when the fp, general, and branch registers get + * saved. This must come before alloc_spill_area() because + * we need to know which registers are spilled to their home + * locations. + */ + if (sr->imask) { + unsigned char kind, mask = 0, *cp = sr->imask; + unsigned long t; + static const unsigned char limit[3] = { + UNW_REG_F31, UNW_REG_R7, UNW_REG_B5 + }; + struct unw_reg_info *(regs[3]); + + regs[0] = sr->curr.reg + UNW_REG_F2; + regs[1] = sr->curr.reg + UNW_REG_R4; + regs[2] = sr->curr.reg + UNW_REG_B1; + + for (t = 0; t < sr->region_len; ++t) { + if ((t & 3) == 0) + mask = *cp++; + kind = (mask >> 2*(3-(t & 3))) & 3; + if (kind > 0) + spill_next_when(®s[kind - 1], sr->curr.reg + limit[kind - 1], + sr->region_start + t); + } + } + /* + * Next, lay out the memory stack spill area: + */ + if (sr->any_spills) { + off = sr->spill_offset; + alloc_spill_area(&off, 16, sr->curr.reg + UNW_REG_F2, sr->curr.reg + UNW_REG_F31); + alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_B1, sr->curr.reg + UNW_REG_B5); + alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_R4, sr->curr.reg + UNW_REG_R7); + } +} + +/* + * Region header descriptors. + */ + +static void +desc_prologue (int body, unw_word rlen, unsigned char mask, unsigned char grsave, + struct unw_state_record *sr) +{ + int i; + + if (!(sr->in_body || sr->first_region)) + finish_prologue(sr); + sr->first_region = 0; + + /* check if we're done: */ + if (body && sr->when_target < sr->region_start + sr->region_len) { + sr->done = 1; + return; + } + + for (i = 0; i < sr->epilogue_count; ++i) + pop(sr); + sr->epilogue_count = 0; + sr->epilogue_start = UNW_WHEN_NEVER; + + if (!body) + push(sr); + + sr->region_start += sr->region_len; + sr->region_len = rlen; + sr->in_body = body; + + if (!body) { + for (i = 0; i < 4; ++i) { + if (mask & 0x8) + set_reg(sr->curr.reg + unw.save_order[i], UNW_WHERE_GR, + sr->region_start + sr->region_len - 1, grsave++); + mask <<= 1; + } + sr->gr_save_loc = grsave; + sr->any_spills = 0; + sr->imask = 0; + sr->spill_offset = 0x10; /* default to psp+16 */ + } +} + +/* + * Prologue descriptors. + */ + +static inline void +desc_abi (unsigned char abi, unsigned char context, struct unw_state_record *sr) +{ + if (abi == 0 && context == 'i') + sr->flags |= UNW_FLAG_INTERRUPT_FRAME; + else + dprintk("unwind: ignoring unwabi(abi=0x%x,context=0x%x)\n", abi, context); +} + +static inline void +desc_br_gr (unsigned char brmask, unsigned char gr, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) { + if (brmask & 1) + set_reg(sr->curr.reg + UNW_REG_B1 + i, UNW_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + brmask >>= 1; + } +} + +static inline void +desc_br_mem (unsigned char brmask, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) { + if (brmask & 1) { + set_reg(sr->curr.reg + UNW_REG_B1 + i, UNW_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + brmask >>= 1; + } +} + +static inline void +desc_frgr_mem (unsigned char grmask, unw_word frmask, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) { + if ((grmask & 1) != 0) { + set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } + for (i = 0; i < 20; ++i) { + if ((frmask & 1) != 0) { + set_reg(sr->curr.reg + UNW_REG_F2 + i, UNW_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_fr_mem (unsigned char frmask, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) { + if ((frmask & 1) != 0) { + set_reg(sr->curr.reg + UNW_REG_F2 + i, UNW_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_gr_gr (unsigned char grmask, unsigned char gr, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) { + if ((grmask & 1) != 0) + set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + grmask >>= 1; + } +} + +static inline void +desc_gr_mem (unsigned char grmask, struct unw_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) { + if ((grmask & 1) != 0) { + set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } +} + +static inline void +desc_mem_stack_f (unw_word t, unw_word size, struct unw_state_record *sr) +{ + set_reg(sr->curr.reg + UNW_REG_PSP, UNW_WHERE_NONE, + sr->region_start + MIN((int)t, sr->region_len - 1), 16*size); +} + +static inline void +desc_mem_stack_v (unw_word t, struct unw_state_record *sr) +{ + sr->curr.reg[UNW_REG_PSP].when = sr->region_start + MIN((int)t, sr->region_len - 1); +} + +static inline void +desc_reg_gr (unsigned char reg, unsigned char dst, struct unw_state_record *sr) +{ + set_reg(sr->curr.reg + reg, UNW_WHERE_GR, sr->region_start + sr->region_len - 1, dst); +} + +static inline void +desc_reg_psprel (unsigned char reg, unw_word pspoff, struct unw_state_record *sr) +{ + set_reg(sr->curr.reg + reg, UNW_WHERE_PSPREL, sr->region_start + sr->region_len - 1, + 0x10 - 4*pspoff); +} + +static inline void +desc_reg_sprel (unsigned char reg, unw_word spoff, struct unw_state_record *sr) +{ + set_reg(sr->curr.reg + reg, UNW_WHERE_SPREL, sr->region_start + sr->region_len - 1, + 4*spoff); +} + +static inline void +desc_rp_br (unsigned char dst, struct unw_state_record *sr) +{ + sr->return_link_reg = dst; +} + +static inline void +desc_reg_when (unsigned char regnum, unw_word t, struct unw_state_record *sr) +{ + struct unw_reg_info *reg = sr->curr.reg + regnum; + + if (reg->where == UNW_WHERE_NONE) + reg->where = UNW_WHERE_GR_SAVE; + reg->when = sr->region_start + MIN((int)t, sr->region_len - 1); +} + +static inline void +desc_spill_base (unw_word pspoff, struct unw_state_record *sr) +{ + sr->spill_offset = 0x10 - 4*pspoff; +} + +static inline unsigned char * +desc_spill_mask (unsigned char *imaskp, struct unw_state_record *sr) +{ + sr->imask = imaskp; + return imaskp + (2*sr->region_len + 7)/8; +} + +/* + * Body descriptors. + */ +static inline void +desc_epilogue (unw_word t, unw_word ecount, struct unw_state_record *sr) +{ + sr->epilogue_start = sr->region_start + sr->region_len - 1 - t; + sr->epilogue_count = ecount + 1; +} + +static inline void +desc_copy_state (unw_word label, struct unw_state_record *sr) +{ + struct unw_reg_state *rs; + + for (rs = sr->reg_state_list; rs; rs = rs->next) { + if (rs->label == label) { + memcpy (&sr->curr, rs, sizeof(sr->curr)); + return; + } + } + printk("unwind: failed to find state labelled 0x%lx\n", label); +} + +static inline void +desc_label_state (unw_word label, struct unw_state_record *sr) +{ + struct unw_reg_state *rs; + + rs = alloc_reg_state(); + memcpy(rs, &sr->curr, sizeof(*rs)); + rs->label = label; + rs->next = sr->reg_state_list; + sr->reg_state_list = rs; +} + +/* + * General descriptors. + */ + +static inline int +desc_is_active (unsigned char qp, unw_word t, struct unw_state_record *sr) +{ + if (sr->when_target <= sr->region_start + MIN((int)t, sr->region_len - 1)) + return 0; + if (qp > 0) { + if ((sr->pr_val & (1UL << qp)) == 0) + return 0; + sr->pr_mask |= (1UL << qp); + } + return 1; +} + +static inline void +desc_restore_p (unsigned char qp, unw_word t, unsigned char abreg, struct unw_state_record *sr) +{ + struct unw_reg_info *r; + + if (!desc_is_active(qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg(abreg, 0); + r->where = UNW_WHERE_NONE; + r->when = sr->region_start + MIN((int)t, sr->region_len - 1); + r->val = 0; +} + +static inline void +desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg, unsigned char x, + unsigned char ytreg, struct unw_state_record *sr) +{ + enum unw_where where = UNW_WHERE_GR; + struct unw_reg_info *r; + + if (!desc_is_active(qp, t, sr)) + return; + + if (x) + where = UNW_WHERE_BR; + else if (ytreg & 0x80) + where = UNW_WHERE_FR; + + r = sr->curr.reg + decode_abreg(abreg, 0); + r->where = where; + r->when = sr->region_start + MIN((int)t, sr->region_len - 1); + r->val = (ytreg & 0x7f); +} + +static inline void +desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word pspoff, + struct unw_state_record *sr) +{ + struct unw_reg_info *r; + + if (!desc_is_active(qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg(abreg, 1); + r->where = UNW_WHERE_PSPREL; + r->when = sr->region_start + MIN((int)t, sr->region_len - 1); + r->val = 0x10 - 4*pspoff; +} + +static inline void +desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word spoff, + struct unw_state_record *sr) +{ + struct unw_reg_info *r; + + if (!desc_is_active(qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg(abreg, 1); + r->where = UNW_WHERE_SPREL; + r->when = sr->region_start + MIN((int)t, sr->region_len - 1); + r->val = 4*spoff; +} + +#define UNW_DEC_BAD_CODE(code) printk("unwind: unknown code 0x%02x\n", code); + +/* + * region headers: + */ +#define UNW_DEC_PROLOGUE_GR(fmt,r,m,gr,arg) desc_prologue(0,r,m,gr,arg) +#define UNW_DEC_PROLOGUE(fmt,b,r,arg) desc_prologue(b,r,0,32,arg) +/* + * prologue descriptors: + */ +#define UNW_DEC_ABI(fmt,a,c,arg) desc_abi(a,c,arg) +#define UNW_DEC_BR_GR(fmt,b,g,arg) desc_br_gr(b,g,arg) +#define UNW_DEC_BR_MEM(fmt,b,arg) desc_br_mem(b,arg) +#define UNW_DEC_FRGR_MEM(fmt,g,f,arg) desc_frgr_mem(g,f,arg) +#define UNW_DEC_FR_MEM(fmt,f,arg) desc_fr_mem(f,arg) +#define UNW_DEC_GR_GR(fmt,m,g,arg) desc_gr_gr(m,g,arg) +#define UNW_DEC_GR_MEM(fmt,m,arg) desc_gr_mem(m,arg) +#define UNW_DEC_MEM_STACK_F(fmt,t,s,arg) desc_mem_stack_f(t,s,arg) +#define UNW_DEC_MEM_STACK_V(fmt,t,arg) desc_mem_stack_v(t,arg) +#define UNW_DEC_REG_GR(fmt,r,d,arg) desc_reg_gr(r,d,arg) +#define UNW_DEC_REG_PSPREL(fmt,r,o,arg) desc_reg_psprel(r,o,arg) +#define UNW_DEC_REG_SPREL(fmt,r,o,arg) desc_reg_sprel(r,o,arg) +#define UNW_DEC_REG_WHEN(fmt,r,t,arg) desc_reg_when(r,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) desc_reg_when(UNW_REG_PRI_UNAT_GR,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) desc_reg_when(UNW_REG_PRI_UNAT_MEM,t,arg) +#define UNW_DEC_PRIUNAT_GR(fmt,r,arg) desc_reg_gr(UNW_REG_PRI_UNAT_GR,r,arg) +#define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) desc_reg_psprel(UNW_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) desc_reg_sprel(UNW_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_RP_BR(fmt,d,arg) desc_rp_br(d,arg) +#define UNW_DEC_SPILL_BASE(fmt,o,arg) desc_spill_base(o,arg) +#define UNW_DEC_SPILL_MASK(fmt,m,arg) (m = desc_spill_mask(m,arg)) +/* + * body descriptors: + */ +#define UNW_DEC_EPILOGUE(fmt,t,c,arg) desc_epilogue(t,c,arg) +#define UNW_DEC_COPY_STATE(fmt,l,arg) desc_copy_state(l,arg) +#define UNW_DEC_LABEL_STATE(fmt,l,arg) desc_label_state(l,arg) +/* + * general unwind descriptors: + */ +#define UNW_DEC_SPILL_REG_P(f,p,t,a,x,y,arg) desc_spill_reg_p(p,t,a,x,y,arg) +#define UNW_DEC_SPILL_REG(f,t,a,x,y,arg) desc_spill_reg_p(0,t,a,x,y,arg) +#define UNW_DEC_SPILL_PSPREL_P(f,p,t,a,o,arg) desc_spill_psprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_PSPREL(f,t,a,o,arg) desc_spill_psprel_p(0,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL_P(f,p,t,a,o,arg) desc_spill_sprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL(f,t,a,o,arg) desc_spill_sprel_p(0,t,a,o,arg) +#define UNW_DEC_RESTORE_P(f,p,t,a,arg) desc_restore_p(p,t,a,arg) +#define UNW_DEC_RESTORE(f,t,a,arg) desc_restore_p(0,t,a,arg) + +#include "unwind_decoder.c" + + +/* Unwind scripts. */ + +static inline unw_hash_index_t +hash (unsigned long ip) +{ +# define magic 0x9e3779b97f4a7c16 /* (sqrt(5)/2-1)*2^64 */ + + return (ip >> 4)*magic >> (64 - UNW_LOG_HASH_SIZE); +} + +static inline long +cache_match (struct unw_script *script, unsigned long ip, unsigned long pr_val) +{ + read_lock(&script->lock); + if ((ip) == (script)->ip && (((pr_val) ^ (script)->pr_val) & (script)->pr_mask) == 0) + /* keep the read lock... */ + return 1; + read_unlock(&script->lock); + return 0; +} + +static inline struct unw_script * +script_lookup (struct unw_frame_info *info) +{ + struct unw_script *script = unw.cache + info->hint; + unsigned long ip, pr_val; + + STAT(++unw.stat.cache.lookups); + + ip = info->ip; + pr_val = info->pr_val; + + if (cache_match(script, ip, pr_val)) { + STAT(++unw.stat.cache.hinted_hits); + return script; + } + + script = unw.cache + unw.hash[hash(ip)]; + while (1) { + if (cache_match(script, ip, pr_val)) { + /* update hint; no locking required as single-word writes are atomic */ + STAT(++unw.stat.cache.normal_hits); + unw.cache[info->prev_script].hint = script - unw.cache; + return script; + } + if (script->coll_chain >= UNW_HASH_SIZE) + return 0; + script = unw.cache + script->coll_chain; + STAT(++unw.stat.cache.collision_chain_traversals); + } +} + +/* + * On returning, a write lock for the SCRIPT is still being held. + */ +static inline struct unw_script * +script_new (unsigned long ip) +{ + struct unw_script *script, *prev, *tmp; + unsigned long flags; + unsigned char index; + unsigned short head; + + STAT(++unw.stat.script.news); + + /* + * Can't (easily) use cmpxchg() here because of ABA problem + * that is intrinsic in cmpxchg()... + */ + spin_lock_irqsave(&unw.lock, flags); + { + head = unw.lru_head; + script = unw.cache + head; + unw.lru_head = script->lru_chain; + } + spin_unlock(&unw.lock); + + /* + * XXX We'll deadlock here if we interrupt a thread that is + * holding a read lock on script->lock. A try_write_lock() + * might be mighty handy here... Alternatively, we could + * disable interrupts whenever we hold a read-lock, but that + * seems silly. + */ + write_lock(&script->lock); + + spin_lock(&unw.lock); + { + /* re-insert script at the tail of the LRU chain: */ + unw.cache[unw.lru_tail].lru_chain = head; + unw.lru_tail = head; + + /* remove the old script from the hash table (if it's there): */ + index = hash(script->ip); + tmp = unw.cache + unw.hash[index]; + prev = 0; + while (1) { + if (tmp == script) { + if (prev) + prev->coll_chain = tmp->coll_chain; + else + unw.hash[index] = tmp->coll_chain; + break; + } else + prev = tmp; + if (tmp->coll_chain >= UNW_CACHE_SIZE) + /* old script wasn't in the hash-table */ + break; + tmp = unw.cache + tmp->coll_chain; + } + + /* enter new script in the hash table */ + index = hash(ip); + script->coll_chain = unw.hash[index]; + unw.hash[index] = script - unw.cache; + + script->ip = ip; /* set new IP while we're holding the locks */ + + STAT(if (script->coll_chain < UNW_CACHE_SIZE) ++unw.stat.script.collisions); + } + spin_unlock_irqrestore(&unw.lock, flags); + + script->flags = 0; + script->hint = 0; + script->count = 0; + return script; +} + +static void +script_finalize (struct unw_script *script, struct unw_state_record *sr) +{ + script->pr_mask = sr->pr_mask; + script->pr_val = sr->pr_val; + /* + * We could down-grade our write-lock on script->lock here but + * the rwlock API doesn't offer atomic lock downgrading, so + * we'll just keep the write-lock and release it later when + * we're done using the script. + */ +} + +static inline void +script_emit (struct unw_script *script, struct unw_insn insn) +{ + if (script->count >= UNW_MAX_SCRIPT_LEN) { + dprintk("unwind: script exceeds maximum size of %u instructions!\n", + UNW_MAX_SCRIPT_LEN); + return; + } + script->insn[script->count++] = insn; +} + +static inline void +emit_nat_info (struct unw_state_record *sr, int i, struct unw_script *script) +{ + struct unw_reg_info *r = sr->curr.reg + i; + enum unw_insn_opcode opc; + struct unw_insn insn; + unsigned long val; + + switch (r->where) { + case UNW_WHERE_GR: + if (r->val >= 32) { + /* register got spilled to a stacked register */ + opc = UNW_INSN_SETNAT_TYPE; + val = UNW_NAT_STACKED; + } else { + /* register got spilled to a scratch register */ + opc = UNW_INSN_SETNAT_TYPE; + val = UNW_NAT_SCRATCH; + } + break; + + case UNW_WHERE_FR: + opc = UNW_INSN_SETNAT_TYPE; + val = UNW_NAT_VAL; + break; + + case UNW_WHERE_BR: + opc = UNW_INSN_SETNAT_TYPE; + val = UNW_NAT_NONE; + break; + + case UNW_WHERE_PSPREL: + case UNW_WHERE_SPREL: + opc = UNW_INSN_SETNAT_PRI_UNAT; + val = 0; + break; + + default: + dprintk("unwind: don't know how to emit nat info for where = %u\n", r->where); + return; + } + insn.opc = opc; + insn.dst = unw.preg_index[i]; + insn.val = val; + script_emit(script, insn); +} + +static void +compile_reg (struct unw_state_record *sr, int i, struct unw_script *script) +{ + struct unw_reg_info *r = sr->curr.reg + i; + enum unw_insn_opcode opc; + unsigned long val, rval; + struct unw_insn insn; + long need_nat_info; + + if (r->where == UNW_WHERE_NONE || r->when >= sr->when_target) + return; + + opc = UNW_INSN_MOVE; + val = rval = r->val; + need_nat_info = (i >= UNW_REG_R4 && i <= UNW_REG_R7); + + switch (r->where) { + case UNW_WHERE_GR: + if (rval >= 32) { + opc = UNW_INSN_MOVE_STACKED; + val = rval - 32; + } else if (rval >= 4 && rval <= 7) { + if (need_nat_info) { + opc = UNW_INSN_MOVE2; + need_nat_info = 0; + } + val = unw.preg_index[UNW_REG_R4 + (rval - 4)]; + } else { + opc = UNW_INSN_LOAD_SPREL; + val = -sizeof(struct pt_regs); + if (rval >= 1 && rval <= 3) + val += struct_offset(struct pt_regs, r1) + 8*(rval - 1); + else if (rval <= 11) + val += struct_offset(struct pt_regs, r8) + 8*(rval - 8); + else if (rval <= 15) + val += struct_offset(struct pt_regs, r12) + 8*(rval - 12); + else if (rval <= 31) + val += struct_offset(struct pt_regs, r16) + 8*(rval - 16); + else + dprintk("unwind: bad scratch reg r%lu\n", rval); + } + break; + + case UNW_WHERE_FR: + if (rval <= 5) + val = unw.preg_index[UNW_REG_F2 + (rval - 1)]; + else if (rval >= 16 && rval <= 31) + val = unw.preg_index[UNW_REG_F16 + (rval - 16)]; + else { + opc = UNW_INSN_LOAD_SPREL; + val = -sizeof(struct pt_regs); + if (rval <= 9) + val += struct_offset(struct pt_regs, f6) + 16*(rval - 6); + else + dprintk("unwind: kernel may not touch f%lu\n", rval); + } + break; + + case UNW_WHERE_BR: + if (rval >= 1 && rval <= 5) + val = unw.preg_index[UNW_REG_B1 + (rval - 1)]; + else { + opc = UNW_INSN_LOAD_SPREL; + val = -sizeof(struct pt_regs); + if (rval == 0) + val += struct_offset(struct pt_regs, b0); + else if (rval == 6) + val += struct_offset(struct pt_regs, b6); + else + val += struct_offset(struct pt_regs, b7); + } + break; + + case UNW_WHERE_SPREL: + opc = UNW_INSN_LOAD_SPREL; + break; + + case UNW_WHERE_PSPREL: + opc = UNW_INSN_LOAD_PSPREL; + break; + + default: + dprintk("unwind: register %u has unexpected `where' value of %u\n", i, r->where); + break; + } + insn.opc = opc; + insn.dst = unw.preg_index[i]; + insn.val = val; + script_emit(script, insn); + if (need_nat_info) + emit_nat_info(sr, i, script); +} + +static inline struct unw_table_entry * +lookup (struct unw_table *table, unsigned long rel_ip) +{ + struct unw_table_entry *e = 0; + unsigned long lo, hi, mid; + + /* do a binary search for right entry: */ + for (lo = 0, hi = table->length; lo < hi; ) { + mid = (lo + hi) / 2; + e = &table->array[mid]; + if (rel_ip < e->start_offset) + hi = mid; + else if (rel_ip >= e->end_offset) + lo = mid + 1; + else + break; + } + return e; +} + +/* + * Build an unwind script that unwinds from state OLD_STATE to the + * entrypoint of the function that called OLD_STATE. + */ +static inline struct unw_script * +build_script (struct unw_frame_info *info) +{ + struct unw_reg_state *rs, *next; + struct unw_table_entry *e = 0; + struct unw_script *script = 0; + unsigned long ip = info->ip; + struct unw_state_record sr; + struct unw_table *table; + struct unw_reg_info *r; + struct unw_insn insn; + u8 *dp, *desc_end; + u64 hdr; + int i; + STAT(unsigned long start, parse_start;) + + STAT(++unw.stat.script.builds; start = ia64_get_itc()); + + /* build state record */ + memset(&sr, 0, sizeof(sr)); + for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r) + r->when = UNW_WHEN_NEVER; + sr.pr_val = info->pr_val; + + script = script_new(ip); + if (!script) { + dprintk("unwind: failed to create unwind script\n"); + STAT(unw.stat.script.build_time += ia64_get_itc() - start); + return 0; + } + unw.cache[info->prev_script].hint = script - unw.cache; + + /* search the kernels and the modules' unwind tables for IP: */ + + STAT(parse_start = ia64_get_itc()); + + for (table = unw.tables; table; table = table->next) { + if (ip >= table->start && ip < table->end) { + e = lookup(table, ip - table->segment_base); + break; + } + } + if (!e) { + /* no info, return default unwinder (leaf proc, no mem stack, no saved regs) */ + dprintk("unwind: no unwind info for ip=0x%lx (prev ip=0x%lx)\n", ip, + unw.cache[info->prev_script].ip); + sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR; + sr.curr.reg[UNW_REG_RP].when = -1; + sr.curr.reg[UNW_REG_RP].val = 0; + compile_reg(&sr, UNW_REG_RP, script); + script_finalize(script, &sr); + STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start); + STAT(unw.stat.script.build_time += ia64_get_itc() - start); + return script; + } + + sr.when_target = (3*((ip & ~0xfUL) - (table->segment_base + e->start_offset))/16 + + (ip & 0xfUL)); + hdr = *(u64 *) (table->segment_base + e->info_offset); + dp = (u8 *) (table->segment_base + e->info_offset + 8); + desc_end = dp + 8*UNW_LENGTH(hdr); + + while (!sr.done && dp < desc_end) + dp = unw_decode(dp, sr.in_body, &sr); + + if (sr.when_target > sr.epilogue_start) { + /* + * sp has been restored and all values on the memory stack below + * psp also have been restored. + */ + sr.curr.reg[UNW_REG_PSP].where = UNW_WHERE_NONE; + sr.curr.reg[UNW_REG_PSP].val = 0; + for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r) + if ((r->where == UNW_WHERE_PSPREL && r->val <= 0x10) + || r->where == UNW_WHERE_SPREL) + r->where = UNW_WHERE_NONE; + } + + script->flags = sr.flags; + + /* + * If RP did't get saved, generate entry for the return link + * register. + */ + if (sr.curr.reg[UNW_REG_RP].when >= sr.when_target) { + sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR; + sr.curr.reg[UNW_REG_RP].when = -1; + sr.curr.reg[UNW_REG_RP].val = sr.return_link_reg; + } + +#if UNW_DEBUG + printk ("unwind: state record for func 0x%lx, t=%u:\n", + table->segment_base + e->start_offset, sr.when_target); + for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r) { + if (r->where != UNW_WHERE_NONE || r->when != UNW_WHEN_NEVER) { + printk(" %s <- ", unw.preg_name[r - sr.curr.reg]); + switch (r->where) { + case UNW_WHERE_GR: printk("r%lu", r->val); break; + case UNW_WHERE_FR: printk("f%lu", r->val); break; + case UNW_WHERE_BR: printk("b%lu", r->val); break; + case UNW_WHERE_SPREL: printk("[sp+0x%lx]", r->val); break; + case UNW_WHERE_PSPREL: printk("[psp+0x%lx]", r->val); break; + case UNW_WHERE_NONE: + printk("%s+0x%lx", unw.preg_name[r - sr.curr.reg], r->val); + break; + default: printk("BADWHERE(%d)", r->where); break; + } + printk ("\t\t%d\n", r->when); + } + } +#endif + + STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start); + + /* translate state record into unwinder instructions: */ + + if (sr.curr.reg[UNW_REG_PSP].where == UNW_WHERE_NONE + && sr.when_target > sr.curr.reg[UNW_REG_PSP].when && sr.curr.reg[UNW_REG_PSP].val != 0) + { + /* new psp is sp plus frame size */ + insn.opc = UNW_INSN_ADD; + insn.dst = unw.preg_index[UNW_REG_PSP]; + insn.val = sr.curr.reg[UNW_REG_PSP].val; + script_emit(script, insn); + } + + /* determine where the primary UNaT is: */ + if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_GR].when) + i = UNW_REG_PRI_UNAT_MEM; + else if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when) + i = UNW_REG_PRI_UNAT_GR; + else if (sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when > sr.curr.reg[UNW_REG_PRI_UNAT_GR].when) + i = UNW_REG_PRI_UNAT_MEM; + else + i = UNW_REG_PRI_UNAT_GR; + + compile_reg(&sr, i, script); + + for (i = UNW_REG_BSP; i < UNW_NUM_REGS; ++i) + compile_reg(&sr, i, script); + + /* free labelled register states & stack: */ + + STAT(parse_start = ia64_get_itc()); + for (rs = sr.reg_state_list; rs; rs = next) { + next = rs->next; + free_reg_state(rs); + } + while (sr.stack) + pop(&sr); + STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start); + + script_finalize(script, &sr); + STAT(unw.stat.script.build_time += ia64_get_itc() - start); + return script; +} + +/* + * Apply the unwinding actions represented by OPS and update SR to + * reflect the state that existed upon entry to the function that this + * unwinder represents. + */ +static inline void +run_script (struct unw_script *script, struct unw_frame_info *state) +{ + struct unw_insn *ip, *limit, next_insn; + unsigned long opc, dst, val, off; + unsigned long *s = (unsigned long *) state; + STAT(unsigned long start;) + + STAT(++unw.stat.script.runs; start = ia64_get_itc()); + state->flags = script->flags; + ip = script->insn; + limit = script->insn + script->count; + next_insn = *ip; + + while (ip++ < limit) { + opc = next_insn.opc; + dst = next_insn.dst; + val = next_insn.val; + next_insn = *ip; + + redo: + switch (opc) { + case UNW_INSN_ADD: + s[dst] += val; + break; + + case UNW_INSN_MOVE2: + if (!s[val]) + goto lazy_init; + s[dst+1] = s[val+1]; + s[dst] = s[val]; + break; + + case UNW_INSN_MOVE: + if (!s[val]) + goto lazy_init; + s[dst] = s[val]; + break; + + case UNW_INSN_MOVE_STACKED: + s[dst] = (unsigned long) ia64_rse_skip_regs((unsigned long *)state->bsp, + val); + break; + + case UNW_INSN_LOAD_PSPREL: + s[dst] = state->psp + val; + break; + + case UNW_INSN_LOAD_SPREL: + s[dst] = state->sp + val; + break; + + case UNW_INSN_SETNAT_PRI_UNAT: + if (!state->pri_unat) + state->pri_unat = &state->sw->caller_unat; + s[dst+1] = ((*state->pri_unat - s[dst]) << 32) | UNW_NAT_PRI_UNAT; + break; + + case UNW_INSN_SETNAT_TYPE: + s[dst+1] = val; + break; + } + } + STAT(unw.stat.script.run_time += ia64_get_itc() - start); + return; + + lazy_init: + off = unw.sw_off[val]; + s[val] = (unsigned long) state->sw + off; + if (off >= struct_offset (struct unw_frame_info, r4) + && off <= struct_offset (struct unw_frame_info, r7)) + /* + * We're initializing a general register: init NaT info, too. Note that we + * rely on the fact that call_unat is the first field in struct switch_stack: + */ + s[val+1] = (-off << 32) | UNW_NAT_PRI_UNAT; + goto redo; +} + +static int +find_save_locs (struct unw_frame_info *info) +{ + int have_write_lock = 0; + struct unw_script *scr; + + if ((info->ip & (my_cpu_data.unimpl_va_mask | 0xf)) + || REGION_NUMBER(info->ip) != REGION_KERNEL) + { + /* don't let obviously bad addresses pollute the cache */ + dprintk("unwind: rejecting bad ip=0x%lx\n", info->ip); + info->rp = 0; + return -1; + } + + scr = script_lookup(info); + if (!scr) { + scr = build_script(info); + if (!scr) { + dprintk("unwind: failed to locate/build unwind script for ip %lx\n", + info->ip); + return -1; + } + have_write_lock = 1; + } + info->hint = scr->hint; + info->prev_script = scr - unw.cache; + + run_script(scr, info); + + if (have_write_lock) + write_unlock(&scr->lock); + else + read_unlock(&scr->lock); + return 0; +} + +int +unw_unwind (struct unw_frame_info *info) +{ + unsigned long prev_ip, prev_sp, prev_bsp; + unsigned long ip, pr, num_regs; + STAT(unsigned long start, flags;) + int retval; + + STAT(local_irq_save(flags); ++unw.stat.api.unwinds; start = ia64_get_itc()); + + prev_ip = info->ip; + prev_sp = info->sp; + prev_bsp = info->bsp; + + /* restore the ip */ + if (!info->rp) { + dprintk("unwind: failed to locate return link (ip=0x%lx)!\n", info->ip); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + ip = info->ip = *info->rp; + if (ip < GATE_ADDR + PAGE_SIZE) { + /* + * We don't have unwind info for the gate page, so we consider that part + * of user-space for the purpose of unwinding. + */ + dprintk("unwind: reached user-space (ip=0x%lx)\n", ip); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + + /* restore the cfm: */ + if (!info->pfs) { + dprintk("unwind: failed to locate ar.pfs!\n"); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + info->cfm = info->pfs; + + /* restore the bsp: */ + pr = info->pr_val; + num_regs = 0; + if ((info->flags & UNW_FLAG_INTERRUPT_FRAME)) { + if ((pr & (1UL << pNonSys)) != 0) + num_regs = *info->cfm & 0x7f; /* size of frame */ + info->pfs = + (unsigned long *) (info->sp + 16 + struct_offset(struct pt_regs, ar_pfs)); + } else + num_regs = (*info->cfm >> 7) & 0x7f; /* size of locals */ + info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -num_regs); + if (info->bsp < info->regstk.limit || info->bsp > info->regstk.top) { + dprintk("unwind: bsp (0x%lx) out of range [0x%lx-0x%lx]\n", + info->bsp, info->regstk.limit, info->regstk.top); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + + /* restore the sp: */ + info->sp = info->psp; + if (info->sp < info->memstk.top || info->sp > info->memstk.limit) { + dprintk("unwind: sp (0x%lx) out of range [0x%lx-0x%lx]\n", + info->sp, info->regstk.top, info->regstk.limit); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + + if (info->ip == prev_ip && info->sp == prev_sp && info->bsp == prev_bsp) { + dprintk("unwind: ip, sp, bsp remain unchanged; stopping here (ip=0x%lx)\n", ip); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return -1; + } + + /* finally, restore the predicates: */ + unw_get_pr(info, &info->pr_val); + + retval = find_save_locs(info); + STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); + return retval; +} + +int +unw_unwind_to_user (struct unw_frame_info *info) +{ + unsigned long ip; + + while (unw_unwind(info) >= 0) { + if (unw_get_rp(info, &ip) < 0) { + unw_get_ip(info, &ip); + dprintk("unwind: failed to read return pointer (ip=0x%lx)\n", ip); + return -1; + } + /* + * We don't have unwind info for the gate page, so we consider that part + * of user-space for the purpose of unwinding. + */ + if (ip < GATE_ADDR + PAGE_SIZE) + return 0; + } + unw_get_ip(info, &ip); + dprintk("unwind: failed to unwind to user-level (ip=0x%lx)\n", ip); + return -1; +} + +void +unw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw) +{ + unsigned long rbslimit, rbstop, stklimit, stktop, sol; + STAT(unsigned long start, flags;) + + STAT(local_irq_save(flags); ++unw.stat.api.inits; start = ia64_get_itc()); + + /* + * Subtle stuff here: we _could_ unwind through the + * switch_stack frame but we don't want to do that because it + * would be slow as each preserved register would have to be + * processed. Instead, what we do here is zero out the frame + * info and start the unwind process at the function that + * created the switch_stack frame. When a preserved value in + * switch_stack needs to be accessed, run_script() will + * initialize the appropriate pointer on demand. + */ + memset(info, 0, sizeof(*info)); + + rbslimit = (unsigned long) t + IA64_RBS_OFFSET; + rbstop = sw->ar_bspstore; + if (rbstop - (unsigned long) t >= IA64_STK_OFFSET) + rbstop = rbslimit; + + stklimit = (unsigned long) t + IA64_STK_OFFSET; + stktop = (unsigned long) sw - 16; + if (stktop <= rbstop) + stktop = rbstop; + + info->regstk.limit = rbslimit; + info->regstk.top = rbstop; + info->memstk.limit = stklimit; + info->memstk.top = stktop; + info->task = t; + info->sw = sw; + info->sp = info->psp = (unsigned long) (sw + 1) - 16; + info->cfm = &sw->ar_pfs; + sol = (*info->cfm >> 7) & 0x7f; + info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol); + info->ip = sw->b0; + info->pr_val = sw->pr; + + find_save_locs(info); + STAT(unw.stat.api.init_time += ia64_get_itc() - start; local_irq_restore(flags)); +} + +#endif /* CONFIG_IA64_NEW_UNWIND */ + void -ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, struct task_struct *t) +unw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t) { struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16); + +#ifdef CONFIG_IA64_NEW_UNWIND + unw_init_frame_info(info, t, sw); +#else unsigned long sol, limit, top; memset(info, 0, sizeof(*info)); @@ -22,17 +1802,25 @@ ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, struct task_st if (top - (unsigned long) t >= IA64_STK_OFFSET) top = limit; - info->regstk.limit = (unsigned long *) limit; - info->regstk.top = (unsigned long *) top; - info->bsp = ia64_rse_skip_regs(info->regstk.top, -sol); - info->top_rnat = sw->ar_rnat; - info->cfm = sw->ar_pfs; - info->ip = sw->b0; + info->regstk.limit = limit; + info->regstk.top = top; + info->sw = sw; + info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol); + info->cfm = &sw->ar_pfs; + info->ip = sw->b0; +#endif } void -ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *regs) +unw_init_from_current (struct unw_frame_info *info, struct pt_regs *regs) { +#ifdef CONFIG_IA64_NEW_UNWIND + struct switch_stack *sw = (struct switch_stack *) regs - 1; + + unw_init_frame_info(info, current, sw); + /* skip over interrupt frame: */ + unw_unwind(info); +#else struct switch_stack *sw = (struct switch_stack *) regs - 1; unsigned long sol, sof, *bsp, limit, top; @@ -44,34 +1832,40 @@ ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *reg memset(info, 0, sizeof(*info)); sol = (sw->ar_pfs >> 7) & 0x7f; /* size of frame */ - info->regstk.limit = (unsigned long *) limit; - info->regstk.top = (unsigned long *) top; - info->top_rnat = sw->ar_rnat; /* this gives us the bsp top level frame (kdb interrupt frame): */ bsp = ia64_rse_skip_regs((unsigned long *) top, -sol); /* now skip past the interrupt frame: */ sof = regs->cr_ifs & 0x7f; /* size of frame */ - info->cfm = regs->cr_ifs; - info->bsp = ia64_rse_skip_regs(bsp, -sof); + + info->regstk.limit = limit; + info->regstk.top = top; + info->sw = sw; + info->bsp = (unsigned long) ia64_rse_skip_regs(bsp, -sof); + info->cfm = ®s->cr_ifs; info->ip = regs->cr_iip; +#endif } +#ifndef CONFIG_IA64_NEW_UNWIND + static unsigned long -read_reg (struct ia64_frame_info *info, int regnum, int *is_nat) +read_reg (struct unw_frame_info *info, int regnum, int *is_nat) { unsigned long *addr, *rnat_addr, rnat; - addr = ia64_rse_skip_regs(info->bsp, regnum); - if (addr < info->regstk.limit || addr >= info->regstk.top || ((long) addr & 0x7) != 0) { + addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum); + if ((unsigned long) addr < info->regstk.limit + || (unsigned long) addr >= info->regstk.top || ((long) addr & 0x7) != 0) + { *is_nat = 1; return 0xdeadbeefdeadbeef; } rnat_addr = ia64_rse_rnat_addr(addr); - if (rnat_addr >= info->regstk.top) - rnat = info->top_rnat; + if ((unsigned long) rnat_addr >= info->regstk.top) + rnat = info->sw->ar_rnat; else rnat = *rnat_addr; *is_nat = (rnat & (1UL << ia64_rse_slot_num(addr))) != 0; @@ -83,9 +1877,9 @@ read_reg (struct ia64_frame_info *info, int regnum, int *is_nat) * store for r32. */ int -ia64_unwind_to_previous_frame (struct ia64_frame_info *info) +unw_unwind (struct unw_frame_info *info) { - unsigned long sol, cfm = info->cfm; + unsigned long sol, cfm = *info->cfm; int is_nat; sol = (cfm >> 7) & 0x7f; /* size of locals */ @@ -103,16 +1897,187 @@ ia64_unwind_to_previous_frame (struct ia64_frame_info *info) return -1; info->ip = read_reg(info, sol - 2, &is_nat); - if (is_nat) + if (is_nat || (info->ip & (my_cpu_data.unimpl_va_mask | 0xf))) + /* reject let obviously bad addresses */ return -1; + info->cfm = ia64_rse_skip_regs((unsigned long *) info->bsp, sol - 1); cfm = read_reg(info, sol - 1, &is_nat); if (is_nat) return -1; sol = (cfm >> 7) & 0x7f; - info->cfm = cfm; - info->bsp = ia64_rse_skip_regs(info->bsp, -sol); + info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -sol); return 0; } +#endif /* !CONFIG_IA64_NEW_UNWIND */ + +#ifdef CONFIG_IA64_NEW_UNWIND + +static void +init_unwind_table (struct unw_table *table, const char *name, unsigned long segment_base, + unsigned long gp, void *table_start, void *table_end) +{ + struct unw_table_entry *start = table_start, *end = table_end; + +#ifdef UNWIND_TABLE_SORT_BUG + { + struct unw_table_entry *e1, *e2, tmp; + + /* stupid bubble sort... */ + + for (e1 = start; e1 < end; ++e1) { + for (e2 = e1 + 1; e2 < end; ++e2) { + if (e2->start_offset < e1->start_offset) { + tmp = *e1; + *e1 = *e2; + *e2 = tmp; + } + } + } + } +#endif + table->name = name; + table->segment_base = segment_base; + table->gp = gp; + table->start = segment_base + start[0].start_offset; + table->end = segment_base + end[-1].end_offset; + table->array = start; + table->length = end - start; +} + +void * +unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp, + void *table_start, void *table_end) +{ + struct unw_table_entry *start = table_start, *end = table_end; + struct unw_table *table; + unsigned long flags; + + if (end - start <= 0) { + dprintk("unwind: ignoring attempt to insert empty unwind table\n"); + return 0; + } + + table = kmalloc(sizeof(*table), GFP_USER); + if (!table) + return 0; + + init_unwind_table(table, name, segment_base, gp, table_start, table_end); + + spin_lock_irqsave(&unw.lock, flags); + { + /* keep kernel unwind table at the front (it's searched most commonly): */ + table->next = unw.tables->next; + unw.tables->next = table; + } + spin_unlock_irqrestore(&unw.lock, flags); + + return table; +} + +void +unw_remove_unwind_table (void *handle) +{ + struct unw_table *table, *prevt; + struct unw_script *tmp, *prev; + unsigned long flags; + long index; + + if (!handle) { + dprintk("unwind: ignoring attempt to remove non-existent unwind table\n"); + return; + } + + table = handle; + if (table == &unw.kernel_table) { + dprintk("unwind: sorry, freeing the kernel's unwind table is a no-can-do!\n"); + return; + } + + spin_lock_irqsave(&unw.lock, flags); + { + /* first, delete the table: */ + + for (prevt = (struct unw_table *) &unw.tables; prevt; prevt = prevt->next) + if (prevt->next == table) + break; + if (!prevt) { + dprintk("unwind: failed to find unwind table %p\n", table); + spin_unlock_irqrestore(&unw.lock, flags); + return; + } + prevt->next = table->next; + + /* next, remove hash table entries for this table */ + + for (index = 0; index <= UNW_HASH_SIZE; ++index) { + if (unw.hash[index] >= UNW_CACHE_SIZE) + continue; + + tmp = unw.cache + unw.hash[index]; + prev = 0; + while (1) { + write_lock(&tmp->lock); + { + if (tmp->ip >= table->start && tmp->ip < table->end) { + if (prev) + prev->coll_chain = tmp->coll_chain; + else + unw.hash[index] = -1; + tmp->ip = 0; + } else + prev = tmp; + } + write_unlock(&tmp->lock); + } + } + } + spin_unlock_irqrestore(&unw.lock, flags); + + kfree(table); +} +#endif /* CONFIG_IA64_NEW_UNWIND */ + +void +unw_init (void) +{ +#ifdef CONFIG_IA64_NEW_UNWIND + extern int ia64_unw_start, ia64_unw_end, __gp; + extern void unw_hash_index_t_is_too_narrow (void); + long i, off; + + if (8*sizeof(unw_hash_index_t) < UNW_LOG_HASH_SIZE) + unw_hash_index_t_is_too_narrow(); + + unw.sw_off[unw.preg_index[UNW_REG_PRI_UNAT_GR]] = SW(AR_UNAT); + unw.sw_off[unw.preg_index[UNW_REG_BSPSTORE]] = SW(AR_BSPSTORE); + unw.sw_off[unw.preg_index[UNW_REG_PFS]] = SW(AR_UNAT); + unw.sw_off[unw.preg_index[UNW_REG_RP]] = SW(B0); + unw.sw_off[unw.preg_index[UNW_REG_UNAT]] = SW(AR_UNAT); + unw.sw_off[unw.preg_index[UNW_REG_PR]] = SW(PR); + unw.sw_off[unw.preg_index[UNW_REG_LC]] = SW(AR_LC); + unw.sw_off[unw.preg_index[UNW_REG_FPSR]] = SW(AR_FPSR); + for (i = UNW_REG_R4, off = SW(R4); i <= UNW_REG_R7; ++i, off += 8) + unw.sw_off[unw.preg_index[i]] = off; + for (i = UNW_REG_B1, off = SW(B1); i <= UNW_REG_B5; ++i, off += 8) + unw.sw_off[unw.preg_index[i]] = off; + for (i = UNW_REG_F2, off = SW(F2); i <= UNW_REG_F5; ++i, off += 16) + unw.sw_off[unw.preg_index[i]] = off; + for (i = UNW_REG_F16, off = SW(F16); i <= UNW_REG_F31; ++i, off += 16) + unw.sw_off[unw.preg_index[i]] = off; + + unw.cache[0].coll_chain = -1; + for (i = 1; i < UNW_CACHE_SIZE; ++i) { + unw.cache[i].lru_chain = (i - 1); + unw.cache[i].coll_chain = -1; + unw.cache[i].lock = RW_LOCK_UNLOCKED; + } + unw.lru_head = UNW_CACHE_SIZE - 1; + unw.lru_tail = 0; + + init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) &__gp, + &ia64_unw_start, &ia64_unw_end); +#endif /* CONFIG_IA64_NEW_UNWIND */ +} diff --git a/arch/ia64/kernel/unwind_decoder.c b/arch/ia64/kernel/unwind_decoder.c new file mode 100644 index 000000000..50ac2d82f --- /dev/null +++ b/arch/ia64/kernel/unwind_decoder.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Generic IA-64 unwind info decoder. + * + * This file is used both by the Linux kernel and objdump. Please keep + * the two copies of this file in sync. + * + * You need to customize the decoder by defining the following + * macros/constants before including this file: + * + * Types: + * unw_word Unsigned integer type with at least 64 bits + * + * Register names: + * UNW_REG_BSP + * UNW_REG_BSPSTORE + * UNW_REG_FPSR + * UNW_REG_LC + * UNW_REG_PFS + * UNW_REG_PR + * UNW_REG_RNAT + * UNW_REG_PSP + * UNW_REG_RP + * UNW_REG_UNAT + * + * Decoder action macros: + * UNW_DEC_BAD_CODE(code) + * UNW_DEC_ABI(fmt,abi,context,arg) + * UNW_DEC_BR_GR(fmt,brmask,gr,arg) + * UNW_DEC_BR_MEM(fmt,brmask,arg) + * UNW_DEC_COPY_STATE(fmt,label,arg) + * UNW_DEC_EPILOGUE(fmt,t,ecount,arg) + * UNW_DEC_FRGR_MEM(fmt,grmask,frmask,arg) + * UNW_DEC_FR_MEM(fmt,frmask,arg) + * UNW_DEC_GR_GR(fmt,grmask,gr,arg) + * UNW_DEC_GR_MEM(fmt,grmask,arg) + * UNW_DEC_LABEL_STATE(fmt,label,arg) + * UNW_DEC_MEM_STACK_F(fmt,t,size,arg) + * UNW_DEC_MEM_STACK_V(fmt,t,arg) + * UNW_DEC_PRIUNAT_GR(fmt,r,arg) + * UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) + * UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) + * UNW_DEC_PRIUNAT_WHEN_PSPREL(fmt,pspoff,arg) + * UNW_DEC_PRIUNAT_WHEN_SPREL(fmt,spoff,arg) + * UNW_DEC_PROLOGUE(fmt,body,rlen,arg) + * UNW_DEC_PROLOGUE_GR(fmt,rlen,mask,grsave,arg) + * UNW_DEC_REG_PSPREL(fmt,reg,pspoff,arg) + * UNW_DEC_REG_REG(fmt,src,dst,arg) + * UNW_DEC_REG_SPREL(fmt,reg,spoff,arg) + * UNW_DEC_REG_WHEN(fmt,reg,t,arg) + * UNW_DEC_RESTORE(fmt,t,abreg,arg) + * UNW_DEC_RESTORE_P(fmt,qp,t,abreg,arg) + * UNW_DEC_SPILL_BASE(fmt,pspoff,arg) + * UNW_DEC_SPILL_MASK(fmt,imaskp,arg) + * UNW_DEC_SPILL_PSPREL(fmt,t,abreg,pspoff,arg) + * UNW_DEC_SPILL_PSPREL_P(fmt,qp,t,abreg,pspoff,arg) + * UNW_DEC_SPILL_REG(fmt,t,abreg,x,ytreg,arg) + * UNW_DEC_SPILL_REG_P(fmt,qp,t,abreg,x,ytreg,arg) + * UNW_DEC_SPILL_SPREL(fmt,t,abreg,spoff,arg) + * UNW_DEC_SPILL_SPREL_P(fmt,qp,t,abreg,pspoff,arg) + */ + +static unw_word +unw_decode_uleb128 (unsigned char **dpp) +{ + unsigned shift = 0; + unw_word byte, result = 0; + unsigned char *bp = *dpp; + + while (1) + { + byte = *bp++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + *dpp = bp; + return result; +} + +static unsigned char * +unw_decode_x1 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, abreg; + unw_word t, off; + + byte1 = *dp++; + t = unw_decode_uleb128 (&dp); + off = unw_decode_uleb128 (&dp); + abreg = (byte1 & 0x7f); + if (byte1 & 0x80) + UNW_DEC_SPILL_SPREL(X1, t, abreg, off, arg); + else + UNW_DEC_SPILL_PSPREL(X1, t, abreg, off, arg); + return dp; +} + +static unsigned char * +unw_decode_x2 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, abreg, x, ytreg; + unw_word t; + + byte1 = *dp++; byte2 = *dp++; + t = unw_decode_uleb128 (&dp); + abreg = (byte1 & 0x7f); + ytreg = byte2; + x = (byte1 >> 7) & 1; + if ((byte1 & 0x80) == 0 && ytreg == 0) + UNW_DEC_RESTORE(X2, t, abreg, arg); + else + UNW_DEC_SPILL_REG(X2, t, abreg, x, ytreg, arg); + return dp; +} + +static unsigned char * +unw_decode_x3 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, abreg, qp; + unw_word t, off; + + byte1 = *dp++; byte2 = *dp++; + t = unw_decode_uleb128 (&dp); + off = unw_decode_uleb128 (&dp); + + qp = (byte1 & 0x3f); + abreg = (byte2 & 0x7f); + + if (byte1 & 0x80) + UNW_DEC_SPILL_SPREL_P(X3, qp, t, abreg, off, arg); + else + UNW_DEC_SPILL_PSPREL_P(X3, qp, t, abreg, off, arg); + return dp; +} + +static unsigned char * +unw_decode_x4 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, byte3, qp, abreg, x, ytreg; + unw_word t; + + byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; + t = unw_decode_uleb128 (&dp); + + qp = (byte1 & 0x3f); + abreg = (byte2 & 0x7f); + x = (byte2 >> 7) & 1; + ytreg = byte3; + + if ((byte2 & 0x80) == 0 && byte3 == 0) + UNW_DEC_RESTORE_P(X4, qp, t, abreg, arg); + else + UNW_DEC_SPILL_REG_P(X4, qp, t, abreg, x, ytreg, arg); + return dp; +} + +static unsigned char * +unw_decode_r1 (unsigned char *dp, unsigned char code, void *arg) +{ + int body = (code & 0x20) != 0; + unw_word rlen; + + rlen = (code & 0x1f); + UNW_DEC_PROLOGUE(R1, body, rlen, arg); + return dp; +} + +static unsigned char * +unw_decode_r2 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, mask, grsave; + unw_word rlen; + + byte1 = *dp++; + + mask = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); + grsave = (byte1 & 0x7f); + rlen = unw_decode_uleb128 (&dp); + UNW_DEC_PROLOGUE_GR(R2, rlen, mask, grsave, arg); + return dp; +} + +static unsigned char * +unw_decode_r3 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word rlen; + + rlen = unw_decode_uleb128 (&dp); + UNW_DEC_PROLOGUE(R3, ((code & 0x3) == 1), rlen, arg); + return dp; +} + +static unsigned char * +unw_decode_p1 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char brmask = (code & 0x1f); + + UNW_DEC_BR_MEM(P1, brmask, arg); + return dp; +} + +static unsigned char * +unw_decode_p2_p5 (unsigned char *dp, unsigned char code, void *arg) +{ + if ((code & 0x10) == 0) + { + unsigned char byte1 = *dp++; + + UNW_DEC_BR_GR(P2, ((code & 0xf) << 1) | ((byte1 >> 7) & 1), + (byte1 & 0x7f), arg); + } + else if ((code & 0x08) == 0) + { + unsigned char byte1 = *dp++, r, dst; + + r = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); + dst = (byte1 & 0x7f); + switch (r) + { + case 0: UNW_DEC_REG_GR(P3, UNW_REG_PSP, dst, arg); break; + case 1: UNW_DEC_REG_GR(P3, UNW_REG_RP, dst, arg); break; + case 2: UNW_DEC_REG_GR(P3, UNW_REG_PFS, dst, arg); break; + case 3: UNW_DEC_REG_GR(P3, UNW_REG_PR, dst, arg); break; + case 4: UNW_DEC_REG_GR(P3, UNW_REG_UNAT, dst, arg); break; + case 5: UNW_DEC_REG_GR(P3, UNW_REG_LC, dst, arg); break; + case 6: UNW_DEC_RP_BR(P3, dst, arg); break; + case 7: UNW_DEC_REG_GR(P3, UNW_REG_RNAT, dst, arg); break; + case 8: UNW_DEC_REG_GR(P3, UNW_REG_BSP, dst, arg); break; + case 9: UNW_DEC_REG_GR(P3, UNW_REG_BSPSTORE, dst, arg); break; + case 10: UNW_DEC_REG_GR(P3, UNW_REG_FPSR, dst, arg); break; + case 11: UNW_DEC_PRIUNAT_GR(P3, dst, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + else if ((code & 0x7) == 0) + UNW_DEC_SPILL_MASK(P4, dp, arg); + else if ((code & 0x7) == 1) + { + unw_word grmask, frmask, byte1, byte2, byte3; + + byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; + grmask = ((byte1 >> 4) & 0xf); + frmask = ((byte1 & 0xf) << 16) | (byte2 << 8) | byte3; + UNW_DEC_FRGR_MEM(P5, grmask, frmask, arg); + } + else + UNW_DEC_BAD_CODE(code); + return dp; +} + +static unsigned char * +unw_decode_p6 (unsigned char *dp, unsigned char code, void *arg) +{ + int gregs = (code & 0x10) != 0; + unsigned char mask = (code & 0x0f); + + if (gregs) + UNW_DEC_GR_MEM(P6, mask, arg); + else + UNW_DEC_FR_MEM(P6, mask, arg); + return dp; +} + +static unsigned char * +unw_decode_p7_p10 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char r, byte1, byte2; + unw_word t, size; + + if ((code & 0x10) == 0) + { + r = (code & 0xf); + t = unw_decode_uleb128 (&dp); + switch (r) + { + case 0: + size = unw_decode_uleb128 (&dp); + UNW_DEC_MEM_STACK_F(P7, t, size, arg); + break; + + case 1: UNW_DEC_MEM_STACK_V(P7, t, arg); break; + case 2: UNW_DEC_SPILL_BASE(P7, t, arg); break; + case 3: UNW_DEC_REG_SPREL(P7, UNW_REG_PSP, t, arg); break; + case 4: UNW_DEC_REG_WHEN(P7, UNW_REG_RP, t, arg); break; + case 5: UNW_DEC_REG_PSPREL(P7, UNW_REG_RP, t, arg); break; + case 6: UNW_DEC_REG_WHEN(P7, UNW_REG_PFS, t, arg); break; + case 7: UNW_DEC_REG_PSPREL(P7, UNW_REG_PFS, t, arg); break; + case 8: UNW_DEC_REG_WHEN(P7, UNW_REG_PR, t, arg); break; + case 9: UNW_DEC_REG_PSPREL(P7, UNW_REG_PR, t, arg); break; + case 10: UNW_DEC_REG_WHEN(P7, UNW_REG_LC, t, arg); break; + case 11: UNW_DEC_REG_PSPREL(P7, UNW_REG_LC, t, arg); break; + case 12: UNW_DEC_REG_WHEN(P7, UNW_REG_UNAT, t, arg); break; + case 13: UNW_DEC_REG_PSPREL(P7, UNW_REG_UNAT, t, arg); break; + case 14: UNW_DEC_REG_WHEN(P7, UNW_REG_FPSR, t, arg); break; + case 15: UNW_DEC_REG_PSPREL(P7, UNW_REG_FPSR, t, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + else + { + switch (code & 0xf) + { + case 0x0: /* p8 */ + { + r = *dp++; + t = unw_decode_uleb128 (&dp); + switch (r) + { + case 1: UNW_DEC_REG_SPREL(P8, UNW_REG_RP, t, arg); break; + case 2: UNW_DEC_REG_SPREL(P8, UNW_REG_PFS, t, arg); break; + case 3: UNW_DEC_REG_SPREL(P8, UNW_REG_PR, t, arg); break; + case 4: UNW_DEC_REG_SPREL(P8, UNW_REG_LC, t, arg); break; + case 5: UNW_DEC_REG_SPREL(P8, UNW_REG_UNAT, t, arg); break; + case 6: UNW_DEC_REG_SPREL(P8, UNW_REG_FPSR, t, arg); break; + case 7: UNW_DEC_REG_WHEN(P8, UNW_REG_BSP, t, arg); break; + case 8: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSP, t, arg); break; + case 9: UNW_DEC_REG_SPREL(P8, UNW_REG_BSP, t, arg); break; + case 10: UNW_DEC_REG_WHEN(P8, UNW_REG_BSPSTORE, t, arg); break; + case 11: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSPSTORE, t, arg); break; + case 12: UNW_DEC_REG_SPREL(P8, UNW_REG_BSPSTORE, t, arg); break; + case 13: UNW_DEC_REG_WHEN(P8, UNW_REG_RNAT, t, arg); break; + case 14: UNW_DEC_REG_PSPREL(P8, UNW_REG_RNAT, t, arg); break; + case 15: UNW_DEC_REG_SPREL(P8, UNW_REG_RNAT, t, arg); break; + case 16: UNW_DEC_PRIUNAT_WHEN_GR(P8, t, arg); break; + case 17: UNW_DEC_PRIUNAT_PSPREL(P8, t, arg); break; + case 18: UNW_DEC_PRIUNAT_SPREL(P8, t, arg); break; + case 19: UNW_DEC_PRIUNAT_WHEN_MEM(P8, t, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + break; + + case 0x1: + byte1 = *dp++; byte2 = *dp++; + UNW_DEC_GR_GR(P9, (byte1 & 0xf), (byte2 & 0x7f), arg); + break; + + case 0xf: /* p10 */ + byte1 = *dp++; byte2 = *dp++; + UNW_DEC_ABI(P10, byte1, byte2, arg); + break; + + case 0x9: + return unw_decode_x1 (dp, code, arg); + + case 0xa: + return unw_decode_x2 (dp, code, arg); + + case 0xb: + return unw_decode_x3 (dp, code, arg); + + case 0xc: + return unw_decode_x4 (dp, code, arg); + + default: + UNW_DEC_BAD_CODE(code); + break; + } + } + return dp; +} + +static unsigned char * +unw_decode_b1 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word label = (code & 0x1f); + + if ((code & 0x20) != 0) + UNW_DEC_COPY_STATE(B1, label, arg); + else + UNW_DEC_LABEL_STATE(B1, label, arg); + return dp; +} + +static unsigned char * +unw_decode_b2 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word t; + + t = unw_decode_uleb128 (&dp); + UNW_DEC_EPILOGUE(B2, t, (code & 0x1f), arg); + return dp; +} + +static unsigned char * +unw_decode_b3_x4 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word t, ecount, label; + + if ((code & 0x10) == 0) + { + t = unw_decode_uleb128 (&dp); + ecount = unw_decode_uleb128 (&dp); + UNW_DEC_EPILOGUE(B3, t, ecount, arg); + } + else if ((code & 0x07) == 0) + { + label = unw_decode_uleb128 (&dp); + if ((code & 0x08) != 0) + UNW_DEC_COPY_STATE(B4, label, arg); + else + UNW_DEC_LABEL_STATE(B4, label, arg); + } + else + switch (code & 0x7) + { + case 1: return unw_decode_x1 (dp, code, arg); + case 2: return unw_decode_x2 (dp, code, arg); + case 3: return unw_decode_x3 (dp, code, arg); + case 4: return unw_decode_x4 (dp, code, arg); + default: UNW_DEC_BAD_CODE(code); break; + } + return dp; +} + +typedef unsigned char *(*unw_decoder) (unsigned char *, unsigned char, void *); + +static unw_decoder unw_decode_table[2][8] = +{ + /* prologue table: */ + { + unw_decode_r1, /* 0 */ + unw_decode_r1, + unw_decode_r2, + unw_decode_r3, + unw_decode_p1, /* 4 */ + unw_decode_p2_p5, + unw_decode_p6, + unw_decode_p7_p10 + }, + { + unw_decode_r1, /* 0 */ + unw_decode_r1, + unw_decode_r2, + unw_decode_r3, + unw_decode_b1, /* 4 */ + unw_decode_b1, + unw_decode_b2, + unw_decode_b3_x4 + } +}; + +/* + * Decode one descriptor and return address of next descriptor. + */ +static inline unsigned char * +unw_decode (unsigned char *dp, int inside_body, void *arg) +{ + unw_decoder decoder; + unsigned char code; + + code = *dp++; + decoder = unw_decode_table[inside_body][code >> 5]; + dp = (*decoder) (dp, code, arg); + return dp; +} diff --git a/arch/ia64/kernel/unwind_i.h b/arch/ia64/kernel/unwind_i.h new file mode 100644 index 000000000..fea655efd --- /dev/null +++ b/arch/ia64/kernel/unwind_i.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * Kernel unwind support. + */ + +#define UNW_VER(x) ((x) >> 48) +#define UNW_FLAG_MASK 0x0000ffff00000000 +#define UNW_FLAG_OSMASK 0x0000f00000000000 +#define UNW_FLAG_EHANDLER(x) ((x) & 0x0000000100000000L) +#define UNW_FLAG_UHANDLER(x) ((x) & 0x0000000200000000L) +#define UNW_LENGTH(x) ((x) & 0x00000000ffffffffL) + +enum unw_register_index { + /* primary unat: */ + UNW_REG_PRI_UNAT_GR, + UNW_REG_PRI_UNAT_MEM, + + /* register stack */ + UNW_REG_BSP, /* register stack pointer */ + UNW_REG_BSPSTORE, + UNW_REG_PFS, /* previous function state */ + UNW_REG_RNAT, + /* memory stack */ + UNW_REG_PSP, /* previous memory stack pointer */ + /* return pointer: */ + UNW_REG_RP, + + /* preserved registers: */ + UNW_REG_R4, UNW_REG_R5, UNW_REG_R6, UNW_REG_R7, + UNW_REG_UNAT, UNW_REG_PR, UNW_REG_LC, UNW_REG_FPSR, + UNW_REG_B1, UNW_REG_B2, UNW_REG_B3, UNW_REG_B4, UNW_REG_B5, + UNW_REG_F2, UNW_REG_F3, UNW_REG_F4, UNW_REG_F5, + UNW_REG_F16, UNW_REG_F17, UNW_REG_F18, UNW_REG_F19, + UNW_REG_F20, UNW_REG_F21, UNW_REG_F22, UNW_REG_F23, + UNW_REG_F24, UNW_REG_F25, UNW_REG_F26, UNW_REG_F27, + UNW_REG_F28, UNW_REG_F29, UNW_REG_F30, UNW_REG_F31, + UNW_NUM_REGS +}; + +struct unw_info_block { + u64 header; + u64 desc[0]; /* unwind descriptors */ + /* personality routine and language-specific data follow behind descriptors */ +}; + +struct unw_table_entry { + u64 start_offset; + u64 end_offset; + u64 info_offset; +}; + +struct unw_table { + struct unw_table *next; /* must be first member! */ + const char *name; + unsigned long gp; /* global pointer for this load-module */ + unsigned long segment_base; /* base for offsets in the unwind table entries */ + unsigned long start; + unsigned long end; + struct unw_table_entry *array; + unsigned long length; +}; + +enum unw_where { + UNW_WHERE_NONE, /* register isn't saved at all */ + UNW_WHERE_GR, /* register is saved in a general register */ + UNW_WHERE_FR, /* register is saved in a floating-point register */ + UNW_WHERE_BR, /* register is saved in a branch register */ + UNW_WHERE_SPREL, /* register is saved on memstack (sp-relative) */ + UNW_WHERE_PSPREL, /* register is saved on memstack (psp-relative) */ + /* + * At the end of each prologue these locations get resolved to + * UNW_WHERE_PSPREL and UNW_WHERE_GR, respectively: + */ + UNW_WHERE_SPILL_HOME, /* register is saved in its spill home */ + UNW_WHERE_GR_SAVE /* register is saved in next general register */ +}; + +#define UNW_WHEN_NEVER 0x7fffffff + +struct unw_reg_info { + unsigned long val; /* save location: register number or offset */ + enum unw_where where; /* where the register gets saved */ + int when; /* when the register gets saved */ +}; + +struct unw_state_record { + unsigned int first_region : 1; /* is this the first region? */ + unsigned int done : 1; /* are we done scanning descriptors? */ + unsigned int any_spills : 1; /* got any register spills? */ + unsigned int in_body : 1; /* are we inside a body (as opposed to a prologue)? */ + unsigned long flags; /* see UNW_FLAG_* in unwind.h */ + + u8 *imask; /* imask of of spill_mask record or NULL */ + unsigned long pr_val; /* predicate values */ + unsigned long pr_mask; /* predicate mask */ + long spill_offset; /* psp-relative offset for spill base */ + int region_start; + int region_len; + int epilogue_start; + int epilogue_count; + int when_target; + + u8 gr_save_loc; /* next general register to use for saving a register */ + u8 return_link_reg; /* branch register in which the return link is passed */ + + struct unw_reg_state { + struct unw_reg_state *next; + unsigned long label; /* label of this state record */ + struct unw_reg_info reg[UNW_NUM_REGS]; + } curr, *stack, *reg_state_list; +}; + +enum unw_nat_type { + UNW_NAT_NONE, /* NaT not represented */ + UNW_NAT_VAL, /* NaT represented by NaT value (fp reg) */ + UNW_NAT_PRI_UNAT, /* NaT value is in unat word at offset OFF */ + UNW_NAT_SCRATCH, /* NaT value is in scratch.pri_unat */ + UNW_NAT_STACKED /* NaT is in rnat */ +}; + +enum unw_insn_opcode { + UNW_INSN_ADD, /* s[dst] += val */ + UNW_INSN_MOVE, /* s[dst] = s[val] */ + UNW_INSN_MOVE2, /* s[dst] = s[val]; s[dst+1] = s[val+1] */ + UNW_INSN_MOVE_STACKED, /* s[dst] = ia64_rse_skip(*s.bsp, val) */ + UNW_INSN_LOAD_PSPREL, /* s[dst] = *(*s.psp + 8*val) */ + UNW_INSN_LOAD_SPREL, /* s[dst] = *(*s.sp + 8*val) */ + UNW_INSN_SETNAT_PRI_UNAT, /* s[dst+1].nat.type = PRI_UNAT; + s[dst+1].nat.off = *s.pri_unat - s[dst] */ + UNW_INSN_SETNAT_TYPE /* s[dst+1].nat.type = val */ +}; + +struct unw_insn { + unsigned int opc : 4; + unsigned int dst : 9; + signed int val : 19; +}; + +/* + * Preserved general static registers (r2-r5) give rise to two script + * instructions; everything else yields at most one instruction; at + * the end of the script, the psp gets popped, accounting for one more + * instruction. + */ +#define UNW_MAX_SCRIPT_LEN (UNW_NUM_REGS + 5) + +struct unw_script { + unsigned long ip; /* ip this script is for */ + unsigned long pr_mask; /* mask of predicates script depends on */ + unsigned long pr_val; /* predicate values this script is for */ + rwlock_t lock; + unsigned int flags; /* see UNW_FLAG_* in unwind.h */ + unsigned short lru_chain; /* used for least-recently-used chain */ + unsigned short coll_chain; /* used for hash collisions */ + unsigned short hint; /* hint for next script to try (or -1) */ + unsigned short count; /* number of instructions in script */ + struct unw_insn insn[UNW_MAX_SCRIPT_LEN]; +}; diff --git a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile index 376d0d6d4..882bdaed9 100644 --- a/arch/ia64/lib/Makefile +++ b/arch/ia64/lib/Makefile @@ -5,15 +5,21 @@ .S.o: $(CC) $(AFLAGS) -c $< -o $@ -OBJS = __divdi3.o __divsi3.o __udivdi3.o __udivsi3.o \ +L_TARGET = lib.a + +L_OBJS = __divdi3.o __divsi3.o __udivdi3.o __udivsi3.o \ __moddi3.o __modsi3.o __umoddi3.o __umodsi3.o \ checksum.o clear_page.o csum_partial_copy.o copy_page.o \ copy_user.o clear_user.o memset.o strncpy_from_user.o \ strlen.o strlen_user.o strnlen_user.o \ flush.o do_csum.o -lib.a: $(OBJS) - $(AR) rcs lib.a $(OBJS) +LX_OBJS = io.o + +IGNORE_FLAGS_OBJS = __divdi3.o __divsi3.o __udivdi3.o __udivsi3.o \ + __moddi3.o __modsi3.o __umoddi3.o __umodsi3.o + +include $(TOPDIR)/Rules.make __divdi3.o: idiv.S $(CC) $(AFLAGS) -c -o $@ $< @@ -38,5 +44,3 @@ __umoddi3.o: idiv.S __umodsi3.o: idiv.S $(CC) $(AFLAGS) -c -DMODULO -DUNSIGNED -DSINGLE -c -o $@ $< - -include $(TOPDIR)/Rules.make diff --git a/arch/ia64/lib/clear_page.S b/arch/ia64/lib/clear_page.S index 314311c5c..c544e3725 100644 --- a/arch/ia64/lib/clear_page.S +++ b/arch/ia64/lib/clear_page.S @@ -10,10 +10,11 @@ * Output: * none * - * Copyright (C) 1999 Hewlett-Packard Co + * Copyright (C) 1999-2000 Hewlett-Packard Co * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ +#include <asm/asmmacro.h> #include <asm/page.h> .text @@ -21,12 +22,14 @@ .psr lsb .lsb - .align 32 - .global clear_page - .proc clear_page -clear_page: +GLOBAL_ENTRY(clear_page) + UNW(.prologue) alloc r11=ar.pfs,1,0,0,0 + UNW(.save ar.lc, r16) mov r16=ar.lc // slow + + UNW(.body) + mov r17=PAGE_SIZE/32-1 // -1 = repeat/until ;; adds r18=16,in0 @@ -38,5 +41,4 @@ clear_page: ;; mov ar.lc=r16 // restore lc br.ret.sptk.few rp - - .endp clear_page +END(clear_page) diff --git a/arch/ia64/lib/clear_user.S b/arch/ia64/lib/clear_user.S index 0b9a453b1..d937db016 100644 --- a/arch/ia64/lib/clear_user.S +++ b/arch/ia64/lib/clear_user.S @@ -11,6 +11,8 @@ * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> */ +#include <asm/asmmacro.h> + // // arguments // @@ -23,11 +25,10 @@ #define cnt r16 #define buf2 r17 #define saved_lc r18 -#define saved_pr r19 -#define saved_pfs r20 -#define tmp r21 -#define len2 r22 -#define len3 r23 +#define saved_pfs r19 +#define tmp r20 +#define len2 r21 +#define len3 r22 // // Theory of operations: @@ -65,14 +66,14 @@ .psr lsb .lsb - .align 32 - .global __do_clear_user - .proc __do_clear_user - -__do_clear_user: +GLOBAL_ENTRY(__do_clear_user) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,2,0,0,0 cmp.eq p6,p0=r0,len // check for zero length + UNW(.save ar.lc, saved_lc) mov saved_lc=ar.lc // preserve ar.lc (slow) + .body ;; // avoid WAW on CFM adds tmp=-1,len // br.ctop is repeat/until mov ret0=len // return value is length at this point @@ -222,4 +223,4 @@ long_do_clear: mov ret0=len mov ar.lc=saved_lc br.ret.dptk.few rp - .endp +END(__do_clear_user) diff --git a/arch/ia64/lib/copy_page.S b/arch/ia64/lib/copy_page.S index 7595ac83a..811db4c96 100644 --- a/arch/ia64/lib/copy_page.S +++ b/arch/ia64/lib/copy_page.S @@ -13,6 +13,7 @@ * Copyright (C) 1999 Hewlett-Packard Co * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> */ +#include <asm/asmmacro.h> #include <asm/page.h> #define PIPE_DEPTH 6 @@ -32,19 +33,21 @@ .psr lsb .lsb - .align 32 - .global copy_page - .proc copy_page - -copy_page: +GLOBAL_ENTRY(copy_page) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,3,((2*PIPE_DEPTH+7)&~7),0,((2*PIPE_DEPTH+7)&~7) .rotr t1[PIPE_DEPTH], t2[PIPE_DEPTH] .rotp p[PIPE_DEPTH] + UNW(.save ar.lc, saved_lc) mov saved_lc=ar.lc // save ar.lc ahead of time + UNW(.save pr, saved_pr) mov saved_pr=pr // rotating predicates are preserved // resgisters we must save. + UNW(.body) + mov src1=in1 // initialize 1st stream source adds src2=8,in1 // initialize 2nd stream source mov lcount=PAGE_SIZE/16-1 // as many 16bytes as there are on a page @@ -87,5 +90,4 @@ copy_page: mov ar.pfs=saved_pfs // restore ar.ec mov ar.lc=saved_lc // restore saved lc br.ret.sptk.few rp // bye... - - .endp copy_page +END(copy_page) diff --git a/arch/ia64/lib/copy_user.S b/arch/ia64/lib/copy_user.S index 3743174ba..4a0abaed0 100644 --- a/arch/ia64/lib/copy_user.S +++ b/arch/ia64/lib/copy_user.S @@ -29,6 +29,8 @@ * - fix extraneous stop bit introduced by the EX() macro. */ +#include <asm/asmmacro.h> + // The label comes first because our store instruction contains a comma // and confuse the preprocessor otherwise // @@ -81,10 +83,9 @@ .psr abi64 .psr lsb - .align 16 - .global __copy_user - .proc __copy_user -__copy_user: +GLOBAL_ENTRY(__copy_user) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,3,((2*PIPE_DEPTH+7)&~7),0,((2*PIPE_DEPTH+7)&~7) .rotr val1[PIPE_DEPTH],val2[PIPE_DEPTH] @@ -95,13 +96,17 @@ __copy_user: ;; // RAW of cfm when len=0 cmp.eq p8,p0=r0,len // check for zero length + UNW(.save ar.lc, saved_lc) mov saved_lc=ar.lc // preserve ar.lc (slow) (p8) br.ret.spnt.few rp // empty mempcy() ;; add enddst=dst,len // first byte after end of source add endsrc=src,len // first byte after end of destination + UNW(.save pr, saved_pr) mov saved_pr=pr // preserve predicates + UNW(.body) + mov dst1=dst // copy because of rotation mov ar.ec=PIPE_DEPTH mov pr.rot=1<<16 // p16=true all others are false @@ -400,7 +405,4 @@ failure_out: mov ar.pfs=saved_pfs br.ret.dptk.few rp - - - .endp __copy_user - +END(__copy_user) diff --git a/arch/ia64/lib/do_csum.S b/arch/ia64/lib/do_csum.S index d8174f10a..11bdfe031 100644 --- a/arch/ia64/lib/do_csum.S +++ b/arch/ia64/lib/do_csum.S @@ -13,6 +13,8 @@ * */ +#include <asm/asmmacro.h> + // // Theory of operations: // The goal is to go as quickly as possible to the point where @@ -100,10 +102,9 @@ // unsigned long do_csum(unsigned char *buf,int len) - .align 32 - .global do_csum - .proc do_csum -do_csum: +GLOBAL_ENTRY(do_csum) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,2,8,0,8 .rotr p[4], result[3] @@ -125,6 +126,7 @@ do_csum: ;; and lastoff=7,tmp1 // how many bytes off for last element andcm last=tmp2,tmp3 // address of word containing last byte + UNW(.save pr, saved_pr) mov saved_pr=pr // preserve predicates (rotation) ;; sub tmp3=last,first // tmp3=distance from first to last @@ -145,8 +147,12 @@ do_csum: shl hmask=hmask,tmp2 // build head mask, mask off [0,firstoff[ ;; shr.u tmask=tmask,tmp1 // build tail mask, mask off ]8,lastoff] + UNW(.save ar.lc, saved_lc) mov saved_lc=ar.lc // save lc ;; + + UNW(.body) + (p8) and hmask=hmask,tmask // apply tail mask to head mask if 1 word only (p9) and p[1]=lastval,tmask // mask last it as appropriate shr.u tmp3=tmp3,3 // we do 8 bytes per loop @@ -228,3 +234,4 @@ do_csum: mov ar.lc=saved_lc (p10) shr.u ret0=ret0,64-16 // + shift back to position = swap bytes br.ret.sptk.few rp +END(do_csum) diff --git a/arch/ia64/lib/flush.S b/arch/ia64/lib/flush.S index 0195ae5f5..ba9d59f84 100644 --- a/arch/ia64/lib/flush.S +++ b/arch/ia64/lib/flush.S @@ -1,9 +1,10 @@ /* * Cache flushing routines. * - * Copyright (C) 1999 Hewlett-Packard Co - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ +#include <asm/asmmacro.h> #include <asm/page.h> .text @@ -11,12 +12,14 @@ .psr lsb .lsb - .align 16 - .global ia64_flush_icache_page - .proc ia64_flush_icache_page -ia64_flush_icache_page: +GLOBAL_ENTRY(ia64_flush_icache_page) + UNW(.prologue) alloc r2=ar.pfs,1,0,0,0 + UNW(.save ar.lc, r3) mov r3=ar.lc // save ar.lc + + .body + mov r8=PAGE_SIZE/64-1 // repeat/until loop ;; mov ar.lc=r8 @@ -34,4 +37,4 @@ ia64_flush_icache_page: ;; mov ar.lc=r3 // restore ar.lc br.ret.sptk.few rp - .endp ia64_flush_icache_page +END(ia64_flush_icache_page) diff --git a/arch/ia64/lib/idiv.S b/arch/ia64/lib/idiv.S index a12097c94..af424c41b 100644 --- a/arch/ia64/lib/idiv.S +++ b/arch/ia64/lib/idiv.S @@ -31,6 +31,7 @@ nops while maximizing parallelism */ +#include <asm/asmmacro.h> #include <asm/break.h> .text @@ -73,12 +74,10 @@ #define PASTE(a,b) PASTE1(a,b) #define NAME PASTE(PASTE(__,SGN),PASTE(OP,PASTE(PREC,3))) - .align 32 - .global NAME - .proc NAME -NAME: - +GLOBAL_ENTRY(NAME) + UNW(.prologue) alloc r2=ar.pfs,2,6,0,8 + UNW(.save pr, r18) mov r18=pr #ifdef SINGLE # ifdef UNSIGNED @@ -101,6 +100,10 @@ NAME: #endif setf.sig f8=in0 + UNW(.save ar.lc, r3) + + UNW(.body) + mov r3=ar.lc // save ar.lc setf.sig f9=in1 ;; @@ -156,3 +159,4 @@ NAME: mov ar.lc=r3 // restore ar.lc mov pr=r18,0xffffffffffff0000 // restore p16-p63 br.ret.sptk.few rp +END(NAME) diff --git a/arch/ia64/lib/io.c b/arch/ia64/lib/io.c new file mode 100644 index 000000000..466335172 --- /dev/null +++ b/arch/ia64/lib/io.c @@ -0,0 +1,54 @@ +#include <linux/module.h> +#include <linux/types.h> + +#include <asm/io.h> + +/* + * Copy data from IO memory space to "real" memory space. + * This needs to be optimized. + */ +void +__ia64_memcpy_fromio (void * to, unsigned long from, long count) +{ + while (count) { + count--; + *(char *) to = readb(from); + ((char *) to)++; + from++; + } +} + +/* + * Copy data from "real" memory space to IO memory space. + * This needs to be optimized. + */ +void +__ia64_memcpy_toio (unsigned long to, void * from, long count) +{ + while (count) { + count--; + writeb(*(char *) from, to); + ((char *) from)++; + to++; + } +} + +/* + * "memset" on IO memory space. + * This needs to be optimized. + */ +void +__ia64_memset_c_io (unsigned long dst, unsigned long c, long count) +{ + unsigned char ch = (char)(c & 0xff); + + while (count) { + count--; + writeb(ch, dst); + dst++; + } +} + +EXPORT_SYMBOL(__ia64_memcpy_fromio); +EXPORT_SYMBOL(__ia64_memcpy_toio); +EXPORT_SYMBOL(__ia64_memset_c_io); diff --git a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S index 595720a2d..ddb42cc56 100644 --- a/arch/ia64/lib/memset.S +++ b/arch/ia64/lib/memset.S @@ -14,6 +14,7 @@ * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> */ +#include <asm/asmmacro.h> // arguments // @@ -28,22 +29,23 @@ #define cnt r18 #define buf2 r19 #define saved_lc r20 -#define saved_pr r21 -#define tmp r22 +#define tmp r21 .text .psr abi64 .psr lsb - .align 16 - .global memset - .proc memset - -memset: +GLOBAL_ENTRY(memset) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here cmp.eq p8,p0=r0,len // check for zero length + UNW(.save ar.lc, saved_lc) mov saved_lc=ar.lc // preserve ar.lc (slow) ;; + + UNW(.body) + adds tmp=-1,len // br.ctop is repeat/until tbit.nz p6,p0=buf,0 // odd alignment (p8) br.ret.spnt.few rp @@ -108,4 +110,4 @@ long_memset: ;; (p6) st1 [buf]=val // only 1 byte left br.ret.dptk.few rp - .endp +END(memset) diff --git a/arch/ia64/lib/strlen.S b/arch/ia64/lib/strlen.S index 22f205656..fbc786b41 100644 --- a/arch/ia64/lib/strlen.S +++ b/arch/ia64/lib/strlen.S @@ -16,6 +16,8 @@ * 09/24/99 S.Eranian add speculation recovery code */ +#include <asm/asmmacro.h> + // // // This is an enhanced version of the basic strlen. it includes a combination @@ -82,10 +84,9 @@ .psr lsb .lsb - .align 32 - .global strlen - .proc strlen -strlen: +GLOBAL_ENTRY(strlen) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,11,0,0,8 // rotating must be multiple of 8 .rotr v[2], w[2] // declares our 4 aliases @@ -93,8 +94,12 @@ strlen: extr.u tmp=in0,0,3 // tmp=least significant 3 bits mov orig=in0 // keep trackof initial byte address dep src=0,in0,0,3 // src=8byte-aligned in0 address + UNW(.save pr, saved_pr) mov saved_pr=pr // preserve predicates (rotation) ;; + + UNW(.body) + ld8 v[1]=[src],8 // must not speculate: can fail here shl tmp=tmp,3 // multiply by 8bits/byte mov mask=-1 // our mask @@ -194,5 +199,4 @@ recover: sub ret0=ret0,tmp // length=now - back -1 mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what br.ret.sptk.few rp // end of sucessful recovery code - - .endp strlen +END(strlen) diff --git a/arch/ia64/lib/strlen_user.S b/arch/ia64/lib/strlen_user.S index 8149dde8a..7f222bb13 100644 --- a/arch/ia64/lib/strlen_user.S +++ b/arch/ia64/lib/strlen_user.S @@ -15,6 +15,8 @@ * 09/24/99 S.Eranian added speculation recovery code */ +#include <asm/asmmacro.h> + // // int strlen_user(char *) // ------------------------ @@ -93,10 +95,9 @@ .psr lsb .lsb - .align 32 - .global __strlen_user - .proc __strlen_user -__strlen_user: +GLOBAL_ENTRY(__strlen_user) + UNW(.prologue) + UNW(.save ar.pfs, saved_pfs) alloc saved_pfs=ar.pfs,11,0,0,8 .rotr v[2], w[2] // declares our 4 aliases @@ -104,8 +105,12 @@ __strlen_user: extr.u tmp=in0,0,3 // tmp=least significant 3 bits mov orig=in0 // keep trackof initial byte address dep src=0,in0,0,3 // src=8byte-aligned in0 address + UNW(.save pr, saved_pr) mov saved_pr=pr // preserve predicates (rotation) ;; + + .body + ld8.s v[1]=[src],8 // load the initial 8bytes (must speculate) shl tmp=tmp,3 // multiply by 8bits/byte mov mask=-1 // our mask @@ -209,5 +214,4 @@ recover: mov pr=saved_pr,0xffffffffffff0000 mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what br.ret.sptk.few rp - - .endp __strlen_user +END(__strlen_user) diff --git a/arch/ia64/lib/strncpy_from_user.S b/arch/ia64/lib/strncpy_from_user.S index f2d40984a..6ff7fa9b6 100644 --- a/arch/ia64/lib/strncpy_from_user.S +++ b/arch/ia64/lib/strncpy_from_user.S @@ -16,6 +16,8 @@ * by Andreas Schwab <schwab@suse.de>). */ +#include <asm/asmmacro.h> + #define EX(x...) \ 99: x; \ .section __ex_table,"a"; \ @@ -28,10 +30,7 @@ .psr lsb .lsb - .align 32 - .global __strncpy_from_user - .proc __strncpy_from_user -__strncpy_from_user: +GLOBAL_ENTRY(__strncpy_from_user) alloc r2=ar.pfs,3,0,0,0 mov r8=0 mov r9=in1 @@ -53,5 +52,4 @@ __strncpy_from_user: .Lexit: br.ret.sptk.few rp - - .endp __strncpy_from_user +END(__strncpy_from_user) diff --git a/arch/ia64/lib/strnlen_user.S b/arch/ia64/lib/strnlen_user.S index c227a9003..d70f54f2b 100644 --- a/arch/ia64/lib/strnlen_user.S +++ b/arch/ia64/lib/strnlen_user.S @@ -12,6 +12,8 @@ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> */ +#include <asm/asmmacro.h> + /* If a fault occurs, r8 gets set to -EFAULT and r9 gets cleared. */ #define EX(x...) \ .section __ex_table,"a"; \ @@ -25,12 +27,14 @@ .psr lsb .lsb - .align 32 - .global __strnlen_user - .proc __strnlen_user -__strnlen_user: +GLOBAL_ENTRY(__strnlen_user) + UNW(.prologue) alloc r2=ar.pfs,2,0,0,0 + UNW(.save ar.lc, r16) mov r16=ar.lc // preserve ar.lc + + UNW(.body) + add r3=-1,in1 ;; mov ar.lc=r3 @@ -51,5 +55,4 @@ __strnlen_user: mov r8=r9 mov ar.lc=r16 // restore ar.lc br.ret.sptk.few rp - - .endp __strnlen_user +END(__strnlen_user) diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index 02c4c5792..eaac24372 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -1,8 +1,8 @@ /* * MMU fault handling support. * - * Copyright (C) 1998, 1999 Hewlett-Packard Co - * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1998-2000 Hewlett-Packard Co + * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ #include <linux/sched.h> #include <linux/kernel.h> @@ -94,7 +94,14 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re * sure we exit gracefully rather than endlessly redo the * fault. */ - if (!handle_mm_fault(mm, vma, address, (isr & IA64_ISR_W) != 0)) { + switch (handle_mm_fault(mm, vma, address, (mask & VM_WRITE) != 0)) { + case 1: + ++current->min_flt; + break; + case 2: + ++current->maj_flt; + break; + case 0: /* * We ran out of memory, or some other thing happened * to us that made us unable to handle the page fault @@ -102,6 +109,8 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re */ signal = SIGBUS; goto bad_area; + default: + goto out_of_memory; } up(&mm->mmap_sem); return; @@ -128,15 +137,11 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re return; } if (user_mode(regs)) { -#if 0 -printk("%s(%d): segfault accessing %lx\n", current->comm, current->pid, address); -show_regs(regs); -#endif si.si_signo = signal; si.si_errno = 0; si.si_code = SI_KERNEL; si.si_addr = (void *) address; - force_sig_info(SIGSEGV, &si, current); + force_sig_info(signal, &si, current); return; } @@ -161,4 +166,11 @@ show_regs(regs); die_if_kernel("Oops", regs, isr); do_exit(SIGKILL); return; + + out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + goto no_context; } diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index b3047ce34..aed999573 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/swap.h> +#include <asm/bitops.h> #include <asm/dma.h> #include <asm/efi.h> #include <asm/ia32.h> @@ -182,6 +183,19 @@ free_initmem (void) } void +free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldkB freed\n", (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start)].flags); + set_page_count(&mem_map[MAP_NR(start)], 1); + free_page(start); + ++totalram_pages; + } +} + +void si_meminfo (struct sysinfo *val) { val->totalram = totalram_pages; @@ -265,7 +279,7 @@ put_gate_page (struct page *page, unsigned long address) void __init ia64_rid_init (void) { - unsigned long flags, rid, pta; + unsigned long flags, rid, pta, impl_va_msb; /* Set up the kernel identity mappings (regions 6 & 7) and the vmalloc area (region 5): */ ia64_clear_ic(flags); @@ -300,11 +314,15 @@ ia64_rid_init (void) # define ld_max_addr_space_size (ld_max_addr_space_pages + PAGE_SHIFT) # define ld_max_vpt_size (ld_max_addr_space_pages + ld_pte_size) # define POW2(n) (1ULL << (n)) -# define IMPL_VA_MSB 50 - if (POW2(ld_max_addr_space_size - 1) + POW2(ld_max_vpt_size) > POW2(IMPL_VA_MSB)) + impl_va_msb = ffz(~my_cpu_data.unimpl_va_mask) - 1; + + if (impl_va_msb < 50 || impl_va_msb > 60) + panic("Bogus impl_va_msb value of %lu!\n", impl_va_msb); + + if (POW2(ld_max_addr_space_size - 1) + POW2(ld_max_vpt_size) > POW2(impl_va_msb)) panic("mm/init: overlap between virtually mapped linear page table and " "mapped kernel space!"); - pta = POW2(61) - POW2(IMPL_VA_MSB); + pta = POW2(61) - POW2(impl_va_msb); /* * Set the (virtually mapped linear) page table address. Bit * 8 selects between the short and long format, bits 2-7 the @@ -314,54 +332,6 @@ ia64_rid_init (void) ia64_set_pta(pta | (0<<8) | ((3*(PAGE_SHIFT-3)+3)<<2) | 1); } -#ifdef CONFIG_IA64_VIRTUAL_MEM_MAP - -static int -create_mem_map_page_table (u64 start, u64 end, void *arg) -{ - unsigned long address, start_page, end_page; - struct page *map_start, *map_end; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - void *page; - - map_start = mem_map + MAP_NR(start); - map_end = mem_map + MAP_NR(end); - - start_page = (unsigned long) map_start & PAGE_MASK; - end_page = PAGE_ALIGN((unsigned long) map_end); - - printk("[%lx,%lx) -> %lx-%lx\n", start, end, start_page, end_page); - - for (address = start_page; address < end_page; address += PAGE_SIZE) { - pgd = pgd_offset_k(address); - if (pgd_none(*pgd)) { - pmd = alloc_bootmem_pages(PAGE_SIZE); - clear_page(pmd); - pgd_set(pgd, pmd); - pmd += (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); - } else - pmd = pmd_offset(pgd, address); - if (pmd_none(*pmd)) { - pte = alloc_bootmem_pages(PAGE_SIZE); - clear_page(pte); - pmd_set(pmd, pte); - pte += (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); - } else - pte = pte_offset(pmd, address); - - if (pte_none(*pte)) { - page = alloc_bootmem_pages(PAGE_SIZE); - clear_page(page); - set_pte(pte, mk_pte_phys(__pa(page), PAGE_KERNEL)); - } - } - return 0; -} - -#endif /* CONFIG_IA64_VIRTUAL_MEM_MAP */ - /* * Set up the page tables. */ @@ -372,14 +342,11 @@ paging_init (void) clear_page((void *) ZERO_PAGE_ADDR); - ia64_rid_init(); - __flush_tlb_all(); - /* initialize mem_map[] */ memset(zones_size, 0, sizeof(zones_size)); - max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS); + max_dma = (PAGE_ALIGN(MAX_DMA_ADDRESS) >> PAGE_SHIFT); if (max_low_pfn < max_dma) zones_size[ZONE_DMA] = max_low_pfn; else { @@ -427,8 +394,6 @@ mem_init (void) max_mapnr = max_low_pfn; high_memory = __va(max_low_pfn * PAGE_SIZE); - ia64_tlb_init(); - totalram_pages += free_all_bootmem(); reserved_pages = 0; diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index acad4e200..2a57d0ae3 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c @@ -42,6 +42,70 @@ ia64_ptce_info_t ia64_ptce_info; */ spinlock_t ptcg_lock = SPIN_LOCK_UNLOCKED; /* see <asm/pgtable.h> */ +#if defined(CONFIG_SMP) && !defined(CONFIG_ITANIUM_PTCG) + +#include <linux/irq.h> + +unsigned long flush_end, flush_start, flush_nbits, flush_rid; +atomic_t flush_cpu_count; + +/* + * flush_tlb_no_ptcg is called with ptcg_lock locked + */ +static inline void +flush_tlb_no_ptcg (unsigned long start, unsigned long end, unsigned long nbits) +{ + extern void smp_send_flush_tlb (void); + unsigned long saved_tpr = 0; + unsigned long flags; + + /* + * Some times this is called with interrupts disabled and causes + * dead-lock; to avoid this we enable interrupt and raise the TPR + * to enable ONLY IPI. + */ + __save_flags(flags); + if (!(flags & IA64_PSR_I)) { + saved_tpr = ia64_get_tpr(); + ia64_srlz_d(); + ia64_set_tpr(IPI_IRQ - 16); + ia64_srlz_d(); + local_irq_enable(); + } + + spin_lock(&ptcg_lock); + flush_rid = ia64_get_rr(start); + ia64_srlz_d(); + flush_start = start; + flush_end = end; + flush_nbits = nbits; + atomic_set(&flush_cpu_count, smp_num_cpus - 1); + smp_send_flush_tlb(); + /* + * Purge local TLB entries. ALAT invalidation is done in ia64_leave_kernel. + */ + do { + asm volatile ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory"); + start += (1UL << nbits); + } while (start < end); + + ia64_srlz_i(); /* srlz.i implies srlz.d */ + + /* + * Wait for other CPUs to finish purging entries. + */ + while (atomic_read(&flush_cpu_count)) { + /* Nothing */ + } + if (!(flags & IA64_PSR_I)) { + local_irq_disable(); + ia64_set_tpr(saved_tpr); + ia64_srlz_d(); + } +} + +#endif /* CONFIG_SMP && !CONFIG_ITANIUM_PTCG */ + void get_new_mmu_context (struct mm_struct *mm) { @@ -97,7 +161,7 @@ __flush_tlb_all (void) stride0 = ia64_ptce_info.stride[0]; stride1 = ia64_ptce_info.stride[1]; - __save_and_cli(flags); + local_irq_save(flags); for (i = 0; i < count0; ++i) { for (j = 0; j < count1; ++j) { asm volatile ("ptc.e %0" :: "r"(addr)); @@ -105,7 +169,7 @@ __flush_tlb_all (void) } addr += stride0; } - __restore_flags(flags); + local_irq_restore(flags); ia64_insn_group_barrier(); ia64_srlz_i(); /* srlz.i implies srlz.d */ ia64_insn_group_barrier(); @@ -142,23 +206,29 @@ flush_tlb_range (struct mm_struct *mm, unsigned long start, unsigned long end) } start &= ~((1UL << nbits) - 1); +#if defined(CONFIG_SMP) && !defined(CONFIG_ITANIUM_PTCG) + flush_tlb_no_ptcg(start, end, nbits); +#else spin_lock(&ptcg_lock); do { -#ifdef CONFIG_SMP - __asm__ __volatile__ ("ptc.g %0,%1;;srlz.i;;" - :: "r"(start), "r"(nbits<<2) : "memory"); -#else - __asm__ __volatile__ ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory"); -#endif +# ifdef CONFIG_SMP + /* + * Flush ALAT entries also. + */ + asm volatile ("ptc.ga %0,%1;;srlz.i;;" :: "r"(start), "r"(nbits<<2) : "memory"); +# else + asm volatile ("ptc.l %0,%1" :: "r"(start), "r"(nbits<<2) : "memory"); +# endif start += (1UL << nbits); } while (start < end); +#endif /* CONFIG_SMP && !defined(CONFIG_ITANIUM_PTCG) */ spin_unlock(&ptcg_lock); ia64_insn_group_barrier(); ia64_srlz_i(); /* srlz.i implies srlz.d */ ia64_insn_group_barrier(); } -void +void __init ia64_tlb_init (void) { ia64_get_ptce(&ia64_ptce_info); diff --git a/arch/ia64/tools/Makefile b/arch/ia64/tools/Makefile index 06e38e3b4..b3d2e74b9 100644 --- a/arch/ia64/tools/Makefile +++ b/arch/ia64/tools/Makefile @@ -44,4 +44,4 @@ print_offsets.s: print_offsets.c endif -.PHONY: all +.PHONY: all modules diff --git a/arch/ia64/tools/print_offsets.c b/arch/ia64/tools/print_offsets.c index 7e5901144..d7d7e3e6f 100644 --- a/arch/ia64/tools/print_offsets.c +++ b/arch/ia64/tools/print_offsets.c @@ -45,6 +45,9 @@ tab[] = { "IA64_PT_REGS_SIZE", sizeof (struct pt_regs) }, { "IA64_SWITCH_STACK_SIZE", sizeof (struct switch_stack) }, { "IA64_SIGINFO_SIZE", sizeof (struct siginfo) }, +#ifdef CONFIG_IA64_NEW_UNWIND + { "UNW_FRAME_INFO_SIZE", sizeof (struct unw_frame_info) }, +#endif { "", 0 }, /* spacer */ { "IA64_TASK_FLAGS_OFFSET", offsetof (struct task_struct, flags) }, { "IA64_TASK_SIGPENDING_OFFSET", offsetof (struct task_struct, sigpending) }, @@ -58,11 +61,95 @@ tab[] = { "IA64_TASK_PID_OFFSET", offsetof (struct task_struct, pid) }, { "IA64_TASK_MM_OFFSET", offsetof (struct task_struct, mm) }, { "IA64_PT_REGS_CR_IPSR_OFFSET", offsetof (struct pt_regs, cr_ipsr) }, + { "IA64_PT_REGS_CR_IIP_OFFSET", offsetof (struct pt_regs, cr_iip) }, + { "IA64_PT_REGS_CR_IFS_OFFSET", offsetof (struct pt_regs, cr_ifs) }, + { "IA64_PT_REGS_AR_UNAT_OFFSET", offsetof (struct pt_regs, ar_unat) }, + { "IA64_PT_REGS_AR_PFS_OFFSET", offsetof (struct pt_regs, ar_pfs) }, + { "IA64_PT_REGS_AR_RSC_OFFSET", offsetof (struct pt_regs, ar_rsc) }, + { "IA64_PT_REGS_AR_RNAT_OFFSET", offsetof (struct pt_regs, ar_rnat) }, + { "IA64_PT_REGS_AR_BSPSTORE_OFFSET",offsetof (struct pt_regs, ar_bspstore) }, + { "IA64_PT_REGS_PR_OFFSET", offsetof (struct pt_regs, pr) }, + { "IA64_PT_REGS_B6_OFFSET", offsetof (struct pt_regs, b6) }, + { "IA64_PT_REGS_LOADRS_OFFSET", offsetof (struct pt_regs, loadrs) }, + { "IA64_PT_REGS_R1_OFFSET", offsetof (struct pt_regs, r1) }, + { "IA64_PT_REGS_R2_OFFSET", offsetof (struct pt_regs, r2) }, + { "IA64_PT_REGS_R3_OFFSET", offsetof (struct pt_regs, r3) }, { "IA64_PT_REGS_R12_OFFSET", offsetof (struct pt_regs, r12) }, + { "IA64_PT_REGS_R13_OFFSET", offsetof (struct pt_regs, r13) }, + { "IA64_PT_REGS_R14_OFFSET", offsetof (struct pt_regs, r14) }, + { "IA64_PT_REGS_R15_OFFSET", offsetof (struct pt_regs, r15) }, { "IA64_PT_REGS_R8_OFFSET", offsetof (struct pt_regs, r8) }, + { "IA64_PT_REGS_R9_OFFSET", offsetof (struct pt_regs, r9) }, + { "IA64_PT_REGS_R10_OFFSET", offsetof (struct pt_regs, r10) }, + { "IA64_PT_REGS_R11_OFFSET", offsetof (struct pt_regs, r11) }, { "IA64_PT_REGS_R16_OFFSET", offsetof (struct pt_regs, r16) }, - { "IA64_SWITCH_STACK_B0_OFFSET", offsetof (struct switch_stack, b0) }, - { "IA64_SWITCH_STACK_CALLER_UNAT_OFFSET", offsetof (struct switch_stack, caller_unat) }, + { "IA64_PT_REGS_R17_OFFSET", offsetof (struct pt_regs, r17) }, + { "IA64_PT_REGS_R18_OFFSET", offsetof (struct pt_regs, r18) }, + { "IA64_PT_REGS_R19_OFFSET", offsetof (struct pt_regs, r19) }, + { "IA64_PT_REGS_R20_OFFSET", offsetof (struct pt_regs, r20) }, + { "IA64_PT_REGS_R21_OFFSET", offsetof (struct pt_regs, r21) }, + { "IA64_PT_REGS_R22_OFFSET", offsetof (struct pt_regs, r22) }, + { "IA64_PT_REGS_R23_OFFSET", offsetof (struct pt_regs, r23) }, + { "IA64_PT_REGS_R24_OFFSET", offsetof (struct pt_regs, r24) }, + { "IA64_PT_REGS_R25_OFFSET", offsetof (struct pt_regs, r25) }, + { "IA64_PT_REGS_R26_OFFSET", offsetof (struct pt_regs, r26) }, + { "IA64_PT_REGS_R27_OFFSET", offsetof (struct pt_regs, r27) }, + { "IA64_PT_REGS_R28_OFFSET", offsetof (struct pt_regs, r28) }, + { "IA64_PT_REGS_R29_OFFSET", offsetof (struct pt_regs, r29) }, + { "IA64_PT_REGS_R30_OFFSET", offsetof (struct pt_regs, r30) }, + { "IA64_PT_REGS_R31_OFFSET", offsetof (struct pt_regs, r31) }, + { "IA64_PT_REGS_AR_CCV_OFFSET", offsetof (struct pt_regs, ar_ccv) }, + { "IA64_PT_REGS_AR_FPSR_OFFSET", offsetof (struct pt_regs, ar_fpsr) }, + { "IA64_PT_REGS_B0_OFFSET", offsetof (struct pt_regs, b0) }, + { "IA64_PT_REGS_B7_OFFSET", offsetof (struct pt_regs, b7) }, + { "IA64_PT_REGS_F6_OFFSET", offsetof (struct pt_regs, f6) }, + { "IA64_PT_REGS_F7_OFFSET", offsetof (struct pt_regs, f7) }, + { "IA64_PT_REGS_F8_OFFSET", offsetof (struct pt_regs, f8) }, + { "IA64_PT_REGS_F9_OFFSET", offsetof (struct pt_regs, f9) }, + { "IA64_SWITCH_STACK_CALLER_UNAT_OFFSET", offsetof (struct switch_stack, caller_unat) }, + { "IA64_SWITCH_STACK_AR_FPSR_OFFSET", offsetof (struct switch_stack, ar_fpsr) }, + { "IA64_SWITCH_STACK_F2_OFFSET", offsetof (struct switch_stack, f2) }, + { "IA64_SWITCH_STACK_F3_OFFSET", offsetof (struct switch_stack, f3) }, + { "IA64_SWITCH_STACK_F4_OFFSET", offsetof (struct switch_stack, f4) }, + { "IA64_SWITCH_STACK_F5_OFFSET", offsetof (struct switch_stack, f5) }, + { "IA64_SWITCH_STACK_F10_OFFSET", offsetof (struct switch_stack, f10) }, + { "IA64_SWITCH_STACK_F11_OFFSET", offsetof (struct switch_stack, f11) }, + { "IA64_SWITCH_STACK_F12_OFFSET", offsetof (struct switch_stack, f12) }, + { "IA64_SWITCH_STACK_F13_OFFSET", offsetof (struct switch_stack, f13) }, + { "IA64_SWITCH_STACK_F14_OFFSET", offsetof (struct switch_stack, f14) }, + { "IA64_SWITCH_STACK_F15_OFFSET", offsetof (struct switch_stack, f15) }, + { "IA64_SWITCH_STACK_F16_OFFSET", offsetof (struct switch_stack, f16) }, + { "IA64_SWITCH_STACK_F17_OFFSET", offsetof (struct switch_stack, f17) }, + { "IA64_SWITCH_STACK_F18_OFFSET", offsetof (struct switch_stack, f18) }, + { "IA64_SWITCH_STACK_F19_OFFSET", offsetof (struct switch_stack, f19) }, + { "IA64_SWITCH_STACK_F20_OFFSET", offsetof (struct switch_stack, f20) }, + { "IA64_SWITCH_STACK_F21_OFFSET", offsetof (struct switch_stack, f21) }, + { "IA64_SWITCH_STACK_F22_OFFSET", offsetof (struct switch_stack, f22) }, + { "IA64_SWITCH_STACK_F23_OFFSET", offsetof (struct switch_stack, f23) }, + { "IA64_SWITCH_STACK_F24_OFFSET", offsetof (struct switch_stack, f24) }, + { "IA64_SWITCH_STACK_F25_OFFSET", offsetof (struct switch_stack, f25) }, + { "IA64_SWITCH_STACK_F26_OFFSET", offsetof (struct switch_stack, f26) }, + { "IA64_SWITCH_STACK_F27_OFFSET", offsetof (struct switch_stack, f27) }, + { "IA64_SWITCH_STACK_F28_OFFSET", offsetof (struct switch_stack, f28) }, + { "IA64_SWITCH_STACK_F29_OFFSET", offsetof (struct switch_stack, f29) }, + { "IA64_SWITCH_STACK_F30_OFFSET", offsetof (struct switch_stack, f30) }, + { "IA64_SWITCH_STACK_F31_OFFSET", offsetof (struct switch_stack, f31) }, + { "IA64_SWITCH_STACK_R4_OFFSET", offsetof (struct switch_stack, r4) }, + { "IA64_SWITCH_STACK_R5_OFFSET", offsetof (struct switch_stack, r5) }, + { "IA64_SWITCH_STACK_R6_OFFSET", offsetof (struct switch_stack, r6) }, + { "IA64_SWITCH_STACK_R7_OFFSET", offsetof (struct switch_stack, r7) }, + { "IA64_SWITCH_STACK_B0_OFFSET", offsetof (struct switch_stack, b0) }, + { "IA64_SWITCH_STACK_B1_OFFSET", offsetof (struct switch_stack, b1) }, + { "IA64_SWITCH_STACK_B2_OFFSET", offsetof (struct switch_stack, b2) }, + { "IA64_SWITCH_STACK_B3_OFFSET", offsetof (struct switch_stack, b3) }, + { "IA64_SWITCH_STACK_B4_OFFSET", offsetof (struct switch_stack, b4) }, + { "IA64_SWITCH_STACK_B5_OFFSET", offsetof (struct switch_stack, b5) }, + { "IA64_SWITCH_STACK_AR_PFS_OFFSET", offsetof (struct switch_stack, ar_pfs) }, + { "IA64_SWITCH_STACK_AR_LC_OFFSET", offsetof (struct switch_stack, ar_lc) }, + { "IA64_SWITCH_STACK_AR_UNAT_OFFSET", offsetof (struct switch_stack, ar_unat) }, + { "IA64_SWITCH_STACK_AR_RNAT_OFFSET", offsetof (struct switch_stack, ar_rnat) }, + { "IA64_SWITCH_STACK_AR_BSPSTORE_OFFSET", offsetof (struct switch_stack, ar_bspstore) }, + { "IA64_SWITCH_STACK_PR_OFFSET", offsetof (struct switch_stack, b0) }, { "IA64_SIGCONTEXT_AR_BSP_OFFSET", offsetof (struct sigcontext, sc_ar_bsp) }, { "IA64_SIGCONTEXT_AR_RNAT_OFFSET", offsetof (struct sigcontext, sc_ar_rnat) }, { "IA64_SIGCONTEXT_FLAGS_OFFSET", offsetof (struct sigcontext, sc_flags) }, diff --git a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S index 08e7f9f9a..0e2dc7aaa 100644 --- a/arch/ia64/vmlinux.lds.S +++ b/arch/ia64/vmlinux.lds.S @@ -32,6 +32,13 @@ SECTIONS #endif _etext = .; + /* Read-only data */ + + __gp = ALIGN(8) + 0x200000; + + /* Global data */ + _data = .; + /* Exception table */ . = ALIGN(16); __start___ex_table = .; @@ -39,19 +46,33 @@ SECTIONS { *(__ex_table) } __stop___ex_table = .; - /* Kernel symbol names for modules: */ + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : AT(ADDR(__ksymtab) - PAGE_OFFSET) + { *(__ksymtab) } + __stop___ksymtab = .; + + /* Unwind table */ + ia64_unw_start = .; + .IA_64.unwind : AT(ADDR(.IA_64.unwind) - PAGE_OFFSET) + { *(.IA_64.unwind) } + ia64_unw_end = .; + .IA_64.unwind_info : AT(ADDR(.IA_64.unwind_info) - PAGE_OFFSET) + { *(.IA_64.unwind_info) } + + .rodata : AT(ADDR(.rodata) - PAGE_OFFSET) + { *(.rodata) } .kstrtab : AT(ADDR(.kstrtab) - PAGE_OFFSET) { *(.kstrtab) } + .opd : AT(ADDR(.opd) - PAGE_OFFSET) + { *(.opd) } - /* The initial task and kernel stack */ - . = ALIGN(PAGE_SIZE); - init_task : AT(ADDR(init_task) - PAGE_OFFSET) - { *(init_task) } + /* Initialization code and data: */ - /* Startup code */ + . = ALIGN(PAGE_SIZE); __init_begin = .; .text.init : AT(ADDR(.text.init) - PAGE_OFFSET) { *(.text.init) } + .data.init : AT(ADDR(.data.init) - PAGE_OFFSET) { *(.data.init) } . = ALIGN(16); @@ -66,6 +87,10 @@ SECTIONS . = ALIGN(PAGE_SIZE); __init_end = .; + /* The initial task and kernel stack */ + init_task : AT(ADDR(init_task) - PAGE_OFFSET) + { *(init_task) } + .data.page_aligned : AT(ADDR(.data.page_aligned) - PAGE_OFFSET) { *(.data.idt) } @@ -73,18 +98,13 @@ SECTIONS .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - PAGE_OFFSET) { *(.data.cacheline_aligned) } - /* Global data */ - _data = .; + /* Kernel symbol names for modules: */ + .kstrtab : AT(ADDR(.kstrtab) - PAGE_OFFSET) + { *(.kstrtab) } - .rodata : AT(ADDR(.rodata) - PAGE_OFFSET) - { *(.rodata) } - .opd : AT(ADDR(.opd) - PAGE_OFFSET) - { *(.opd) } .data : AT(ADDR(.data) - PAGE_OFFSET) { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } - __gp = ALIGN (8) + 0x200000; - .got : AT(ADDR(.got) - PAGE_OFFSET) { *(.got.plt) *(.got) } /* We want the small data sections together, so single-instruction offsets diff --git a/arch/m68k/atari/joystick.c b/arch/m68k/atari/joystick.c index 86bd24c73..2d20eb653 100644 --- a/arch/m68k/atari/joystick.c +++ b/arch/m68k/atari/joystick.c @@ -136,7 +136,7 @@ int __init atari_joystick_init(void) if (devfs_register_chrdev(MAJOR_NR, "Joystick", &atari_joystick_fops)) printk("unable to get major %d for joystick devices\n", MAJOR_NR); devfs_register_series (NULL, "joysticks/digital%u", 2, DEVFS_FL_DEFAULT, - MAJOR_NR, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + MAJOR_NR, 128, S_IFCHR | S_IRUSR | S_IWUSR, &atari_joystick_fops, NULL); return 0; diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 9cb3b4cf0..2191e502a 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -264,7 +264,6 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/mips/defconfig-decstation b/arch/mips/defconfig-decstation index 2d4e2e521..add5390d8 100644 --- a/arch/mips/defconfig-decstation +++ b/arch/mips/defconfig-decstation @@ -261,7 +261,6 @@ CONFIG_EXT2_FS=y # CONFIG_ROOT_NFS is not set # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set # CONFIG_SUNRPC is not set # CONFIG_LOCKD is not set # CONFIG_SMB_FS is not set diff --git a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22 index 9cb3b4cf0..2191e502a 100644 --- a/arch/mips/defconfig-ip22 +++ b/arch/mips/defconfig-ip22 @@ -264,7 +264,6 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/mips/defconfig-rm200 b/arch/mips/defconfig-rm200 index aba08c0de..545d3ea6d 100644 --- a/arch/mips/defconfig-rm200 +++ b/arch/mips/defconfig-rm200 @@ -280,7 +280,6 @@ CONFIG_EXT2_FS=y # CONFIG_ROOT_NFS is not set # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set # CONFIG_SUNRPC is not set # CONFIG_LOCKD is not set # CONFIG_SMB_FS is not set diff --git a/arch/mips64/defconfig b/arch/mips64/defconfig index 1c177bfee..72aa28cbc 100644 --- a/arch/mips64/defconfig +++ b/arch/mips64/defconfig @@ -376,7 +376,6 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22 index c85de57af..50e0132a2 100644 --- a/arch/mips64/defconfig-ip22 +++ b/arch/mips64/defconfig-ip22 @@ -295,7 +295,6 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27 index 1c177bfee..72aa28cbc 100644 --- a/arch/mips64/defconfig-ip27 +++ b/arch/mips64/defconfig-ip27 @@ -376,7 +376,6 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set -# CONFIG_NFSD_TCP is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 6d07d32c6..0f4bb555f 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,6 +1,6 @@ -# $Id: config.in,v 1.94 2000/06/04 22:23:10 anton Exp $ +# $Id: config.in,v 1.96 2000/06/20 01:10:00 anton Exp $ # For a description of the syntax of this configuration file, -# see Documentation/kbuild/config-language.txt. +# see the Configure script. # mainmenu_name "Linux/SPARC Kernel Configuration" @@ -92,9 +92,10 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM -if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then - bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD +if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 fi +dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP tristate 'Network block device support' CONFIG_BLK_DEV_NBD @@ -105,22 +106,27 @@ if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi -define_bool CONFIG_IDE n -define_bool CONFIG_BLK_DEV_IDE_MODES n -define_bool CONFIG_BLK_DEV_HD n +# Don't frighten a common SBus user +if [ "$CONFIG_PCI" = "y" ]; then -# mainmenu_option next_comment -# comment 'ATA/IDE/MFM/RLL support' -# -# tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE -# -# if [ "$CONFIG_IDE" != "n" ]; then -# source drivers/ide/Config.in -# else -# define_bool CONFIG_BLK_DEV_IDE_MODES n -# define_bool CONFIG_BLK_DEV_HD n -# fi -# endmenu + mainmenu_option next_comment + comment 'ATA/IDE/MFM/RLL support' + + tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE + + if [ "$CONFIG_IDE" != "n" ]; then + source drivers/ide/Config.in + else + define_bool CONFIG_BLK_DEV_IDE_MODES n + define_bool CONFIG_BLK_DEV_HD n + fi + endmenu +else + + define_bool CONFIG_IDE n + define_bool CONFIG_BLK_DEV_IDE_MODES n + define_bool CONFIG_BLK_DEV_HD n +fi mainmenu_option next_comment comment 'ISDN subsystem' @@ -211,6 +217,9 @@ if [ "$CONFIG_NET" = "y" ]; then fi tristate ' Sun QuadEthernet support' CONFIG_SUNQE tristate ' MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS + if [ "$CONFIG_PCI" = "y" ]; then + tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX + fi # bool ' FDDI driver support' CONFIG_FDDI # if [ "$CONFIG_FDDI" = "y" ]; then diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c index c9972ab16..b508bab4c 100644 --- a/arch/sparc/kernel/ebus.c +++ b/arch/sparc/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.9 2000/01/22 07:35:25 zaitcev Exp $ +/* $Id: ebus.c,v 1.10 2000/06/20 01:10:00 anton Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -127,13 +127,13 @@ void __init fill_ebus_child(int node, struct linux_prom_registers *preg, if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_name)) != 0) { dev->num_irqs = 1; } else if ((len = prom_getproperty(node, "interrupts", - (char *)&irqs, sizeof(irqs)) == -1) || (len == 0)) { + (char *)&irqs, sizeof(irqs))) == -1 || len == 0) { dev->num_irqs = 0; dev->irqs[0] = 0; if (dev->parent->num_irqs != 0) { dev->num_irqs = 1; dev->irqs[0] = dev->parent->irqs[0]; -/* P3 remove */ printk("EBUS: dev %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); +/* P3 */ /* printk("EBUS: dev %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */ } } else { dev->num_irqs = len / sizeof(irqs[0]); @@ -219,11 +219,11 @@ void __init fill_ebus_device(int node, struct linux_ebus_device *dev) if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_name)) != 0) { dev->num_irqs = 1; } else if ((len = prom_getproperty(node, "interrupts", - (char *)&irqs, sizeof(irqs)) == -1) || (len == 0)) { + (char *)&irqs, sizeof(irqs))) == -1 || len == 0) { dev->num_irqs = 0; if ((dev->irqs[0] = dev->bus->self->irq) != 0) { dev->num_irqs = 1; -/* P3 remove */ printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); +/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */ } } else { dev->num_irqs = 1; /* dev->num_irqs = len / sizeof(irqs[0]); */ diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 71f5dbc88..ca81aa872 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.38 2000/06/04 06:23:52 anton Exp $ +/* $Id: ioport.c,v 1.39 2000/06/20 01:10:00 anton Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -43,6 +43,8 @@ #include <asm/pgalloc.h> #include <asm/pgtable.h> +#define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */ + struct resource *_sparc_find_resource(struct resource *r, unsigned long); static void *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz); diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index b497201f5..b1767a097 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -1,4 +1,4 @@ -/* $Id: pcic.c,v 1.14 2000/03/01 02:53:28 davem Exp $ +/* $Id: pcic.c,v 1.15 2000/06/20 01:10:00 anton Exp $ * pcic.c: Sparc/PCI controller support * * Copyright (C) 1998 V. Roganov and G. Raiko @@ -155,8 +155,15 @@ static struct pcic_ca2irq pcic_i_se6[] = { /* * Krups (courtesy of Varol Kaptan) - * No documentation available, so we guess it, based on Espresso layout. - * Since we always run PROLL on Krups we may put map in there. + * No documentation available, but it was easy to guess + * because it was very similar to Espresso. + * + * pin 0 - kbd, mouse, serial; + * pin 1 - Ethernet; + * pin 2 - igs (we do not use it); + * pin 3 - audio; + * pin 4,5,6 - unused; + * pin 7 - RTC (from P2 onwards as David B. says). */ static struct pcic_ca2irq pcic_i_jk[] = { { 0, 0x00, 0, 13, 0 }, /* Ebus - serial and keyboard */ @@ -600,10 +607,10 @@ pcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node) } else { /* Corrupted map */ printk("PCIC: BAD PIN %d\n", i); for (;;) {} } -/* P3 remove later */ printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq); +/* P3 */ /* printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq); */ /* - * dev->irq=0 means PROM did not bothered to program the upper + * dev->irq=0 means PROM did not bother to program the upper * half of PCIC. This happens on JS-E with PROM 3.11, for instance. */ if (dev->irq == 0 || p->force) { @@ -730,7 +737,7 @@ pcic_pin_to_irq(unsigned int pin, char *name) printk("PCIC: BAD PIN %d FOR %s\n", pin, name); for (;;) {} /* XXX Cannot panic properly in case of PROLL */ } -/* P3 remove later */ printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq); +/* P3 */ /* printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq); */ return irq; } diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 18fe3790a..05ed9d932 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.62 2000/06/19 06:24:37 davem Exp $ +/* $Id: sys_sparc.c,v 1.63 2000/06/22 11:42:25 davem Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -215,6 +215,7 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, goto out; } + down(¤t->mm->mmap_sem); lock_kernel(); retval = -EINVAL; len = PAGE_ALIGN(len); @@ -229,12 +230,11 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, goto out_putf; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - down(¤t->mm->mmap_sem); retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); - up(¤t->mm->mmap_sem); out_putf: unlock_kernel(); + up(¤t->mm->mmap_sem); if (file) fput(file); out: diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index e770f1dd1..1c3dfe6e2 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.124 2000/06/19 06:24:37 davem Exp $ +/* $Id: sys_sunos.c,v 1.125 2000/06/22 11:42:25 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -68,6 +68,7 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, struct file * file = NULL; unsigned long retval, ret_type; + down(¤t->mm->mmap_sem); lock_kernel(); if(flags & MAP_NORESERVE) { static int cnt; @@ -117,9 +118,7 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - down(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); - up(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < PAGE_OFFSET) ? 0 : retval); @@ -128,6 +127,7 @@ out_putf: fput(file); out: unlock_kernel(); + up(¤t->mm->mmap_sem); return retval; } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index fe497c290..6bb5e9964 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.212 2000/06/13 22:59:14 anton Exp $ +/* $Id: srmmu.c,v 1.214 2000/06/22 01:28:44 anton Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -88,6 +88,8 @@ ctxd_t *srmmu_context_table; int viking_mxcc_present = 0; spinlock_t srmmu_context_spinlock = SPIN_LOCK_UNLOCKED; +int is_hypersparc; + /* * In general all page table modifications should use the V8 atomic * swap instruction. This insures the mmu and the cpu are in sync @@ -124,6 +126,7 @@ void *srmmu_nocache_pool; void *srmmu_nocache_bitmap; int srmmu_nocache_low; int srmmu_nocache_used; +spinlock_t srmmu_nocache_spinlock; /* This makes sense. Honest it does - Anton */ #define __nocache_pa(VADDR) (((unsigned long)VADDR) - SRMMU_NOCACHE_VADDR + __pa((unsigned long)srmmu_nocache_pool)) @@ -237,7 +240,6 @@ static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address) static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address) { return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); } -/* XXX Make this SMP safe - Anton */ unsigned long __srmmu_get_nocache(int size, int align) { int offset = srmmu_nocache_low; @@ -247,6 +249,8 @@ unsigned long __srmmu_get_nocache(int size, int align) size = size >> SRMMU_NOCACHE_BITMAP_SHIFT; + spin_lock(&srmmu_nocache_spinlock); + repeat: offset = find_next_zero_bit(srmmu_nocache_bitmap, SRMMU_NOCACHE_BITMAP_SIZE, offset); @@ -260,6 +264,7 @@ repeat: if ((SRMMU_NOCACHE_BITMAP_SIZE - offset) < size) { printk("Run out of nocached RAM!\n"); + spin_unlock(&srmmu_nocache_spinlock); return 0; } @@ -283,6 +288,8 @@ repeat: if (!lowest_failed && ((align >> SRMMU_NOCACHE_BITMAP_SHIFT) <= 1) && (offset > srmmu_nocache_low)) srmmu_nocache_low = offset; + spin_unlock(&srmmu_nocache_spinlock); + return (SRMMU_NOCACHE_VADDR + (offset << SRMMU_NOCACHE_BITMAP_SHIFT)); } @@ -298,13 +305,14 @@ unsigned inline long srmmu_get_nocache(int size, int align) return tmp; } -/* XXX Make this SMP safe - Anton */ void srmmu_free_nocache(unsigned long vaddr, int size) { int offset = (vaddr - SRMMU_NOCACHE_VADDR) >> SRMMU_NOCACHE_BITMAP_SHIFT; size = size >> SRMMU_NOCACHE_BITMAP_SHIFT; + spin_lock(&srmmu_nocache_spinlock); + while(size--) { clear_bit(offset + size, srmmu_nocache_bitmap); srmmu_nocache_used--; @@ -312,6 +320,8 @@ void srmmu_free_nocache(unsigned long vaddr, int size) if (offset < srmmu_nocache_low) srmmu_nocache_low = offset; + + spin_unlock(&srmmu_nocache_spinlock); } void srmmu_early_allocate_ptable_skeleton(unsigned long start, unsigned long end); @@ -336,6 +346,8 @@ void srmmu_nocache_init(void) srmmu_early_allocate_ptable_skeleton(SRMMU_NOCACHE_VADDR, SRMMU_NOCACHE_END); + spin_lock_init(&srmmu_nocache_spinlock); + paddr = __pa((unsigned long)srmmu_nocache_pool); vaddr = SRMMU_NOCACHE_VADDR; @@ -379,6 +391,51 @@ static void srmmu_pgd_free(pgd_t *pgd) srmmu_free_nocache((unsigned long)pgd, SRMMU_PGD_TABLE_SIZE); } +pmd_t *empty_bad_pmd_table; +pte_t *empty_bad_pte_table; + +/* + * We init them before every return and make them writable-shared. + * This guarantees we get out of the kernel in some more or less sane + * way. + */ +static pmd_t * get_bad_pmd_table(void) +{ + int i; + + for (i = 0; i < PAGE_SIZE/sizeof(pmd_t); i++) + srmmu_pmd_set(&(empty_bad_pmd_table[i]), empty_bad_pte_table); + + return empty_bad_pmd_table; +} + +static pte_t * get_bad_pte_table(void) +{ + pte_t v; + int i; + + memset((void *)&empty_bad_page, 0, PAGE_SIZE); + + v = srmmu_pte_mkdirty(srmmu_mk_pte_phys(__pa(&empty_bad_page) + phys_base, PAGE_SHARED)); + + for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++) + srmmu_set_pte(&(empty_bad_pte_table[i]), v); + + return empty_bad_pte_table; +} + +void __handle_bad_pgd(pgd_t *pgd) +{ + pgd_ERROR(*pgd); + srmmu_pgd_set(pgd, get_bad_pmd_table()); +} + +void __handle_bad_pmd(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + srmmu_pmd_set(pmd, get_bad_pte_table()); +} + static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address) { address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1); @@ -389,14 +446,11 @@ static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address) srmmu_pmd_set(pmd, page); return page + address; } - /* XXX fix this - Anton */ - pmd_set(pmd, BAD_PAGETABLE); + srmmu_pmd_set(pmd, get_bad_pte_table()); return NULL; } if(srmmu_pmd_bad(*pmd)) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - /* XXX fix this - Anton */ - pmd_set(pmd, BAD_PAGETABLE); + __handle_bad_pmd(pmd); return NULL; } return ((pte_t *) pmd_page(*pmd)) + address; @@ -417,14 +471,11 @@ static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address) srmmu_pgd_set(pgd, page); return page + address; } - /* XXX fix this - Anton */ - pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); + srmmu_pgd_set(pgd, get_bad_pmd_table()); return NULL; } if(srmmu_pgd_bad(*pgd)) { - printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); - /* XXX fix this - Anton */ - pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); + __handle_bad_pgd(pgd); return NULL; } return (pmd_t *) srmmu_pgd_page(*pgd) + address; @@ -493,7 +544,10 @@ static void srmmu_switch_mm(struct mm_struct *old_mm, struct mm_struct *mm, spin_unlock(&srmmu_context_spinlock); srmmu_ctxd_set(&srmmu_context_table[mm->context], mm->pgd); } - /* XXX should we hyper_flush_whole_icache() here - Anton */ + + if (is_hypersparc) + hyper_flush_whole_icache(); + srmmu_set_context(mm->context); } @@ -1189,6 +1243,9 @@ void __init srmmu_paging_init(void) flush_cache_all(); flush_tlb_all(); + empty_bad_pmd_table = (pte_t *)srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE); + empty_bad_pte_table = (pte_t *)srmmu_get_nocache(SRMMU_PTE_TABLE_SIZE, SRMMU_PTE_TABLE_SIZE); + /* * This does not logically belong here, but we need to * call it at the moment we are able to use the bootmem @@ -1397,6 +1454,8 @@ static void __init init_hypersparc(void) init_vac_layout(); + is_hypersparc = 1; + BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM); diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index ac0f91bdb..8bf06e44f 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.114 2000/06/04 22:23:10 anton Exp $ +# $Id: config.in,v 1.115 2000/06/20 04:36:22 ecd Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -93,6 +93,9 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM +if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 +fi if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index b560b7e22..9e51aadad 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.40 2000/06/19 06:24:37 davem Exp $ +/* $Id: sys_sparc.c,v 1.41 2000/06/22 11:42:25 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -227,6 +227,7 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, len = PAGE_ALIGN(len); retval = -EINVAL; + down(¤t->mm->mmap_sem); lock_kernel(); if (current->thread.flags & SPARC_FLAG_32BIT) { @@ -240,12 +241,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, goto out_putf; } - down(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); - up(¤t->mm->mmap_sem); out_putf: unlock_kernel(); + up(¤t->mm->mmap_sem); if (file) fput(file); out: diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 13aecb756..26c5faecd 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.149 2000/06/19 06:24:37 davem Exp $ +/* $Id: sys_sparc32.c,v 1.152 2000/06/22 17:44:47 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -2624,7 +2624,8 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up * the cmsg_len for MSG_TRUNC cases, we need not check that case either. */ ucmsg = (struct cmsghdr *) orig_cmsg_uptr; - while(((unsigned long)ucmsg) < ((unsigned long)kmsg->msg_control)) { + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; int clen64, clen32; @@ -3028,7 +3029,9 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); + lock_kernel(); file = open_exec(filename); + unlock_kernel(); retval = PTR_ERR(file); if (IS_ERR(file)) @@ -3040,12 +3043,10 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count32(argv)) < 0) { - allow_write_access(file); fput(file); return bprm.argc; } if ((bprm.envc = count32(envp)) < 0) { - allow_write_access(file); fput(file); return bprm.envc; } @@ -3074,7 +3075,6 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) out: /* Something went wrong, return the inode and free the argument pages*/ - allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); @@ -3778,8 +3778,6 @@ static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res return copy_to_user(res32, kres, sizeof(*res32)); } -extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp); - int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) { struct nfsctl_arg *karg = NULL; diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 376569eab..b13846fe9 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.48 2000/06/19 06:24:37 davem Exp $ +/* $Id: sys_sunos32.c,v 1.49 2000/06/22 11:42:25 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -68,6 +68,7 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of struct file *file = NULL; unsigned long retval, ret_type; + down(¤t->mm->mmap_sem); lock_kernel(); if(flags & MAP_NORESERVE) { static int cnt; @@ -101,12 +102,10 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of flags &= ~_MAP_NEW; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - down(¤t->mm->mmap_sem); retval = do_mmap(file, (unsigned long) addr, (unsigned long) len, (unsigned long) prot, (unsigned long) flags, (unsigned long) off); - up(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < 0xf0000000) ? 0 : retval); out_putf: @@ -114,6 +113,7 @@ out_putf: fput(file); out: unlock_kernel(); + up(¤t->mm->mmap_sem); return (u32) retval; } diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index 220e549ac..9a2556bbc 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -1,4 +1,4 @@ -/* $Id: socksys.c,v 1.13 2000/03/29 11:56:54 davem Exp $ +/* $Id: socksys.c,v 1.14 2000/06/22 11:42:25 davem Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -181,9 +181,9 @@ init_socksys(void) printk ("Couldn't create socket\n"); return ret; } - devfs_handle = devfs_register (NULL, "socksys", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register (NULL, "socksys", DEVFS_FL_DEFAULT, 30, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &socksys_fops, NULL); file = fcheck(ret); /* N.B. Is this valid? Suppose the f_ops are in a module ... */ diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index c6f8531f4..41ec3e2f5 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1473,9 +1473,8 @@ static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, Command->SegmentCount = Request->nr_segments; Command->BufferHeader = Request->bh; RequestBuffer = Request->buffer; - Request->rq_status = RQ_INACTIVE; blkdev_dequeue_request(Request); - wake_up(&wait_for_request); + blkdev_release_request(Request); if (Command->SegmentCount == 1) { DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c index 51bb6b1a3..62758a297 100644 --- a/drivers/block/acsi_slm.c +++ b/drivers/block/acsi_slm.c @@ -1009,7 +1009,7 @@ int slm_init( void ) devfs_handle = devfs_mk_dir (NULL, "slm", 3, NULL); devfs_register_series (devfs_handle, "%u", MAX_SLM, DEVFS_FL_DEFAULT, - MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &slm_fops, NULL); return 0; } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 40e639830..9bbe50523 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -22,7 +22,7 @@ * driver, you'll probably need the Compaq Array Controller Interface * Specificiation (Document number ECG086/1198) */ -#include <linux/config.h> +#include <linux/config.h> /* CONFIG_PROC_FS */ #include <linux/module.h> #include <linux/version.h> #include <linux/types.h> @@ -44,8 +44,8 @@ #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.4)" -#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,4) +#define DRIVER_NAME "Compaq SMART2 Driver (v 2.4.0)" +#define DRIVER_VERSION SMART2_DRIVER_VERSION(2,4,0) #define MAJOR_NR COMPAQ_SMART2_MAJOR #include <linux/blk.h> #include <linux/blkdev.h> @@ -73,7 +73,7 @@ static int eisa[8] = { 0, 0 ,0 ,0, 0, 0 ,0 ,0 }; * product = Marketing Name for the board * access = Address of the struct of function pointers */ -struct board_type products[] = { +static struct board_type products[] = { { 0x0040110E, "IDA", &smart1_access }, { 0x0140110E, "IDA-2", &smart1_access }, { 0x1040110E, "IAES", &smart1_access }, @@ -87,6 +87,7 @@ struct board_type products[] = { { 0x40400E11, "Integrated Array", &smart4_access }, { 0x40500E11, "Smart Array 4200", &smart4_access }, { 0x40510E11, "Smart Array 4250ES", &smart4_access }, + { 0x40580E11, "Smart Array 431", &smart4_access }, }; static struct hd_struct * ida; @@ -95,7 +96,7 @@ static int * ida_blocksizes; static int * ida_hardsizes; static struct gendisk ida_gendisk[MAX_CTLR]; -struct proc_dir_entry *proc_array = NULL; +static struct proc_dir_entry *proc_array = NULL; /* Debug... */ #define DBG(s) do { s } while(0) @@ -106,7 +107,7 @@ struct proc_dir_entry *proc_array = NULL; /* Debug Extra Paranoid... */ #define DBGPX(s) do { } while(0) -void cpqarray_init(void); +int cpqarray_init(void); static int cpqarray_pci_detect(void); static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn); static ulong remap_pci_mem(ulong base, ulong size); @@ -312,9 +313,8 @@ EXPORT_NO_SYMBOLS; /* This is a bit of a hack... */ int __init init_module(void) { - cpqarray_init(); - if (nr_ctlr == 0) - return -EIO; + if (cpqarray_init() == 0) /* all the block dev numbers already used */ + return -EIO; /* or no controllers were found */ return 0; } @@ -357,8 +357,9 @@ void cleanup_module(void) /* * This is it. Find all the controllers and register them. I really hate * stealing all these major device numbers. + * returns the number of block devices registered. */ -void __init cpqarray_init(void) +int __init cpqarray_init(void) { void (*request_fns[MAX_CTLR])(request_queue_t *) = { do_ida_request0, do_ida_request1, @@ -367,31 +368,52 @@ void __init cpqarray_init(void) do_ida_request6, do_ida_request7, }; int i,j; + int num_cntlrs_reg = 0; /* detect controllers */ cpqarray_pci_detect(); cpqarray_eisa_detect(); if (nr_ctlr == 0) - return; + return(num_cntlrs_reg); printk(DRIVER_NAME "\n"); printk("Found %d controller(s)\n", nr_ctlr); /* allocate space for disk structs */ ida = kmalloc(sizeof(struct hd_struct)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida==NULL) - goto bail; - ida_sizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); + { + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + + ida_sizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_sizes==NULL) - goto bail2; + { + kfree(ida); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + ida_blocksizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_blocksizes==NULL) - goto bail3; - ida_hardsizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); + { + kfree(ida); + kfree(ida_sizes); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + + ida_hardsizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_hardsizes==NULL) - goto bail4; + { + kfree(ida); + kfree(ida_sizes); + kfree(ida_blocksizes); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } memset(ida, 0, sizeof(struct hd_struct)*nr_ctlr*NWD*16); memset(ida_sizes, 0, sizeof(int)*nr_ctlr*NWD*16); @@ -399,74 +421,90 @@ void __init cpqarray_init(void) memset(ida_hardsizes, 0, sizeof(int)*nr_ctlr*NWD*16); memset(ida_gendisk, 0, sizeof(struct gendisk)*MAX_CTLR); - /* + /* * register block devices * Find disks and fill in structs * Get an interrupt, set the Q depth and get into /proc */ for(i=0; i< nr_ctlr; i++) { + /* If this successful it should insure that we are the only */ + /* instance of the driver */ + if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) { + printk(KERN_ERR "cpqarray: Unable to get major number %d for ida\n", + MAJOR_NR+i); + continue; + } + + hba[i]->access.set_intr_mask(hba[i], 0); if (request_irq(hba[i]->intr, do_ida_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) { - printk("Unable to get irq %d for %s\n", + printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); continue; } - if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) { - printk("Unable to get major number %d for ida\n", - MAJOR_NR+i); - continue; - } - + num_cntlrs_reg++; hba[i]->cmd_pool = (cmdlist_t *)kmalloc( NR_CMDS * sizeof(cmdlist_t), GFP_KERNEL); hba[i]->cmd_pool_bits = (__u32*)kmalloc( ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); - if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL) + if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL) { - int j; + nr_ctlr = i; if(hba[i]->cmd_pool_bits) kfree(hba[i]->cmd_pool_bits); if(hba[i]->cmd_pool) kfree(hba[i]->cmd_pool); - for(j=0;i<i;j++) - { - free_irq(hba[j]->intr, hba[j]); - unregister_blkdev(MAJOR_NR+j, hba[j]->devname); - kfree(hba[j]->cmd_pool_bits); - kfree(hba[j]->cmd_pool); - } free_irq(hba[i]->intr, hba[i]); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); - goto bail5; + num_cntlrs_reg--; + printk( KERN_ERR "cpqarray: out of memory"); + + /* If num_cntlrs_reg == 0, no controllers worked. + * init_module will fail, so clean up global + * memory that clean_module would do. + */ + + if (num_cntlrs_reg == 0) + { + kfree(ida); + kfree(ida_sizes); + kfree(ida_hardsizes); + kfree(ida_blocksizes); + } + return(num_cntlrs_reg); + } memset(hba[i]->cmd_pool, 0, NR_CMDS * sizeof(cmdlist_t)); memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+31)/32)*sizeof(__u32)); - printk("Finding drives on %s", hba[i]->devname); + printk(KERN_INFO "cpqarray: Finding drives on %s", + hba[i]->devname); getgeometry(i); start_fwbk(i); hba[i]->access.set_intr_mask(hba[i], FIFO_NOT_EMPTY); + ida_procinit(i); - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), request_fns[i]); - blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), + request_fns[i]); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256); hardsect_size[MAJOR_NR+i] = ida_hardsizes + (i*256); - read_ahead[MAJOR_NR+i] = READ_AHEAD; + ida_gendisk[i].major = MAJOR_NR + i; ida_gendisk[i].major_name = "ida"; ida_gendisk[i].minor_shift = NWD_SHIFT; ida_gendisk[i].max_p = 16; ida_gendisk[i].part = ida + (i*256); ida_gendisk[i].sizes = ida_sizes + (i*256); - /* ida_gendisk[i].nr_real is handled by getgeometry */ - + ida_gendisk[i].nr_real = 0; + /* Get on the disk list */ ida_gendisk[i].next = gendisk_head; gendisk_head = &ida_gendisk[i]; @@ -479,21 +517,13 @@ void __init cpqarray_init(void) ida_geninit(i); for(j=0; j<NWD; j++) - register_disk(&ida_gendisk[i], MKDEV(MAJOR_NR+i,j<<4), - 16, &ida_fops, hba[i]->drv[j].nr_blks); + register_disk(&ida_gendisk[i], + MKDEV(MAJOR_NR+i,j<<4), + 16, &ida_fops, hba[i]->drv[j].nr_blks); + } /* done ! */ - return; -bail5: - kfree(ida_hardsizes); -bail4: - kfree(ida_blocksizes); -bail3: - kfree(ida_sizes); -bail2: - kfree(ida); -bail: - printk(KERN_ERR "cpqarray: out of memory.\n"); + return(num_cntlrs_reg); } /* @@ -506,103 +536,68 @@ static int cpqarray_pci_detect(void) { int index; unchar bus=0, dev_fn=0; - - /* This seems dumb, surely we could use an array of types to match ?? */ - - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_DEC, - PCI_DEVICE_ID_COMPAQ_42XX, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "42XX Device has been found at %x %x\n", - bus, dev_fn); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - } - - for(index=0; ; index++) { - unsigned short subvendor=0; - if (pcibios_find_device(PCI_VENDOR_ID_NCR, - PCI_DEVICE_ID_NCR_53C1510, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "Integrated RAID Chip has been found at %x %x\n", - bus, dev_fn); - if(pcibios_read_config_word(bus, dev_fn, - PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - { - printk(KERN_DEBUG "cpqarray failed to read subvendor\n"); - break; - } - if(subvendor != PCI_VENDOR_ID_COMPAQ) - break; - printk(KERN_DEBUG "Its a compaq RAID Chip\n"); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } +#define IDA_BOARD_TYPES 3 + static int ida_vendor_id[IDA_BOARD_TYPES] = { PCI_VENDOR_ID_DEC, + PCI_VENDOR_ID_NCR, PCI_VENDOR_ID_COMPAQ }; + static int ida_device_id[IDA_BOARD_TYPES] = { PCI_DEVICE_ID_COMPAQ_42XX, PCI_DEVICE_ID_NCR_53C1510, PCI_DEVICE_ID_COMPAQ_SMART2P }; + int brdtype; + + /* search for all PCI board types that could be for this driver */ + for(brdtype=0; brdtype<IDA_BOARD_TYPES; brdtype++) + { + for(index=0; ; index++) { + if (pcibios_find_device(ida_vendor_id[brdtype], + ida_device_id[brdtype], index, &bus, &dev_fn)) + break; + printk(KERN_DEBUG "cpqarray: Device %x has been found at %x %x\n", + ida_vendor_id[brdtype], bus, dev_fn); + if (index == 1000000) break; + if (nr_ctlr == 8) { + printk(KERN_WARNING "cpqarray: This driver" + " supports a maximum of 8 controllers.\n"); + break; + } - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - /* DOESNT THIS LEAK MEMORY ?????? - AC */ - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - } +/* if it is a PCI_DEVICE_ID_NCR_53C1510, make sure it's the Compaq version of the chip */ + + if (ida_device_id[brdtype] == PCI_DEVICE_ID_NCR_53C1510) { + unsigned short subvendor=0; + if(pcibios_read_config_word(bus, dev_fn, + PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) + { + printk(KERN_DEBUG "cpqarray: failed to read subvendor\n"); + continue; + } + if(subvendor != PCI_VENDOR_ID_COMPAQ) + { + printk(KERN_DEBUG + "cpqarray: not a Compaq integrated array controller\n"); + continue; + } + } - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ, - PCI_DEVICE_ID_COMPAQ_SMART2P, index, &bus, &dev_fn)) - break; + hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); if(hba[nr_ctlr]==NULL) + { + printk(KERN_ERR "cpqarray: out of memory.\n"); + continue; + } + memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); + if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) + { + kfree(hba[nr_ctlr]); + continue; + } + sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); + hba[nr_ctlr]->ctlr = nr_ctlr; + nr_ctlr++; - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; } return nr_ctlr; } + /* * Find the IO address of the controller, its IRQ and so forth. Fill * in some basic stuff into the ctlr_info_t structure. @@ -671,8 +666,9 @@ DBGINFO( } } if (i == NR_PRODUCTS) { - printk("Sorry, I don't know how to access the SMART Array" - " controller %08lx\n", (unsigned long)board_id); + printk(KERN_WARNING "cpqarray: Sorry, I don't know how" + " to access the SMART Array controller %08lx\n", + (unsigned long)board_id); return -1; } @@ -734,8 +730,8 @@ static int cpqarray_eisa_detect(void) while(i<8 && eisa[i]) { if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); + printk(KERN_WARNING "cpqarray: This driver supports" + " a maximum of 8 controllers.\n"); break; } board_id = inl(eisa[i]+0xC80); @@ -744,11 +740,11 @@ static int cpqarray_eisa_detect(void) break; if (j == NR_PRODUCTS) { - printk("Sorry, I don't know how to access the SMART" - " Array controller %08lx\n", (unsigned long)board_id); + printk(KERN_WARNING "cpqarray: Sorry, I don't know how" + " to access the SMART Array controller %08lx\n", (unsigned long)board_id); continue; } - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + hba[nr_ctlr] = (ctlr_info_t *) kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); if(hba[nr_ctlr]==NULL) { printk(KERN_ERR "cpqarray: out of memory.\n"); @@ -885,21 +881,34 @@ static void do_ida_request(int ctlr) queue_head = &blk_dev[MAJOR_NR+ctlr].request_queue.queue_head; if (list_empty(queue_head)) - goto doreq_done; + { + start_io(h); + return; + } + creq = blkdev_entry_next_request(queue_head); if (creq->rq_status == RQ_INACTIVE) - goto doreq_done; + { + start_io(h); + return; + } + if (ctlr != MAJOR(creq->rq_dev)-MAJOR_NR || - ctlr > nr_ctlr || h == NULL) { + ctlr > nr_ctlr || h == NULL) + { printk("doreq cmd for %d, %x at %p\n", ctlr, creq->rq_dev, creq); complete_buffers(creq->bh, 0); - goto doreq_done; + start_io(h); + return; } if ((c = cmd_alloc(h)) == NULL) - goto doreq_done; + { + start_io(h); + return; + } bh = creq->bh; @@ -972,9 +981,9 @@ DBGPX( printk("Done with %p\n", creq); ); /* Put the request on the tail of the request queue */ addQ(&h->reqQ, c); h->Qdepth++; - if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth; + if (h->Qdepth > h->maxQsinceinit) + h->maxQsinceinit = h->Qdepth; -doreq_done: start_io(h); } @@ -1022,28 +1031,24 @@ static inline void complete_buffers(struct buffer_head *bh, int ok) */ static inline void complete_command(cmdlist_t *cmd, int timeout) { - char buf[80]; int ok=1; if (cmd->req.hdr.rcode & RCODE_NONFATAL && (hba[cmd->ctlr]->misc_tflags & MISC_NONFATAL_WARN) == 0) { - sprintf(buf, "Non Fatal error on ida/c%dd%d\n", + printk(KERN_WARNING "Non Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); - console_print(buf); hba[cmd->ctlr]->misc_tflags |= MISC_NONFATAL_WARN; } if (cmd->req.hdr.rcode & RCODE_FATAL) { - sprintf(buf, "Fatal error on ida/c%dd%d\n", + printk(KERN_WARNING "Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); - console_print(buf); ok = 0; } if (cmd->req.hdr.rcode & RCODE_INVREQ) { - sprintf(buf, "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", + printk(KERN_WARNING "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", cmd->ctlr, cmd->hdr.unit, cmd->req.hdr.cmd, cmd->req.hdr.blk, cmd->req.hdr.blk_cnt, cmd->req.hdr.sg_cnt, cmd->req.hdr.rcode); - console_print(buf); ok = 0; } if (timeout) ok = 0; @@ -1077,10 +1082,15 @@ static void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs) if (istat & FIFO_NOT_EMPTY) { while((a = h->access.command_completed(h))) { a1 = a; a &= ~3; - if ((c = h->cmpQ) == NULL) goto bad_completion; + if ((c = h->cmpQ) == NULL) + { + printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1); + continue; + } while(c->busaddr != a) { c = c->next; - if (c == h->cmpQ) break; + if (c == h->cmpQ) + break; } /* * If we've found the command, take it off the @@ -1096,8 +1106,6 @@ static void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs) } continue; } -bad_completion: - printk("Completion of %08lx ignored\n", (unsigned long)a1); } } @@ -1227,16 +1235,27 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) switch(io->cmd) { case PASSTHRU_A: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); - c->req.bp = virt_to_bus(&(io->c)); + c->req.hdr.blk = virt_to_bus(&(io->c)); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; break; case IDA_READ: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } + c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; @@ -1245,7 +1264,12 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) case IDA_WRITE_MEDIA: case DIAG_PASS_THRU: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); @@ -1284,10 +1308,8 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) } io->rcode = c->req.hdr.rcode; - error = 0; -ioctl_err_exit: cmd_free(NULL, c); - return error; + return(0); } /* @@ -1390,8 +1412,8 @@ static int sendcmd( } udelay(10); DBG( - printk("ida%d: idaSendPciCmd FIFO full, waiting!\n", - ctlr); + printk(KERN_WARNING "cpqarray ida%d: idaSendPciCmd FIFO full," + " waiting!\n", ctlr); ); } /* @@ -1401,16 +1423,16 @@ DBG( complete = pollcomplete(ctlr); if (complete != 1) { if (complete != c->busaddr) { - printk( - "ida%d: idaSendPciCmd " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd " "Invalid command list address returned! (%08lx)\n", ctlr, (unsigned long)complete); cmd_free(info_p, c); return (IO_ERROR); } } else { - printk( - "ida%d: idaSendPciCmd Timeout out, " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd Timeout out, " "No command list address returned!\n", ctlr); cmd_free(info_p, c); @@ -1419,9 +1441,9 @@ DBG( if (c->req.hdr.rcode & 0x00FE) { if (!(c->req.hdr.rcode & BIG_PROBLEM)) { - printk( - "ida%d: idaSendPciCmd, error: Controller failed " - "at init time " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd, error: " + "Controller failed at init time " "cmd: 0x%x, return code = 0x%x\n", ctlr, c->req.hdr.cmd, c->req.hdr.rcode); @@ -1461,8 +1483,8 @@ static int revalidate_allvol(kdev_t dev) spin_lock_irqsave(&io_request_lock, flags); if (hba[ctlr]->usage_count > 1) { spin_unlock_irqrestore(&io_request_lock, flags); - printk("Device busy for volume revalidation (usage=%d)\n", - hba[ctlr]->usage_count); + printk(KERN_WARNING "cpqarray: Device busy for volume" + " revalidation (usage=%d)\n", hba[ctlr]->usage_count); return -EBUSY; } spin_unlock_irqrestore(&io_request_lock, flags); @@ -1514,8 +1536,9 @@ static int revalidate_logvol(kdev_t dev, int maxusage) spin_lock_irqsave(&io_request_lock, flags); if (hba[ctlr]->drv[target].usage_count > maxusage) { spin_unlock_irqrestore(&io_request_lock, flags); - printk("Device busy for revalidation (usage=%d)\n", - hba[ctlr]->drv[target].usage_count); + printk(KERN_WARNING "cpqarray: Device busy for " + "revalidation (usage=%d)\n", + hba[ctlr]->drv[target].usage_count); return -EBUSY; } @@ -1581,22 +1604,28 @@ static void start_fwbk(int ctlr) id_ctlr_t *id_ctlr_buf; int ret_code; - if( hba[ctlr]->board_id != 0x40400E11) + if( (hba[ctlr]->board_id != 0x40400E11) + && (hba[ctlr]->board_id != 0x40480E11) ) + /* Not a Integrated Raid, so there is nothing for us to do */ return; - printk(KERN_DEBUG "Starting firmware's background processing\n"); + printk(KERN_DEBUG "cpqarray: Starting firmware's background" + " processing\n"); /* Command does not return anything, but idasend command needs a buffer */ id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL); if(id_ctlr_buf==NULL) { - printk(KERN_WARNING "Out of memory. Unable to start background processing.\n"); + printk(KERN_WARNING "cpqarray: Out of memory. " + "Unable to start background processing.\n"); return; } ret_code = sendcmd(RESUME_BACKGROUND_ACTIVITY, ctlr, id_ctlr_buf, 0, 0, 0, 0); if(ret_code != IO_OK) - printk(KERN_WARNING "Unable to start background processing\n"); + printk(KERN_WARNING "cpqarray: Unable to start" + " background processing\n"); + kfree(id_ctlr_buf); } /***************************************************************** @@ -1620,16 +1649,38 @@ static void getgeometry(int ctlr) id_ldrive = (id_log_drv_t *)kmalloc(sizeof(id_log_drv_t), GFP_KERNEL); if(id_ldrive == NULL) + { + printk( KERN_ERR "cpqarray: out of memory.\n"); return; + } + id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL); if(id_ctlr_buf == NULL) - goto bail2; + { + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + id_lstatus_buf = (sense_log_drv_stat_t *)kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL); if(id_lstatus_buf == NULL) - goto bail3; + { + kfree(id_ctlr_buf); + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + sense_config_buf = (config_t *)kmalloc(sizeof(config_t), GFP_KERNEL); if(sense_config_buf == NULL) - goto bail4; + { + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + memset(id_ldrive, 0, sizeof(id_log_drv_t)); memset(id_ctlr_buf, 0, sizeof(id_ctlr_t)); memset(id_lstatus_buf, 0, sizeof(sense_log_drv_stat_t)); @@ -1648,8 +1699,15 @@ static void getgeometry(int ctlr) * so the idastubopen will fail on all logical drives * on the controller. */ - goto geo_ret; /* release the buf and return */ - } + /* Free all the buffers and return */ + printk(KERN_ERR "cpqarray: error sending ID controller\n"); + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; + } + info_p->log_drives = id_ctlr_buf->nr_drvs;; *(__u32*)(info_p->firm_rev) = *(__u32*)(id_ctlr_buf->firm_rev); info_p->ctlr_sig = id_ctlr_buf->cfg_sig; @@ -1663,8 +1721,9 @@ static void getgeometry(int ctlr) * Get drive geometry for all logical drives */ if (id_ctlr_buf->nr_drvs > 16) - printk("ida%d: This driver supports 16 logical drives " - "per controller.\n. Additional drives will not be " + printk(KERN_WARNING "cpqarray ida%d: This driver supports " + "16 logical drives per controller.\n. " + " Additional drives will not be " "detected\n", ctlr); for (log_unit = 0; @@ -1687,13 +1746,17 @@ static void getgeometry(int ctlr) on the controller. */ info_p->log_drv_map = 0; - printk( - "ida%d: idaGetGeometry - Controller failed " - "to report status of logical drive %d\n" + printk( KERN_WARNING + "cpqarray ida%d: idaGetGeometry - Controller" + " failed to report status of logical drive %d\n" "Access to this controller has been disabled\n", ctlr, log_unit); - goto geo_ret; /* release the buf and return */ - + /* Free all the buffers and return */ + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; } /* Make sure the logical drive is configured @@ -1715,14 +1778,21 @@ static void getgeometry(int ctlr) drv->sectors = id_ldrive->drv.sect_per_track; info_p->log_drv_map |= (1 << log_unit); - printk("ida/c%dd%d: blksz=%d nr_blks=%d\n", + printk(KERN_INFO "cpqarray ida/c%dd%d: blksz=%d nr_blks=%d\n", ctlr, log_unit, drv->blk_size, drv->nr_blks); ret_code = sendcmd(SENSE_CONFIG, ctlr, sense_config_buf, sizeof(config_t), 0, 0, log_unit); if (ret_code == IO_ERROR) { info_p->log_drv_map = 0; - goto geo_ret; /* release the buf and return */ + /* Free all the buffers and return */ + printk(KERN_ERR "cpqarray: error sending sense config\n"); + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; + } info_p->phys_drives = sense_config_buf->ctlr_phys_drv; @@ -1736,12 +1806,10 @@ static void getgeometry(int ctlr) log_index = log_index + 1; } /* end of if logical drive configured */ } /* end of for log_unit */ -geo_ret: kfree(sense_config_buf); -bail4: - kfree(id_ldrive); -bail3: - kfree(id_lstatus_buf); -bail2: + kfree(id_ldrive); + kfree(id_lstatus_buf); kfree(id_ctlr_buf); + return; + } diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 26f02abe3..2bf92251c 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -4,6 +4,16 @@ * Block device elevator/IO-scheduler. * * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE + * + * 30042000 Jens Axboe <axboe@suse.de> : + * + * Split the elevator a bit so that it is possible to choose a different + * one or even write a new "plug in". There are three pieces: + * - elevator_fn, inserts a new request in the queue list + * - elevator_merge_fn, decides whether a new buffer can be merged with + * an existing request + * - elevator_dequeue_fn, called when a request is taken off the active list + * */ #include <linux/fs.h> @@ -12,9 +22,9 @@ #include <linux/blk.h> #include <asm/uaccess.h> -static void elevator_default(struct request * req, elevator_t * elevator, - struct list_head * real_head, - struct list_head * head, int orig_latency) +void elevator_default(struct request *req, elevator_t * elevator, + struct list_head * real_head, + struct list_head * head, int orig_latency) { struct list_head * entry = real_head, * point = NULL; struct request * tmp; @@ -22,6 +32,12 @@ static void elevator_default(struct request * req, elevator_t * elevator, int latency = orig_latency -= elevator->nr_segments, pass = 0; int point_latency = 0xbeefbeef; + if (list_empty(real_head)) { + req->elevator_sequence = elevator_sequence(elevator, orig_latency); + list_add(&req->queue, real_head); + return; + } + while ((entry = entry->prev) != head) { if (!point && latency >= 0) { point = entry; @@ -49,19 +65,189 @@ static void elevator_default(struct request * req, elevator_t * elevator, req->elevator_sequence = elevator_sequence(elevator, latency); } +int elevator_default_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + elevator_t *elevator = &q->elevator; + int orig_latency, latency, sequence, action, starving = 0; + + /* + * Avoid write-bombs as not to hurt interactiveness of reads + */ + if (rw == WRITE) + *max_segments = elevator->max_bomb_segments; + + latency = orig_latency = elevator_request_latency(elevator, rw); + sequence = elevator->sequence; + + entry = head; + if (q->head_active && !q->plugged) + head = head->next; + + while ((entry = entry->prev) != head && !starving) { + *req = blkdev_entry_to_request(entry); + latency += (*req)->nr_segments; + if (elevator_sequence_before((*req)->elevator_sequence, sequence)) + starving = 1; + if (latency < 0) + continue; + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) { + if (latency - (*req)->nr_segments < 0) + break; + action = ELEVATOR_BACK_MERGE; + } else if ((*req)->sector - count == bh->b_rsector) { + if (starving) + break; + action = ELEVATOR_FRONT_MERGE; + } else { + continue; + } + q->elevator.sequence++; + return action; + } + return ELEVATOR_NO_MERGE; +} + +inline void elevator_default_dequeue(struct request *req) +{ + if (req->cmd == READ) + req->e->read_pendings--; + + req->e->nr_segments -= req->nr_segments; +} + +/* + * Order ascending, but only allow a request to be skipped a certain + * number of times + */ +void elevator_linus(struct request *req, elevator_t *elevator, + struct list_head *real_head, + struct list_head *head, int orig_latency) +{ + struct list_head *entry = real_head; + struct request *tmp; + + if (list_empty(real_head)) { + list_add(&req->queue, real_head); + return; + } + + while ((entry = entry->prev) != head) { + tmp = blkdev_entry_to_request(entry); + if (!tmp->elevator_sequence) + break; + if (IN_ORDER(tmp, req)) + break; + tmp->elevator_sequence--; + } + list_add(&req->queue, entry); +} + +int elevator_linus_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + entry = head; + if (q->head_active && !q->plugged) + head = head->next; + + while ((entry = entry->prev) != head) { + *req = blkdev_entry_to_request(entry); + if (!(*req)->elevator_sequence) + break; + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*req)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + (*req)->elevator_sequence--; + } + return ELEVATOR_NO_MERGE; +} + +/* + * No request sorting, just add it to the back of the list + */ +void elevator_noop(struct request *req, elevator_t *elevator, + struct list_head *real_head, struct list_head *head, + int orig_latency) +{ + list_add_tail(&req->queue, real_head); +} + +/* + * See if we can find a request that is buffer can be coalesced with. + */ +int elevator_noop_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + if (q->head_active && !q->plugged) + head = head->next; + + entry = head; + while ((entry = entry->prev) != head) { + *req = blkdev_entry_to_request(entry); + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*req)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + } + return ELEVATOR_NO_MERGE; +} + +/* + * The noop "elevator" does not do any accounting + */ +void elevator_noop_dequeue(struct request *req) {} + #ifdef ELEVATOR_DEBUG -void elevator_debug(request_queue_t * q, kdev_t dev) +void elevator_default_debug(request_queue_t * q, kdev_t dev) { int read_pendings = 0, nr_segments = 0; elevator_t * elevator = &q->elevator; struct list_head * entry = &q->queue_head; static int counter; + if (elevator->elevator_fn != elevator_default) + return; + if (counter++ % 100) return; - while ((entry = entry->prev) != &q->queue_head) - { + while ((entry = entry->prev) != &q->queue_head) { struct request * req; req = blkdev_entry_to_request(entry); @@ -81,16 +267,14 @@ void elevator_debug(request_queue_t * q, kdev_t dev) nr_segments += req->nr_segments; } - if (read_pendings != elevator->read_pendings) - { + if (read_pendings != elevator->read_pendings) { printk(KERN_WARNING "%s: elevator read_pendings %d should be %d\n", kdevname(dev), elevator->read_pendings, read_pendings); elevator->read_pendings = read_pendings; } - if (nr_segments != elevator->nr_segments) - { + if (nr_segments != elevator->nr_segments) { printk(KERN_WARNING "%s: elevator nr_segments %d should be %d\n", kdevname(dev), elevator->nr_segments, @@ -102,7 +286,6 @@ void elevator_debug(request_queue_t * q, kdev_t dev) int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) { - int ret; blkelv_ioctl_arg_t output; output.queue_ID = elevator->queue_ID; @@ -110,44 +293,37 @@ int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) output.write_latency = elevator->write_latency; output.max_bomb_segments = elevator->max_bomb_segments; - ret = -EFAULT; if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t))) - goto out; - ret = 0; - out: - return ret; + return -EFAULT; + + return 0; } int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg) { blkelv_ioctl_arg_t input; - int ret; - ret = -EFAULT; if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t))) - goto out; + return -EFAULT; - ret = -EINVAL; if (input.read_latency < 0) - goto out; + return -EINVAL; if (input.write_latency < 0) - goto out; + return -EINVAL; if (input.max_bomb_segments <= 0) - goto out; + return -EINVAL; elevator->read_latency = input.read_latency; elevator->write_latency = input.write_latency; elevator->max_bomb_segments = input.max_bomb_segments; - ret = 0; - out: - return ret; + return 0; } -void elevator_init(elevator_t * elevator) +void elevator_init(elevator_t * elevator, elevator_t type) { static unsigned int queue_ID; - *elevator = ELEVATOR_DEFAULTS; + *elevator = type; elevator->queue_ID = queue_ID++; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8f4734818..1fcd8b73f 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3878,10 +3878,10 @@ static void __init register_devfs_entries (int drive) char name[16]; sprintf (name, "%d%s", drive, table[table_sup[UDP->cmos][i]]); - devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, MAJOR_NR, + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, MAJOR_NR, base_minor + (table_sup[UDP->cmos][i] << 2), S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP, - 0, 0, &floppy_fops, NULL); + &floppy_fops, NULL); } while (table_sup[UDP->cmos][i++]); } } diff --git a/drivers/block/ida_cmd.h b/drivers/block/ida_cmd.h index 056863b60..6d57a85ee 100644 --- a/drivers/block/ida_cmd.h +++ b/drivers/block/ida_cmd.h @@ -191,7 +191,7 @@ typedef struct { __u8 expn_fail; __u8 unit_flags; __u16 big_fail_map[8]; - __u16 big_remap_map[8]; + __u16 big_remap_map[128]; __u16 big_repl_map[8]; __u16 big_act_spare_map[8]; __u8 big_spar_repl_map[128]; @@ -336,7 +336,7 @@ typedef struct { __u32 sense_info; __u8 sense_code; __u8 sense_qual; - __u8 residual; + __u32 residual; __u8 reserved[4]; __u8 cdb[12]; } scsi_param_t; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 18c7dc1fa..2ed93b300 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, Karl Keyte: Added support for disk statistics * Elevator latency, (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE + * Queue request tables / lock, selectable elevator, Jens Axboe <axboe@suse.de> */ /* @@ -37,10 +38,9 @@ extern int mac_floppy_init(void); #endif /* - * The request-struct contains all necessary data - * to load a nr of sectors into memory + * For the allocated request tables */ -static struct request all_requests[NR_REQUEST]; +static kmem_cache_t *request_cachep; /* * The "disk" task queue is used to start the actual requests @@ -62,11 +62,6 @@ DECLARE_TASK_QUEUE(tq_disk); */ spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; -/* - * used to wait on when there are no free requests - */ -DECLARE_WAIT_QUEUE_HEAD(wait_for_request); - /* This specifies how many sectors to read ahead on the disk. */ int read_ahead[MAX_BLKDEV]; @@ -127,29 +122,61 @@ static inline int get_max_sectors(kdev_t dev) return max_sectors[MAJOR(dev)][MINOR(dev)]; } +static inline request_queue_t *__blk_get_queue(kdev_t dev) +{ + struct blk_dev_struct *bdev = blk_dev + MAJOR(dev); + + if (bdev->queue) + return bdev->queue(dev); + else + return &blk_dev[MAJOR(dev)].request_queue; +} + /* * NOTE: the device-specific queue() functions * have to be atomic! */ -request_queue_t * blk_get_queue (kdev_t dev) +request_queue_t *blk_get_queue(kdev_t dev) { - int major = MAJOR(dev); - struct blk_dev_struct *bdev = blk_dev + major; - unsigned long flags; request_queue_t *ret; + unsigned long flags; spin_lock_irqsave(&io_request_lock,flags); - if (bdev->queue) - ret = bdev->queue(dev); - else - ret = &blk_dev[major].request_queue; + ret = __blk_get_queue(dev); spin_unlock_irqrestore(&io_request_lock,flags); return ret; } +/* + * Hopefully the low level driver has finished any out standing requests + * first... + */ void blk_cleanup_queue(request_queue_t * q) { + struct list_head *entry; + struct request *rq; + int i = QUEUE_NR_REQUESTS; + + if (list_empty(&q->request_freelist)) + return; + + if (q->queue_requests) + BUG(); + + entry = &q->request_freelist; + entry = entry->next; + do { + rq = list_entry(entry, struct request, table); + entry = entry->next; + list_del(&rq->table); + kmem_cache_free(request_cachep, rq); + i--; + } while (!list_empty(&q->request_freelist)); + + if (i) + printk("blk_cleanup_queue: leaked requests (%d)\n", i); + memset(q, 0, sizeof(*q)); } @@ -222,7 +249,7 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req, * This is called with interrupts off and no requests on the queue. * (and with the request spinlock aquired) */ -static void generic_plug_device (request_queue_t *q, kdev_t dev) +static void generic_plug_device(request_queue_t *q, kdev_t dev) { #ifdef CONFIG_BLK_DEV_MD if (MAJOR(dev) == MD_MAJOR) { @@ -230,25 +257,51 @@ static void generic_plug_device (request_queue_t *q, kdev_t dev) BUG(); } #endif - if (!list_empty(&q->queue_head)) + /* + * no need to replug device + */ + if (!list_empty(&q->queue_head) || q->plugged) return; q->plugged = 1; queue_task(&q->plug_tq, &tq_disk); } +static void blk_init_free_list(request_queue_t *q) +{ + struct request *rq; + int i; + + /* + * Divide requests in half between read and write. This used to + * be a 2/3 advantage for reads, but now reads can steal from + * the write free list. + */ + for (i = 0; i < QUEUE_NR_REQUESTS; i++) { + rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); + rq->rq_status = RQ_INACTIVE; + list_add(&rq->table, &q->request_freelist); + } + + q->queue_requests = 0; + init_waitqueue_head(&q->wait_for_request); + spin_lock_init(&q->request_lock); +} + void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) { INIT_LIST_HEAD(&q->queue_head); - elevator_init(&q->elevator); + INIT_LIST_HEAD(&q->request_freelist); + elevator_init(&q->elevator, ELEVATOR_LINUS); + blk_init_free_list(q); q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; q->merge_requests_fn = ll_merge_requests_fn; q->make_request_fn = NULL; - q->plug_tq.sync = 0; + q->plug_tq.sync = 0; q->plug_tq.routine = &generic_unplug_device; - q->plug_tq.data = q; + q->plug_tq.data = q; q->plugged = 0; /* * These booleans describe the queue properties. We set the @@ -263,89 +316,88 @@ void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) /* * remove the plug and let it rip.. */ -void generic_unplug_device(void * data) +static inline void __generic_unplug_device(request_queue_t *q) { - request_queue_t * q = (request_queue_t *) data; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock,flags); if (q->plugged) { q->plugged = 0; if (!list_empty(&q->queue_head)) - (q->request_fn)(q); + q->request_fn(q); } - spin_unlock_irqrestore(&io_request_lock,flags); } +void generic_unplug_device(void *data) +{ + request_queue_t *q = (request_queue_t *) data; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + __generic_unplug_device(q); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +#define blkdev_free_rq(list) list_entry((list)->next, struct request, table); /* - * look for a free request in the first N entries. - * NOTE: interrupts must be disabled on the way in (on SMP the request queue - * spinlock has to be aquired), and will still be disabled on the way out. + * Get a free request. io_request_lock must be held and interrupts + * disabled on the way in. */ -static inline struct request * get_request(int n, kdev_t dev) +static inline struct request *get_request(request_queue_t *q, int rw) { - static struct request *prev_found = NULL, *prev_limit = NULL; - register struct request *req, *limit; + register struct request *rq = NULL; - if (n <= 0) - panic("get_request(%d): impossible!\n", n); + if (!list_empty(&q->request_freelist)) { + elevator_t *e = &q->elevator; - limit = all_requests + n; - if (limit != prev_limit) { - prev_limit = limit; - prev_found = all_requests; - } - req = prev_found; - for (;;) { - req = ((req > all_requests) ? req : limit) - 1; - if (req->rq_status == RQ_INACTIVE) - break; - if (req == prev_found) + if ((q->queue_requests > QUEUE_WRITES_MAX) && (rw == WRITE)) return NULL; + + rq = blkdev_free_rq(&q->request_freelist); + list_del(&rq->table); + rq->rq_status = RQ_ACTIVE; + rq->special = NULL; + rq->q = q; + if (rq->cmd == READ) + rq->elevator_sequence = e->read_latency; + else + rq->elevator_sequence = e->write_latency; + q->queue_requests++; } - prev_found = req; - req->rq_status = RQ_ACTIVE; - req->rq_dev = dev; - req->special = NULL; - return req; + return rq; } /* - * wait until a free request in the first N entries is available. + * No available requests for this queue, unplug the device. */ -static struct request * __get_request_wait(int n, kdev_t dev) +static struct request *__get_request_wait(request_queue_t *q, int rw) { - register struct request *req; + register struct request *rq; DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - add_wait_queue_exclusive(&wait_for_request, &wait); + add_wait_queue_exclusive(&q->wait_for_request, &wait); for (;;) { - __set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE); - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) + __set_current_state(TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) break; - run_task_queue(&tq_disk); + generic_unplug_device(q); schedule(); } - remove_wait_queue(&wait_for_request, &wait); + remove_wait_queue(&q->wait_for_request, &wait); current->state = TASK_RUNNING; - return req; + return rq; } -static inline struct request * get_request_wait(int n, kdev_t dev) +static inline struct request *get_request_wait(request_queue_t *q, int rw) { - register struct request *req; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) - return req; - return __get_request_wait(n, dev); + register struct request *rq; + + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) + return rq; + return __get_request_wait(q, rw); } /* RO fail safe mechanism */ @@ -405,36 +457,45 @@ inline void drive_stat_acct (kdev_t dev, int rw, */ static inline void add_request(request_queue_t * q, struct request * req, - struct list_head * head, int latency) + struct list_head *head, int lat) { int major; drive_stat_acct(req->rq_dev, req->cmd, req->nr_sectors, 1); - - elevator_account_request(&q->elevator, req); - if (list_empty(head)) { - req->elevator_sequence = elevator_sequence(&q->elevator, latency); - list_add(&req->queue, &q->queue_head); - return; - } - q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency); - + elevator_account_request(req); /* + * let selected elevator insert the request + */ + q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, lat); + + /* * FIXME(eric) I don't understand why there is a need for this * special case code. It clearly doesn't fit any more with * the new queueing architecture, and it got added in 2.3.10. * I am leaving this in here until I hear back from the COMPAQ * people. - */ + */ major = MAJOR(req->rq_dev); if (major >= COMPAQ_SMART2_MAJOR+0 && major <= COMPAQ_SMART2_MAJOR+7) - { (q->request_fn)(q); - } - if (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) - { (q->request_fn)(q); +} + +/* + * Must be called with io_request_lock held and interrupts disabled + */ +void inline blkdev_release_request(struct request *req) +{ + req->rq_status = RQ_INACTIVE; + + /* + * Request may not have originated from ll_rw_blk + */ + if (req->q) { + list_add(&req->table, &req->q->request_freelist); + req->q->queue_requests--; + wake_up(&req->q->wait_for_request); } } @@ -462,13 +523,12 @@ static void attempt_merge(request_queue_t * q, if(!(q->merge_requests_fn)(q, req, next, max_segments)) return; - elevator_merge_requests(&q->elevator, req, next); + elevator_merge_requests(req, next); req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors; - next->rq_status = RQ_INACTIVE; list_del(&next->queue); - wake_up (&wait_for_request); + blkdev_release_request(next); } static inline void attempt_back_merge(request_queue_t * q, @@ -496,18 +556,16 @@ static inline void attempt_front_merge(request_queue_t * q, } static inline void __make_request(request_queue_t * q, int rw, - struct buffer_head * bh) + struct buffer_head * bh) { int major = MAJOR(bh->b_rdev); unsigned int sector, count; int max_segments = MAX_SEGMENTS; - struct request * req; - int rw_ahead, max_req, max_sectors; - unsigned long flags; - - int orig_latency, latency, starving, sequence; - struct list_head * entry, * head = &q->queue_head; - elevator_t * elevator; + struct request * req = NULL; + int rw_ahead, max_sectors, el_ret; + struct list_head *head = &q->queue_head; + int latency; + elevator_t *elevator = &q->elevator; count = bh->b_size >> 9; sector = bh->b_rsector; @@ -541,7 +599,6 @@ static inline void __make_request(request_queue_t * q, int rw, if (buffer_uptodate(bh)) /* Hmmph! Already have it */ goto end_io; kstat.pgpgin++; - max_req = NR_REQUEST; /* reads take precedence */ break; case WRITERAW: rw = WRITE; @@ -558,7 +615,6 @@ static inline void __make_request(request_queue_t * q, int rw, * requests are only for reads. */ kstat.pgpgout++; - max_req = (NR_REQUEST * 2) / 3; break; default: BUG(); @@ -583,158 +639,82 @@ static inline void __make_request(request_queue_t * q, int rw, /* look for a free request. */ /* - * Loop uses two requests, 1 for loop and 1 for the real device. - * Cut max_req in half to avoid running out and deadlocking. - */ - if ((major == LOOP_MAJOR) || (major == NBD_MAJOR)) - max_req >>= 1; - - /* * Try to coalesce the new request with old requests */ max_sectors = get_max_sectors(bh->b_rdev); - elevator = &q->elevator; - orig_latency = elevator_request_latency(elevator, rw); + latency = elevator_request_latency(elevator, rw); /* * Now we acquire the request spinlock, we have to be mega careful * not to schedule or do something nonatomic */ - spin_lock_irqsave(&io_request_lock,flags); - elevator_debug(q, bh->b_rdev); + spin_lock_irq(&io_request_lock); + elevator_default_debug(q, bh->b_rdev); if (list_empty(head)) { q->plug_device_fn(q, bh->b_rdev); /* is atomic */ goto get_rq; } - /* avoid write-bombs to not hurt iteractiveness of reads */ - if (rw != READ && elevator->read_pendings) - max_segments = elevator->max_bomb_segments; - - sequence = elevator->sequence; - latency = orig_latency - elevator->nr_segments; - starving = 0; - entry = head; + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); + switch (el_ret) { - /* - * The scsi disk and cdrom drivers completely remove the request - * from the queue when they start processing an entry. For this - * reason it is safe to continue to add links to the top entry - * for those devices. - * - * All other drivers need to jump over the first entry, as that - * entry may be busy being processed and we thus can't change - * it. - */ - if (q->head_active && !q->plugged) - head = head->next; - - while ((entry = entry->prev) != head && !starving) { - req = blkdev_entry_to_request(entry); - if (!req->q) - break; - latency += req->nr_segments; - if (elevator_sequence_before(req->elevator_sequence, sequence)) - starving = 1; - if (latency < 0) - continue; - - if (req->sem) - continue; - if (req->cmd != rw) - continue; - if (req->nr_sectors + count > max_sectors) - continue; - if (req->rq_dev != bh->b_rdev) - continue; - /* Can we add it to the end of this request? */ - if (req->sector + req->nr_sectors == sector) { - if (latency - req->nr_segments < 0) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->back_merge_fn)(q, req, bh, max_segments)) + case ELEVATOR_BACK_MERGE: + if (!q->back_merge_fn(q, req, bh, max_segments)) break; req->bhtail->b_reqnext = bh; req->bhtail = bh; - req->nr_sectors = req->hard_nr_sectors += count; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_after(elevator, req, latency); - - /* Can we now merge this req with the next? */ attempt_back_merge(q, req, max_sectors, max_segments); - /* or to the beginning? */ - } else if (req->sector - count == sector) { - if (starving) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->front_merge_fn)(q, req, bh, max_segments)) + goto out; + + case ELEVATOR_FRONT_MERGE: + if (!q->front_merge_fn(q, req, bh, max_segments)) break; - bh->b_reqnext = req->bh; - req->bh = bh; - req->buffer = bh->b_data; - req->current_nr_sectors = count; - req->sector = req->hard_sector = sector; - req->nr_sectors = req->hard_nr_sectors += count; + bh->b_reqnext = req->bh; + req->bh = bh; + req->buffer = bh->b_data; + req->current_nr_sectors = count; + req->sector = req->hard_sector = sector; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_before(elevator, req, latency); - attempt_front_merge(q, head, req, max_sectors, max_segments); - } else - continue; - - q->elevator.sequence++; - spin_unlock_irqrestore(&io_request_lock,flags); - return; + goto out; + /* + * elevator says don't/can't merge. get new request + */ + case ELEVATOR_NO_MERGE: + break; + default: + printk("elevator returned crap (%d)\n", el_ret); + BUG(); } - -/* find an unused request. */ -get_rq: - req = get_request(max_req, bh->b_rdev); - + /* - * if no request available: if rw_ahead, forget it, - * otherwise try again blocking.. + * Grab a free request from the freelist. Read first try their + * own queue - if that is empty, we steal from the write list. + * Writes must block if the write list is empty, and read aheads + * are not crucial. */ - if (!req) { - spin_unlock_irqrestore(&io_request_lock,flags); +get_rq: + if ((req = get_request(q, rw)) == NULL) { + spin_unlock_irq(&io_request_lock); if (rw_ahead) goto end_io; - req = __get_request_wait(max_req, bh->b_rdev); - spin_lock_irqsave(&io_request_lock,flags); - /* revalidate elevator */ - head = &q->queue_head; - if (q->head_active && !q->plugged) - head = head->next; + req = __get_request_wait(q, rw); + spin_lock_irq(&io_request_lock); } + head = &q->queue_head; + if (q->head_active && !q->plugged) + head = head->next; + /* fill up the request-info, and add it to the queue */ req->cmd = rw; req->errors = 0; @@ -747,19 +727,18 @@ get_rq: req->sem = NULL; req->bh = bh; req->bhtail = bh; - req->q = q; - add_request(q, req, head, orig_latency); - - spin_unlock_irqrestore(&io_request_lock, flags); + req->rq_dev = bh->b_rdev; + req->e = elevator; + add_request(q, req, head, latency); +out: + spin_unlock_irq(&io_request_lock); return; - end_io: bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); } int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { - unsigned long flags; int ret; /* @@ -767,7 +746,6 @@ int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) * still free to implement/resolve their own stacking * by explicitly returning 0) */ - while (q->make_request_fn) { ret = q->make_request_fn(q, rw, bh); if (ret > 0) { @@ -781,10 +759,10 @@ int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) * the IO request? (normal case) */ __make_request(q, rw, bh); - spin_lock_irqsave(&io_request_lock,flags); + spin_lock_irq(&io_request_lock); if (q && !q->plugged) (q->request_fn)(q); - spin_unlock_irqrestore(&io_request_lock,flags); + spin_unlock_irq(&io_request_lock); return 0; } @@ -923,28 +901,27 @@ int end_that_request_first (struct request *req, int uptodate, char *name) void end_that_request_last(struct request *req) { - if (req->q) + if (req->e) { + printk("end_that_request_last called with non-dequeued req\n"); BUG(); + } if (req->sem != NULL) up(req->sem); - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + + blkdev_release_request(req); } int __init blk_dev_init(void) { - struct request * req; struct blk_dev_struct *dev; - for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) { + request_cachep = kmem_cache_create("blkdev_requests", + sizeof(struct request), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) dev->queue = NULL; - blk_init_queue(&dev->request_queue, NULL); - } - req = all_requests + NR_REQUEST; - while (--req >= all_requests) { - req->rq_status = RQ_INACTIVE; - } memset(ro_bits,0,sizeof(ro_bits)); memset(max_readahead, 0, sizeof(max_readahead)); memset(max_sectors, 0, sizeof(max_sectors)); @@ -1070,3 +1047,4 @@ EXPORT_SYMBOL(blk_queue_headactive); EXPORT_SYMBOL(blk_queue_pluggable); EXPORT_SYMBOL(blk_queue_make_request); EXPORT_SYMBOL(generic_make_request); +EXPORT_SYMBOL(blkdev_release_request); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 96dacdc22..89018e54a 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -785,7 +785,7 @@ int __init loop_init(void) devfs_handle = devfs_mk_dir (NULL, "loop", 0, NULL); devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, &lo_fops, NULL); if ((max_loop < 1) || (max_loop > 255)) { diff --git a/drivers/block/md.c b/drivers/block/md.c index 918dbdaf2..617990e77 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -3629,7 +3629,7 @@ int md__init md_init (void) } devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL); devfs_register_series (devfs_handle, "%u",MAX_MD_DEVS,DEVFS_FL_DEFAULT, - MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); blk_dev[MD_MAJOR].queue = md_get_queue; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 8256c57bd..222622c35 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -519,9 +519,9 @@ int nbd_init(void) } devfs_handle = devfs_mk_dir (NULL, "nbd", 0, NULL); devfs_register_series (devfs_handle, "%u", MAX_NBD, - DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, - &nbd_fops, NULL); + DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUSR | S_IWUSR, + &nbd_fops, NULL); return 0; } diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 3cb11286d..5c4dbbee8 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -309,7 +309,7 @@ int pg_init (void) /* preliminary initialisation */ } devfs_handle = devfs_mk_dir (NULL, "pg", 2, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pg_fops, NULL); return 0; } diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 739755a93..52c67d017 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -314,10 +314,10 @@ int pt_init (void) /* preliminary initialisation */ devfs_handle = devfs_mk_dir (NULL, "pt", 2, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pt_fops, NULL); devfs_register_series (devfs_handle, "%un", 4, DEVFS_FL_DEFAULT, - major, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 128, S_IFCHR | S_IRUSR | S_IWUSR, &pt_fops, NULL); return 0; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 211550d1e..26c0509d2 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -107,9 +107,7 @@ static struct inode *rd_inode[NUM_RAMDISKS]; /* Protected device inodes */ * architecture-specific setup routine (from the stored boot sector * information). */ - -int rd_size = CONFIG_BLK_DEV_RAM_SIZE; /* Size of the RAM disks */ - +int rd_size = 4096; /* Size of the RAM disks */ /* * It would be very desiderable to have a soft-blocksize (that in the case * of the ramdisk driver is also the hardblocksize ;) of PAGE_SIZE because @@ -413,7 +411,7 @@ int __init rd_init (void) devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL); devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + S_IFBLK | S_IRUSR | S_IWUSR, &fd_fops, NULL); for (i = 0; i < NUM_RAMDISKS; i++) diff --git a/drivers/block/smart1,2.h b/drivers/block/smart1,2.h index 221e4a5f3..015980013 100644 --- a/drivers/block/smart1,2.h +++ b/drivers/block/smart1,2.h @@ -62,13 +62,14 @@ static void smart4_intr_mask(ctlr_info_t *h, unsigned long val) } /* - * For this card fifo is full if reading this port returns 0! + * For older cards FIFO Full = 0. + * On this card 0 means there is room, anything else FIFO Full. * */ static unsigned long smart4_fifo_full(ctlr_info_t *h) { - return (~readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); + return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); } /* This type of controller returns -1 if the fifo is empty, @@ -81,7 +82,7 @@ static unsigned long smart4_completed(ctlr_info_t *h) = readl(h->vaddr + S42XX_REPLY_PORT_OFFSET); /* Fifo is empty */ - if( register_value == -1) + if( register_value == 0xffffffff) return 0; /* Need to let it know we got the reply */ diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index aaad8e0c6..e32238d32 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -1791,8 +1791,8 @@ int __init aztcd_init(void) return -EIO; } } - devfs_register (NULL, "aztcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &azt_fops, NULL); + devfs_register (NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) { printk("aztcd: Unable to get major %d for Aztech CD-ROM\n", diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 6b117a6be..caa3900b3 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -408,9 +408,9 @@ int register_cdrom(struct cdrom_device_info *cdi) } else { cdi->de = - devfs_register (devfs_handle, vname, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, vname, DEVFS_FL_DEFAULT, MAJOR (cdi->dev), MINOR (cdi->dev), - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); } cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c index f563a16c0..b0fa47b3e 100644 --- a/drivers/cdrom/gscd.c +++ b/drivers/cdrom/gscd.c @@ -1075,8 +1075,8 @@ int result; MAJOR_NR); return -EIO; } - devfs_register (NULL, "gscd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &gscd_fops, NULL); + devfs_register (NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = gscd_blocksizes; diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index 07f1f030c..480a01833 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -2064,8 +2064,8 @@ int __init optcd_init(void) printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR); return -EIO; } - devfs_register (NULL, "optcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &opt_fops, NULL); + devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL); hardsect_size[MAJOR_NR] = &hsecsize; blksize_size[MAJOR_NR] = &blksize; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 01dd94bf5..08400a52c 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -5803,9 +5803,9 @@ int __init SBPCD_INIT(void) sprintf (nbuff, "c%dt%d/cd", SBPCD_ISSUE - 1, D_S[j].drv_id); sbpcd_infop->de = - devfs_register (devfs_handle, nbuff, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, nbuff, DEVFS_FL_DEFAULT, MAJOR_NR, j, S_IFBLK | S_IRUGO | S_IWUGO, - 0, 0, &cdrom_fops, NULL); + &cdrom_fops, NULL); if (register_cdrom(sbpcd_infop)) { printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c index 82dd5e5b9..a867c3819 100644 --- a/drivers/cdrom/sjcd.c +++ b/drivers/cdrom/sjcd.c @@ -1565,8 +1565,8 @@ int __init sjcd_init( void ){ } printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base); - devfs_register (NULL, "sjcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &sjcd_fops, NULL); + devfs_register (NULL, "sjcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &sjcd_fops, NULL); sjcd_present++; return( 0 ); diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index 1f5fea7b9..d85fbc01f 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -1587,11 +1587,11 @@ sony535_init(void) printk("IRQ%d, ", tmp_irq); printk("using %d byte buffer\n", sony_buffer_size); - devfs_register (NULL, CDU535_HANDLE, 0, + devfs_register (NULL, CDU535_HANDLE, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUGO | S_IWUGO, - 0, 0, &cdu_fops, NULL); + &cdu_fops, NULL); if (devfs_register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { printk("Unable to get major %d for %s\n", MAJOR_NR, CDU535_MESSAGE_NAME); diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 3fc7005ed..d5091ff4d 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -226,7 +226,6 @@ int __init applicom_init(void) continue; } - /* &ac_open as dev_id? David, could you pass me this joint? */ if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &ac_open)) { printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); iounmap(RamIO); diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index dc076dd5a..6fe41e118 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -519,9 +519,9 @@ int __init dsp56k_init(void) printk("DSP56k driver: Unable to register driver\n"); return -ENODEV; } - devfs_handle = devfs_register (NULL, "dsp56k", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register (NULL, "dsp56k", DEVFS_FL_DEFAULT, DSP56K_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &dsp56k_fops, NULL); dsp56k.in_use = 0; diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index 730b69b17..475e1d052 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -352,9 +352,9 @@ static int __init dtlk_init(void) } if (dtlk_dev_probe() == 0) printk(", MAJOR %d\n", dtlk_major); - devfs_handle = devfs_register (NULL, "dtlk", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register (NULL, "dtlk", DEVFS_FL_DEFAULT, dtlk_major, DTLK_MINOR, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &dtlk_fops, NULL); init_timer(&dtlk_timer); diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 883f4a106..e6cb87d1e 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -439,34 +439,34 @@ KERN_INFO char devname[9]; sprintf (devname, "qft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 4, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "zqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 16, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nzqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 20, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "rawqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 32, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nrawqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 36, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); } diff --git a/drivers/char/i2c-old.c b/drivers/char/i2c-old.c index b509c9a18..bd9750fc3 100644 --- a/drivers/char/i2c-old.c +++ b/drivers/char/i2c-old.c @@ -37,8 +37,8 @@ static struct i2c_driver *drivers[I2C_DRIVER_MAX]; static int bus_count = 0, driver_count = 0; #ifdef CONFIG_VIDEO_BT848 -extern int tuner_init_module(void); -extern int msp3400_init_module(void); +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); #endif #ifdef CONFIG_VIDEO_BUZ extern int saa7111_init(void); @@ -55,8 +55,8 @@ int i2c_init(void) scan ? " (i2c bus scan enabled)" : ""); /* anything to do here ? */ #ifdef CONFIG_VIDEO_BT848 - tuner_init_module(); - msp3400_init_module(); + i2c_tuner_init(); + msp3400c_init(); #endif #ifdef CONFIG_VIDEO_BUZ saa7111_init(); diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c index 033eb9aaf..a645832c4 100644 --- a/drivers/char/ip2main.c +++ b/drivers/char/ip2main.c @@ -877,19 +877,19 @@ old_ip2_init(void) #ifdef CONFIG_DEVFS_FS sprintf( name, "ipl%d", i ); i2BoardPtrTable[i]->devfs_ipl_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); sprintf( name, "stat%d", i ); i2BoardPtrTable[i]->devfs_stat_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i + 1, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); for ( box = 0; box < ABS_MAX_BOXES; ++box ) { diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 8d16c9daf..81421c032 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -5310,7 +5310,7 @@ int __init stli_init(void) devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &stli_fsiomem, NULL); /* diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in index 3e371744e..1547e5f38 100644 --- a/drivers/char/joystick/Config.in +++ b/drivers/char/joystick/Config.in @@ -1,34 +1,56 @@ # -# Joystick driver +# Joystick driver configuration # mainmenu_option next_comment comment 'Joysticks' tristate 'Joystick support' CONFIG_JOYSTICK - if [ "$CONFIG_JOYSTICK" != "n" ]; then - dep_tristate ' Classic PC analog' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK - dep_tristate ' FPGaming and MadCatz A3D' CONFIG_JOY_ASSASSIN $CONFIG_JOYSTICK - dep_tristate ' Gravis GrIP' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK - dep_tristate ' Logitech ADI' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK - dep_tristate ' Microsoft SideWinder' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK - dep_tristate ' ThrustMaster DirectConnect' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK - dep_tristate ' Creative Labs Blaster' CONFIG_JOY_CREATIVE $CONFIG_JOYSTICK - dep_tristate ' PDPI Lightning 4 card' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK - dep_tristate ' Trident 4DWave and Aureal Vortex gameport' CONFIG_JOY_PCI $CONFIG_JOYSTICK - dep_tristate ' Magellan and Space Mouse' CONFIG_JOY_MAGELLAN $CONFIG_JOYSTICK - dep_tristate ' SpaceTec SpaceOrb 360 and SpaceBall Avenger' CONFIG_JOY_SPACEORB $CONFIG_JOYSTICK - dep_tristate ' SpaceTec SpaceBall 4000 FLX' CONFIG_JOY_SPACEBALL $CONFIG_JOYSTICK - dep_tristate ' Logitech WingMan Warrior' CONFIG_JOY_WARRIOR $CONFIG_JOYSTICK + + define_tristate CONFIG_USB $CONFIG_JOYSTICK + define_tristate CONFIG_INPUT_JOYDEV $CONFIG_JOYSTICK + + comment 'Game port support' + dep_tristate ' ns558 gameports' CONFIG_INPUT_NS558 $CONFIG_JOYSTICK + dep_tristate ' PDPI Lightning 4 gamecard' CONFIG_INPUT_LIGHTNING $CONFIG_JOYSTICK + dep_tristate ' Aureal Vortex and Trident 4DWave gameports' CONFIG_INPUT_PCIGAME $CONFIG_JOYSTICK + + comment 'Gameport joysticks' + dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_INPUT_ANALOG $CONFIG_JOYSTICK + dep_tristate ' Assasin 3D and MadCatz Panther devices' CONFIG_INPUT_A3D $CONFIG_JOYSTICK + dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_INPUT_ADI $CONFIG_JOYSTICK + dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_INPUT_COBRA $CONFIG_JOYSTICK + dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_INPUT_GF2K $CONFIG_JOYSTICK + dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_INPUT_GRIP $CONFIG_JOYSTICK + dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_INPUT_INTERACT $CONFIG_JOYSTICK + dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_INPUT_TMDC $CONFIG_JOYSTICK + dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_INPUT_SIDEWINDER $CONFIG_JOYSTICK + + comment 'Serial port support' + dep_tristate ' Serial port input line discipline' CONFIG_INPUT_SERPORT $CONFIG_JOYSTICK + + comment 'Serial port joysticks' + dep_tristate ' Logitech WingMan Warrior joystick' CONFIG_INPUT_WARRIOR $CONFIG_JOYSTICK + dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controller' CONFIG_INPUT_MAGELLAN $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controller' CONFIG_INPUT_SPACEORB $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceBall 4000 FLX 6dof controller' CONFIG_INPUT_SPACEBALL $CONFIG_JOYSTICK + dep_tristate ' I-Force joysticks/wheels' CONFIG_INPUT_IFORCE_232 $CONFIG_JOYSTICK + if [ "$CONFIG_INPUT_IFORCE_232" != "n" ]; then + define_tristate CONFIG_INPUT_IFORCE $CONFIG_INPUT_IFORCE_232 + fi + if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' NES, SNES, PSX, N64, Multi' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' Sega, Multi' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' TurboGraFX interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT - fi + comment 'Parallel port joysticks' + dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_INPUT_DB9 $CONFIG_JOYSTICK + dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_INPUT_GAMECON $CONFIG_JOYSTICK + dep_tristate ' Multisystem joysticks via TurboGraFX device' CONFIG_INPUT_TURBOGRAFX $CONFIG_JOYSTICK + fi + if [ "$CONFIG_AMIGA" = "y" ]; then - dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK + comment 'System joysticks' + dep_tristate ' Amiga joysticks' CONFIG_INPUT_AMIJOY $CONFIG_JOYSTICK fi fi - + endmenu diff --git a/drivers/char/joystick/Makefile b/drivers/char/joystick/Makefile index 2bb5870b0..2ad452618 100644 --- a/drivers/char/joystick/Makefile +++ b/drivers/char/joystick/Makefile @@ -2,154 +2,73 @@ # Makefile for the joystick drivers. # -O_TARGET := js.o -OX_OBJS := -O_OBJS := -MX_OBJS := -M_OBJS := - -ifeq ($(CONFIG_JOYSTICK),y) -OX_OBJS += joystick.o -else - ifeq ($(CONFIG_JOYSTICK),m) - MX_OBJS += joystick.o - endif -endif - -ifeq ($(CONFIG_JOY_AMIGA),y) -O_OBJS += joy-amiga.o -else - ifeq ($(CONFIG_JOY_AMIGA),m) - M_OBJS += joy-amiga.o - endif -endif - -ifeq ($(CONFIG_JOY_ANALOG),y) -O_OBJS += joy-analog.o -else - ifeq ($(CONFIG_JOY_ANALOG),m) - M_OBJS += joy-analog.o - endif -endif - -ifeq ($(CONFIG_JOY_ASSASSIN),y) -O_OBJS += joy-assassin.o -else - ifeq ($(CONFIG_JOY_ASSASSIN),m) - M_OBJS += joy-assassin.o - endif -endif - -ifeq ($(CONFIG_JOY_CONSOLE),y) -O_OBJS += joy-console.o -else - ifeq ($(CONFIG_JOY_CONSOLE),m) - M_OBJS += joy-console.o - endif -endif - -ifeq ($(CONFIG_JOY_CREATIVE),y) -O_OBJS += joy-creative.o -else - ifeq ($(CONFIG_JOY_CREATIVE),m) - M_OBJS += joy-creative.o - endif -endif - -ifeq ($(CONFIG_JOY_DB9),y) -O_OBJS += joy-db9.o -else - ifeq ($(CONFIG_JOY_DB9),m) - M_OBJS += joy-db9.o - endif -endif - -ifeq ($(CONFIG_JOY_GRAVIS),y) -O_OBJS += joy-gravis.o -else - ifeq ($(CONFIG_JOY_GRAVIS),m) - M_OBJS += joy-gravis.o - endif -endif - -ifeq ($(CONFIG_JOY_LIGHTNING),y) -O_OBJS += joy-lightning.o -else - ifeq ($(CONFIG_JOY_LIGHTNING),m) - M_OBJS += joy-lightning.o - endif -endif - -ifeq ($(CONFIG_JOY_LOGITECH),y) -O_OBJS += joy-logitech.o -else - ifeq ($(CONFIG_JOY_LOGITECH),m) - M_OBJS += joy-logitech.o - endif -endif - -ifeq ($(CONFIG_JOY_MAGELLAN),y) -O_OBJS += joy-magellan.o -else - ifeq ($(CONFIG_JOY_MAGELLAN),m) - M_OBJS += joy-magellan.o - endif -endif - -ifeq ($(CONFIG_JOY_PCI),y) -O_OBJS += joy-pci.o -else - ifeq ($(CONFIG_JOY_PCI),m) - M_OBJS += joy-pci.o - endif -endif - -ifeq ($(CONFIG_JOY_SIDEWINDER),y) -O_OBJS += joy-sidewinder.o -else - ifeq ($(CONFIG_JOY_SIDEWINDER),m) - M_OBJS += joy-sidewinder.o - endif -endif - -ifeq ($(CONFIG_JOY_SPACEORB),y) -O_OBJS += joy-spaceorb.o -else - ifeq ($(CONFIG_JOY_SPACEORB),m) - M_OBJS += joy-spaceorb.o - endif -endif - -ifeq ($(CONFIG_JOY_SPACEBALL),y) -O_OBJS += joy-spaceball.o -else - ifeq ($(CONFIG_JOY_SPACEBALL),m) - M_OBJS += joy-spaceball.o - endif -endif - -ifeq ($(CONFIG_JOY_THRUSTMASTER),y) -O_OBJS += joy-thrustmaster.o -else - ifeq ($(CONFIG_JOY_THRUSTMASTER),m) - M_OBJS += joy-thrustmaster.o - endif -endif - -ifeq ($(CONFIG_JOY_TURBOGRAFX),y) -O_OBJS += joy-turbografx.o -else - ifeq ($(CONFIG_JOY_TURBOGRAFX),m) - M_OBJS += joy-turbografx.o - endif -endif - -ifeq ($(CONFIG_JOY_WARRIOR),y) -O_OBJS += joy-warrior.o -else - ifeq ($(CONFIG_JOY_WARRIOR),m) - M_OBJS += joy-warrior.o - endif -endif +# Subdirs. + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +# The target object and module list name. + +O_TARGET := js.o +M_OBJS := +O_OBJS := +#MOD_LIST_NAME := INPUT_MODULES + +# Objects that export symbols. + +export-objs := serio.o gameport.o + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_SERPORT) += serport.o serio.o + +obj-$(CONFIG_INPUT_NS558) += ns558.o gameport.o +obj-$(CONFIG_INPUT_LIGHTNING) += lightning.o gameport.o +obj-$(CONFIG_INPUT_PCIGAME) += pcigame.o gameport.o + +obj-$(CONFIG_INPUT_WARRIOR) += warrior.o serio.o +obj-$(CONFIG_INPUT_MAGELLAN) += magellan.o serio.o +obj-$(CONFIG_INPUT_SPACEORB) += spaceorb.o serio.o +obj-$(CONFIG_INPUT_SPACEBALL) += spaceball.o serio.o +obj-$(CONFIG_INPUT_IFORCE_232) += serio.o + +obj-$(CONFIG_INPUT_ANALOG) += analog.o gameport.o +obj-$(CONFIG_INPUT_A3D) += a3d.o gameport.o +obj-$(CONFIG_INPUT_ADI) += adi.o gameport.o +obj-$(CONFIG_INPUT_COBRA) += cobra.o gameport.o +obj-$(CONFIG_INPUT_GF2K) += gf2k.o gameport.o +obj-$(CONFIG_INPUT_GRIP) += grip.o gameport.o +obj-$(CONFIG_INPUT_INTERACT) += interact.o gameport.o +obj-$(CONFIG_INPUT_TMDC) += tmdc.o gameport.o +obj-$(CONFIG_INPUT_SIDEWINDER) += sidewinder.o gameport.o + +obj-$(CONFIG_INPUT_DB9) += db9.o +obj-$(CONFIG_INPUT_GAMECON) += gamecon.o +obj-$(CONFIG_INPUT_TURBOGRAFX) += turbografx.o + +obj-$(CONFIG_INPUT_AMIJOY) += amijoy.o + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +# The global Rules.make. include $(TOPDIR)/Rules.make diff --git a/drivers/char/joystick/a3d.c b/drivers/char/joystick/a3d.c new file mode 100644 index 000000000..bd17f8de2 --- /dev/null +++ b/drivers/char/joystick/a3d.c @@ -0,0 +1,387 @@ +/* + * $Id: a3d.c,v 1.10 2000/05/29 11:19:50 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * FP-Gaming Assasin 3D joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define A3D_MAX_START 400 /* 400 us */ +#define A3D_MAX_STROBE 60 /* 40 us */ +#define A3D_DELAY_READ 3 /* 3 ms */ +#define A3D_MAX_LENGTH 40 /* 40*3 bits */ +#define A3D_REFRESH_TIME HZ/50 /* 20 ms */ + +#define A3D_MODE_A3D 1 /* Assassin 3D */ +#define A3D_MODE_PAN 2 /* Panther */ +#define A3D_MODE_OEM 3 /* Panther OEM version */ +#define A3D_MODE_PXL 4 /* Panther XL */ + +char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther", + "MadCatz Panther XL", "MadCatz Panther XL w/ rudder" }; + +struct a3d { + struct gameport *gameport; + struct gameport adc; + struct input_dev dev; + struct timer_list timer; + int axes[4]; + int buttons; + int mode; + int length; + int used; + int reads; + int bads; +}; + +/* + * a3d_read_packet() reads an Assassin 3D packet. + */ + +static int a3d_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + t = gameport_time(gameport, A3D_MAX_START); + s = gameport_time(gameport, A3D_MAX_STROBE); + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (~v & u & 0x10) { + data[i++] = v >> 5; + t = s; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * a3d_csum() computes checksum of triplet packet + */ + +static int a3d_csum(char *data, int count) +{ + int i, csum = 0; + for (i = 0; i < count - 2; i++) csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +static void a3d_read(struct a3d *a3d, unsigned char *data) +{ + struct input_dev *dev = &a3d->dev; + + switch (a3d->mode) { + + case A3D_MODE_A3D: + case A3D_MODE_OEM: + case A3D_MODE_PAN: + + input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + + a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + a3d->buttons = ((data[3] << 3) | data[4]) & 0xf; + + return; + + case A3D_MODE_PXL: + + input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + input_report_key(dev, BTN_SIDE, data[7] & 2); + input_report_key(dev, BTN_EXTRA, data[7] & 4); + + input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128); + input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128); + input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128); + input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128); + + input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1)); + + input_report_key(dev, BTN_TRIGGER, data[8] & 1); + input_report_key(dev, BTN_THUMB, data[8] & 2); + input_report_key(dev, BTN_TOP, data[8] & 4); + input_report_key(dev, BTN_PINKIE, data[7] & 1); + + return; + } +} + + +/* + * a3d_timer() reads and analyzes A3D joystick data. + */ + +static void a3d_timer(unsigned long private) +{ + struct a3d *a3d = (void *) private; + unsigned char data[A3D_MAX_LENGTH]; + a3d->reads++; + if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length + || data[0] != a3d->mode || a3d_csum(data, a3d->length)) + a3d->bads++; else a3d_read(a3d, data); + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); +} + +/* + * a3d_adc_cooked_read() copies the acis and button data to the + * callers arrays. It could do the read itself, but the caller could + * call this more than 50 times a second, which would use too much CPU. + */ + +int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct a3d *a3d = gameport->driver; + int i; + for (i = 0; i < 4; i++) + axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1; + *buttons = a3d->buttons; + return 0; +} + +/* + * a3d_adc_open() is the gameport open routine. It refuses to serve + * any but cooked data. + */ + +int a3d_adc_open(struct gameport *gameport, int mode) +{ + struct a3d *a3d = gameport->driver; + if (mode != GAMEPORT_MODE_COOKED) + return -1; + if (!a3d->used++) + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); + return 0; +} + +/* + * a3d_adc_close() is a callback from the input close routine. + */ + +static void a3d_adc_close(struct gameport *gameport) +{ + struct a3d *a3d = gameport->driver; + if (!--a3d->used) + del_timer(&a3d->timer); +} + +/* + * a3d_open() is a callback from the input open routine. + */ + +static int a3d_open(struct input_dev *dev) +{ + struct a3d *a3d = dev->private; + if (!a3d->used++) + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); + return 0; +} + +/* + * a3d_close() is a callback from the input close routine. + */ + +static void a3d_close(struct input_dev *dev) +{ + struct a3d *a3d = dev->private; + if (!--a3d->used) + del_timer(&a3d->timer); +} + +/* + * a3d_connect() probes for A3D joysticks. + */ + +static void a3d_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct a3d *a3d; + unsigned char data[A3D_MAX_LENGTH]; + int i; + + if (!(a3d = kmalloc(sizeof(struct a3d), GFP_KERNEL))) + return; + memset(a3d, 0, sizeof(struct a3d)); + + gameport->private = a3d; + + a3d->gameport = gameport; + init_timer(&a3d->timer); + a3d->timer.data = (long) a3d; + a3d->timer.function = a3d_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data); + + if (!i || a3d_csum(data, i)) + goto fail2; + + a3d->mode = data[0]; + + if (!a3d->mode || a3d->mode > 5) { + printk(KERN_WARNING "a3d.c: Unknown A3D device detected " + "(gameport%d, id=%d), contact <vojtech@suse.cz>\n", gameport->number, a3d->mode); + goto fail2; + } + + + if (a3d->mode == A3D_MODE_PXL) { + + int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER }; + + a3d->length = 33; + + a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); + a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); + a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER) + | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y); + + a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE) + | BIT(BTN_SIDE) | BIT(BTN_EXTRA); + + a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE); + + a3d_read(a3d, data); + + for (i = 0; i < 4; i++) { + if (i < 2) { + a3d->dev.absmin[axes[i]] = 48; + a3d->dev.absmax[axes[i]] = a3d->dev.abs[axes[i]] * 2 - 48; + a3d->dev.absflat[axes[i]] = 8; + } else { + a3d->dev.absmin[axes[i]] = 2; + a3d->dev.absmax[axes[i]] = 253; + } + a3d->dev.absmin[ABS_HAT0X + i] = -1; + a3d->dev.absmax[ABS_HAT0X + i] = 1; + } + + } else { + a3d->length = 29; + + a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL); + a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); + a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE); + + a3d->adc.driver = a3d; + a3d->adc.open = a3d_adc_open; + a3d->adc.close = a3d_adc_close; + a3d->adc.cooked_read = a3d_adc_cooked_read; + a3d->adc.fuzz = 1; + a3d->adc.type = GAMEPORT_EXT; + + a3d_read(a3d, data); + + gameport_register_port(&a3d->adc); + printk(KERN_INFO "gameport%d: %s on gameport%d.0\n", + a3d->adc.number, a3d_names[a3d->mode], gameport->number); + } + + a3d->dev.private = a3d; + a3d->dev.open = a3d_open; + a3d->dev.close = a3d_close; + + a3d->dev.name = a3d_names[a3d->mode]; + a3d->dev.idbus = BUS_GAMEPORT; + a3d->dev.idvendor = GAMEPORT_ID_VENDOR_MADCATZ; + a3d->dev.idproduct = a3d->mode; + a3d->dev.idversion = 0x0100; + + input_register_device(&a3d->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + a3d->dev.number, a3d_names[a3d->mode], gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(a3d); +} + +static void a3d_disconnect(struct gameport *gameport) +{ + + struct a3d *a3d = gameport->private; + input_unregister_device(&a3d->dev); + if (a3d->mode < A3D_MODE_PXL) + gameport_unregister_port(&a3d->adc); + gameport_close(gameport); + kfree(a3d); +} + +static struct gameport_dev a3d_dev = { + connect: a3d_connect, + disconnect: a3d_disconnect, +}; + +int __init a3d_init(void) +{ + gameport_register_device(&a3d_dev); + return 0; +} + +void __exit a3d_exit(void) +{ + gameport_unregister_device(&a3d_dev); +} + +module_init(a3d_init); +module_exit(a3d_exit); diff --git a/drivers/char/joystick/adi.c b/drivers/char/joystick/adi.c new file mode 100644 index 000000000..3195fce03 --- /dev/null +++ b/drivers/char/joystick/adi.c @@ -0,0 +1,554 @@ +/* + * $Id: adi.c,v 1.12 2000/06/03 20:18:52 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Logitech ADI joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/gameport.h> +#include <linux/init.h> + +/* + * Times, array sizes, flags, ids. + */ + +#define ADI_MAX_START 200 /* Trigger to packet timeout [200us] */ +#define ADI_MAX_STROBE 40 /* Single bit timeout [40us] */ +#define ADI_REFRESH_TIME HZ/50 /* How often to poll the joystick [20 ms] */ +#define ADI_INIT_DELAY 10 /* Delay after init packet [10ms] */ +#define ADI_DATA_DELAY 4 /* Delay after data packet [4ms] */ + +#define ADI_MAX_LENGTH 256 +#define ADI_MIN_LENGTH 8 +#define ADI_MIN_LEN_LENGTH 10 +#define ADI_MIN_ID_LENGTH 66 +#define ADI_MAX_NAME_LENGTH 48 +#define ADI_MAX_CNAME_LENGTH 16 + +#define ADI_FLAG_HAT 0x04 +#define ADI_FLAG_10BIT 0x08 + +#define ADI_ID_TPD 0x01 +#define ADI_ID_WGP 0x06 +#define ADI_ID_WGPE 0x08 +#define ADI_ID_MAX 0x0a + +/* + * Names, buttons, axes ... + */ + +static char *adi_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", + "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", + "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", + "WingMan GamePad USB", "Unknown Device %#x" }; + +static char adi_wmgpe_abs[] = { ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y }; +static char adi_wmi_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static char adi_wmed3d_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y }; +static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; + +static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }; +static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; +static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 }; +static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; + +static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs, + adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs }; + +static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key, + adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key }; + +/* + * Hat to axis conversion arrays. + */ + +static struct { + int x; + int y; +} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +/* + * Per-port information. + */ + +struct adi { + struct input_dev dev; + int length; + int ret; + int idx; + unsigned char id; + char buttons; + char axes10; + char axes8; + signed char pad; + char hats; + char *abs; + short *key; + char name[ADI_MAX_NAME_LENGTH]; + char cname[ADI_MAX_CNAME_LENGTH]; + unsigned char data[ADI_MAX_LENGTH]; +}; + +struct adi_port { + struct gameport *gameport; + struct timer_list timer; + struct adi adi[2]; + int bad; + int reads; + int used; +}; + +/* + * adi_read_packet() reads a Logitech ADI packet. + */ + +static void adi_read_packet(struct adi_port *port) +{ + struct adi *adi = port->adi; + struct gameport *gameport = port->gameport; + unsigned char u, v, w, x, z; + int t[2], s[2], i; + unsigned long flags; + + for (i = 0; i < 2; i++) { + adi[i].ret = -1; + t[i] = gameport_time(gameport, ADI_MAX_START); + s[i] = 0; + } + + __save_flags(flags); + __cli(); + + gameport_trigger(gameport); + v = z = gameport_read(gameport); + + do { + u = v; + w = u ^ (v = x = gameport_read(gameport)); + for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { + t[i]--; + if ((w & 0x30) && s[i]) { + if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) { + adi[i].data[++adi[i].ret] = w; + t[i] = gameport_time(gameport, ADI_MAX_STROBE); + } else t[i] = 0; + } else if (!(x & 0x30)) s[i] = 1; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + return; +} + +/* + * adi_move_bits() detects a possible 2-stream mode, and moves + * the bits accordingly. + */ + +static void adi_move_bits(struct adi_port *port, int length) +{ + int i; + struct adi *adi = port->adi; + + adi[0].idx = adi[1].idx = 0; + + if (adi[0].ret <= 0 || adi[1].ret <= 0) return; + if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return; + + for (i = 1; i <= adi[1].ret; i++) + adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i]; + + adi[0].ret += adi[1].ret; + adi[1].ret = -1; +} + +/* + * adi_get_bits() gathers bits from the data packet. + */ + +static inline int adi_get_bits(struct adi *adi, int count) +{ + int bits = 0; + int i; + if ((adi->idx += count) > adi->ret) return 0; + for (i = 0; i < count; i++) + bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i; + return bits; +} + +/* + * adi_decode() decodes Logitech joystick data into input events. + */ + +static int adi_decode(struct adi *adi) +{ + struct input_dev *dev = &adi->dev; + char *abs = adi->abs; + short *key = adi->key; + int i, t; + + if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4))) + return -1; + + for (i = 0; i < adi->axes10; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 10)); + + for (i = 0; i < adi->axes8; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 8)); + + for (i = 0; i < adi->buttons && i < 63; i++) { + if (i == adi->pad) { + t = adi_get_bits(adi, 4); + input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t & 1)); + input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1)); + } + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + } + + for (i = 0; i < adi->hats; i++) { + if ((t = adi_get_bits(adi, 4)) > 8) t = 0; + input_report_abs(dev, *abs++, adi_hat_to_axis[t].x); + input_report_abs(dev, *abs++, adi_hat_to_axis[t].y); + } + + for (i = 63; i < adi->buttons; i++) + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + + return 0; +} + +/* + * adi_read() reads the data packet and decodes it. + */ + +static int adi_read(struct adi_port *port) +{ + int i; + int result = 0; + + adi_read_packet(port); + adi_move_bits(port, port->adi[0].length); + + for (i = 0; i < 2; i++) + if (port->adi[i].length) + result |= adi_decode(port->adi + i); + + return result; +} + +/* + * adi_timer() repeatedly polls the Logitech joysticks. + */ + +static void adi_timer(unsigned long data) +{ + struct adi_port *port = (void *) data; + port->bad -= adi_read(port); + port->reads++; + mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME); +} + +/* + * adi_open() is a callback from the input open routine. + */ + +static int adi_open(struct input_dev *dev) +{ + struct adi_port *port = dev->private; + if (!port->used++) + mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME); + return 0; +} + +/* + * adi_close() is a callback from the input close routine. + */ + +static void adi_close(struct input_dev *dev) +{ + struct adi_port *port = dev->private; + if (!--port->used) + del_timer(&port->timer); +} + +/* + * adi_init_digital() sends a trigger & delay sequence + * to reset and initialize a Logitech joystick into digital mode. + */ + +static void adi_init_digital(struct gameport *gameport) +{ + int seq[] = { 3, -2, -3, 10, -6, -11, -7, -9, 11, 0 }; + int i; + + for (i = 0; seq[i]; i++) { + gameport_trigger(gameport); + if (seq[i] > 0) wait_ms(seq[i]); + if (seq[i] < 0) mdelay(-seq[i]); + } +} + +static void adi_id_decode(struct adi *adi, struct adi_port *port) +{ + int i, t; + + if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */ + return; + + if (adi->ret < (t = adi_get_bits(adi, 10))) { + printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret); + return; + } + + adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4); + + if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++; + + adi->length = adi_get_bits(adi, 10); + + if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) { + printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length); + adi->length = 0; + return; + } + + adi->axes8 = adi_get_bits(adi, 4); + adi->buttons = adi_get_bits(adi, 6); + + if (adi_get_bits(adi, 6) != 8 && adi->hats) { + printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n"); + adi->length = 0; + return; + } + + adi->buttons += adi_get_bits(adi, 6); + adi->hats += adi_get_bits(adi, 4); + + i = adi_get_bits(adi, 4); + + if (t & ADI_FLAG_10BIT) { + adi->axes10 = adi->axes8 - i; + adi->axes8 = i; + } + + t = adi_get_bits(adi, 4); + + for (i = 0; i < t; i++) + adi->cname[i] = adi_get_bits(adi, 8); + adi->cname[i] = 0; + + if (adi->length != (t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4)) { + printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length); + adi->length = 0; + return; + } + + switch (adi->id) { + case ADI_ID_TPD: + adi->pad = 4; + adi->buttons -= 4; + break; + case ADI_ID_WGP: + adi->pad = 0; + adi->buttons -= 4; + break; + default: + adi->pad = -1; + break; + } +} + +static void adi_init_input(struct adi *adi, struct adi_port *port) +{ + int i, t; + char buf[ADI_MAX_NAME_LENGTH]; + + if (!adi->length) return; + + t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX; + + sprintf(buf, adi_names[t], adi->id); + sprintf(adi->name, "Logitech %s", buf); + + adi->abs = adi_abs[t]; + adi->key = adi_key[t]; + + adi->dev.open = adi_open; + adi->dev.close = adi_close; + + adi->dev.name = adi->name; + adi->dev.idbus = BUS_GAMEPORT; + adi->dev.idvendor = GAMEPORT_ID_VENDOR_LOGITECH; + adi->dev.idproduct = adi->id; + adi->dev.idversion = 0x0100; + + adi->dev.private = port; + adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < adi->axes10 + adi->axes8 + adi->hats * 2; i++) + set_bit(adi->abs[i], &adi->dev.absbit); + + for (i = 0; i < adi->buttons; i++) + set_bit(adi->key[i], &adi->dev.keybit); +} + +static void adi_init_center(struct adi *adi) +{ + int i, t, x; + + if (!adi->length) return; + + for (i = 0; i < adi->axes10 + adi->axes8 + adi->hats * 2; i++) { + + t = adi->abs[i]; + x = adi->dev.abs[t]; + + if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE) { + if (i < adi->axes10) x = 512; else x = 128; + } + + if (i < adi->axes10) { + adi->dev.absmax[t] = x * 2 - 64; + adi->dev.absmin[t] = 64; + adi->dev.absfuzz[t] = 2; + adi->dev.absflat[t] = 16; + continue; + } + + if (i < adi->axes10 + adi->axes8) { + adi->dev.absmax[t] = x * 2 - 48; + adi->dev.absmin[t] = 48; + adi->dev.absfuzz[t] = 1; + adi->dev.absflat[t] = 16; + continue; + } + + adi->dev.absmax[t] = 1; + adi->dev.absmin[t] = -1; + } +} + +/* + * adi_connect() probes for Logitech ADI joysticks. + */ + +static void adi_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct adi_port *port; + int i; + + if (!(port = kmalloc(sizeof(struct adi_port), GFP_KERNEL))) + return; + memset(port, 0, sizeof(struct adi_port)); + + gameport->private = port; + + port->gameport = gameport; + init_timer(&port->timer); + port->timer.data = (long) port; + port->timer.function = adi_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { + kfree(port); + return; + } + + adi_init_digital(gameport); + adi_read_packet(port); + + if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH) + adi_move_bits(port, adi_get_bits(port->adi, 10)); + + for (i = 0; i < 2; i++) { + adi_id_decode(port->adi + i, port); + adi_init_input(port->adi + i, port); + } + + if (!port->adi[0].length && !port->adi[1].length) { + gameport_close(gameport); + kfree(port); + return; + } + + wait_ms(ADI_INIT_DELAY); + if (adi_read(port)) { + wait_ms(ADI_DATA_DELAY); + adi_read(port); + } + + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) { + adi_init_center(port->adi + i); + input_register_device(&port->adi[i].dev); + printk(KERN_INFO "input%d: %s [%s] on gameport%d.%d\n", + port->adi[i].dev.number, port->adi[i].name, port->adi[i].cname, gameport->number, i); + } +} + +static void adi_disconnect(struct gameport *gameport) +{ + int i; + + struct adi_port *port = gameport->private; + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) + input_unregister_device(&port->adi[i].dev); + gameport_close(gameport); + kfree(port); +} + +/* + * The gameport device structure. + */ + +static struct gameport_dev adi_dev = { + connect: adi_connect, + disconnect: adi_disconnect, +}; + +int __init adi_init(void) +{ + gameport_register_device(&adi_dev); + return 0; +} + +void __exit adi_exit(void) +{ + gameport_unregister_device(&adi_dev); +} + +module_init(adi_init); +module_exit(adi_exit); diff --git a/drivers/char/joystick/amijoy.c b/drivers/char/joystick/amijoy.c new file mode 100644 index 000000000..f17aeda24 --- /dev/null +++ b/drivers/char/joystick/amijoy.c @@ -0,0 +1,149 @@ +/* + * $Id: amijoy.c,v 1.4 2000/05/29 10:39:54 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Driver for Amiga joysticks for Linux/m68k + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> + +#include <asm/system.h> +#include <asm/amigahw.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(amijoy, "1-2i"); + +static int amijoy[2] = { 0, 1 }; +static int amijoy_used[2] = { 0, 0 }; +static struct input_dev amijoy_dev[2]; + +static char *amijoy_name = "Amiga joystick"; + +static void amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp) +{ + int i, data = 0, button = 0; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + + switch (i) { + case 0: data = ~custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break; + case 1: data = ~custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break; + } + + input_report_key(amijoy_dev + i, BTN_TRIGGER, button); + + input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1); + data = ~(data ^ (data << 1)); + input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1); + } +} + +static int amijoy_open(struct input_dev *dev) +{ + int *used = dev->private; + + if ((*used)++) + return 0; + + if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", NULL)) { + amijoy_used--; + printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", amijoy_irq); + return -EBUSY; + } + + return 0; +} + +static void amijoy_close(struct input_dev *dev) +{ + int *used = dev->private; + + if (!--(*port->used)) + free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); +} + +static int __init amijoy_setup(char *str) +{ + int i; + int ints[4] + str = get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) amijoy[i] = ints[i+1]; + return 1; +} +__setup("amijoy=", amijoy_setup); + +static int __init amijoy_init(void) +{ + int i, j; + + init_timer(amijoy_timer); + port->timer.function = amijoy_timer; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + + amijoy_dev[i].open = amijoy_open; + amijoy_dev[i].close = amijoy_close; + amijoy_dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + amijoy_dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + amijoy_dev[i].keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + for (j = 0; j < 2; j++) + amijoy_dev[i].absmin[ABS_X + j] = -1; + amijoy_dev[i].absmax[ABS_X + j] = 1; + } + + amijoy->dev[i].name = amijoy_name; + amijoy->dev[i].idbus = BUS_AMIGA; + amijoy->dev[i].idvendor = 0x0001; + amijoy->dev[i].idproduct = 0x0003; + amijoy->dev[i].version = 0x0100; + + amijoy_dev[i].private = amijoy_used + i; + + input_register_device(amijoy_dev + i); + printk(KERN_INFO "input%d: %s at joy%ddat\n", amijoy_dev[i].number, amijoy_name, i); + } +} + +static void _exit amijoy_exit(void) +{ + int i; + + for (i = 0; i < 2; i++) + if (amijoy[i]) + input_unregister_device(amijoy_dev + i); +} + +module_init(amijoy_init); +module_exit(amijoy_exit); diff --git a/drivers/char/joystick/analog.c b/drivers/char/joystick/analog.c new file mode 100644 index 000000000..6514bef9b --- /dev/null +++ b/drivers/char/joystick/analog.c @@ -0,0 +1,758 @@ +/* + * $Id: analog.c,v 1.52 2000/06/07 13:07:06 vojtech Exp $ + * + * Copyright (c) 1996-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Analog joystick and gamepad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +/* + * Option parsing. + */ + +MODULE_PARM(js,"1-16s"); + +#define ANALOG_PORTS 16 + +static char *js[ANALOG_PORTS]; +static int analog_options[ANALOG_PORTS]; + +/* + * Times, feature definitions. + */ + +#define ANALOG_RUDDER 0x00004 +#define ANALOG_THROTTLE 0x00008 +#define ANALOG_AXES_STD 0x0000f +#define ANALOG_BTNS_STD 0x000f0 + +#define ANALOG_BTNS_CHF 0x00100 +#define ANALOG_HAT1_CHF 0x00200 +#define ANALOG_HAT2_CHF 0x00400 +#define ANALOG_HAT_FCS 0x00800 +#define ANALOG_HATS_ALL 0x00e00 +#define ANALOG_BTN_TL 0x01000 +#define ANALOG_BTN_TR 0x02000 +#define ANALOG_BTN_TL2 0x04000 +#define ANALOG_BTN_TR2 0x08000 +#define ANALOG_BTNS_TLR 0x03000 +#define ANALOG_BTNS_TLR2 0x0c000 +#define ANALOG_BTNS_GAMEPAD 0x0f000 + +#define ANALOG_HBTN_CHF 0x10000 +#define ANALOG_ANY_CHF 0x10700 +#define ANALOG_SAITEK 0x20000 +#define ANALOG_EXTENSIONS 0x7ff00 +#define ANALOG_GAMEPAD 0x80000 + +#define ANALOG_MAX_TIME 3 /* 3 ms */ +#define ANALOG_LOOP_TIME 2000 /* 2 * loop */ +#define ANALOG_REFRESH_TIME HZ/100 /* 10 ms */ +#define ANALOG_SAITEK_DELAY 200 /* 200 us */ +#define ANALOG_SAITEK_TIME 2000 /* 2000 us */ +#define ANALOG_AXIS_TIME 2 /* 2 * refresh */ +#define ANALOG_INIT_RETRIES 8 /* 8 times */ +#define ANALOG_FUZZ_BITS 2 /* 2 bit more */ +#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */ + +#define ANALOG_MAX_NAME_LENGTH 128 + +static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE }; +static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR }; +static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS }; +static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE }; +static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, + BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 }; + +static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 }; + +struct analog { + struct input_dev dev; + int mask; + short *buttons; + char name[ANALOG_MAX_NAME_LENGTH]; +}; + +struct analog_port { + struct gameport *gameport; + struct timer_list timer; + struct analog analog[2]; + unsigned char mask; + char saitek; + char cooked; + int bads; + int reads; + int speed; + int loop; + int fuzz; + int axes[4]; + int buttons; + int initial[4]; + int used; + int axtime; +}; + +/* + * Time macros. + */ + +#ifdef __i386__ +#ifdef CONFIG_X86_TSC +#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "TSC" +#else +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((x)-(y)+((x)<(y)?1193180L/HZ:0)) +#define TIME_NAME "PIT" +#endif +#elif __alpha__ +#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "PCC" +#endif + +#ifndef GET_TIME +#define FAKE_TIME +static unsigned long analog_faketime = 0; +#define GET_TIME(x) do { x = analog_faketime++; } while(0) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "Unreliable" +#endif + +/* + * analog_decode() decodes analog joystick data and reports input events. + */ + +static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons) +{ + struct input_dev *dev = &analog->dev; + int i, j; + + if (analog->mask & ANALOG_HAT_FCS) + for (i = 0; i < 4; i++) + if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) { + buttons |= 1 << (i + 14); + break; + } + + for (i = j = 0; i < 6; i++) + if (analog->mask & (0x10 << i)) + input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1); + + if (analog->mask & ANALOG_BTN_TL) + input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1)); + if (analog->mask & ANALOG_BTN_TR) + input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1)); + if (analog->mask & ANALOG_BTN_TL2) + input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1))); + if (analog->mask & ANALOG_BTN_TR2) + input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1))); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) + input_report_abs(dev, analog_axes[j++], axes[i]); + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) { + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1)); + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1)); + } +} + +/* + * analog_cooked_read() reads analog joystick data. + */ + +static int analog_cooked_read(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int time[4], start, loop, now, loopout, timeout; + unsigned char data[4], this, last; + unsigned long flags; + int i, j; + + loopout = (ANALOG_LOOP_TIME * port->loop) / 1000; + timeout = ANALOG_MAX_TIME * port->speed; + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + GET_TIME(now); + __restore_flags(flags); + + start = now; + this = port->mask; + i = 0; + + do { + loop = now; + last = this; + + __cli(); + this = gameport_read(gameport) & port->mask; + GET_TIME(now); + __restore_flags(flags); + + if ((last ^ this) && (DELTA(loop, now) < loopout)) { + data[i] = last ^ this; + time[i] = now; + i++; + } + + } while (this && (i < 4) && (DELTA(start, now) < timeout)); + + this <<= 4; + + for (--i; i >= 0; i--) { + this |= data[i]; + for (j = 0; j < 4; j++) + if (data[i] & (1 << j)) + port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; + } + + return -(this != port->mask); +} + +static int analog_button_read(struct analog_port *port, char saitek, char chf) +{ + unsigned char u; + int t = 1, i = 0; + int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME); + + u = gameport_read(port->gameport); + + if (!chf) { + port->buttons = (~u >> 4) & 0xf; + return 0; + } + + port->buttons = 0; + + while ((~u & 0xf0) && (i < 16) && t) { + port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf]; + if (!saitek) return 0; + udelay(ANALOG_SAITEK_DELAY); + t = strobe; + gameport_trigger(port->gameport); + while (((u = gameport_read(port->gameport)) & port->mask) && t) t--; + i++; + } + + return -(!t || (i == 16)); +} + +/* + * analog_timer() repeatedly polls the Analog joysticks. + */ + +static void analog_timer(unsigned long data) +{ + struct analog_port *port = (void *) data; + int i; + + char saitek = !!(port->analog[0].mask & ANALOG_SAITEK); + char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF); + + if (port->cooked) { + port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons); + if (chf) + port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0; + port->reads++; + } else { + if (!port->axtime--) { + port->bads -= analog_cooked_read(port); + port->bads -= analog_button_read(port, saitek, chf); + port->reads++; + port->axtime = ANALOG_AXIS_TIME - 1; + } else { + if (!saitek) + analog_button_read(port, saitek, chf); + } + } + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + analog_decode(port->analog + i, port->axes, port->initial, port->buttons); + + mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); +} + +/* + * analog_open() is a callback from the input open routine. + */ + +static int analog_open(struct input_dev *dev) +{ + struct analog_port *port = dev->private; + if (!port->used++) + mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); + return 0; +} + +/* + * analog_close() is a callback from the input close routine. + */ + +static void analog_close(struct input_dev *dev) +{ + struct analog_port *port = dev->private; + if (!--port->used) + del_timer(&port->timer); +} + +/* + * analog_calibrate_timer() calibrates the timer and computes loop + * and timeout values for a joystick port. + */ + +static void analog_calibrate_timer(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int i, t, tx, t1, t2, t3; + unsigned long flags; + + save_flags(flags); + cli(); + GET_TIME(t1); +#ifdef FAKE_TIME + analog_faketime += 830; +#endif + udelay(1000); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + + port->speed = DELTA(t1, t2) - DELTA(t2, t3); + + tx = ~0; + + for (i = 0; i < 50; i++) { + save_flags(flags); + cli(); + GET_TIME(t1); + for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } + GET_TIME(t3); + restore_flags(flags); + udelay(i); + t = DELTA(t1, t2) - DELTA(t2, t3); + if (t < tx) tx = t; + } + + port->loop = tx / 50; +} + +/* + * analog_name() constructs a name for an analog joystick. + */ + +static void analog_name(struct analog *analog) +{ + sprintf(analog->name, "Analog %d-axis %d-button", + hweight8(analog->mask & ANALOG_AXES_STD), + hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 + + hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4); + + if (analog->mask & ANALOG_HATS_ALL) + sprintf(analog->name, "%s %d-hat", + analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); + + if (analog->mask & ANALOG_HAT_FCS) + strcat(analog->name, " FCS"); + if (analog->mask & ANALOG_ANY_CHF) + strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF"); + + strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick"); +} + +/* + * analog_init_device() + */ + +static void analog_init_device(struct analog_port *port, struct analog *analog, int index) +{ + int i, j, t, v, w, x, y, z; + + analog_name(analog); + + analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn; + + analog->dev.name = analog->name; + analog->dev.idbus = BUS_GAMEPORT; + analog->dev.idvendor = GAMEPORT_ID_VENDOR_ANALOG; + analog->dev.idproduct = analog->mask >> 4; + analog->dev.idversion = 0x0100; + + analog->dev.open = analog_open; + analog->dev.close = analog_close; + analog->dev.private = port; + analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) { + + t = analog_axes[j]; + x = port->axes[i]; + y = (port->axes[0] + port->axes[1]) >> 1; + z = y - port->axes[i]; + z = z > 0 ? z : -z; + v = (x >> 3); + w = (x >> 3); + + set_bit(t, analog->dev.absbit); + + if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3))) + x = y; + + if (analog->mask & ANALOG_SAITEK) { + if (i == 2) x = port->axes[i]; + v = x - (x >> 2); + w = (x >> 4); + } + + analog->dev.absmax[t] = (x << 1) - v; + analog->dev.absmin[t] = v; + analog->dev.absfuzz[t] = port->fuzz; + analog->dev.absflat[t] = w; + + j++; + } + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) + for (x = 0; x < 2; x++) { + t = analog_hats[j++]; + set_bit(t, analog->dev.absbit); + analog->dev.absmax[t] = 1; + analog->dev.absmin[t] = -1; + } + + for (i = j = 0; i < 4; i++) + if (analog->mask & (0x10 << i)) + set_bit(analog->buttons[j++], analog->dev.keybit); + + if (analog->mask & ANALOG_BTNS_CHF) + for (i = 0; i < 2; i++) + set_bit(analog->buttons[j++], analog->dev.keybit); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + set_bit(analog->buttons[j++], analog->dev.keybit); + + for (i = 0; i < 4; i++) + if (analog->mask & (ANALOG_BTN_TL << i)) + set_bit(analog_pads[i], analog->dev.keybit); + + analog_decode(analog, port->axes, port->initial, port->buttons); + + input_register_device(&analog->dev); + + printk(KERN_INFO "input%d: %s at gameport%d.%d", + analog->dev.number, analog->name, port->gameport->number, index); + + if (port->cooked) + printk(" [ADC port]\n"); + else + printk(" ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", + port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed, + port->speed > 10000 ? "M" : "k", (port->loop * 1000000) / port->speed); +} + +/* + * analog_init_devices() sets up device-specific values and registers the input devices. + */ + +static int analog_init_masks(struct analog_port *port) +{ + int i; + struct analog *analog = port->analog; + int max[4]; + + if (!port->mask) + return -1; + + if ((port->mask & 3) != 3 && port->mask != 0xc) { + printk(KERN_WARNING "analog.c: Unknown joystick device found " + "(data=%#x, gameport%d), probably not analog joystick.\n", + port->mask, port->gameport->number); + return -1; + } + + i = port->gameport->number < ANALOG_PORTS ? analog_options[port->gameport->number] : 0xff; + + analog[0].mask = i & 0xfffff; + + analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD) + | port->mask | ((port->mask << 8) & ANALOG_HAT_FCS) + | ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2); + + analog[0].mask &= ~(ANALOG_HAT2_CHF) + | ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2) + | ((~analog[0].mask & ANALOG_HAT_FCS) >> 8) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 2) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 4); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER) + | (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10) + & ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12)); + + analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000); + + analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD + : (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD); + + if (port->cooked) { + + for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1; + + if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1; + if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1; + if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1; + + gameport_calibrate(port->gameport, port->axes, max); + } + + for (i = 0; i < 4; i++) + port->initial[i] = port->axes[i]; + + return -!(analog[0].mask || analog[1].mask); +} + +static int analog_init_port(struct gameport *gameport, struct gameport_dev *dev, struct analog_port *port) +{ + int i, t, u, v; + + gameport->private = port; + port->gameport = gameport; + init_timer(&port->timer); + port->timer.data = (long) port; + port->timer.function = analog_timer; + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { + + analog_calibrate_timer(port); + + gameport_trigger(gameport); + t = gameport_read(gameport); + wait_ms(ANALOG_MAX_TIME); + port->mask = (gameport_read(gameport) ^ t) & t & 0xf; + port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS; + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) { + if (!analog_cooked_read(port)) break; + wait_ms(ANALOG_MAX_TIME); + } + + u = v = 0; + + wait_ms(ANALOG_MAX_TIME); + t = gameport_time(gameport, ANALOG_MAX_TIME * 1000); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (u < t)) u++; + udelay(ANALOG_SAITEK_DELAY); + t = gameport_time(gameport, ANALOG_SAITEK_TIME); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (v < t)) v++; + + if (v < (u >> 1) && port->gameport->number < ANALOG_PORTS) { + analog_options[port->gameport->number] |= + ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF; + return 0; + } + + gameport_close(gameport); + } + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) { + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) + if (!gameport_cooked_read(gameport, port->axes, &port->buttons)) + break; + for (i = 0; i < 4; i++) + if (port->axes[i] != -1) port->mask |= 1 << i; + + port->fuzz = gameport->fuzz; + port->cooked = 1; + return 0; + } + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + return 0; + + return -1; +} + +static void analog_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct analog_port *port; + int i; + + if (!(port = kmalloc(sizeof(struct analog_port), GFP_KERNEL))) + return; + memset(port, 0, sizeof(struct analog_port)); + + if (analog_init_port(gameport, dev, port)) { + kfree(port); + return; + } + + if (analog_init_masks(port)) { + gameport_close(gameport); + kfree(port); + return; + } + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + analog_init_device(port, port->analog + i, i); +} + +static void analog_disconnect(struct gameport *gameport) +{ + int i; + + struct analog_port *port = gameport->private; + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + input_unregister_device(&port->analog[i].dev); + gameport_close(gameport); + printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on gameport%d failed\n", + port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0, + port->gameport->number); + kfree(port); +} + +struct analog_types { + char *name; + int value; +}; + +struct analog_types analog_types[] = { + { "none", 0x00000000 }, + { "auto", 0x000000ff }, + { "2btn", 0x0000003f }, + { "y-joy", 0x0cc00033 }, + { "y-pad", 0x8cc80033 }, + { "fcs", 0x000008f7 }, + { "chf", 0x000002ff }, + { "fullchf", 0x000007ff }, + { "gamepad", 0x000830f3 }, + { "gamepad8", 0x0008f0f3 }, + { NULL, 0 } +}; + +static void analog_parse_options(void) +{ + int i, j; + char *end; + + for (i = 0; i < ANALOG_PORTS && js[i]; i++) { + + for (j = 0; analog_types[j].name; j++) + if (!strcmp(analog_types[j].name, js[i])) { + analog_options[i] = analog_types[j].value; + break; + } + if (analog_types[j].name) continue; + + analog_options[i] = simple_strtoul(js[i], &end, 0); + if (end != js[i]) continue; + + analog_options[i] = 0xff; + if (!strlen(js[i])) continue; + + printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]); + } + + for (; i < ANALOG_PORTS; i++) + analog_options[i] = 0xff; +} + +/* + * The gameport device structure. + */ + +static struct gameport_dev analog_dev = { + connect: analog_connect, + disconnect: analog_disconnect, +}; + +#ifndef MODULE +static int __init analog_setup(char *str) +{ + char *s = str; + int i = 0; + + if (!str || !*str) return 0; + + while ((str = s) && (i < ANALOG_PORTS)) { + if ((s = strchr(str,','))) *s++ = 0; + js[i++] = str; + } + + return 1; +} +__setup("js=", analog_setup); +#endif + +int __init analog_init(void) +{ + analog_parse_options(); + gameport_register_device(&analog_dev); + return 0; +} + +void __exit analog_exit(void) +{ + gameport_unregister_device(&analog_dev); +} + +module_init(analog_init); +module_exit(analog_exit); diff --git a/drivers/char/joystick/cobra.c b/drivers/char/joystick/cobra.c new file mode 100644 index 000000000..f059a2ff6 --- /dev/null +++ b/drivers/char/joystick/cobra.c @@ -0,0 +1,250 @@ +/* + * $Id: cobra.c,v 1.10 2000/06/08 10:23:45 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Creative Labd Blaster GamePad Cobra driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ +#define COBRA_REFRESH_TIME HZ/50 /* 20 ms between reads */ +#define COBRA_LENGTH 36 + +static char* cobra_name = "Creative Labs Blaster GamePad Cobra"; + +static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 }; + +struct cobra { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + int used; + int reads; + int bads; + unsigned char exists; +}; + +static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v, w; + __u64 buf[2]; + int r[2], t[2]; + int i, j, ret; + + int strobe = gameport_time(gameport, COBRA_MAX_STROBE); + + for (i = 0; i < 2; i++) { + r[i] = buf[i] = 0; + t[i] = COBRA_MAX_STROBE; + } + + __save_flags(flags); + __cli(); + + u = gameport_read(gameport); + + do { + t[0]--; t[1]--; + v = gameport_read(gameport); + for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) + if (w & 0x30) { + if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) { + buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; + t[i] = strobe; + u = v; + } else t[i] = 0; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + ret = 0; + + for (i = 0; i < 2; i++) { + + if (r[i] != COBRA_LENGTH) continue; + + for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) + buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1)); + + if (j < COBRA_LENGTH) ret |= (1 << i); + + data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) + | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) + | ((buf[i] >> 11) & 0x1f00000); + + } + + return ret; +} + +static void cobra_timer(unsigned long private) +{ + struct cobra *cobra = (void *) private; + struct input_dev *dev; + unsigned int data[2]; + int i, j, r; + + cobra->reads++; + + if ((r = cobra_read_packet(cobra->gameport, data)) != cobra->exists) + cobra->bads++; + + for (i = 0; i < 2; i++) + if (cobra->exists & r & (1 << i)) { + + dev = cobra->dev + i; + + input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1)); + input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1)); + + for (j = 0; cobra_btn[j]; j++) + input_report_key(dev, cobra_btn[j], data[i] & (0x20 << i)); + + } + + mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME); +} + +static int cobra_open(struct input_dev *dev) +{ + struct cobra *cobra = dev->private; + if (!cobra->used++) + mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME); + return 0; +} + +static void cobra_close(struct input_dev *dev) +{ + struct cobra *cobra = dev->private; + if (!--cobra->used) + del_timer(&cobra->timer); +} + +static void cobra_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct cobra *cobra; + unsigned int data[2]; + int i, j; + + if (!(cobra = kmalloc(sizeof(struct cobra), GFP_KERNEL))) + return; + memset(cobra, 0, sizeof(struct cobra)); + + gameport->private = cobra; + + cobra->gameport = gameport; + init_timer(&cobra->timer); + cobra->timer.data = (long) cobra; + cobra->timer.function = cobra_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + cobra->exists = cobra_read_packet(gameport, data); + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & data[i] & 1) { + printk(KERN_WARNING "cobra.c: Device on gameport%d.%d has the Ext bit set. ID is: %d" + " Contact vojtech@suse.cz\n", gameport->number, i, (data[i] >> 2) & 7); + cobra->exists &= ~(1 << i); + } + + if (!cobra->exists) + goto fail2; + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & 1) { + + cobra->dev[i].private = cobra; + cobra->dev[i].open = cobra_open; + cobra->dev[i].close = cobra_close; + + cobra->dev[i].name = cobra_name; + cobra->dev[i].idbus = BUS_GAMEPORT; + cobra->dev[i].idvendor = GAMEPORT_ID_VENDOR_CREATIVE; + cobra->dev[i].idproduct = 0x0008; + cobra->dev[i].idversion = 0x0100; + + cobra->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + cobra->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; cobra_btn[j]; j++) + set_bit(cobra_btn[j], cobra->dev[i].keybit); + + cobra->dev[i].absmin[ABS_X] = -1; cobra->dev[i].absmax[ABS_X] = 1; + cobra->dev[i].absmin[ABS_Y] = -1; cobra->dev[i].absmax[ABS_Y] = 1; + + input_register_device(cobra->dev + i); + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + cobra->dev[i].number, cobra_name, gameport->number, i); + } + + + return; +fail2: gameport_close(gameport); +fail1: kfree(cobra); +} + +static void cobra_disconnect(struct gameport *gameport) +{ + int i; + + struct cobra *cobra = gameport->private; + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & 1) + input_unregister_device(cobra->dev + i); + gameport_close(gameport); + kfree(cobra); +} + +static struct gameport_dev cobra_dev = { + connect: cobra_connect, + disconnect: cobra_disconnect, +}; + +int __init cobra_init(void) +{ + gameport_register_device(&cobra_dev); + return 0; +} + +void __exit cobra_exit(void) +{ + gameport_unregister_device(&cobra_dev); +} + +module_init(cobra_init); +module_exit(cobra_exit); diff --git a/drivers/char/joystick/db9.c b/drivers/char/joystick/db9.c new file mode 100644 index 000000000..f9edd0755 --- /dev/null +++ b/drivers/char/joystick/db9.c @@ -0,0 +1,423 @@ +/* + * $Id: db9.c,v 1.5 2000/05/29 20:39:38 vojtech Exp $ + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Based on the work of: + * Andree Borrmann Mats Sjövall + * + * Sponsored by SuSE + */ + +/* + * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/parport.h> +#include <linux/input.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(db9, "2i"); +MODULE_PARM(db9_2, "2i"); +MODULE_PARM(db9_3, "2i"); + +#define DB9_MULTI_STICK 0x01 +#define DB9_MULTI2_STICK 0x02 +#define DB9_GENESIS_PAD 0x03 +#define DB9_GENESIS5_PAD 0x05 +#define DB9_GENESIS6_PAD 0x06 +#define DB9_SATURN_PAD 0x07 +#define DB9_MULTI_0802 0x08 +#define DB9_MULTI_0802_2 0x09 +#define DB9_CD32_PAD 0x0A +#define DB9_MAX_PAD 0x0B + +#define DB9_UP 0x01 +#define DB9_DOWN 0x02 +#define DB9_LEFT 0x04 +#define DB9_RIGHT 0x08 +#define DB9_FIRE1 0x10 +#define DB9_FIRE2 0x20 +#define DB9_FIRE3 0x40 +#define DB9_FIRE4 0x80 + +#define DB9_NORMAL 0x0a +#define DB9_NOSELECT 0x08 + +#define DB9_SATURN0 0x00 +#define DB9_SATURN1 0x02 +#define DB9_SATURN2 0x04 +#define DB9_SATURN3 0x06 + +#define DB9_GENESIS6_DELAY 14 +#define DB9_REFRESH_TIME HZ/100 + +static int db9[] __initdata = { -1, 0 }; +static int db9_2[] __initdata = { -1, 0 }; +static int db9_3[] __initdata = { -1, 0 }; + +struct db9 { + struct input_dev dev[2]; + struct timer_list timer; + struct pardevice *pd; + int mode; + int used; +}; + +static struct db9 *db9_base[3]; + +static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB }; +static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE }; +static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START }; + +static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 }; +static short *db9_btn[DB9_MAX_PAD] = { db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn, + db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn }; +static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", + "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad" }; + +static void db9_timer(unsigned long private) +{ + struct db9 *db9 = (void *) private; + struct parport *port = db9->pd->port; + struct input_dev *dev = db9->dev; + int data, i; + + switch(db9->mode) { + case DB9_MULTI_0802_2: + + data = parport_read_data(port) >> 3; + + input_report_abs(dev + 1, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev + 1, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev + 1, BTN_TRIGGER, ~data&DB9_FIRE1); + + case DB9_MULTI_0802: + + data = parport_read_status(port) >> 3; + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, data&DB9_FIRE1); + break; + + case DB9_MULTI_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, ~data&DB9_FIRE1); + break; + + case DB9_MULTI2_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, ~data&DB9_FIRE1); + input_report_key(dev, BTN_THUMB, ~data&DB9_FIRE2); + break; + + case DB9_GENESIS_PAD: + + parport_write_control(port, DB9_NOSELECT); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_START, ~data&DB9_FIRE2); + break; + + case DB9_GENESIS5_PAD: + + parport_write_control(port, DB9_NOSELECT); + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_X, ~data&DB9_FIRE2); + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_START, ~data&DB9_RIGHT); + break; + + case DB9_GENESIS6_PAD: + + parport_write_control(port, DB9_NOSELECT); /* 1 */ + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_X, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NOSELECT); /* 2 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 3 */ + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_Z, ~data&DB9_DOWN); + input_report_key(dev, BTN_MODE, ~data&DB9_UP); + input_report_key(dev, BTN_START, ~data&DB9_RIGHT); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 4 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + break; + + case DB9_SATURN_PAD: + + parport_write_control(port, DB9_SATURN0); + data = parport_read_data(port); + + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_Z, ~data&DB9_DOWN); + input_report_key(dev, BTN_TL,~data&DB9_UP); + input_report_key(dev, BTN_TR,~data&DB9_RIGHT); + + parport_write_control(port, DB9_SATURN2); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + + parport_write_control(port, DB9_NORMAL); + data = parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_LEFT); + input_report_key(dev, BTN_B, ~data&DB9_UP); + input_report_key(dev, BTN_C, ~data&DB9_DOWN); + input_report_key(dev, BTN_X, ~data&DB9_RIGHT); + break; + + case DB9_CD32_PAD: + + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + + parport_write_control(port, 0x0a); + + for (i = 0; i < 7; i++) { + data = parport_read_data(port); + parport_write_control(port, 0x02); + parport_write_control(port, 0x0a); + input_report_key(dev, db9_cd32_btn[i], ~data&DB9_FIRE2); + } + + parport_write_control(port, 0x00); + break; + } + + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); +} + +static int db9_open(struct input_dev *dev) +{ + struct db9 *db9 = dev->private; + struct parport *port = db9->pd->port; + + if (!db9->used++) { + parport_claim(db9->pd); + parport_write_data(port, 0xff); + parport_data_reverse(port); + parport_write_control(port, DB9_NORMAL); + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); + } + + return 0; +} + +static void db9_close(struct input_dev *dev) +{ + struct db9 *db9 = dev->private; + struct parport *port = db9->pd->port; + + if (!--db9->used) { + del_timer(&db9->timer); + parport_write_control(port, 0x00); + parport_data_forward(port); + parport_release(db9->pd); + } +} + +static struct db9 __init *db9_probe(int *config) +{ + struct db9 *db9; + struct parport *pp; + int i, j; + + if (config[0] < 0) + return NULL; + if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) { + printk(KERN_ERR "db9.c: bad config\n"); + return NULL; + } + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "db9.c: no such parport\n"); + return NULL; + } + + if (!(pp->modes & PARPORT_MODE_TRISTATE) && config[1] != DB9_MULTI_0802) { + printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); + return NULL; + } + + if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) + return NULL; + memset(db9, 0, sizeof(struct db9)); + + db9->mode = config[1]; + init_timer(&db9->timer); + db9->timer.data = (long) db9; + db9->timer.function = db9_timer; + + db9->pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!db9->pd) { + printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n"); + kfree(db9); + return NULL; + } + + for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) { + + db9->dev[i].private = db9; + db9->dev[i].open = db9_open; + db9->dev[i].close = db9_close; + + db9->dev[i].name = db9_name[db9->mode]; + db9->dev[i].idbus = BUS_PARPORT; + db9->dev[i].idvendor = 0x0002; + db9->dev[i].idproduct = config[1]; + db9->dev[i].idversion = 0x0100; + + db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + db9->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; j < db9_buttons[db9->mode]; j++) + set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit); + + db9->dev[i].absmin[ABS_X] = -1; db9->dev[i].absmax[ABS_X] = 1; + db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1; + + input_register_device(db9->dev + i); + printk(KERN_INFO "input%d: %s on %s\n", + db9->dev[i].number, db9_name[db9->mode], db9->pd->port->name); + } + + return db9; +} + +#ifndef MODULE +int __init db9_setup(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9[i] = ints[i + 1]; + return 1; +} +int __init db9_setup_2(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9_2[i] = ints[i + 1]; + return 1; +} +int __init db9_setup_3(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9_3[i] = ints[i + 1]; + return 1; +} +__setup("db9=", db9_setup); +__setup("db9_2=", db9_setup_2); +__setup("db9_3=", db9_setup_3); +#endif + +int __init db9_init(void) +{ + db9_base[0] = db9_probe(db9); + db9_base[1] = db9_probe(db9_2); + db9_base[2] = db9_probe(db9_3); + + if (db9_base[0] || db9_base[1] || db9_base[2]) + return 0; + + return -ENODEV; +} + +void __exit db9_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) + if (db9_base[i]) { + for (j = 0; j < 1 + (db9_base[i]->mode == DB9_MULTI_0802_2); j++) + input_unregister_device(db9_base[i]->dev + j); + parport_unregister_device(db9_base[i]->pd); + } +} + +module_init(db9_init); +module_exit(db9_exit); diff --git a/drivers/char/joystick/gamecon.c b/drivers/char/joystick/gamecon.c new file mode 100644 index 000000000..a92ef58a9 --- /dev/null +++ b/drivers/char/joystick/gamecon.c @@ -0,0 +1,668 @@ +/* + * $Id: gamecon.c,v 1.4 2000/05/29 21:08:45 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * Andree Borrmann John Dahlstrom + * David Kuder + * + * Sponsored by SuSE + */ + +/* + * NES, SNES, N64, Multi1, Multi2, PSX gamepad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/parport.h> +#include <linux/input.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(gc, "2-6i"); +MODULE_PARM(gc_2,"2-6i"); +MODULE_PARM(gc_3,"2-6i"); + +#define GC_SNES 1 +#define GC_NES 2 +#define GC_NES4 3 +#define GC_MULTI 4 +#define GC_MULTI2 5 +#define GC_N64 6 +#define GC_PSX 7 + +#define GC_MAX 7 + +#define GC_REFRESH_TIME HZ/100 + +struct gc { + struct pardevice *pd; + struct input_dev dev[5]; + struct timer_list timer; + unsigned char pads[GC_PSX + 1]; + int used; +}; + +static struct gc *gc_base[3]; + +static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; + +static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; + +static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", + "Multisystem 2-button joystick", "N64 controller", "PSX pad", + "PSX NegCon", "PSX Analog contoller" }; +/* + * N64 support. + */ + +static unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 }; +static short gc_n64_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START }; + +#define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */ +#define GC_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ +#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ +#define GC_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ +#define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ + /* GC_N64_DWS > 24 is known to fail */ +#define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */ +#define GC_N64_POWER_R 0xfd /* power during read */ +#define GC_N64_OUT 0x1d /* output bits to the 4 pads */ + /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ + /* in GC_N64_OUT is pulled low on the output port (by any routine) for more */ + /* than 123 us */ +#define GC_N64_CLOCK 0x02 /* clock bits for read */ + +/* + * gc_n64_read_packet() reads an N64 packet. + * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. + */ + +static void gc_n64_read_packet(struct gc *gc, unsigned char *data) +{ + int i; + unsigned long flags; + +/* + * Request the pad to transmit data + */ + + __save_flags(flags); + __cli(); + for (i = 0; i < GC_N64_REQUEST_LENGTH; i++) { + parport_write_data(gc->pd->port, GC_N64_POWER_W | ((GC_N64_REQUEST >> i) & 1 ? GC_N64_OUT : 0)); + udelay(GC_N64_DWS); + } + __restore_flags(flags); + +/* + * Wait for the pad response to be loaded into the 33-bit register of the adapter + */ + + udelay(GC_N64_DELAY); + +/* + * Grab data (ignoring the last bit, which is a stop bit) + */ + + for (i = 0; i < GC_N64_LENGTH; i++) { + parport_write_data(gc->pd->port, GC_N64_POWER_R); + data[i] = parport_read_status(gc->pd->port); + parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK); + } + +/* + * We must wait 200 ms here for the controller to reinitialize before the next read request. + * No worries as long as gc_read is polled less frequently than this. + */ + +} + +/* + * NES/SNES support. + */ + +#define GC_NES_DELAY 6 /* Delay between bits - 6us */ +#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */ +#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ + +#define GC_NES_POWER 0xfc +#define GC_NES_CLOCK 0x01 +#define GC_NES_LATCH 0x02 + +static unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 }; +static unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 }; +static short gc_snes_btn[] = { BTN_A, BTN_B, BTN_START, BTN_SELECT, BTN_X, BTN_Y, BTN_TL, BTN_TR }; + +/* + * gc_nes_read_packet() reads a NES/SNES packet. + * Each pad uses one bit per byte. So all pads connected to + * this port are read in parallel. + */ + +static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH); + udelay(GC_NES_DELAY * 2); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + + for (i = 0; i < length; i++) { + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + } +} + +/* + * Multisystem joystick support + */ + +#define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */ +#define GC_MULTI2_LENGTH 6 /* One more bit for one more button */ + +/* + * gc_multi_read_packet() reads a Multisystem joystick packet. + */ + +static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + for (i = 0; i < length; i++) { + parport_write_data(gc->pd->port, ~(1 << i)); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + } +} + +/* + * PSX support + */ + +#define GC_PSX_DELAY 10 +#define GC_PSX_LENGTH 8 /* talk to the controller in bytes */ + +#define GC_PSX_MOUSE 0x12 /* PSX Mouse */ +#define GC_PSX_NEGCON 0x23 /* NegCon pad */ +#define GC_PSX_NORMAL 0x41 /* Standard Digital controller */ +#define GC_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ +#define GC_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ + +#define GC_PSX_CLOCK 0x04 /* Pin 3 */ +#define GC_PSX_COMMAND 0x01 /* Pin 1 */ +#define GC_PSX_POWER 0xf8 /* Pins 5-9 */ +#define GC_PSX_SELECT 0x02 /* Pin 2 */ +#define GC_PSX_NOPOWER 0x04 + +static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y }; +static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, + BTN_START, BTN_SELECT, BTN_THUMB, BTN_THUMB2 }; + +/* + * gc_psx_command() writes 8bit command and reads 8bit data from + * the psx pad. + */ + +static int gc_psx_command(struct gc *gc, int b) +{ + int i, cmd, ret = 0; + + cmd = (b & 1) ? GC_PSX_COMMAND : 0; + for (i = 0; i < 8; i++) { + parport_write_data(gc->pd->port, cmd | GC_PSX_POWER); + udelay(GC_PSX_DELAY); + ret |= ((parport_read_status(gc->pd->port) ^ 0x80) & gc->pads[GC_PSX]) ? (1 << i) : 0; + cmd = (b & 1) ? GC_PSX_COMMAND : 0; + parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); + udelay(GC_PSX_DELAY); + b >>= 1; + } + return ret; +} + +/* + * gc_psx_read_packet() reads a whole psx packet and returns + * device identifier code. + */ + +static int gc_psx_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i, ret; + unsigned long flags; + + __save_flags(flags); + __cli(); + + parport_write_data(gc->pd->port, GC_PSX_POWER); + + parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */ + udelay(GC_PSX_DELAY * 2); + gc_psx_command(gc, 0x01); /* Access pad */ + ret = gc_psx_command(gc, 0x42); /* Get device id */ + if (gc_psx_command(gc, 0) == 'Z') /* okay? */ + for (i = 0; i < length; i++) + data[i] = gc_psx_command(gc, 0); + else ret = -1; + + parport_write_data(gc->pd->port, GC_PSX_SELECT | GC_PSX_CLOCK | GC_PSX_POWER); + __restore_flags(flags); + + return ret; +} + +/* + * gc_timer() reads and analyzes console pads data. + */ + +#define GC_MAX_LENGTH GC_N64_LENGTH + +static void gc_timer(unsigned long private) +{ + struct gc *gc = (void *) private; + struct input_dev *dev = gc->dev; + unsigned char data[GC_MAX_LENGTH]; + int i, j, s; + +/* + * N64 pads - must be read first, any read confuses them for 200 us + */ + + if (gc->pads[GC_N64]) { + + gc_n64_read_packet(gc, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) { + + signed char axes[2]; + axes[0] = axes[1] = 0; + + for (j = 0; j < 8; j++) { + if (data[23 - j] & s) axes[0] |= 1 << j; + if (data[31 - j] & s) axes[1] |= 1 << j; + } + + input_report_abs(dev + i, ABS_X, axes[0]); + input_report_abs(dev + i, ABS_Y, -axes[1]); + + input_report_abs(dev + i, ABS_HAT0X, !!(s & data[7]) - !!(s & data[6])); + input_report_abs(dev + i, ABS_HAT0Y, !!(s & data[5]) - !!(s & data[4])); + + for (j = 0; j < 10; j++) + input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]); + } + } + } + +/* + * NES and SNES pads + */ + + if (gc->pads[GC_NES] || gc->pads[GC_SNES]) { + + gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) { + input_report_abs(dev + i, ABS_X, !!(s & data[7]) - !!(s & data[6])); + input_report_abs(dev + i, ABS_Y, !!(s & data[5]) - !!(s & data[4])); + } + + if (s & gc->pads[GC_NES]) + for (j = 0; j < 4; j++) + input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]); + + if (s & gc->pads[GC_SNES]) + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]); + } + } + +/* + * Multi and Multi2 joysticks + */ + + if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) { + + gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) { + input_report_abs(dev + i, ABS_X, !!(s & data[3]) - !!(s & data[2])); + input_report_abs(dev + i, ABS_Y, !!(s & data[1]) - !!(s & data[0])); + input_report_key(dev + i, BTN_TRIGGER, s & data[4]); + } + + if (s & gc->pads[GC_MULTI2]) + input_report_key(dev + i, BTN_THUMB, s & data[5]); + } + } + +/* + * PSX controllers + */ + + if (gc->pads[GC_PSX]) { + + for (i = 0; i < 5; i++) + if (gc->pads[GC_PSX] & gc_status_bit[i]) + break; + + switch (gc_psx_read_packet(gc, 6, data)) { + + case GC_PSX_ANALOGG: + + for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j], data[j + 2]); + + input_report_abs(dev + i, ABS_HAT0X, !!(data[0]&0x20) - !!(data[0]&0x80)); + input_report_abs(dev + i, ABS_HAT0Y, !!(data[0]&0x40) - !!(data[0]&0x10)); + + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << i)); + + input_report_key(dev + i, BTN_START, ~data[0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + + break; + + case GC_PSX_ANALOGR: + + input_report_key(dev + i, BTN_THUMB, ~data[0] & 0x04); + input_report_key(dev + i, BTN_THUMB2, ~data[0] & 0x02); + + case GC_PSX_NORMAL: + case GC_PSX_NEGCON: + + input_report_abs(dev + i, ABS_X, 128 + !!(data[0] & 0x20) * 127 - !!(data[0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !!(data[0] & 0x40) * 127 - !!(data[0] & 0x10) * 128); + + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << i)); + + input_report_key(dev + i, BTN_START, ~data[0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + + break; + } + } + + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); +} + +static int gc_open(struct input_dev *dev) +{ + struct gc *gc = dev->private; + if (!gc->used++) { + parport_claim(gc->pd); + parport_write_control(gc->pd->port, 0x04); + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); + } + return 0; +} + +static void gc_close(struct input_dev *dev) +{ + struct gc *gc = dev->private; + if (!--gc->used) { + del_timer(&gc->timer); + parport_write_control(gc->pd->port, 0x00); + parport_release(gc->pd); + } +} + + + +static struct gc __init *gc_probe(int *config) +{ + struct gc *gc; + struct parport *pp; + int i, j, psx, pbtn; + unsigned char data[2]; + + if (config[0] < 0) + return NULL; + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "gamecon.c: no such parport\n"); + return NULL; + } + + if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) + return NULL; + memset(gc, 0, sizeof(struct gc)); + + gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!gc->pd) { + printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n"); + kfree(gc); + return NULL; + } + + parport_claim(gc->pd); + + init_timer(&gc->timer); + gc->timer.data = (long) gc; + gc->timer.function = gc_timer; + + for (i = 0; i < 5; i++) { + + if (!config[i + 1]) + continue; + + if (config[i + 1] < 1 || config[i + 1] > GC_MAX) { + printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]); + continue; + } + + gc->dev[i].private = gc; + gc->dev[i].open = gc_open; + gc->dev[i].close = gc_close; + + gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; j < 2; j++) { + set_bit(ABS_X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_X + j] = -1; + gc->dev[i].absmax[ABS_X + j] = 1; + } + + gc->pads[0] |= gc_status_bit[i]; + gc->pads[config[i + 1]] |= gc_status_bit[i]; + + switch(config[i + 1]) { + + case GC_N64: + for (j = 0; j < 10; j++) + set_bit(gc_n64_btn[j], gc->dev[j].keybit); + + for (j = 0; j < 2; j++) { + set_bit(ABS_X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_X + j] = -127; + gc->dev[i].absmax[ABS_X + j] = 126; + gc->dev[i].absflat[ABS_X + j] = 2; + set_bit(ABS_HAT0X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_HAT0X + j] = -1; + gc->dev[i].absmax[ABS_HAT0X + j] = 1; + } + + break; + + case GC_SNES: + for (j = 0; j < 8; j++) + set_bit(gc_snes_btn[j], gc->dev[j].keybit); + break; + + case GC_NES: + for (j = 0; j < 4; j++) + set_bit(gc_snes_btn[j], gc->dev[j].keybit); + break; + + case GC_MULTI2: + set_bit(BTN_THUMB, gc->dev[i].keybit); + + case GC_MULTI: + set_bit(BTN_TRIGGER, gc->dev[i].keybit); + break; + + case GC_PSX: + + psx = gc_psx_read_packet(gc, 2, data); + + switch(psx) { + case GC_PSX_NEGCON: + config[i + 1] += 1; + case GC_PSX_NORMAL: + pbtn = 10; + break; + + case GC_PSX_ANALOGG: + case GC_PSX_ANALOGR: + config[i + 1] += 2; + pbtn = 12; + for (j = 0; j < 6; j++) { + psx = gc_psx_abs[j]; + set_bit(psx, gc->dev[i].absbit); + gc->dev[i].absmin[psx] = 4; + gc->dev[i].absmax[psx] = 252; + gc->dev[i].absflat[psx] = 2; + } + break; + + case -1: + gc->pads[GC_PSX] &= ~gc_status_bit[i]; + pbtn = 0; + printk(KERN_ERR "gamecon.c: No PSX controller found.\n"); + break; + + default: + gc->pads[GC_PSX] &= ~gc_status_bit[i]; + pbtn = 0; + printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x," + " please report to <vojtech@suse.cz>.\n", psx); + } + + for (j = 0; j < pbtn; j++) + set_bit(gc_psx_btn[j], gc->dev[i].keybit); + + break; + } + + gc->dev[i].name = gc_names[config[i + 1]]; + gc->dev[i].idbus = BUS_PARPORT; + gc->dev[i].idvendor = 0x0001; + gc->dev[i].idproduct = config[i + 1]; + gc->dev[i].idversion = 0x0100; + } + + parport_release(gc->pd); + + if (!gc->pads[0]) { + parport_unregister_device(gc->pd); + kfree(gc); + return NULL; + } + + for (i = 0; i < 5; i++) + if (gc->pads[0] & gc_status_bit[i]) { + input_register_device(gc->dev + i); + printk(KERN_INFO "input%d: %s on %s\n", gc->dev[i].number, gc->dev[i].name, gc->pd->port->name); + } + + return gc; +} + +#ifndef MODULE +int __init gc_setup(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc[i] = ints[i + 1]; + return 1; +} +int __init gc_setup_2(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc_2[i] = ints[i + 1]; + return 1; +} +int __init gc_setup_3(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc_3[i] = ints[i + 1]; + return 1; +} +__setup("gc=", gc_setup); +__setup("gc_2=", gc_setup_2); +__setup("gc_3=", gc_setup_3); +#endif + +int __init gc_init(void) +{ + gc_base[0] = gc_probe(gc); + gc_base[1] = gc_probe(gc_2); + gc_base[2] = gc_probe(gc_3); + + if (gc_base[0] || gc_base[1] || gc_base[2]) + return 0; + + return -ENODEV; +} + +void __exit gc_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 5; j++) + if (gc_base[i]->pads[0] & gc_status_bit[j]) + input_unregister_device(gc_base[i]->dev + j); + parport_unregister_device(gc_base[i]->pd); + } +} + +module_init(gc_init); +module_exit(gc_exit); diff --git a/drivers/char/joystick/gameport.c b/drivers/char/joystick/gameport.c new file mode 100644 index 000000000..5a5e1219b --- /dev/null +++ b/drivers/char/joystick/gameport.c @@ -0,0 +1,198 @@ +/* + * $Id: gameport.c,v 1.5 2000/05/29 10:54:53 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Generic gameport layer + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> +#include <linux/isapnp.h> +#include <linux/stddef.h> +#include <linux/delay.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +EXPORT_SYMBOL(gameport_register_port); +EXPORT_SYMBOL(gameport_unregister_port); +EXPORT_SYMBOL(gameport_register_device); +EXPORT_SYMBOL(gameport_unregister_device); +EXPORT_SYMBOL(gameport_open); +EXPORT_SYMBOL(gameport_close); +EXPORT_SYMBOL(gameport_rescan); +EXPORT_SYMBOL(gameport_cooked_read); + +static struct gameport *gameport_list = NULL; +static struct gameport_dev *gameport_dev = NULL; +static int gameport_number = 0; + +/* + * gameport_measure_speed() measures the gameport i/o speed. + */ + +static int gameport_measure_speed(struct gameport *gameport) +{ +#ifdef __i386__ + +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) + + unsigned int i, t, t1, t2, t3, tx; + unsigned long flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + save_flags(flags); /* Yes, all CPUs */ + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) gameport_read(gameport); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + return 59659 / (tx < 1 ? 1 : tx); + +#else + + unsigned int j, t = 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); } + + return t * HZ / 1000; + +#endif + + gameport_close(gameport); +} + +static void gameport_find_dev(struct gameport *gameport) +{ + struct gameport_dev *dev = gameport_dev; + + while (dev && !gameport->dev) { + if (dev->connect) + dev->connect(gameport, dev); + dev = dev->next; + } +} + +void gameport_rescan(struct gameport *gameport) +{ + gameport_close(gameport); + gameport_find_dev(gameport); +} + +void gameport_register_port(struct gameport *gameport) +{ + gameport->number = gameport_number++; + gameport->next = gameport_list; + gameport_list = gameport; + + gameport->speed = gameport_measure_speed(gameport); + + gameport_find_dev(gameport); +} + +void gameport_unregister_port(struct gameport *gameport) +{ + struct gameport **gameportptr = &gameport_list; + + while (*gameportptr && (*gameportptr != gameport)) gameportptr = &((*gameportptr)->next); + *gameportptr = (*gameportptr)->next; + + if (gameport->dev && gameport->dev->disconnect) + gameport->dev->disconnect(gameport); + + gameport_number--; +} + +void gameport_register_device(struct gameport_dev *dev) +{ + struct gameport *gameport = gameport_list; + + dev->next = gameport_dev; + gameport_dev = dev; + + while (gameport) { + if (!gameport->dev && dev->connect) + dev->connect(gameport, dev); + gameport = gameport->next; + } +} + +void gameport_unregister_device(struct gameport_dev *dev) +{ + struct gameport_dev **devptr = &gameport_dev; + struct gameport *gameport = gameport_list; + + while (*devptr && (*devptr != dev)) devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + while (gameport) { + if (gameport->dev == dev && dev->disconnect) + dev->disconnect(gameport); + gameport_find_dev(gameport); + gameport = gameport->next; + } +} + +int gameport_open(struct gameport *gameport, struct gameport_dev *dev, int mode) +{ + if (gameport->open) { + if (gameport->open(gameport, mode)) + return -1; + } else { + if (mode != GAMEPORT_MODE_RAW) + return -1; + } + + if (gameport->dev) + return -1; + + gameport->dev = dev; + + return 0; +} + +void gameport_close(struct gameport *gameport) +{ + gameport->dev = NULL; + if (gameport->close) gameport->close(gameport); +} diff --git a/drivers/char/joystick/gf2k.c b/drivers/char/joystick/gf2k.c new file mode 100644 index 000000000..cad8be16b --- /dev/null +++ b/drivers/char/joystick/gf2k.c @@ -0,0 +1,359 @@ +/* + * $Id: gf2k.c,v 1.12 2000/06/04 14:53:44 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Genius Flight 2000 joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +#define GF2K_START 400 /* The time we wait for the first bit [400 us] */ +#define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */ +#define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */ +#define GF2K_LENGTH 80 /* Max number of triplets in a packet */ +#define GF2K_REFRESH HZ/50 /* Time between joystick polls [20 ms] */ + +/* + * Genius joystick ids ... + */ + +#define GF2K_ID_G09 1 +#define GF2K_ID_F30D 2 +#define GF2K_ID_F30 3 +#define GF2K_ID_F31D 4 +#define GF2K_ID_F305 5 +#define GF2K_ID_F23P 6 +#define GF2K_ID_F31 7 +#define GF2K_ID_MAX 7 + +static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 }; +static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D", + "Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"}; +static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 }; +static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 }; +static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 }; +static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 }; +static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 }; + +static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE }; +static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }; +static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT }; + + +static short gf2k_seq_reset[] = { 240, 340, 0 }; +static short gf2k_seq_digital[] = { 590, 320, 860, 0 }; + +struct gf2k { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev; + int reads; + int bads; + int used; + unsigned char id; + unsigned char length; +}; + +/* + * gf2k_read_packet() reads a Genius Flight2000 packet. + */ + +static int gf2k_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, p; + unsigned long flags; + + t = gameport_time(gameport, GF2K_START); + p = gameport_time(gameport, GF2K_STROBE); + + i = 0; + + __save_flags(flags); + __cli(); + + gameport_trigger(gameport); + v = gameport_read(gameport);; + + while (t > 0 && i < length) { + t--; u = v; + v = gameport_read(gameport); + if (v & ~u & 0x10) { + data[i++] = v >> 5; + t = p; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * gf2k_trigger_seq() initializes a Genius Flight2000 joystick + * into digital mode. + */ + +static void gf2k_trigger_seq(struct gameport *gameport, short *seq) +{ + + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + gameport_trigger(gameport); + t = gameport_time(gameport, GF2K_TIMEOUT * 1000); + while ((gameport_read(gameport) & 1) && t) t--; + udelay(seq[i]); + } while (seq[++i]); + + gameport_trigger(gameport); + + __restore_flags(flags); +} + +/* + * js_sw_get_bits() composes bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(p,n,s) gf2k_get_bits(data, p, n, s) + +static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift) +{ + __u64 data = 0; + int i; + + for (i = 0; i < num / 3 + 2; i++) + data |= buf[pos / 3 + i] << (i * 3); + data >>= pos % 3; + data &= (1 << num) - 1; + data <<= shift; + + return data; +} + +static void gf2k_read(struct gf2k *gf2k, unsigned char *data) +{ + struct input_dev *dev = &gf2k->dev; + int i, t; + + for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++) + input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9)); + + for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++) + input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9)); + + t = GB(40,4,0); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]); + + t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10); + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1); +} + +/* + * gf2k_timer() reads and analyzes Genius joystick data. + */ + +static void gf2k_timer(unsigned long private) +{ + struct gf2k *gf2k = (void *) private; + unsigned char data[GF2K_LENGTH]; + + gf2k->reads++; + + if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id]) { + gf2k->bads++; + } else gf2k_read(gf2k, data); + + mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH); +} + +static int gf2k_open(struct input_dev *dev) +{ + struct gf2k *gf2k = dev->private; + if (!gf2k->used++) + mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH); + return 0; +} + +static void gf2k_close(struct input_dev *dev) +{ + struct gf2k *gf2k = dev->private; + if (!--gf2k->used) + del_timer(&gf2k->timer); +} + +/* + * gf2k_connect() probes for Genius id joysticks. + */ + +static void gf2k_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct gf2k *gf2k; + unsigned char data[GF2K_LENGTH]; + int i; + + if (!(gf2k = kmalloc(sizeof(struct gf2k), GFP_KERNEL))) + return; + memset(gf2k, 0, sizeof(struct gf2k)); + + gameport->private = gf2k; + + gf2k->gameport = gameport; + init_timer(&gf2k->timer); + gf2k->timer.data = (long) gf2k; + gf2k->timer.function = gf2k_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + gf2k_trigger_seq(gameport, gf2k_seq_reset); + + wait_ms(GF2K_TIMEOUT); + + gf2k_trigger_seq(gameport, gf2k_seq_digital); + + wait_ms(GF2K_TIMEOUT); + + if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) + goto fail2; + + if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) + goto fail2; + +#ifdef RESET_WORKS + if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) || + (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) + goto fail2; +#else + gf2k->id = 6; +#endif + + if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) { + printk(KERN_WARNING "gf2k.c: Not yet supported joystick on gameport%d. [id: %d type:%s]\n", + gameport->number, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]); + goto fail2; + } + + gf2k->length = gf2k_lens[gf2k->id]; + + gf2k->dev.private = gf2k; + gf2k->dev.open = gf2k_open; + gf2k->dev.close = gf2k_close; + gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + gf2k->dev.name = gf2k_names[gf2k->id]; + gf2k->dev.idbus = BUS_GAMEPORT; + gf2k->dev.idvendor = GAMEPORT_ID_VENDOR_GENIUS; + gf2k->dev.idproduct = gf2k->id; + gf2k->dev.idversion = 0x0100; + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) + set_bit(gf2k_abs[i], gf2k->dev.absbit); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) { + set_bit(ABS_HAT0X + i, gf2k->dev.absbit); + gf2k->dev.absmin[ABS_HAT0X + i] = -1; + gf2k->dev.absmax[ABS_HAT0X + i] = 1; + } + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + set_bit(gf2k_btn_joy[i], gf2k->dev.keybit); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + set_bit(gf2k_btn_pad[i], gf2k->dev.keybit); + + gf2k_read_packet(gameport, gf2k->length, data); + gf2k_read(gf2k, data); + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) { + gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 : + gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; + gf2k->dev.absmin[gf2k_abs[i]] = 32; + gf2k->dev.absfuzz[gf2k_abs[i]] = 8; + gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0; + } + + input_register_device(&gf2k->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + gf2k->dev.number, gf2k_names[gf2k->id], gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(gf2k); +} + +static void gf2k_disconnect(struct gameport *gameport) +{ + struct gf2k *gf2k = gameport->private; + input_unregister_device(&gf2k->dev); + gameport_close(gameport); + kfree(gf2k); +} + +static struct gameport_dev gf2k_dev = { + connect: gf2k_connect, + disconnect: gf2k_disconnect, +}; + +int __init gf2k_init(void) +{ + gameport_register_device(&gf2k_dev); + return 0; +} + +void __exit gf2k_exit(void) +{ + gameport_unregister_device(&gf2k_dev); +} + +module_init(gf2k_init); +module_exit(gf2k_exit); diff --git a/drivers/char/joystick/grip.c b/drivers/char/joystick/grip.c new file mode 100644 index 000000000..4cedd7892 --- /dev/null +++ b/drivers/char/joystick/grip.c @@ -0,0 +1,423 @@ +/* + * $Id: grip.c,v 1.14 2000/06/06 21:13:36 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define GRIP_MODE_GPP 1 +#define GRIP_MODE_BD 2 +#define GRIP_MODE_XT 3 +#define GRIP_MODE_DC 4 + +#define GRIP_LENGTH_GPP 24 +#define GRIP_STROBE_GPP 200 /* 200 us */ +#define GRIP_LENGTH_XT 4 +#define GRIP_STROBE_XT 64 /* 64 us */ +#define GRIP_MAX_CHUNKS_XT 10 +#define GRIP_MAX_BITS_XT 30 + +#define GRIP_REFRESH_TIME HZ/50 /* 20 ms */ + +struct grip { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + unsigned char mode[2]; + int used; + int reads; + int bads; +}; + +static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 }; +static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 }; +static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 }; +static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 }; + +static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 }; +static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; +static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 }; +static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital", + "Gravis Xterminator Digital", "Gravis Xterminator DualControl" }; +static int *grip_abs[] = { 0, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc }; +static int *grip_btn[] = { 0, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc }; +static char grip_anx[] = { 0, 0, 3, 5, 5 }; +static char grip_cen[] = { 0, 0, 2, 2, 4 }; + +/* + * grip_gpp_read_packet() reads a Gravis GamePad Pro packet. + */ + +static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t; + int i; + + int strobe = gameport_time(gameport, GRIP_STROBE_GPP); + + data[0] = 0; + t = strobe; + i = 0; + + __save_flags(flags); + __cli(); + + v = gameport_read(gameport) >> shift; + + do { + t--; + u = v; v = (gameport_read(gameport) >> shift) & 3; + if (~v & u & 1) { + data[0] |= (v >> 1) << i++; + t = strobe; + } + } while (i < GRIP_LENGTH_GPP && t > 0); + + __restore_flags(flags); + + if (i < GRIP_LENGTH_GPP) return -1; + + for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) + data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1); + + return -(i == GRIP_LENGTH_GPP); +} + +/* + * grip_xt_read_packet() reads a Gravis Xterminator packet. + */ + +static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned int i, j, buf, crc; + unsigned char u, v, w; + unsigned long flags; + unsigned int t; + char status; + + int strobe = gameport_time(gameport, GRIP_STROBE_XT); + + data[0] = data[1] = data[2] = data[3] = 0; + status = buf = i = j = 0; + t = strobe; + + __save_flags(flags); + __cli(); + + v = w = (gameport_read(gameport) >> shift) & 3; + + do { + t--; + u = (gameport_read(gameport) >> shift) & 3; + + if (u ^ v) { + + if ((u ^ v) & 1) { + buf = (buf << 1) | (u >> 1); + t = strobe; + i++; + } else + + if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + if (i == 20) { + crc = buf ^ (buf >> 7) ^ (buf >> 14); + if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { + data[buf >> 18] = buf >> 4; + status |= 1 << (buf >> 18); + } + j++; + } + t = strobe; + buf = 0; + i = 0; + } + w = v; + v = u; + } + + } while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0); + + __restore_flags(flags); + + return -(status != 0xf); +} + +/* + * grip_timer() repeatedly polls the joysticks and generates events. + */ + +static void grip_timer(unsigned long private) +{ + struct grip *grip = (void*) private; + unsigned int data[GRIP_LENGTH_XT]; + struct input_dev *dev; + int i, j; + + for (i = 0; i < 2; i++) { + + dev = grip->dev + i; + grip->reads++; + + switch (grip->mode[i]) { + + case GRIP_MODE_GPP: + + if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1)); + input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1)); + + for (j = 0; j < 12; j++) + if (grip_btn_gpp[j]) + input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1); + + break; + + case GRIP_MODE_BD: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 5; j++) + input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1); + + break; + + case GRIP_MODE_XT: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_BRAKE, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_GAS, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1)); + + for (j = 0; j < 11; j++) + input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1); + break; + + case GRIP_MODE_DC: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, (data[0] >> 8) & 0x3f); + input_report_abs(dev, ABS_RX, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_RY, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 9; j++) + input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1); + break; + + + } + } + + mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME); +} + +static int grip_open(struct input_dev *dev) +{ + struct grip *grip = dev->private; + if (!grip->used++) + mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME); + return 0; +} + +static void grip_close(struct input_dev *dev) +{ + struct grip *grip = dev->private; + if (!--grip->used) + del_timer(&grip->timer); +} + +static void grip_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct grip *grip; + unsigned int data[GRIP_LENGTH_XT]; + int i, j, t; + + if (!(grip = kmalloc(sizeof(struct grip), GFP_KERNEL))) + return; + memset(grip, 0, sizeof(struct grip)); + + gameport->private = grip; + + grip->gameport = gameport; + init_timer(&grip->timer); + grip->timer.data = (long) grip; + grip->timer.function = grip_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + for (i = 0; i < 2; i++) { + if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) { + grip->mode[i] = GRIP_MODE_GPP; + continue; + } + if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) { + if (!(data[3] & 7)) { + grip->mode[i] = GRIP_MODE_BD; + continue; + } + if (!(data[2] & 0xf0)) { + grip->mode[i] = GRIP_MODE_XT; + continue; + } + grip->mode[i] = GRIP_MODE_DC; + continue; + } + } + + if (!grip->mode[0] && !grip->mode[1]) + goto fail2; + + for (i = 0; i < 2; i++) + if (grip->mode[i]) { + + grip->dev[i].private = grip; + + grip->dev[i].open = grip_open; + grip->dev[i].close = grip_close; + + grip->dev[i].name = grip_name[grip->mode[i]]; + grip->dev[i].idbus = BUS_GAMEPORT; + grip->dev[i].idvendor = GAMEPORT_ID_VENDOR_GRAVIS; + grip->dev[i].idproduct = grip->mode[i]; + grip->dev[i].idversion = 0x0100; + + grip->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) { + + set_bit(t, grip->dev[i].absbit); + + if (j < grip_cen[grip->mode[i]]) { + grip->dev[i].absmin[t] = 14; + grip->dev[i].absmax[t] = 52; + grip->dev[i].absfuzz[t] = 1; + grip->dev[i].absflat[t] = 2; + continue; + } + + if (j < grip_anx[grip->mode[i]]) { + grip->dev[i].absmin[t] = 3; + grip->dev[i].absmax[t] = 57; + grip->dev[i].absfuzz[t] = 1; + continue; + } + + grip->dev[i].absmin[t] = -1; + grip->dev[i].absmax[t] = 1; + } + + for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++) + if (t > 0) + set_bit(t, grip->dev[i].keybit); + + input_register_device(grip->dev + i); + + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + grip->dev[i].number, grip_name[grip->mode[i]], gameport->number, i); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(grip); +} + +static void grip_disconnect(struct gameport *gameport) +{ + int i; + + struct grip *grip = gameport->private; + for (i = 0; i < 2; i++) + if (grip->mode[i]) + input_unregister_device(grip->dev + i); + gameport_close(gameport); + kfree(grip); +} + +static struct gameport_dev grip_dev = { + connect: grip_connect, + disconnect: grip_disconnect, +}; + +int __init grip_init(void) +{ + gameport_register_device(&grip_dev); + return 0; +} + +void __exit grip_exit(void) +{ + gameport_unregister_device(&grip_dev); +} + +module_init(grip_init); +module_exit(grip_exit); diff --git a/drivers/char/joystick/interact.c b/drivers/char/joystick/interact.c new file mode 100644 index 000000000..7104e5d49 --- /dev/null +++ b/drivers/char/joystick/interact.c @@ -0,0 +1,306 @@ +/* + * $Id: interact.c,v 1.8 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * Based on the work of: + * Toby Deshane + * + * Sponsored by SuSE + */ + +/* + * InterAct digital gamepad/joystick driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define INTERACT_MAX_START 400 /* 400 us */ +#define INTERACT_MAX_STROBE 40 /* 40 us */ +#define INTERACT_MAX_LENGTH 32 /* 32 bits */ +#define INTERACT_REFRESH_TIME HZ/50 /* 20 ms */ + +#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */ +#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */ + +struct interact { + struct gameport *gameport; + struct input_dev dev; + struct timer_list timer; + int used; + int bads; + int reads; + unsigned char type; + unsigned char length; +}; + +static short interact_abs_hhfx[] = + { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 }; +static short interact_abs_pp8d[] = + { ABS_X, ABS_Y, -1 }; + +static short interact_btn_hhfx[] = + { BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 }; +static short interact_btn_pp8d[] = + { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 }; + +struct interact_type { + int id; + short *abs; + short *btn; + char *name; + unsigned char length; + unsigned char b8; +}; + +static struct interact_type interact_type[] = { + { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 }, + { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 }, + { 0 }}; + +/* + * interact_read_packet() reads and InterAct joystick data. + */ + +static int interact_read_packet(struct gameport *gameport, int length, u32 *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + data[0] = data[1] = data[2] = 0; + t = gameport_time(gameport, INTERACT_MAX_START); + s = gameport_time(gameport, INTERACT_MAX_STROBE); + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (v & ~u & 0x40) { + data[0] = (data[0] << 1) | ((v >> 4) & 1); + data[1] = (data[1] << 1) | ((v >> 5) & 1); + data[2] = (data[2] << 1) | ((v >> 7) & 1); + i++; + t = s; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * interact_timer() reads and analyzes InterAct joystick data. + */ + +static void interact_timer(unsigned long private) +{ + struct interact *interact = (struct interact *) private; + struct input_dev *dev = &interact->dev; + u32 data[3]; + int i; + + interact->reads++; + + if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) { + interact->bads++; + } else + + for (i = 0; i < 3; i++) + data[i] <<= INTERACT_MAX_LENGTH - interact->length; + + switch (interact->type) { + + case INTERACT_TYPE_HHFX: + + for (i = 0; i < 4; i++) + input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff); + + for (i = 0; i < 2; i++) + input_report_abs(dev, ABS_HAT0Y - i, + ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1); + + for (i = 0; i < 4; i++) + input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1); + + break; + + case INTERACT_TYPE_PP8D: + + for (i = 0; i < 2; i++) + input_report_abs(dev, interact_abs_pp8d[i], + ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1); + + break; + } + + mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME); + +} + +/* + * interact_open() is a callback from the input open routine. + */ + +static int interact_open(struct input_dev *dev) +{ + struct interact *interact = dev->private; + if (!interact->used++) + mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME); + return 0; +} + +/* + * interact_close() is a callback from the input close routine. + */ + +static void interact_close(struct input_dev *dev) +{ + struct interact *interact = dev->private; + if (!--interact->used) + del_timer(&interact->timer); +} + +/* + * interact_connect() probes for InterAct joysticks. + */ + +static void interact_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct interact *interact; + __u32 data[3]; + int i, t; + + if (!(interact = kmalloc(sizeof(struct interact), GFP_KERNEL))) + return; + memset(interact, 0, sizeof(struct interact)); + + gameport->private = interact; + + interact->gameport = gameport; + init_timer(&interact->timer); + interact->timer.data = (long) interact; + interact->timer.function = interact_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data); + + if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) { + goto fail2; + } + + for (i = 0; interact_type[i].length; i++) + if (interact_type[i].id == (data[2] >> 16)) + break; + + if (!interact_type[i].length) { + printk(KERN_WARNING "interact.c: Unknown joystick on gameport%d. [len %d d0 %08x d1 %08x i2 %08x]\n", + gameport->number, i, data[0], data[1], data[2]); + goto fail2; + } + + interact->type = i; + interact->length = interact_type[i].length; + + interact->dev.private = interact; + interact->dev.open = interact_open; + interact->dev.close = interact_close; + + interact->dev.name = interact_type[i].name; + interact->dev.idbus = BUS_GAMEPORT; + interact->dev.idvendor = GAMEPORT_ID_VENDOR_INTERACT; + interact->dev.idproduct = interact_type[i].id; + interact->dev.idversion = 0x0100; + + interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) { + set_bit(t, interact->dev.absbit); + if (i < interact_type[interact->type].b8) { + interact->dev.absmin[t] = 0; + interact->dev.absmax[t] = 255; + } else { + interact->dev.absmin[t] = -1; + interact->dev.absmax[t] = 1; + } + } + + for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++) + set_bit(t, interact->dev.keybit); + + input_register_device(&interact->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + interact->dev.number, interact_type[interact->type].name, gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(interact); +} + +static void interact_disconnect(struct gameport *gameport) +{ + struct interact *interact = gameport->private; + input_unregister_device(&interact->dev); + gameport_close(gameport); + kfree(interact); +} + +static struct gameport_dev interact_dev = { + connect: interact_connect, + disconnect: interact_disconnect, +}; + +int __init interact_init(void) +{ + gameport_register_device(&interact_dev); + return 0; +} + +void __exit interact_exit(void) +{ + gameport_unregister_device(&interact_dev); +} + +module_init(interact_init); +module_exit(interact_exit); diff --git a/drivers/char/joystick/joy-amiga.c b/drivers/char/joystick/joy-amiga.c deleted file mode 100644 index 8c0ba6923..000000000 --- a/drivers/char/joystick/joy-amiga.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * joy-amiga.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * microswitch based joystick connected to Amiga joystick port. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/system.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/amigahw.h> -#include <linux/init.h> - -static struct js_port* js_am_port __initdata = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_am, "1-2i"); - -static int __initdata js_am[] = { 0, 0 }; - -/* - * js_am_read() reads and Amiga joystick data. - */ - -static int js_am_read(void *info, int **axes, int **buttons) -{ - int data = 0; - - switch (*(int*)info) { - case 0: - data = ~custom.joy0dat; - buttons[0][0] = (~ciaa.pra >> 6) & 1; - break; - - case 1: - data = ~custom.joy1dat; - buttons[0][0] = (~ciaa.pra >> 7) & 1; - break; - - default: - return -1; - } - - axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); - data = ~(data ^ (data << 1)); - axes[0][1] = ((data >> 1) & 1) - ((data >> 9) & 1); - - return 0; -} - -/* - * js_am_init_corr() initializes correction values of - * Amiga joysticks. - */ - -static void __init js_am_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } -} - -#ifndef MODULE -int __init js_am_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; - return 1; -} -__setup("js_am=", js_am_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_am_init(void) -#endif -{ - int i; - - for (i = 0; i < 2; i++) - if (js_am[i]) { - js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read); - printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n", - js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", THIS_MODULE, NULL, NULL), i); - js_am_init_corr(js_am_port->corr); - } - if (js_am_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-amiga: no joysticks specified\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - while (js_am_port) { - if (js_am_port->devs[0]) - js_unregister_device(js_am_port->devs[0]); - js_am_port = js_unregister_port(js_am_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-analog.c b/drivers/char/joystick/joy-analog.c deleted file mode 100644 index f73ee8ded..000000000 --- a/drivers/char/joystick/joy-analog.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * joy-analog.c Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * up to two analog (or CHF/FCS) joysticks on a single joystick port. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/param.h> -#include <asm/system.h> -#include <linux/config.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_AN_MAX_TIME 3000 /* 3 ms */ -#define JS_AN_LOOP_TIME 2000 /* 2 t */ - -static int js_an_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_an_port __initdata = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_an, "2-24i"); - -static int __initdata js_an[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -#include "joy-analog.h" - -struct js_ax_info { - int io; - int speed; - int loop; - int timeout; - struct js_an_info an; -}; - -/* - * Time macros. - */ - -#ifdef __i386__ -#ifdef CONFIG_X86_TSC -#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "TSC" -#else -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) -#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) -#define TIME_NAME "PIT" -#endif -#elif __alpha__ -#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "PCC" -#endif - -#ifndef GET_TIME -#define FAKE_TIME -static unsigned long js_an_faketime = 0; -#define GET_TIME(x) do { x = js_an_faketime++; } while(0) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "Unreliable" -#endif - -/* - * js_an_read() reads analog joystick data. - */ - -static int js_an_read(void *xinfo, int **axes, int **buttons) -{ - struct js_ax_info *info = xinfo; - struct js_an_info *an = &info->an; - int io = info->io; - unsigned long flags; - unsigned char buf[4]; - unsigned int time[4]; - unsigned char u, v, w; - unsigned int p, q, r, s, t; - int i, j; - - an->buttons = ~inb(io) >> 4; - - i = 0; - w = ((an->mask[0] | an->mask[1]) & JS_AN_AXES_STD) | (an->extensions & JS_AN_HAT_FCS) - | ((an->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((an->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - p = info->loop; - q = info->timeout; - - __save_flags(flags); - __cli(); - outb(0xff,io); - GET_TIME(r); - __restore_flags(flags); - t = r; - v = w; - do { - s = t; - u = v; - __cli(); - v = inb(io) & w; - GET_TIME(t); - __restore_flags(flags); - if ((u ^ v) && (DELTA(t,s) < p)) { - time[i] = t; - buf[i] = u ^ v; - i++; - } - } while (v && (i < 4) && (DELTA(t,r) < q)); - - v <<= 4; - - for (--i; i >= 0; i--) { - v |= buf[i]; - for (j = 0; j < 4; j++) - if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed; - } - - js_an_decode(an, axes, buttons); - - return -(v != w); -} - -/* - * js_an_calibrate_timer() calibrates the timer and computes loop - * and timeout values for a joystick port. - */ - -static void __init js_an_calibrate_timer(struct js_ax_info *info) -{ - unsigned int i, t, tx, t1, t2, t3; - unsigned long flags; - int io = info->io; - - save_flags(flags); - cli(); - GET_TIME(t1); -#ifdef FAKE_TIME - js_an_faketime += 830; -#endif - udelay(1000); - GET_TIME(t2); - GET_TIME(t3); - restore_flags(flags); - - info->speed = DELTA(t2, t1) - DELTA(t3, t2); - - tx = 1 << 30; - - for(i = 0; i < 50; i++) { - save_flags(flags); - cli(); - GET_TIME(t1); - for(t = 0; t < 50; t++) { inb(io); GET_TIME(t2); } - GET_TIME(t3); - restore_flags(flags); - udelay(i); - if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; - } - - info->loop = (JS_AN_LOOP_TIME * t) / 50000; - info->timeout = (JS_AN_MAX_TIME * info->speed) / 1000; -} - -/* - * js_an_probe() probes for analog joysticks. - */ - -static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) -{ - struct js_ax_info info, *ax; - int i, numdev; - unsigned char u; - - if (io < 0) return port; - - if (check_region(io, 1)) return port; - - outb(0xff,io); - u = inb(io); - udelay(JS_AN_MAX_TIME); - u = (inb(io) ^ u) & u; - - if (!u) return port; - if (u & 0xf0) return port; - - if ((numdev = js_an_probe_devs(&info.an, u, mask0, mask1, port)) <= 0) - return port; - - info.io = io; - js_an_calibrate_timer(&info); - - request_region(info.io, 1, "joystick (analog)"); - port = js_register_port(port, &info, numdev, sizeof(struct js_ax_info), js_an_read); - ax = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s at %#x ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", - js_register_device(port, i, js_an_axes(i, &ax->an), js_an_buttons(i, &ax->an), - js_an_name(i, &ax->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &ax->an), - ax->io, - ax->speed > 10000 ? (ax->speed + 800) / 1000 : ax->speed, - ax->speed > 10000 ? "M" : "k", - ax->loop * 1000000000 / JS_AN_LOOP_TIME / ax->speed); - - js_an_read(ax, port->axes, port->buttons); - js_an_init_corr(&ax->an, port->axes, port->corr, 8); - - return port; -} - -#ifndef MODULE -int __init js_an_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; - return 1; -} -__setup("js_an=", js_an_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_an_init(void) -#endif -{ - int i; - - if (js_an[0] >= 0) { - for (i = 0; (js_an[i*3] >= 0) && i < 8; i++) - js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port); - } else { - for (i = 0; js_an_port_list[i]; i++) - js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port); - } - if (js_an_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-analog: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_ax_info *info; - - while (js_an_port) { - for (i = 0; i < js_an_port->ndevs; i++) - if (js_an_port->devs[i]) - js_unregister_device(js_an_port->devs[i]); - info = js_an_port->info; - release_region(info->io, 1); - js_an_port = js_unregister_port(js_an_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-analog.h b/drivers/char/joystick/joy-analog.h deleted file mode 100644 index a1644350c..000000000 --- a/drivers/char/joystick/joy-analog.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * joy-analog.h Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This file is designed to be included in any joystick driver - * that communicates with standard analog joysticks. This currently - * is: joy-analog.c, joy-assassin.c, and joy-lightning.c - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/bitops.h> - -#define JS_AN_AXES_STD 0x0f -#define JS_AN_BUTTONS_STD 0xf0 - -#define JS_AN_BUTTONS_CHF 0x01 -#define JS_AN_HAT1_CHF 0x02 -#define JS_AN_HAT2_CHF 0x04 -#define JS_AN_ANY_CHF 0x07 -#define JS_AN_HAT_FCS 0x08 -#define JS_AN_HATS_ALL 0x0e -#define JS_AN_BUTTON_PXY_X 0x10 -#define JS_AN_BUTTON_PXY_Y 0x20 -#define JS_AN_BUTTON_PXY_U 0x40 -#define JS_AN_BUTTON_PXY_V 0x80 -#define JS_AN_BUTTONS_PXY_XY 0x30 -#define JS_AN_BUTTONS_PXY_UV 0xc0 -#define JS_AN_BUTTONS_PXY 0xf0 - -static struct { - int x; - int y; -} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; - -struct js_an_info { - unsigned char mask[2]; - unsigned int extensions; - int axes[4]; - int initial[4]; - unsigned char buttons; -}; - -/* - * js_an_decode() decodes analog joystick data. - */ - -static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) -{ - int i, j, k; - int hat1, hat2, hat3; - - hat1 = hat2 = hat3 = 0; - if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0; - if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; - - if (info->extensions & JS_AN_ANY_CHF) { - switch (info->buttons & 0xf) { - case 0x1: buttons[0][0] = 0x01; break; - case 0x2: buttons[0][0] = 0x02; break; - case 0x4: buttons[0][0] = 0x04; break; - case 0x8: buttons[0][0] = 0x08; break; - case 0x5: buttons[0][0] = 0x10; break; - case 0x9: buttons[0][0] = 0x20; break; - case 0xf: hat1 = 1; break; - case 0xb: hat1 = 2; break; - case 0x7: hat1 = 3; break; - case 0x3: hat1 = 4; break; - case 0xe: hat2 = 1; break; - case 0xa: hat2 = 2; break; - case 0x6: hat2 = 3; break; - case 0xc: hat2 = 4; break; - } - k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4; - } else { - for (i = 1; i >= 0; i--) - for (j = k = 0; j < 4; j++) - if (info->mask[i] & (0x10 << j)) - buttons[i][0] |= ((info->buttons >> j) & 1) << k++; - } - - if (info->extensions & JS_AN_BUTTON_PXY_X) - buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_Y) - buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_U) - buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_V) - buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++; - - if (info->extensions & JS_AN_HAT_FCS) - for (j = 0; j < 4; j++) - if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) { - hat3 = j + 1; - break; - } - - for (i = 1; i >= 0; i--) - for (j = k = 0; j < 4; j++) - if (info->mask[i] & (1 << j)) - axes[i][k++] = info->axes[j]; - - if (info->extensions & JS_AN_HAT1_CHF) { - axes[0][k++] = js_an_hat_to_axis[hat1].x; - axes[0][k++] = js_an_hat_to_axis[hat1].y; - } - if (info->extensions & JS_AN_HAT2_CHF) { - axes[0][k++] = js_an_hat_to_axis[hat2].x; - axes[0][k++] = js_an_hat_to_axis[hat2].y; - } - if (info->extensions & JS_AN_HAT_FCS) { - axes[0][k++] = js_an_hat_to_axis[hat3].x; - axes[0][k++] = js_an_hat_to_axis[hat3].y; - } -} - - -/* - * js_an_init_corr() initializes the correction values for - * analog joysticks. - */ - -static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec) -{ - int i, j, t; - - for (i = 0; i < 2; i++) - for (j = 0; j < hweight8(info->mask[i] & 0xf); j++) { - - if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || - (j == 3 && (info->mask[i] & 0xf) == 0xf)) { - t = (axes[i][0] + axes[i][1]) >> 1; - } else { - t = axes[i][j]; - } - - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = prec; - corr[i][j].coef[0] = t - (t >> 3); - corr[i][j].coef[1] = t + (t >> 3); - corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1); - corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); - } - - i = hweight8(info->mask[0] & 0xf); - - for (j = i; j < i + (hweight8(info->extensions & JS_AN_HATS_ALL) << 1); j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); - } - - for (i = 0; i < 4; i++) - info->initial[i] = info->axes[i]; -} - - -/* - * js_an_probe_devs() probes for analog joysticks. - */ - -static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port) -{ - info->mask[0] = info->mask[1] = info->extensions = 0; - - if (mask0 || mask1) { - info->mask[0] = mask0 & (exist | 0xf0); - info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; - info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | - ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); - - if (info->extensions & JS_AN_BUTTONS_PXY) { - info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); - info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - info->mask[1] = 0; - } - if (info->extensions & JS_AN_HAT_FCS) { - info->mask[0] &= ~JS_AN_HAT_FCS; - info->mask[1] = 0; - info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_V); - } - if (info->extensions & JS_AN_ANY_CHF) { - info->mask[0] |= 0xf0; - info->mask[1] = 0; - } - if (!(info->mask[0] | info->mask[1])) return -1; - } else { - switch (exist) { - case 0x0: - return -1; - case 0x3: - info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */ - break; - case 0xb: - info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */ - break; - case 0xc: - info->mask[0] = 0xcc; /* joystick 1 */ - break; - case 0xf: - info->mask[0] = 0xff; /* 4-axis 4-button joystick */ - break; - default: - printk(KERN_WARNING "joy-analog: Unknown joystick device detected " - "(data=%#x), contact <vojtech@suse.cz>\n", exist); - return -1; - } - } - - return !!info->mask[0] + !!info->mask[1]; -} - -/* - * js_an_axes() returns the number of axes for an analog joystick. - */ - -static inline int js_an_axes(int i, struct js_an_info *info) -{ - return hweight8(info->mask[i] & 0x0f) + hweight8(info->extensions & JS_AN_HATS_ALL) * 2; -} - -/* - * js_an_buttons() returns the number of buttons for an analog joystick. - */ - -static inline int js_an_buttons(int i, struct js_an_info *info) -{ - return hweight8(info->mask[i] & 0xf0) + - (info->extensions & JS_AN_BUTTONS_CHF) * 2 + - hweight8(info->extensions & JS_AN_BUTTONS_PXY); -} - -/* - * js_an_name() constructs a name for an analog joystick. - */ - -static char js_an_name_buf[128] __initdata = ""; - -static char __init *js_an_name(int i, struct js_an_info *info) -{ - - sprintf(js_an_name_buf, "Analog %d-axis %d-button", - hweight8(info->mask[i] & 0x0f), - js_an_buttons(i, info)); - - if (info->extensions & JS_AN_HATS_ALL) - sprintf(js_an_name_buf, "%s %d-hat", - js_an_name_buf, - hweight8(info->extensions & JS_AN_HATS_ALL)); - - strcat(js_an_name_buf, " joystick"); - - if (info->extensions) - sprintf(js_an_name_buf, "%s with%s%s%s extensions", - js_an_name_buf, - info->extensions & JS_AN_ANY_CHF ? " CHF" : "", - info->extensions & JS_AN_HAT_FCS ? " FCS" : "", - info->extensions & JS_AN_BUTTONS_PXY ? " XY/UV" : ""); - - return js_an_name_buf; -} diff --git a/drivers/char/joystick/joy-assassin.c b/drivers/char/joystick/joy-assassin.c deleted file mode 100644 index 469c6c8ce..000000000 --- a/drivers/char/joystick/joy-assassin.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * joy-assassin.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * joysticks using FP-Gaming's Assassin 3D protocol. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_AS_MAX_START 1000 -#define JS_AS_DELAY_READ 3000 -#define JS_AS_MAX_LENGTH 40 - -#define JS_AS_MODE_A3D 1 /* Assassin 3D */ -#define JS_AS_MODE_PAN 2 /* Panther */ -#define JS_AS_MODE_OEM 3 /* Panther OEM version */ -#define JS_AS_MODE_PXL 4 /* Panther XL */ - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_as, "2-24i"); - -static int __initdata js_as[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -static int js_as_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_as_port __initdata = NULL; - -#include "joy-analog.h" - -struct js_as_info { - int io; - char mode; - char rudder; - struct js_an_info an; -}; - -/* - * js_as_read_packet() reads an Assassin 3D packet. - */ - -static int js_as_read_packet(int io, int length, char *data) -{ - unsigned char u, v; - int i; - unsigned int t, p; - unsigned long flags; - - i = 0; - - __save_flags(flags); - __cli(); - - outb(0xff,io); - v = inb(io); - t = p = JS_AS_MAX_START; - - while (t > 0 && i < length) { - t--; - u = v; v = inb(io); - if (~v & u & 0x10) { - data[i++] = v >> 5; - p = t = (p - t) << 3; - } - } - - __restore_flags(flags); - - return i; -} - -/* - * js_as_csum() computes checksum of triplet packet - */ - -static int js_as_csum(char *data, int count) -{ - int i, csum = 0; - for (i = 0; i < count - 2; i++) csum += data[i]; - return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); -} - -/* - * js_as_read() reads and analyzes A3D joystick data. - */ - -static int js_as_read(void *xinfo, int **axes, int **buttons) -{ - struct js_as_info *info = xinfo; - char data[JS_AS_MAX_LENGTH]; - - switch (info->mode) { - - case JS_AS_MODE_A3D: - case JS_AS_MODE_OEM: - case JS_AS_MODE_PAN: - - if (js_as_read_packet(info->io, 29, data) != 29) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 29)) return -1; - - axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); - axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); - - buttons[0][0] = (data[2] << 2) | (data[3] >> 1); - - info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; - info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; - info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; - info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; - - info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; - - js_an_decode(&info->an, axes + 1, buttons + 1); - - return 0; - - case JS_AS_MODE_PXL: - - if (js_as_read_packet(info->io, 33, data) != 33) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 33)) return -1; - - axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; - axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; - info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; - axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; - - axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); - axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); - axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); - axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); - - axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); - axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); - - buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; - - if (info->rudder) axes[1][0] = info->an.axes[0]; - - return 0; - } - return -1; -} - -/* - * js_as_pxl_init_corr() initializes the correction values for - * the Panther XL. - */ - -static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = axes[0][i] - 4; - corr[0][i].coef[1] = axes[0][i] + 4; - corr[0][i].coef[2] = (1 << 29) / (127 - 32); - corr[0][i].coef[3] = (1 << 29) / (127 - 32); - } - - corr[0][2].type = JS_CORR_BROKEN; - corr[0][2].prec = 0; - corr[0][2].coef[0] = 127 - 4; - corr[0][2].coef[1] = 128 + 4; - corr[0][2].coef[2] = (1 << 29) / (127 - 6); - corr[0][2].coef[3] = (1 << 29) / (127 - 6); - - for (i = 3; i < 7; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } - - for (i = 7; i < 9; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_as_init_corr() initializes the correction values for - * the Panther and Assassin. - */ - -static void __init js_as_as_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_rudder_init_corr() initializes the correction values for - * the Panther XL connected rudder. - */ - -static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) -{ - corr[1][0].type = JS_CORR_BROKEN; - corr[1][0].prec = 0; - corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); - corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); - corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); - corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); -} - -/* - * js_as_probe() probes for A3D joysticks. - */ - -static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) -{ - struct js_as_info iniinfo; - struct js_as_info *info = &iniinfo; - char *name; - char data[JS_AS_MAX_LENGTH]; - unsigned char u; - int i; - int numdev; - - memset(info, 0, sizeof(struct js_as_info)); - - if (io < 0) return port; - - if (check_region(io, 1)) return port; - - i = js_as_read_packet(io, JS_AS_MAX_LENGTH, data); - - printk("%d\n", i); - - if (!i) return port; - if (js_as_csum(data, i)) return port; - - if (data[0] && data[0] <= 4) { - info->mode = data[0]; - info->io = io; - request_region(io, 1, "joystick (assassin)"); - port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); - info = port->info; - } else { - printk(KERN_WARNING "joy-assassin: unknown joystick device detected " - "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io, data[0]); - return port; - } - - udelay(JS_AS_DELAY_READ); - - if (info->mode == JS_AS_MODE_PXL) { - printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", - js_register_device(port, 0, 9, 9, "MadCatz Panther XL", THIS_MODULE, NULL, NULL), - info->io); - js_as_read(port->info, port->axes, port->buttons); - js_as_pxl_init_corr(port->corr, port->axes); - if (info->an.axes[0] < 254) { - printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", - js_register_device(port, 1, 1, 0, "Analog rudder", THIS_MODULE, NULL, NULL)); - info->rudder = 1; - port->axes[1][0] = info->an.axes[0]; - js_as_rudder_init_corr(port->corr, port->axes); - } - return port; - } - - switch (info->mode) { - case JS_AS_MODE_A3D: name = "FP-Gaming Assassin 3D"; break; - case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; - case JS_AS_MODE_OEM: name = "OEM Assassin 3D"; break; - default: name = "This cannot happen"; break; - } - - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, 2, 3, name, THIS_MODULE, NULL, NULL), - name, info->io); - - js_as_as_init_corr(port->corr); - - js_as_read(port->info, port->axes, port->buttons); - - for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &info->an), name); - - js_an_decode(&info->an, port->axes + 1, port->buttons + 1); - js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); - - return port; -} - -#ifndef MODULE -int __init js_as_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; - return 1; -} -__setup("js_as=", js_as_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_as_init(void) -#endif -{ - int i; - - if (js_as[0] >= 0) { - for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) - js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); - } else { - for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); - } - if (js_as_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-assassin: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_as_info *info; - - while (js_as_port) { - for (i = 0; i < js_as_port->ndevs; i++) - if (js_as_port->devs[i]) - js_unregister_device(js_as_port->devs[i]); - info = js_as_port->info; - release_region(info->io, 1); - js_as_port = js_unregister_port(js_as_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-console.c b/drivers/char/joystick/joy-console.c deleted file mode 100644 index e56da540a..000000000 --- a/drivers/char/joystick/joy-console.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * joy-console.c Version 0.14V - * - * Copyright (c) 1998 Andree Borrmann - * Copyright (c) 1999 John Dahlstrom - * Copyright (c) 1999 David Kuder - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * console (NES, SNES, N64, Multi1, Multi2, PSX) gamepads - * connected via parallel port. Up to five such controllers - * can be connected to one parallel port. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_console, "2-6i"); -MODULE_PARM(js_console_2,"2-6i"); -MODULE_PARM(js_console_3,"2-6i"); - - -#define JS_NO_PAD 0 -#define JS_SNES_PAD 1 -#define JS_NES_PAD 2 -#define JS_NES4_PAD 3 -#define JS_MULTI_STICK 4 -#define JS_MULTI2_STICK 5 -#define JS_PSX_PAD 6 -#define JS_N64_PAD 7 -#define JS_N64_PAD_DPP 8 /* DirectPad Pro compatible layout */ - -#define JS_MAX_PAD JS_N64_PAD_DPP - -struct js_console_info { - struct pardevice *port; /* parport device */ - int pads; /* total number of pads */ - int pad_to_device[5]; /* pad to js device mapping (js0, js1, etc.) */ - int snes; /* SNES pads */ - int nes; /* NES pads */ - int n64; /* N64 pads */ - int n64_dpp; /* bits indicate N64 pads treated 14 button, 2 axis */ - int multi; /* Multi joysticks */ - int multi2; /* Multi joysticks with 2 buttons */ - int psx; /* PSX controllers */ -}; - -static struct js_port* js_console_port = NULL; - -static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; - -static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; - -/* - * NES/SNES support. - */ - -#define JS_NES_DELAY 6 /* Delay between bits - 6us */ - -#define JS_NES_LENGTH 8 /* The NES pads use 8 bits of data */ - -#define JS_NES_A 0 -#define JS_NES_B 1 -#define JS_NES_START 2 -#define JS_NES_SELECT 3 -#define JS_NES_UP 4 -#define JS_NES_DOWN 5 -#define JS_NES_LEFT 6 -#define JS_NES_RIGHT 7 - -#define JS_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ - -#define JS_SNES_B 0 -#define JS_SNES_Y 1 -#define JS_SNES_START 2 -#define JS_SNES_SELECT 3 -#define JS_SNES_UP 4 -#define JS_SNES_DOWN 5 -#define JS_SNES_LEFT 6 -#define JS_SNES_RIGHT 7 -#define JS_SNES_A 8 -#define JS_SNES_X 9 -#define JS_SNES_L 10 -#define JS_SNES_R 11 - -#define JS_NES_POWER 0xfc -#define JS_NES_CLOCK 0x01 -#define JS_NES_LATCH 0x02 - -/* - * js_nes_read_packet() reads a NES/SNES packet. - * Each pad uses one bit per byte. So all pads connected to - * this port are read in parallel. - */ - -static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i; - - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK | JS_NES_LATCH, info->port); - udelay(JS_NES_DELAY * 2); - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); - - for (i = 0; i < length; i++) { - udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER, info->port); - data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); - } -} - -/* - * N64 support. - */ - -#define JS_N64_A 0 -#define JS_N64_B 1 -#define JS_N64_Z 2 -#define JS_N64_START 3 -#define JS_N64_UP 4 -#define JS_N64_DOWN 5 -#define JS_N64_LEFT 6 -#define JS_N64_RIGHT 7 -#define JS_N64_UNUSED1 8 -#define JS_N64_UNUSED2 9 -#define JS_N64_L 10 -#define JS_N64_R 11 -#define JS_N64_CU 12 -#define JS_N64_CD 13 -#define JS_N64_CL 14 -#define JS_N64_CR 15 -#define JS_N64_X 23 /* 16 - 23, signed 8-bit int */ -#define JS_N64_Y 31 /* 24 - 31, signed 8-bit int */ - -#define JS_N64_LENGTH 32 /* N64 bit length, not including stop bit */ -#define JS_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ -#define JS_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ -#define JS_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ -#define JS_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ - /* JS_N64_DWS > 24 is known to fail */ -#define JS_N64_POWER_W 0xe2 /* power during write (transmit request) */ -#define JS_N64_POWER_R 0xfd /* power during read */ -#define JS_N64_OUT 0x1d /* output bits to the 4 pads */ - /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ - /* in JS_N64_OUT is pulled low on the output port (by any routine) for more */ - /* than 0.123 consecutive ms */ -#define JS_N64_CLOCK 0x02 /* clock bits for read */ - -/* - * js_n64_read_packet() reads an N64 packet. - * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. - */ - -static void js_n64_read_packet(struct js_console_info *info, unsigned char *data) -{ - int i; - unsigned long flags; - -/* - * Request the pad to transmit data - */ - - save_flags(flags); - cli(); - for (i = 0; i < JS_N64_REQUEST_LENGTH; i++) { - JS_PAR_DATA_OUT(JS_N64_POWER_W | ((JS_N64_REQUEST >> i) & 1 ? JS_N64_OUT : 0), info->port); - udelay(JS_N64_DWS); - } - restore_flags(flags); - -/* - * Wait for the pad response to be loaded into the 33-bit register of the adapter - */ - - udelay(JS_N64_DELAY); - -/* - * Grab data (ignoring the last bit, which is a stop bit) - */ - - for (i = 0; i < JS_N64_LENGTH; i++) { - JS_PAR_DATA_OUT(JS_N64_POWER_R, info->port); - data[i] = JS_PAR_STATUS(info->port); - JS_PAR_DATA_OUT(JS_N64_POWER_R | JS_N64_CLOCK, info->port); - } - -/* - * We must wait ~0.2 ms here for the controller to reinitialize before the next read request. - * No worries as long as js_console_read is polled less frequently than this. - */ - -} - -/* - * Multisystem joystick support - */ - -#define JS_MULTI_LENGTH 5 /* Multi system joystick packet lenght is 5 */ -#define JS_MULTI2_LENGTH 6 /* One more bit for one more button */ - -#define JS_MULTI_UP 0 -#define JS_MULTI_DOWN 1 -#define JS_MULTI_LEFT 2 -#define JS_MULTI_RIGHT 3 -#define JS_MULTI_BUTTON 4 -#define JS_MULTI_BUTTON2 5 - -/* - * js_multi_read_packet() reads a Multisystem joystick packet. - */ - -static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i; - - for (i = 0; i < length; i++) { - JS_PAR_DATA_OUT(~(1 << i), info->port); - data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - printk(" %d", data[i]); - } - printk("\n"); -} - -/* - * PSX support - */ - -#define JS_PSX_DELAY 10 -#define JS_PSX_LENGTH 8 /* talk to the controller in bytes */ - -#define JS_PSX_NORMAL 0x41 /* Standard Digital controller */ -#define JS_PSX_NEGCON 0x23 /* NegCon pad */ -#define JS_PSX_MOUSE 0x12 /* PSX Mouse */ -#define JS_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ -#define JS_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ - -#define JS_PSX_JOYR 0x02 /* These are for the Analog/Dual Shock controller in RED mode */ -#define JS_PSX_JOYL 0x04 /* I'm not sure the exact purpose of these but its in the docs */ -#define JS_PSX_SELBUT 0x01 /* Standard buttons on almost all PSX controllers. */ -#define JS_PSX_START 0x08 -#define JS_PSX_UP 0x10 /* Digital direction pad */ -#define JS_PSX_RIGHT 0x20 -#define JS_PSX_DOWN 0x40 -#define JS_PSX_LEFT 0x80 - -#define JS_PSX_CLOCK 0x04 /* Pin 3 */ -#define JS_PSX_COMMAND 0x01 /* Pin 1 */ -#define JS_PSX_POWER 0xf8 /* Pins 5-9 */ -#define JS_PSX_SELECT 0x02 /* Pin 2 */ -#define JS_PSX_NOPOWER 0x04 - -/* - * js_psx_command() writes 8bit command and reads 8bit data from - * the psx pad. - */ - -static int js_psx_command(struct js_console_info *info, int b) -{ - int i, cmd, ret=0; - - cmd = (b&1)?JS_PSX_COMMAND:0; - for (i=0; i<8; i++) { - JS_PAR_DATA_OUT(cmd | JS_PSX_POWER, info->port); - udelay(JS_PSX_DELAY); - ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0; - cmd = (b&1)?JS_PSX_COMMAND:0; - JS_PAR_DATA_OUT(cmd | JS_PSX_CLOCK | JS_PSX_POWER, info->port); - udelay(JS_PSX_DELAY); - b >>= 1; - } - return ret; -} - -/* - * js_psx_read_packet() reads a whole psx packet and returns - * device identifier code. - */ - -static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i, ret; - unsigned long flags; - - __save_flags(flags); - __cli(); - - JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); - - JS_PAR_DATA_OUT(JS_PSX_CLOCK | JS_PSX_SELECT | JS_PSX_POWER, info->port); /* Select pad */ - udelay(JS_PSX_DELAY*2); - js_psx_command(info, 0x01); /* Access pad */ - ret = js_psx_command(info, 0x42); /* Get device id */ - if (js_psx_command(info, 0)=='Z') /* okay? */ - for (i=0; i<length; i++) - data[i]=js_psx_command(info, 0); - else ret = -1; - - JS_PAR_DATA_OUT(JS_PSX_SELECT | JS_PSX_CLOCK | JS_PSX_POWER, info->port); - __restore_flags(flags); - - return ret; -} - - -/* - * js_console_read() reads and analyzes console pads data. - */ - -#define JS_MAX_LENGTH JS_N64_LENGTH - -static int js_console_read(void *xinfo, int **axes, int **buttons) -{ - struct js_console_info *info = xinfo; - unsigned char data[JS_MAX_LENGTH]; - - int i, j, s; - int n = 0; - -/* - * NES and SNES pads - */ - - if (info->nes || info->snes) { - - js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->nes & s) { - axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); - axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - - buttons[n][0] = (data[JS_NES_A] &s?1:0) | (data[JS_NES_B] &s?2:0) - | (data[JS_NES_START]&s?4:0) | (data[JS_NES_SELECT]&s?8:0); - } else - if (info->snes & s) { - axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); - axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - - buttons[n][0] = (data[JS_SNES_A] &s?0x01:0) | (data[JS_SNES_B] &s?0x02:0) - | (data[JS_SNES_X] &s?0x04:0) | (data[JS_SNES_Y] &s?0x08:0) - | (data[JS_SNES_L] &s?0x10:0) | (data[JS_SNES_R] &s?0x20:0) - | (data[JS_SNES_START]&s?0x40:0) | (data[JS_SNES_SELECT]&s?0x80:0); - } - } - } - -/* - * N64 pads - */ - - if (info->n64) { - if ( (info->nes || info->snes) && (info->n64 & status_bit[0]) ) { - /* SNES/NES compatibility */ - udelay(240); /* 200 us delay + 20% tolerance */ - } - - js_n64_read_packet(info, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->n64 & s & ~(data[JS_N64_UNUSED1] | data[JS_N64_UNUSED2])) { - - buttons[n][0] = ( ((data[JS_N64_A]&s) ? 0x01:0) | ((data[JS_N64_B] & s ) ? 0x02:0) - | ((data[JS_N64_Z]&s) ? 0x04:0) | ((data[JS_N64_L] & s ) ? 0x08:0) - | ((data[JS_N64_R]&s) ? 0x10:0) | ((data[JS_N64_START]&s)? 0x20:0) - | ((data[JS_N64_CU]&s)? 0x40:0) | ((data[JS_N64_CR]&s) ? 0x80:0) - | ((data[JS_N64_CD]&s)?0x100:0) | ((data[JS_N64_CL]&s) ?0x200:0) ); - - if (info->n64_dpp & s) { - buttons[n][0] |= ((data[JS_N64_LEFT]&s) ? 0x400:0) | ((data[JS_N64_UP] & s)? 0x800:0) - |((data[JS_N64_RIGHT]&s)?0x1000:0) | ((data[JS_N64_DOWN]&s)?0x2000:0); - } else { - axes[n][2] = (data[JS_N64_RIGHT]&s?1:0) - (data[JS_N64_LEFT]&s?1:0); - axes[n][3] = (data[JS_N64_DOWN] &s?1:0) - (data[JS_N64_UP] &s?1:0); - } - - /* build int from bits of signed 8-bit int's */ - j = 7; - axes[n][0] = (data[JS_N64_X - j] & s) ? ~0x7f : 0; - axes[n][1] = (data[JS_N64_Y - j] & s) ? ~0x7f : 0; - while ( j-- > 0 ) { - axes[n][0] |= (data[JS_N64_X - j] & s) ? (1 << j) : 0; - axes[n][1] |= (data[JS_N64_Y - j] & s) ? (1 << j) : 0; - } - /* flip Y-axis for conformity */ - axes[n][1] = -axes[n][1]; - - } - } - } - -/* - * Multi and Multi2 joysticks - */ - - if (info->multi || info->multi2) { - - js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->multi & s) { - axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); - axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); - - buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; - } else - if (info->multi2 & s) { - axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); - axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); - - buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; - } - } - } - -/* - * PSX controllers - */ - - if (info->psx) { - - for ( i = 0; i < 5; i++ ) - if ( info->psx & status_bit[i] ) { - n = info->pad_to_device[i]; - break; - } - - buttons[n][0] = 0; - - switch (js_psx_read_packet(info, 6, data)) { - - case JS_PSX_ANALOGR: - - buttons[n][0] |= (data[0]&JS_PSX_JOYL?0:0x800) | (data[0]&JS_PSX_JOYR?0:0x400); - - case JS_PSX_ANALOGG: - - axes[n][2] = data[2]; - axes[n][3] = data[3]; - axes[n][4] = data[4]; - axes[n][5] = data[5]; - - case JS_PSX_NORMAL: - case JS_PSX_NEGCON: - - axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); - axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); - - buttons[n][0] |= ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | - (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); - - break; - - } - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: if parport_claim() will sleep we can get into mess. - */ - -int js_console_open(struct js_dev *dev) -{ - struct js_console_info *info = dev->port->info; - if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY; - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_console_close(struct js_dev *dev) -{ - struct js_console_info *info = dev->port->info; - MOD_DEC_USE_COUNT; - if (!MOD_IN_USE) parport_release(info->port); - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_console_info *info; - int i; - - while (js_console_port) { - for (i = 0; i < js_console_port->ndevs; i++) - if (js_console_port->devs[i]) - js_unregister_device(js_console_port->devs[i]); - info = js_console_port->info; - parport_unregister_device(info->port); - js_console_port = js_unregister_port(js_console_port); - } -} -#endif - -/* - * js_console_init_corr() initializes correction values of - * console gamepads. - */ - -static void __init js_console_init_corr(int num_axes, int type, struct js_corr *corr) -{ - int i; - - for (i = 0; i < num_axes; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - if (type == JS_N64_PAD || type == JS_N64_PAD_DPP) { - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 22); - corr[i].coef[3] = (1 << 22); - } - } - - if (type == JS_PSX_ANALOGG || type == JS_PSX_ANALOGR) { - for (i = 2; i < 6; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 127 - 2; - corr[i].coef[1] = 128 + 2; - corr[i].coef[2] = (1 << 29) / (127 - 4); - corr[i].coef[3] = (1 << 29) / (127 - 4); - } - } -} - -/* - * js_console_probe() probes for console gamepads. - * Only PSX pads can really be probed for. - */ - -static struct js_port __init *js_console_probe(int *config, struct js_port *port) -{ - char *name[5]; - int i, psx, axes[5], buttons[5], type[5]; - unsigned char data[2]; /* used for PSX probe */ - struct js_console_info info; - struct parport *pp; - - memset(&info, 0, sizeof(struct js_console_info)); - - if (config[0] < 0) return port; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-console: no such parport\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; - - if (parport_claim(info.port)) - { - parport_unregister_device(info.port); /* port currently not available ... */ - return port; - } - - for (i = 0; i < 5; i++) { - - type[info.pads] = config[i+1]; - info.pad_to_device[i] = info.pads; - - switch(config[i+1]) { - - case JS_NO_PAD: - - break; - - case JS_SNES_PAD: - - axes[info.pads] = 2; - buttons[info.pads] = 8; - name[info.pads] = "SNES pad"; - info.snes |= status_bit[i]; - info.pads++; - break; - - case JS_NES_PAD: - - axes[info.pads] = 2; - buttons[info.pads] = 4; - name[info.pads] = "NES pad"; - info.nes |= status_bit[i]; - info.pads++; - break; - - case JS_N64_PAD: - axes[info.pads] = 4; - buttons[info.pads] = 10; - name[info.pads] = "N64 pad"; - info.n64 |= status_bit[i]; - info.pads++; - break; - - case JS_N64_PAD_DPP: - axes[info.pads] = 2; - buttons[info.pads] = 14; - name[info.pads] = "N64 pad (DPP mode)"; - info.n64 |= status_bit[i]; - info.n64_dpp |= status_bit[i]; - info.pads++; - break; - - case JS_MULTI_STICK: - - axes[info.pads] = 2; - buttons[info.pads] = 1; - name[info.pads] = "Multisystem joystick"; - info.multi |= status_bit[i]; - info.pads++; - break; - - case JS_MULTI2_STICK: - - axes[info.pads] = 2; - buttons[info.pads] = 2; - name[info.pads] = "Multisystem joystick (2 fire)"; - info.multi |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_PAD: - - info.psx |= status_bit[i]; - psx = js_psx_read_packet(&info, 2, data); - psx = js_psx_read_packet(&info, 2, data); - info.psx &= ~status_bit[i]; - - type[i] = psx; - - switch(psx) { - case JS_PSX_NORMAL: - axes[info.pads] = 2; - buttons[info.pads] = 10; - name[info.pads] = "PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_ANALOGR: - axes[info.pads] = 6; - buttons[info.pads] = 12; - name[info.pads] = "Analog Red PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_ANALOGG: - axes[info.pads] = 6; - buttons[info.pads] = 10; - name[info.pads] = "Analog Green PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_NEGCON: - axes[info.pads] = 2; - buttons[info.pads] = 10; - name[info.pads] = "NegCon PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_MOUSE: - printk(KERN_WARNING "joy-psx: PSX mouse not supported...\n"); - break; - - case -1: - printk(KERN_ERR "joy-psx: no PSX controller found...\n"); - break; - - default: - printk(KERN_WARNING "joy-psx: PSX controller unknown: 0x%x," - " please report to <vojtech@suse.cz>.\n", psx); - } - break; - - default: - - printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); - } - } - - if (!info.pads) { - parport_release(info.port); - parport_unregister_device(info.port); - return port; - } - - port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); - - for (i = 0; i < info.pads; i++) { - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i, axes[i], buttons[i], name[i], NULL, js_console_open, js_console_close), - name[i], info.port->port->name); - - js_console_init_corr(axes[i], type[i], port->corr[i]); - } - - parport_release(info.port); - return port; -} - -#ifndef MODULE -int __init js_console_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; - return 1; -} -int __init js_console_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console_2[i] = ints[i+1]; - return 1; -} -int __init js_console_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console_3[i] = ints[i+1]; - return 1; -} -__setup("js_console=", js_console_setup); -__setup("js_console_2=", js_console_setup_2); -__setup("js_console_3=", js_console_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_console_init(void) -#endif -{ - js_console_port = js_console_probe(js_console, js_console_port); - js_console_port = js_console_probe(js_console_2, js_console_port); - js_console_port = js_console_probe(js_console_3, js_console_port); - - if (js_console_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-console: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-creative.c b/drivers/char/joystick/joy-creative.c deleted file mode 100644 index b5ae4c019..000000000 --- a/drivers/char/joystick/joy-creative.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * joy-creative.c Version 1.2 - * - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Creative Labs Blaster gamepad family. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_CR_MAX_STROBE 100 /* 100 us max wait for first strobe */ -#define JS_CR_LENGTH 36 - -#define JS_CR_MODE_BGPC 8 - -static int js_cr_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_cr_port __initdata = NULL; - -struct js_cr_info { - int io; - unsigned char mode[2]; -}; - -/* - * js_cr_read_packet() reads a Blaster gamepad packet. - */ - -static int js_cr_read_packet(int io, unsigned int *data) -{ - unsigned long flags; - unsigned char u, v, w; - __u64 buf[2]; - int r[2], t[2], p[2]; - int i, j, ret; - - for (i = 0; i < 2; i++) { - r[i] = buf[i] = 0; - p[i] = t[i] = JS_CR_MAX_STROBE; - p[i] += JS_CR_MAX_STROBE; - } - - __save_flags(flags); - __cli(); - - u = inb(io); - - do { - t[0]--; t[1]--; - v = inb(io); - for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) - if (w & 0x30) { - if ((w & 0x30) < 0x30 && r[i] < JS_CR_LENGTH && t[i] > 0) { - buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; - p[i] = t[i] = (p[i] - t[i]) << 1; - u = v; - } else t[i] = 0; - } - } while (t[0] > 0 || t[1] > 0); - - __restore_flags(flags); - - - ret = 0; - - for (i = 0; i < 2; i++) { - - if (r[i] != JS_CR_LENGTH) continue; - - for (j = 0; j < JS_CR_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) - buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (JS_CR_LENGTH - 1)); - - if (j < JS_CR_LENGTH) ret |= (1 << i); - - data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) - | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) - | ((buf[i] >> 11) & 0x1f00000); - - } - - return ret; -} - -/* - * js_cr_read() reads and analyzes Blaster gamepad data. - */ - -static int js_cr_read(void *xinfo, int **axes, int **buttons) -{ - struct js_cr_info *info = xinfo; - unsigned int data[2]; - int i, r; - - if (!(r = js_cr_read_packet(info->io, data))) - return -1; - - for (i = 0; i < 2; i++) - if (r & (1 << i)) { - switch (info->mode[i]) { - - case JS_CR_MODE_BGPC: - - axes[i][0] = ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1); - axes[i][1] = ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1); - - buttons[i][0] = ((data[i] >> 12) & 0x007) | ((data[i] >> 6) & 0x038) - | ((data[i] >> 1) & 0x0c0) | ((data[i] >> 7) & 0x300) - | ((data[i] << 5) & 0xc00); - - break; - - default: - break; - - } - } - - return 0; -} - -/* - * js_cr_init_corr() initializes correction values of - * Blaster gamepads. - */ - -static void __init js_cr_init_corr(int mode, struct js_corr *corr) -{ - int i; - - switch (mode) { - - case JS_CR_MODE_BGPC: - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - } -} - -/* - * js_cr_probe() probes for Blaster gamepads. - */ - -static struct js_port __init *js_cr_probe(int io, struct js_port *port) -{ - struct js_cr_info info; - char *names[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Blaster GamePad Cobra" }; - char axes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 2 }; - char buttons[] = { 0, 0, 0, 0, 0, 0, 0, 0, 12 }; - unsigned int data[2]; - int i, r; - - if (check_region(io, 1)) return port; - - info.mode[0] = info.mode[1] = 0; - - if (!(r = js_cr_read_packet(io, data))) - return port; - - for (i = 0; i < 2; i++) { - if (r & (1 << i)) { - if (~data[i] & 1) { - info.mode[i] = JS_CR_MODE_BGPC; - } else { - info.mode[i] = (data[i] >> 2) & 7; - } - if (!names[info.mode[i]]) { - printk(KERN_WARNING "joy-creative: Unknown Creative device %d at %#x\n", - info.mode[i], io); - info.mode[i] = 0; - } - } - } - - if (!info.mode[0] && !info.mode[1]) return port; - - info.io = io; - - request_region(io, 1, "joystick (creative)"); - port = js_register_port(port, &info, 2, sizeof(struct js_cr_info), js_cr_read); - - for (i = 0; i < 2; i++) - if (info.mode[i]) { - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], - names[info.mode[i]], THIS_MODULE, NULL, NULL), - names[info.mode[i]], io); - js_cr_init_corr(info.mode[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_cr_init(void) -#endif -{ - int *p; - - for (p = js_cr_port_list; *p; p++) js_cr_port = js_cr_probe(*p, js_cr_port); - if (js_cr_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-creative: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_cr_info *info; - - while (js_cr_port) { - for (i = 0; i < js_cr_port->ndevs; i++) - if (js_cr_port->devs[i]) - js_unregister_device(js_cr_port->devs[i]); - info = js_cr_port->info; - release_region(info->io, 1); - js_cr_port = js_unregister_port(js_cr_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-db9.c b/drivers/char/joystick/joy-db9.c deleted file mode 100644 index 41169b12c..000000000 --- a/drivers/char/joystick/joy-db9.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * joy-db9.c Version 0.6V - * - * Copyright (c) 1998 Andree Borrmann - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks - * and gamepads connected to the parallel port. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_db9, "2i"); -MODULE_PARM(js_db9_2, "2i"); -MODULE_PARM(js_db9_3, "2i"); - -#define JS_MULTI_STICK 0x01 -#define JS_MULTI2_STICK 0x02 -#define JS_GENESIS_PAD 0x03 -#define JS_GENESIS5_PAD 0x05 -#define JS_GENESIS6_PAD 0x06 -#define JS_SATURN_PAD 0x07 -#define JS_MULTI_0802 0x08 -#define JS_MULTI_0802_2 0x09 -#define JS_MAX_PAD 0x0A - -#define JS_DB9_UP 0x01 -#define JS_DB9_DOWN 0x02 -#define JS_DB9_LEFT 0x04 -#define JS_DB9_RIGHT 0x08 -#define JS_DB9_FIRE1 0x10 -#define JS_DB9_FIRE2 0x20 -#define JS_DB9_FIRE3 0x40 -#define JS_DB9_FIRE4 0x80 - -#define JS_DB9_NORMAL 0x2a -#define JS_DB9_NOSELECT 0x28 - -#define JS_DB9_SATURN0 0x20 -#define JS_DB9_SATURN1 0x22 -#define JS_DB9_SATURN2 0x24 -#define JS_DB9_SATURN3 0x26 - -#define JS_GENESIS6_DELAY 14 - -static struct js_port* js_db9_port = NULL; - -static int js_db9[] __initdata = { -1, 0 }; -static int js_db9_2[] __initdata = { -1, 0 }; -static int js_db9_3[] __initdata = { -1, 0 }; - -struct js_db9_info { - struct pardevice *port; /* parport device */ - int mode; /* pad mode */ -}; - -/* - * js_db9_read() reads and analyzes db9 joystick data. - */ - -static int js_db9_read(void *xinfo, int **axes, int **buttons) -{ - struct js_db9_info *info = xinfo; - int data; - - switch(info->mode) - { - case JS_MULTI_0802_2: - - data = JS_PAR_DATA_IN(info->port) >> 3; - - axes[1][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[1][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[1][0] = (data&JS_DB9_FIRE1?0:1); - - case JS_MULTI_0802: - - data = JS_PAR_STATUS(info->port) >> 3; - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?1:0); - - break; - - case JS_MULTI_STICK: - - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:1); - - break; - - case JS_MULTI2_STICK: - - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2); - - break; - - case JS_GENESIS_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8); - - break; - - case JS_GENESIS5_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) | - (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20); - break; - - case JS_GENESIS6_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */ - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08); - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */ - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN ?0:0x20) | - (data&JS_DB9_UP ?0:0x40) | (data&JS_DB9_RIGHT?0:0x80); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - - break; - - case JS_SATURN_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port); - data = JS_PAR_DATA_IN(info->port); - - buttons[0][0] = (data&JS_DB9_UP ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) | - (data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40); - - JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port); - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - data = JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_UP ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) | - (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80); - - - break; - - default: - return -1; - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: race possible. - */ - -int js_db9_open(struct js_dev *dev) -{ - struct js_db9_info *info = dev->port->info; - - if (!MOD_IN_USE) { - if (parport_claim(info->port)) return -EBUSY; - - JS_PAR_DATA_OUT(0xff, info->port); - if (info->mode != JS_MULTI_0802) - JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ - JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */ - } - - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_db9_close(struct js_dev *dev) -{ - struct js_db9_info *info = dev->port->info; - - MOD_DEC_USE_COUNT; - - if (!MOD_IN_USE) { - - JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ - if (info->mode != JS_MULTI_0802) - JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ - - parport_release(info->port); - } - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_db9_info *info; - int i; - - while (js_db9_port) { - info = js_db9_port->info; - - for (i = 0; i < js_db9_port->ndevs; i++) - if (js_db9_port->devs[i]) - js_unregister_device(js_db9_port->devs[i]); - parport_unregister_device(info->port); - js_db9_port = js_unregister_port(js_db9_port); - } - -} -#endif - -/* - * js_db9_init_corr() initializes correction values of - * db9 gamepads. - */ - -static void __init js_db9_init_corr(struct js_corr *corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } -} - -/* - * js_db9_probe() probes for db9 gamepads. - */ - -static struct js_port __init *js_db9_probe(int *config, struct js_port *port) -{ - struct js_db9_info info; - struct parport *pp; - int i; - char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,8,8,1,1}; - char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", - NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", - "Multisystem (0.8.0.2-dual) joystick"}; - - if (config[0] < 0) return port; - if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; - - info.mode = config[1]; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-db9: no such parport\n"); - return port; - } - - if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2)) && info.mode != JS_MULTI_0802) { - printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; - - port = js_register_port(port, &info, 1 + (info.mode == JS_MULTI_0802_2), sizeof(struct js_db9_info), js_db9_read); - - for (i = 0; i < 1 + (info.mode == JS_MULTI_0802_2); i++) { - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i, 2, buttons[info.mode], name[info.mode], NULL, js_db9_open, js_db9_close), - name[info.mode], info.port->port->name); - - js_db9_init_corr(port->corr[i]); - } - - - return port; -} - -#ifndef MODULE -int __init js_db9_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; - return 1; -} -int __init js_db9_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; - return 1; -} -int __init js_db9_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; - return 1; -} -__setup("js_db9=", js_db9_setup); -__setup("js_db9_2=", js_db9_setup_2); -__setup("js_db9_3=", js_db9_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_db9_init(void) -#endif -{ - js_db9_port = js_db9_probe(js_db9, js_db9_port); - js_db9_port = js_db9_probe(js_db9_2, js_db9_port); - js_db9_port = js_db9_probe(js_db9_3, js_db9_port); - - if (js_db9_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-db9: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-gravis.c b/drivers/char/joystick/joy-gravis.c deleted file mode 100644 index 84a6def16..000000000 --- a/drivers/char/joystick/joy-gravis.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * joy-gravis.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Gravis GrIP digital joystick family. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_GR_MODE_GPP 1 -#define JS_GR_LENGTH_GPP 24 -#define JS_GR_STROBE_GPP 400 - -#define JS_GR_MODE_XT 2 -#define JS_GR_MODE_BD 3 -#define JS_GR_LENGTH_XT 4 -#define JS_GR_STROBE_XT 200 -#define JS_GR_MAX_CHUNKS_XT 10 -#define JS_GR_MAX_BITS_XT 30 - -static int js_gr_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_gr_port __initdata = NULL; - -struct js_gr_info { - int io; - unsigned char mode[2]; -}; - -/* - * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet. - */ - -static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) -{ - unsigned long flags; - unsigned char u, v; - unsigned int t, p; - int i; - - i = 0; - data[0] = 0; - p = t = JS_GR_STROBE_GPP; - p += JS_GR_STROBE_GPP; - - __save_flags(flags); - __cli(); - - v = inb(io) >> shift; - - do { - t--; - u = v; v = (inb(io) >> shift) & 3; - if (~v & u & 1) { - data[0] |= (v >> 1) << i++; - p = t = (p - t) << 1; - } - } while (i < JS_GR_LENGTH_GPP && t > 0); - - __restore_flags(flags); - - if (i < JS_GR_LENGTH_GPP) return -1; - - for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) - data[0] = data[0] >> 1 | (data[0] & 1) << (JS_GR_LENGTH_GPP - 1); - - return -(i == JS_GR_LENGTH_GPP); -} - -/* - * js_gr_xt_read_packet() reads a Gravis Xterminator packet. - */ - -static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) -{ - unsigned int i, j, buf, crc; - unsigned char u, v, w; - unsigned long flags; - unsigned int t, p; - char status; - - data[0] = data[1] = data[2] = data[3] = 0; - status = buf = i = j = 0; - p = t = JS_GR_STROBE_XT; - p += JS_GR_STROBE_XT; - - __save_flags(flags); - __cli(); - - v = w = (inb(io) >> shift) & 3; - - do { - t--; - u = (inb(io) >> shift) & 3; - - if (u ^ v) { - - if ((u ^ v) & 1) { - p = t = (p - t) << 2; - buf = (buf << 1) | (u >> 1); - i++; - } else - - if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { - p = t = (p - t) << 2; - if (i == 20) { - crc = buf ^ (buf >> 7) ^ (buf >> 14); - if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { - data[buf >> 18] = buf >> 4; - status |= 1 << (buf >> 18); - } - j++; - } - buf = 0; - i = 0; - } - - w = v; - v = u; - } - - } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && t > 0); - - __restore_flags(flags); - - return -(status != 0xf); -} - -/* - * js_gr_read() reads and analyzes GrIP joystick data. - */ - -static int js_gr_read(void *xinfo, int **axes, int **buttons) -{ - struct js_gr_info *info = xinfo; - unsigned int data[JS_GR_LENGTH_XT]; - int i; - - for (i = 0; i < 2; i++) - switch (info->mode[i]) { - - case JS_GR_MODE_GPP: - - if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1); - axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1); - - data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) | - ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300); - - buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202); - - break; - - case JS_GR_MODE_XT: - - if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = (data[0] >> 2) & 0x3f; - axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); - axes[i][2] = (data[1] >> 2) & 0x3f; - axes[i][3] = (data[1] >> 8) & 0x3f; - axes[i][4] = (data[2] >> 8) & 0x3f; - - axes[i][5] = ((data[2] >> 1) & 1) - ( data[2] & 1); - axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); - axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1); - axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1); - - buttons[i][0] = (data[3] >> 3) & 0x7ff; - - break; - - case JS_GR_MODE_BD: - - if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = (data[0] >> 2) & 0x3f; - axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); - axes[i][2] = (data[2] >> 8) & 0x3f; - - axes[i][3] = ((data[2] >> 1) & 1) - ( data[2] & 1); - axes[i][4] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); - - buttons[i][0] = ((data[3] >> 6) & 0x01) | ((data[3] >> 3) & 0x06) - | ((data[3] >> 4) & 0x18); - - break; - - default: - break; - - } - - - return 0; -} - -/* - * js_gr_init_corr() initializes correction values of - * GrIP joysticks. - */ - -static void __init js_gr_init_corr(int mode, struct js_corr *corr) -{ - int i; - - switch (mode) { - - case JS_GR_MODE_GPP: - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - case JS_GR_MODE_XT: - - for (i = 0; i < 5; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 31 - 4; - corr[i].coef[1] = 32 + 4; - corr[i].coef[2] = (1 << 29) / (32 - 14); - corr[i].coef[3] = (1 << 29) / (32 - 14); - } - - for (i = 5; i < 9; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - case JS_GR_MODE_BD: - - for (i = 0; i < 3; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 31 - 4; - corr[i].coef[1] = 32 + 4; - corr[i].coef[2] = (1 << 29) / (32 - 14); - corr[i].coef[3] = (1 << 29) / (32 - 14); - } - - for (i = 3; i < 5; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - } -} - -/* - * js_gr_probe() probes for GrIP joysticks. - */ - -static struct js_port __init *js_gr_probe(int io, struct js_port *port) -{ - struct js_gr_info info; - char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator", "Gravis Blackhawk Digital"}; - char axes[] = { 0, 2, 9, 5}; - char buttons[] = { 0, 10, 11, 5}; - unsigned int data[JS_GR_LENGTH_XT]; - int i; - - if (check_region(io, 1)) return port; - - info.mode[0] = info.mode[1] = 0; - - for (i = 0; i < 2; i++) { - if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP; - if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) { - if ((data[3] & 7) == 7) - info.mode[i] = JS_GR_MODE_XT; - if ((data[3] & 7) == 0) - info.mode[i] = JS_GR_MODE_BD; - } - } - - if (!info.mode[0] && !info.mode[1]) return port; - - info.io = io; - - request_region(io, 1, "joystick (gravis)"); - port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read); - - for (i = 0; i < 2; i++) - if (info.mode[i]) { - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], - names[info.mode[i]], THIS_MODULE, NULL, NULL), - names[info.mode[i]], io); - js_gr_init_corr(info.mode[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_gr_init(void) -#endif -{ - int *p; - - for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port); - if (js_gr_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-gravis: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_gr_info *info; - - while (js_gr_port) { - for (i = 0; i < js_gr_port->ndevs; i++) - if (js_gr_port->devs[i]) - js_unregister_device(js_gr_port->devs[i]); - info = js_gr_port->info; - release_region(info->io, 1); - js_gr_port = js_unregister_port(js_gr_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-lightning.c b/drivers/char/joystick/joy-lightning.c deleted file mode 100644 index 26c18856a..000000000 --- a/drivers/char/joystick/joy-lightning.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * joy-lightning.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * PDPI Lightning 4 gamecards and analog joysticks connected - * to them. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_L4_PORT 0x201 -#define JS_L4_SELECT_ANALOG 0xa4 -#define JS_L4_SELECT_DIGITAL 0xa5 -#define JS_L4_SELECT_SECONDARY 0xa6 -#define JS_L4_CMD_ID 0x80 -#define JS_L4_CMD_GETCAL 0x92 -#define JS_L4_CMD_SETCAL 0x93 -#define JS_L4_ID 0x04 -#define JS_L4_BUSY 0x01 -#define JS_L4_TIMEOUT 80 /* 80 us */ - -static struct js_port* __initdata js_l4_port = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_l4, "2-24i"); - -static int __initdata js_l4[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -#include "joy-analog.h" - -struct js_l4_info { - int port; - struct js_an_info an; -}; - -/* - * js_l4_wait_ready() waits for the L4 to become ready. - */ - -static int js_l4_wait_ready(void) -{ - unsigned int t; - t = JS_L4_TIMEOUT; - while ((inb(JS_L4_PORT) & JS_L4_BUSY) && t > 0) t--; - return -(t<=0); -} - -/* - * js_l4_read() reads data from the Lightning 4. - */ - -static int js_l4_read(void *xinfo, int **axes, int **buttons) -{ - struct js_l4_info *info = xinfo; - int i; - unsigned char status; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(info->port & 3, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - status = inb(JS_L4_PORT); - - for (i = 0; i < 4; i++) - if (status & (1 << i)) { - if (js_l4_wait_ready()) return -1; - info->an.axes[i] = inb(JS_L4_PORT); - } - - if (status & 0x10) { - if (js_l4_wait_ready()) return -1; - info->an.buttons = inb(JS_L4_PORT); - } - - js_an_decode(&info->an, axes, buttons); - - return 0; -} - -/* - * js_l4_getcal() reads the L4 with calibration values. - */ - -static int js_l4_getcal(int port, int *cal) -{ - int i; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(JS_L4_CMD_GETCAL, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; - - if (js_l4_wait_ready()) return -1; - outb(port & 3, JS_L4_PORT); - - for (i = 0; i < 4; i++) { - if (js_l4_wait_ready()) return -1; - cal[i] = inb(JS_L4_PORT); - } - - return 0; -} - -/* - * js_l4_setcal() programs the L4 with calibration values. - */ - -static int js_l4_setcal(int port, int *cal) -{ - int i; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(JS_L4_CMD_SETCAL, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; - - if (js_l4_wait_ready()) return -1; - outb(port & 3, JS_L4_PORT); - - for (i = 0; i < 4; i++) { - if (js_l4_wait_ready()) return -1; - outb(cal[i], JS_L4_PORT); - } - - return 0; -} - -/* - * js_l4_calibrate() calibrates the L4 for the attached device, so - * that the device's resistance fits into the L4's 8-bit range. - */ - -static void js_l4_calibrate(struct js_l4_info *info) -{ - int i; - int cal[4]; - int axes[4]; - int t; - - js_l4_getcal(info->port, cal); - - for (i = 0; i < 4; i++) - axes[i] = info->an.axes[i]; - - if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U)) - axes[2] >>= 1; /* Pad button X */ - - if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V)) - axes[3] >>= 1; /* Pad button Y */ - - if (info->an.extensions & JS_AN_HAT_FCS) - axes[3] >>= 1; /* FCS hat */ - - if (((info->an.mask[0] & 0xb) == 0xb) || ((info->an.mask[1] & 0xb) == 0xb)) - axes[3] = (axes[0] + axes[1]) >> 1; /* Throttle */ - - for (i = 0; i < 4; i++) { - t = (axes[i] * cal[i]) / 100; - if (t > 255) t = 255; - info->an.axes[i] = (info->an.axes[i] * cal[i]) / t; - cal[i] = t; - } - - js_l4_setcal(info->port, cal); -} - -/* - * js_l4_probe() probes for joysticks on the L4 cards. - */ - -static struct js_port __init *js_l4_probe(unsigned char *cards, int l4port, int mask0, int mask1, struct js_port *port) -{ - struct js_l4_info iniinfo; - struct js_l4_info *info = &iniinfo; - int cal[4] = {255,255,255,255}; - int i, numdev; - unsigned char u; - - if (l4port < 0) return port; - if (!cards[(l4port >> 2)]) return port; - - memset(info, 0, sizeof(struct js_l4_info)); - info->port = l4port; - - if (cards[l4port >> 2] > 0x28) js_l4_setcal(info->port, cal); - if (js_l4_read(info, NULL, NULL)) return port; - - for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); - - info = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s on L4 port %d\n", - js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &info->an), info->port); - - js_l4_calibrate(info); - js_l4_read(info, port->axes, port->buttons); - js_an_init_corr(&info->an, port->axes, port->corr, 0); - - return port; -} - -/* - * js_l4_card_probe() probes for presence of the L4 card(s). - */ - -static void __init js_l4_card_probe(unsigned char *cards) -{ - int i; - unsigned char rev = 0; - - if (check_region(JS_L4_PORT, 1)) return; - - for (i = 0; i < 2; i++) { - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT); /* Select card 0-1 */ - - if (inb(JS_L4_PORT) & JS_L4_BUSY) continue; - outb(JS_L4_CMD_ID, JS_L4_PORT); /* Get card ID & rev */ - - if (js_l4_wait_ready()) continue; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue; - - if (js_l4_wait_ready()) continue; - if (inb(JS_L4_PORT) != JS_L4_ID) continue; - - if (js_l4_wait_ready()) continue; - rev = inb(JS_L4_PORT); - - cards[i] = rev; - - printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d at %#x\n", - i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); - } - -} - -#ifndef MODULE -int __init js_l4_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; - return 1; -} -__setup("js_l4=", js_l4_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_l4_init(void) -#endif -{ - int i; - unsigned char cards[2] = {0, 0}; - - js_l4_card_probe(cards); - - if (js_l4[0] >= 0) { - for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++) - js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port); - } else { - for (i = 0; i < 8; i++) - js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); - } - - if (!js_l4_port) { -#ifdef MODULE - printk(KERN_WARNING "joy-lightning: no joysticks found\n"); -#endif - return -ENODEV; - } - - request_region(JS_L4_PORT, 1, "joystick (lightning)"); - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - int cal[4] = {59, 59, 59, 59}; - struct js_l4_info *info; - - while (js_l4_port) { - for (i = 0; i < js_l4_port->ndevs; i++) - if (js_l4_port->devs[i]) - js_unregister_device(js_l4_port->devs[i]); - info = js_l4_port->info; - js_l4_setcal(info->port, cal); - js_l4_port = js_unregister_port(js_l4_port); - } - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - release_region(JS_L4_PORT, 1); -} -#endif diff --git a/drivers/char/joystick/joy-logitech.c b/drivers/char/joystick/joy-logitech.c deleted file mode 100644 index 6044cbfb0..000000000 --- a/drivers/char/joystick/joy-logitech.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * joy-logitech.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Logitech ADI joystick family. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/malloc.h> -#include <linux/init.h> - -/* - * Times array sizes, flags, ids. - */ - -#undef JS_LT_DEBUG - -#define JS_LT_MAX_START 400 /* Trigger to packet timeout [400us] */ - -#define JS_LT_MAX_LENGTH 256 -#define JS_LT_MIN_LENGTH 8 -#define JS_LT_MIN_LEN_LENGTH 10 -#define JS_LT_MIN_ID_LENGTH 66 -#define JS_LT_MAX_NAME_LENGTH 16 - -#define JS_LT_INIT_DELAY 10 /* Delay after init packet [10ms] */ -#define JS_LT_DATA_DELAY 4 /* Delay after data packet [4ms] */ - -#define JS_LT_FLAG_HAT 0x04 -#define JS_LT_FLAG_10BIT 0x08 - -#define JS_LT_ID_WMED 0x00 -#define JS_LT_ID_TPD 0x01 -#define JS_LT_ID_WMI 0x04 -#define JS_LT_ID_WGP 0x06 -#define JS_LT_ID_WM3D 0x07 -#define JS_LT_ID_WGPE 0x08 - -#define JS_LT_BUG_BUTTONS 0x01 -#define JS_LT_BUG_LONGID 0x02 -#define JS_LT_BUG_LONGDATA 0x04 -#define JS_LT_BUG_IGNTRIG 0x08 - -/* - * Port probing variables. - */ - -static int js_lt_port_list[] __initdata = { 0x201, 0 }; -static struct js_port* js_lt_port __initdata = NULL; - -/* - * Device names. - */ - -#define JS_LT_MAX_ID 10 - -static char *js_lt_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", - "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", - "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", - "WingMan GamePad USB", "Unknown Device %#x"}; - -/* - * Hat to axis conversion arrays. - */ - -static struct { - int x; - int y; -} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -/* - * Per-port information. - */ - -struct js_lt_info { - int io; - int length[2]; - int ret[2]; - int idx[2]; - unsigned char id[2]; - char buttons[2]; - char axes10[2]; - char axes8[2]; - char pad[2]; - char hats[2]; - char name[2][JS_LT_MAX_NAME_LENGTH]; - unsigned char data[2][JS_LT_MAX_LENGTH]; - char bugs[2]; -}; - -/* - * js_lt_read_packet() reads a Logitech ADI packet. - */ - -static void js_lt_read_packet(struct js_lt_info *info) -{ - unsigned char u, v, w, x, z; - int t[2], s[2], p[2], i; - unsigned long flags; - - for (i = 0; i < 2; i++) { - info->ret[i] = -1; - p[i] = t[i] = JS_LT_MAX_START; - s[i] = 0; - } - - __save_flags(flags); - __cli(); - - outb(0xff, info->io); - v = z = inb(info->io); - - do { - u = v; - w = u ^ (v = x = inb(info->io)); - for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { - t[i]--; - if ((w & 0x30) && s[i]) { - if ((w & 0x30) < 0x30 && info->ret[i] < JS_LT_MAX_LENGTH && t[i] > 0) { - info->data[i][++info->ret[i]] = w; - p[i] = t[i] = (p[i] - t[i]) << 1; - } else t[i] = 0; - } else if (!(x & 0x30)) s[i] = 1; - } - } while (t[0] > 0 || t[1] > 0); - - __restore_flags(flags); - - for (i = 0; i < 2; i++, z >>= 2) - if ((z & 0x30) && info->ret[i] > 0) - info->bugs[i] |= JS_LT_BUG_BUTTONS; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: read %d %d bits\n", info->ret[0], info->ret[1]); - printk(KERN_DEBUG "joy-logitech: stream0:"); - for (i = 0; i <= info->ret[0]; i++) printk("%d", (info->data[0][i] >> 5) & 1); - printk("\n"); - printk(KERN_DEBUG "joy-logitech: stream1:"); - for (i = 0; i <= info->ret[1]; i++) printk("%d", (info->data[1][i] >> 5) & 1); - printk("\n"); -#endif - - return; -} - -/* - * js_lt_move_bits() detects a possible 2-stream mode, and moves - * the bits accordingly. - */ - -static void js_lt_move_bits(struct js_lt_info *info, int length) -{ - int i; - - info->idx[0] = info->idx[1] = 0; - - if (info->ret[0] <= 0 || info->ret[1] <= 0) return; - if (info->data[0][0] & 0x20 || ~info->data[1][0] & 0x20) return; - - for (i = 1; i <= info->ret[1]; i++) - info->data[0][((length - 1) >> 1) + i + 1] = info->data[1][i]; - - info->ret[0] += info->ret[1]; - info->ret[1] = -1; -} - -/* - * js_lt_get_bits() gathers bits from the data packet. - */ - -static inline int js_lt_get_bits(struct js_lt_info *info, int device, int count) -{ - int bits = 0; - int i; - if ((info->idx[device] += count) > info->ret[device]) return 0; - for (i = 0; i < count; i++) bits |= ((info->data[device][info->idx[device] - i] >> 5) & 1) << i; - return bits; -} - -/* - * js_lt_read() reads and analyzes Logitech joystick data. - */ - -static int js_lt_read(void *xinfo, int **axes, int **buttons) -{ - struct js_lt_info *info = xinfo; - int i, j, k, l, t; - int ret = 0; - - js_lt_read_packet(info); - js_lt_move_bits(info, info->length[0]); - - for (i = 0; i < 2; i++) { - - if (!info->length[i]) continue; - - if (info->length[i] > info->ret[i] || - info->id[i] != (js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4))) { - ret = -1; - continue; - } - - if (info->length[i] < info->ret[i]) - info->bugs[i] |= JS_LT_BUG_LONGDATA; - - k = l = 0; - - for (j = 0; j < info->axes10[i]; j++) - axes[i][k++] = js_lt_get_bits(info, i, 10); - - for (j = 0; j < info->axes8[i]; j++) - axes[i][k++] = js_lt_get_bits(info, i, 8); - - for (j = 0; j <= (info->buttons[i] - 1) >> 5; j++) buttons[i][j] = 0; - - for (j = 0; j < info->buttons[i] && j < 63; j++) { - if (j == info->pad[i]) { - t = js_lt_get_bits(info, i, 4); - axes[i][k++] = ((t >> 2) & 1) - ( t & 1); - axes[i][k++] = ((t >> 1) & 1) - ((t >> 3) & 1); - } - buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); - l++; - } - - for (j = 0; j < info->hats[i]; j++) { - if((t = js_lt_get_bits(info, i, 4)) > 8) { - if (t != 15) ret = -1; /* Hat press */ - t = 0; - } - axes[i][k++] = js_lt_hat_to_axis[t].x; - axes[i][k++] = js_lt_hat_to_axis[t].y; - } - - for (j = 63; j < info->buttons[i]; j++) { - buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); - l++; - } - } - - return ret; -} - -/* - * js_lt_init_digital() sends a trigger & delay sequence - * to reset and initialize a Logitech joystick into digital mode. - */ - -static void __init js_lt_init_digital(int io) -{ - int seq[] = { 3, 2, 3, 10, 6, 11, 7, 9, 11, 0 }; - int i; - - for (i = 0; seq[i]; i++) { - outb(0xff,io); - mdelay(seq[i]); - } -} - -/* - * js_lt_init_corr() initializes the correction values for - * Logitech joysticks. - */ - -static void __init js_lt_init_corr(int id, int naxes10, int naxes8, int naxes1, int *axes, struct js_corr *corr) -{ - int j; - - if (id == JS_LT_ID_WMED) axes[2] = 128; /* Throttle fixup */ - if (id == JS_LT_ID_WMI) axes[2] = 512; - if (id == JS_LT_ID_WM3D) axes[3] = 128; - - if (id == JS_LT_ID_WGPE) { /* Tilt fixup */ - axes[0] = 512; - axes[1] = 512; - } - - for (j = 0; j < naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 2; - corr[j].coef[0] = axes[j] - 16; - corr[j].coef[1] = axes[j] + 16; - corr[j].coef[2] = (1 << 29) / (256 - 32); - corr[j].coef[3] = (1 << 29) / (256 - 32); - } - - for (; j < naxes8 + naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 1; - corr[j].coef[0] = axes[j] - 2; - corr[j].coef[1] = axes[j] + 2; - corr[j].coef[2] = (1 << 29) / (64 - 16); - corr[j].coef[3] = (1 << 29) / (64 - 16); - } - - for (; j < naxes1 + naxes8 + naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 0; - corr[j].coef[0] = 0; - corr[j].coef[1] = 0; - corr[j].coef[2] = (1 << 29); - corr[j].coef[3] = (1 << 29); - } -} - -/* - * js_lt_probe() probes for Logitech type joysticks. - */ - -static struct js_port __init *js_lt_probe(int io, struct js_port *port) -{ - struct js_lt_info iniinfo; - struct js_lt_info *info = &iniinfo; - char name[32]; - int i, j, t; - - if (check_region(io, 1)) return port; - - js_lt_init_digital(io); - - memset(info, 0, sizeof(struct js_lt_info)); - - info->length[0] = info->length[1] = JS_LT_MAX_LENGTH; - - info->io = io; - js_lt_read_packet(info); - - if (info->ret[0] >= JS_LT_MIN_LEN_LENGTH) - js_lt_move_bits(info, js_lt_get_bits(info, 0, 10)); - - info->length[0] = info->length[1] = 0; - - for (i = 0; i < 2; i++) { - - if (info->ret[i] < JS_LT_MIN_ID_LENGTH) continue; /* Minimum ID packet length */ - - if (info->ret[i] < (t = js_lt_get_bits(info, i, 10))) { - printk(KERN_WARNING "joy-logitech: Short ID packet: reported: %d != read: %d\n", - t, info->ret[i]); - continue; - } - - if (info->ret[i] > t) - info->bugs[i] |= JS_LT_BUG_LONGID; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: id %d length %d", i, t); -#endif - - info->id[i] = js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4); - - if ((t = js_lt_get_bits(info, i, 4)) & JS_LT_FLAG_HAT) info->hats[i]++; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: id %d flags %d", i, t); -#endif - - if ((info->length[i] = js_lt_get_bits(info, i, 10)) >= JS_LT_MAX_LENGTH) { - printk(KERN_WARNING "joy-logitech: Expected packet length too long (%d).\n", - info->length[i]); - continue; - } - - if (info->length[i] < JS_LT_MIN_LENGTH) { - printk(KERN_WARNING "joy-logitech: Expected packet length too short (%d).\n", - info->length[i]); - continue; - } - - info->axes8[i] = js_lt_get_bits(info, i, 4); - info->buttons[i] = js_lt_get_bits(info, i, 6); - - if (js_lt_get_bits(info, i, 6) != 8 && info->hats[i]) { - printk(KERN_WARNING "joy-logitech: Other than 8-dir POVs not supported yet.\n"); - continue; - } - - info->buttons[i] += js_lt_get_bits(info, i, 6); - info->hats[i] += js_lt_get_bits(info, i, 4); - - j = js_lt_get_bits(info, i, 4); - - if (t & JS_LT_FLAG_10BIT) { - info->axes10[i] = info->axes8[i] - j; - info->axes8[i] = j; - } - - t = js_lt_get_bits(info, i, 4); - - for (j = 0; j < t; j++) - info->name[i][j] = js_lt_get_bits(info, i, 8); - info->name[i][j] = 0; - - switch (info->id[i]) { - case JS_LT_ID_TPD: - info->pad[i] = 4; - info->buttons[i] -= 4; - break; - case JS_LT_ID_WGP: - info->pad[i] = 0; - info->buttons[i] -= 4; - break; - default: - info->pad[i] = -1; - break; - } - - if (info->length[i] != - (t = 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + - info->hats[i] * 4 + (info->pad[i] != -1) * 4)) { - printk(KERN_WARNING "js%d: Expected lenght %d != data length %d\n", i, t, info->length[i]); - } - - } - - if (!info->length[0] && !info->length[1]) - return port; - - request_region(io, 1, "joystick (logitech)"); - - port = js_register_port(port, info, 2, sizeof(struct js_lt_info), js_lt_read); - info = port->info; - - for (i = 0; i < 2; i++) - if (info->length[i] > 0) { - sprintf(name, info->id[i] < JS_LT_MAX_ID ? - js_lt_names[info->id[i]] : js_lt_names[JS_LT_MAX_ID], info->id[i]); - printk(KERN_INFO "js%d: %s [%s] at %#x\n", - js_register_device(port, i, - info->axes10[i] + info->axes8[i] + ((info->hats[i] + (info->pad[i] >= 0)) << 1), - info->buttons[i], name, THIS_MODULE, NULL, NULL), name, info->name[i], io); - } - - mdelay(JS_LT_INIT_DELAY); - if (js_lt_read(info, port->axes, port->buttons)) { - if (info->ret[0] < 1) info->bugs[0] |= JS_LT_BUG_IGNTRIG; - if (info->ret[1] < 1) info->bugs[1] |= JS_LT_BUG_IGNTRIG; - mdelay(JS_LT_DATA_DELAY); - js_lt_read(info, port->axes, port->buttons); - } - - for (i = 0; i < 2; i++) - if (info->length[i] > 0) { -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "js%d: length %d ret %d id %d buttons %d axes10 %d axes8 %d " - "pad %d hats %d name %s explen %d\n", - i, info->length[i], info->ret[i], info->id[i], - info->buttons[i], info->axes10[i], info->axes8[i], - info->pad[i], info->hats[i], info->name[i], - 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + - info->hats[i] * 4 + (info->pad[i] != -1) * 4); -#endif - if (info->bugs[i]) { - printk(KERN_WARNING "js%d: Firmware bugs detected:%s%s%s%s\n", i, - info->bugs[i] & JS_LT_BUG_BUTTONS ? " init_buttons" : "", - info->bugs[i] & JS_LT_BUG_LONGID ? " long_id" : "", - info->bugs[i] & JS_LT_BUG_LONGDATA ? " long_data" : "", - info->bugs[i] & JS_LT_BUG_IGNTRIG ? " ignore_trigger" : ""); - } - js_lt_init_corr(info->id[i], info->axes10[i], info->axes8[i], - ((info->pad[i] >= 0) + info->hats[i]) << 1, port->axes[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_lt_init(void) -#endif -{ - int *p; - - for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port); - if (js_lt_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-logitech: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_lt_info *info; - - while (js_lt_port) { - for (i = 0; i < js_lt_port->ndevs; i++) - if (js_lt_port->devs[i]) - js_unregister_device(js_lt_port->devs[i]); - info = js_lt_port->info; - release_region(info->io, 1); - js_lt_port = js_unregister_port(js_lt_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-magellan.c b/drivers/char/joystick/joy-magellan.c deleted file mode 100644 index cb14646e8..000000000 --- a/drivers/char/joystick/joy-magellan.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * joy-magellan.c Version 0.1 - * - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the Magellan and Space Mouse 6dof controllers. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_MAG 14 -#define JS_MAG_MAX_LENGTH 64 - -/* - * List of Magellans. - */ - -static struct js_port* js_mag_port = NULL; - -/* - * Per-Magellan data. - */ - -struct js_mag_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_MAG_MAX_LENGTH]; - unsigned char name[JS_MAG_MAX_LENGTH]; - char ack; - char used; -}; - -/* - * js_mag_crunch_nibbles() verifies that the bytes sent from the Magellan - * have correct upper nibbles for the lower ones, if not, the packet will - * be thrown away. It also strips these upper halves to simplify further - * processing. - */ - -static int js_mag_crunch_nibbles(unsigned char *data, int count) -{ - static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; - - do { - if (data[count] == nibbles[data[count] & 0xf]) - data[count] = data[count] & 0xf; - else - return -1; - } while (--count); - - return 0; -} - -/* - * js_mag_process_packet() decodes packets the driver receives from the - * Magellan. It updates the data accordingly, and sets an ACK flag - * to the type of last packet received, if received OK. - */ - -static void js_mag_process_packet(struct js_mag_info* info) -{ - int i; - - if (!info->idx) return; - - switch (info->data[0]) { - - case 'd': /* Axis data */ - if (info->idx != 25) return; - if (js_mag_crunch_nibbles(info->data, 24)) return; - if (!info->port->devs[0]) return; - for (i = 0; i < 6; i++) { - info->port->axes[0][i] = - ( info->data[(i << 2) + 1] << 12 | info->data[(i << 2) + 2] << 8 | - info->data[(i << 2) + 3] << 4 | info->data[(i << 2) + 4] ) - - 32768; - } - break; - - case 'e': /* Error packet */ - if (info->idx != 4) return; - if (js_mag_crunch_nibbles(info->data, 3)) return; - switch (info->data[1]) { - case 1: - printk(KERN_ERR "joy-magellan: Received command error packet. Failing command byte: %c\n", - info->data[2] | (info->data[3] << 4)); - break; - case 2: - printk(KERN_ERR "joy-magellan: Received framing error packet.\n"); - break; - default: - printk(KERN_ERR "joy-magellan: Received unknown error packet.\n"); - } - break; - - case 'k': /* Button data */ - if (info->idx != 4) return; - if (js_mag_crunch_nibbles(info->data, 3)) return; - if (!info->port->devs[0]) return; - info->port->buttons[0][0] = (info->data[1] << 1) | (info->data[2] << 5) | info->data[3]; - break; - - case 'm': /* Mode */ - if (info->idx != 2) return; - if (js_mag_crunch_nibbles(info->data, 1)) return; - break; - - case 'n': /* Null radius */ - if (info->idx != 2) return; - if (js_mag_crunch_nibbles(info->data, 1)) return; - break; - - case 'p': /* Data rate */ - if (info->idx != 3) return; - if (js_mag_crunch_nibbles(info->data, 2)) return; - break; - - case 'q': /* Sensitivity */ - if (info->idx != 3) return; - if (js_mag_crunch_nibbles(info->data, 2)) return; - break; - - case 'v': /* Version string */ - info->data[info->idx] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - memcpy(info->name, info->data + i, info->idx - i); - break; - - case 'z': /* Zero position */ - break; - - default: - printk("joy-magellan: Unknown packet %d length %d:", info->data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", info->data[i]); - printk("\n"); - return; - } - - info->ack = info->data[0]; -} - -/* - * js_mag_command() sends a command to the Magellan, and waits for - * acknowledge. - */ - -static int js_mag_command(struct js_mag_info *info, char *command, int timeout) -{ - info->ack = 0; - if (info->tty->driver.write(info->tty, 0, command, strlen(command)) != strlen(command)) return -1; - while (!info->ack && timeout--) mdelay(1); - return -(info->ack != command[0]); -} - -/* - * js_mag_setup() initializes the Magellan to sane state. Also works as - * a probe for Magellan existence. - */ - -static int js_mag_setup(struct js_mag_info *info) -{ - - if (js_mag_command(info, "vQ\r", 800)) /* Read version */ - return -1; - if (js_mag_command(info, "m3\r", 50)) /* Set full 3d mode */ - return -1; - if (js_mag_command(info, "pBB\r", 50)) /* Set 16 reports/second (max) */ - return -1; - if (js_mag_command(info, "z\r", 50)) /* Set zero position */ - return -1; - - return 0; -} - -/* - * js_mag_read() updates the axis and button data upon startup. - */ - -static int js_mag_read(struct js_mag_info *info) -{ - memset(info->port->axes[0],0, sizeof(int) * 6); /* Axes are 0 after zero postition cmd */ - - if (js_mag_command(info, "kQ\r", 50)) /* Read buttons */ - return -1; - - return 0; -} - -/* - * js_mag_open() is a callback from the joystick device open routine. - */ - -static int js_mag_open(struct js_dev *jd) -{ - struct js_mag_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_mag_close() is a callback from the joystick device release routine. - */ - -static int js_mag_close(struct js_dev *jd) -{ - struct js_mag_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_mag_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_mag_init_corr() initializes the correction values for the Magellan. - * It asumes gain setting of 0, question is, what we should do for higher - * gain settings ... - */ - -static void js_mag_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 6; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29) / 256; - corr[0][i].coef[3] = (1 << 29) / 256; - } -} - -/* - * js_mag_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. It looks for the Magellan, and if found, registers - * it as a joystick device. - */ - -static int js_mag_ldisc_open(struct tty_struct *tty) -{ - struct js_mag_info iniinfo; - struct js_mag_info *info = &iniinfo; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_mag_port = js_register_port(js_mag_port, info, 1, sizeof(struct js_mag_info), NULL); - - info = js_mag_port->info; - info->port = js_mag_port; - tty->disc_data = info; - - if (js_mag_setup(info)) { - js_mag_port = js_unregister_port(info->port); - return -ENODEV; - } - - printk(KERN_INFO "js%d: Magellan [%s] on %s%d\n", - js_register_device(js_mag_port, 0, 6, 9, "Magellan", THIS_MODULE, js_mag_open, js_mag_close), - info->name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); - - - js_mag_read(info); - js_mag_init_corr(js_mag_port->corr); - - MOD_INC_USE_COUNT; - - return 0; -} - -/* - * js_mag_ldisc_close() is the opposite of js_mag_ldisc_open() - */ - -static void js_mag_ldisc_close(struct tty_struct *tty) -{ - struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_mag_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_mag_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_mag_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) - if (cp[i] == '\r') { - js_mag_process_packet(info); - info->idx = 0; - } else { - if (info->idx < JS_MAG_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - } -} - -/* - * js_mag_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_mag_ldisc_room(struct tty_struct *tty) -{ - return JS_MAG_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_mag_ldisc = { - magic: TTY_LDISC_MAGIC, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - name: "magellan", -#endif - open: js_mag_ldisc_open, - close: js_mag_ldisc_close, - receive_buf: js_mag_ldisc_receive, - receive_room: js_mag_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_mag_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_MAG, &js_mag_ldisc)) { - printk(KERN_ERR "joy-magellan: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_MAG, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-pci.c b/drivers/char/joystick/joy-pci.c deleted file mode 100644 index fbf9125e5..000000000 --- a/drivers/char/joystick/joy-pci.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * joy-pci.c Version 0.4.0 - * - * Copyright (c) 1999 Raymond Ingles - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting the - * gameports on Trident 4DWave and Aureal Vortex soundcards, and - * analog joysticks connected to them. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/pci.h> -#include <linux/pci_ids.h> -#include <linux/init.h> - -MODULE_AUTHOR("Raymond Ingles <sorceror@tir.com>"); -MODULE_PARM(js_pci, "3-32i"); - -#define NUM_CARDS 8 -static int js_pci[NUM_CARDS * 4] __initdata = { -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0, - -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0 }; - -static struct js_port * js_pci_port __initdata = NULL; - -#include "joy-analog.h" - -struct js_pci_info; -typedef void (*js_pci_func)(struct js_pci_info *); - -struct js_pci_data { - int vendor; /* PCI Vendor ID */ - int model; /* PCI Model ID */ - int size; /* Memory / IO region size */ - int lcr; /* Aureal Legacy Control Register */ - int gcr; /* Gameport control register */ - int buttons; /* Buttons location */ - int axes; /* Axes start */ - int axsize; /* Axis field size */ - int axmax; /* Axis field max value */ - js_pci_func init; - js_pci_func cleanup; - char *name; -}; - -struct js_pci_info { - unsigned char *base; - struct pci_dev *pci_p; - __u32 lcr; - struct js_pci_data *data; - struct js_an_info an; -}; - -/* - * js_pci_*_init() sets the info->base field, disables legacy gameports, - * and enables the enhanced ones. - */ - -static void js_pci_4dwave_init(struct js_pci_info *info) -{ - info->base = ioremap(BASE_ADDRESS(info->pci_p, 1), info->data->size); - pci_read_config_word(info->pci_p, info->data->lcr, (unsigned short *)&info->lcr); - pci_write_config_word(info->pci_p, info->data->lcr, info->lcr & ~0x20); - writeb(0x80, info->base + info->data->gcr); -} - -static void js_pci_vortex_init(struct js_pci_info *info) -{ - info->base = ioremap(BASE_ADDRESS(info->pci_p, 0), info->data->size); - info->lcr = readl(info->base + info->data->lcr); - writel(info->lcr & ~0x8, info->base + info->data->lcr); - writel(0x40, info->base + info->data->gcr); -} - -/* - * js_pci_*_cleanup does the opposite of the above functions. - */ - -static void js_pci_4dwave_cleanup(struct js_pci_info *info) -{ - pci_write_config_word(info->pci_p, info->data->lcr, info->lcr); - writeb(0x00, info->base + info->data->gcr); - iounmap(info->base); -} - -static void js_pci_vortex_cleanup(struct js_pci_info *info) -{ - writel(info->lcr, info->base + info->data->lcr); - writel(0x00, info->base + info->data->gcr); - iounmap(info->base); -} - -static struct js_pci_data js_pci_data[] = -{{ PCI_VENDOR_ID_TRIDENT, 0x2000, 0x10000, 0x00044 ,0x00030, 0x00031, 0x00034, 2, 0xffff, - js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave DX" }, - { PCI_VENDOR_ID_TRIDENT, 0x2001, 0x10000, 0x00044, 0x00030, 0x00031, 0x00034, 2, 0xffff, - js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave NX" }, - { PCI_VENDOR_ID_AUREAL, 0x0001, 0x40000, 0x1280c, 0x1100c, 0x11008, 0x11010, 4, 0x1fff, - js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex1" }, - { PCI_VENDOR_ID_AUREAL, 0x0002, 0x40000, 0x2a00c, 0x2880c, 0x28808, 0x28810, 4, 0x1fff, - js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex2" }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL }}; - -/* - * js_pci_read() reads data from a PCI gameport. - */ - -static int js_pci_read(void *xinfo, int **axes, int **buttons) -{ - struct js_pci_info *info = xinfo; - int i; - - info->an.buttons = ~readb(info->base + info->data->buttons) >> 4; - - for (i = 0; i < 4; i++) - info->an.axes[i] = readw(info->base + info->data->axes + i * info->data->axsize); - - js_an_decode(&info->an, axes, buttons); - - return 0; -} - -static struct js_port * __init js_pci_probe(struct js_port *port, int type, int number, - struct pci_dev *pci_p, struct js_pci_data *data) -{ - int i; - unsigned char u; - int mask0, mask1, numdev; - struct js_pci_info iniinfo; - struct js_pci_info *info = &iniinfo; - - mask0 = mask1 = 0; - - for (i = 0; i < NUM_CARDS; i++) - if (js_pci[i * 4] == type && js_pci[i * 4 + 1] == number) { - mask0 = js_pci[i * 4 + 2]; - mask1 = js_pci[i * 4 + 3]; - if (!mask0 && !mask1) return port; - break; - } - - memset(info, 0, sizeof(struct js_pci_info)); - - info->data = data; - info->pci_p = pci_p; - data->init(info); - - mdelay(10); - js_pci_read(info, NULL, NULL); - - for (i = u = 0; i < 4; i++) - if (info->an.axes[i] < info->data->axmax) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - port = js_register_port(port, info, numdev, sizeof(struct js_pci_info), js_pci_read); - - info = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_WARNING "js%d: %s on %s #%d\n", - js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), js_an_name(i, &info->an), data->name, number); - - js_pci_read(info, port->axes, port->buttons); - js_an_init_corr(&info->an, port->axes, port->corr, 32); - - return port; -} - -#ifndef MODULE -int __init js_pci_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(NUM_CARDS*4); - for (i = 0; i <= ints[0] && i < NUM_CARDS*4; i++) - js_pci[i] = ints[i+1]; - return 1; -} -__setup("js_pci=", js_pci_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_pci_init(void) -#endif -{ - struct pci_dev *pci_p = NULL; - int i, j; - - for (i = 0; js_pci_data[i].vendor; i++) - for (j = 0; (pci_p = pci_find_device(js_pci_data[i].vendor, js_pci_data[i].model, pci_p)); j++) - if (pci_enable_device(pci_p) == 0) - js_pci_port = js_pci_probe(js_pci_port, i, j, pci_p, js_pci_data + i); - - if (!js_pci_port) { -#ifdef MODULE - printk(KERN_WARNING "joy-pci: no joysticks found\n"); -#endif - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_pci_info *info; - - while (js_pci_port) { - for (i = 0; i < js_pci_port->ndevs; i++) - if (js_pci_port->devs[i]) - js_unregister_device(js_pci_port->devs[i]); - info = js_pci_port->info; - info->data->cleanup(info); - js_pci_port = js_unregister_port(js_pci_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-sidewinder.c b/drivers/char/joystick/joy-sidewinder.c deleted file mode 100644 index da11598f4..000000000 --- a/drivers/char/joystick/joy-sidewinder.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * joy-sidewinder.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Microsoft SideWinder digital joystick family. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -/* - * These are really magic values. Changing them can make a problem go away, - * as well as break everything. - */ - -#undef JS_SW_DEBUG - -#define JS_SW_START 400 /* The time we wait for the first bit [400 us] */ -#define JS_SW_STROBE 45 /* Max time per bit [45 us] */ -#define JS_SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ -#define JS_SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ -#define JS_SW_END 8 /* Number of bits before end of packet to kick */ -#define JS_SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ -#define JS_SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ -#define JS_SW_OK 64 /* Number of packet read successes to switch optimization back on */ -#define JS_SW_LENGTH 512 /* Max number of bits in a packet */ - -/* - * SideWinder joystick types ... - */ - -#define JS_SW_TYPE_3DP 1 -#define JS_SW_TYPE_F23 2 -#define JS_SW_TYPE_GP 3 -#define JS_SW_TYPE_PP 4 -#define JS_SW_TYPE_FFP 5 -#define JS_SW_TYPE_FSP 6 -#define JS_SW_TYPE_FFW 7 - -static int js_sw_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_sw_port __initdata = NULL; - -static struct { - int x; - int y; -} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -struct js_sw_info { - int io; - int length; - int speed; - unsigned char type; - unsigned char bits; - unsigned char number; - unsigned char fail; - unsigned char ok; -}; - -/* - * Gameport speed. - */ - -unsigned int js_sw_io_speed = 0; - -/* - * js_sw_measure_speed() measures the gameport i/o speed. - */ - -static int __init js_sw_measure_speed(int io) -{ -#ifdef __i386__ - -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) -#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) - - unsigned int i, t, t1, t2, t3, tx; - unsigned long flags; - - tx = 1 << 30; - - for(i = 0; i < 50; i++) { - save_flags(flags); /* Yes, all CPUs */ - cli(); - GET_TIME(t1); - for(t = 0; t < 50; t++) inb(io); - GET_TIME(t2); - GET_TIME(t3); - restore_flags(flags); - udelay(i * 10); - if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; - } - - return 59659 / t; - -#else - - unsigned int j, t = 0; - - j = jiffies; while (j == jiffies); - j = jiffies; while (j == jiffies) { t++; inb(0x201); } - - return t * HZ / 1000; - -#endif -} - -/* - * js_sw_read_packet() is a function which reads either a data packet, or an - * identification packet from a SideWinder joystick. Better don't try to - * understand this, since all the ugliness of the Microsoft Digital - * Overdrive protocol is concentrated in this function. If you really want - * to know how this works, first go watch a couple horror movies, so that - * you are well prepared, read US patent #5628686 and then e-mail me, - * and I'll send you an explanation. - * Vojtech <vojtech@suse.cz> - */ - -static int js_sw_read_packet(int io, int speed, unsigned char *buf, int length, int id) -{ - unsigned long flags; - int timeout, bitout, sched, i, kick, start, strobe; - unsigned char pending, u, v; - - i = -id; /* Don't care about data, only want ID */ - timeout = id ? (JS_SW_TIMEOUT * speed) >> 10 : 0; /* Set up global timeout for ID packet */ - kick = id ? (JS_SW_KICK * speed) >> 10 : 0; /* Set up kick timeout for ID packet */ - start = (JS_SW_START * speed) >> 10; - strobe = (JS_SW_STROBE * speed) >> 10; - bitout = start; - pending = 0; - sched = 0; - - __save_flags(flags); /* Quiet, please */ - __cli(); - - outb(0xff, io); /* Trigger */ - v = inb(io); - - do { - bitout--; - u = v; - v = inb(io); - } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ - - if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ - - while ((timeout > 0 || bitout > 0) && (i < length)) { - - timeout--; - bitout--; /* Decrement timers */ - sched--; - - u = v; - v = inb(io); - - if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ - if (i >= 0) /* Want this data */ - buf[i] = v >> 5; /* Store it */ - i++; /* Advance index */ - bitout = strobe; /* Extend timeout for next bit */ - } - - if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ - sched = kick; /* Schedule second trigger */ - kick = 0; /* Don't schedule next time on falling edge */ - pending = 1; /* Mark schedule */ - } - - if (pending && sched < 0 && (i > -JS_SW_END)) { /* Second trigger time */ - outb(0xff, io); /* Trigger */ - bitout = start; /* Long bit timeout */ - pending = 0; /* Unmark schedule */ - timeout = 0; /* Switch from global to bit timeouts */ - } - } - - __restore_flags(flags); /* Done - relax */ - -#ifdef JS_SW_DEBUG - { - int j; - printk(KERN_DEBUG "joy-sidewinder: Read %d triplets. [", i); - for (j = 0; j < i; j++) printk("%d", buf[j]); - printk("]\n"); - } -#endif - - return i; -} - -/* - * js_sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. - * Parameter 'pos' is bit number inside packet where to start at, 'num' is number - * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits - * is number of bits per triplet. - */ - -#define GB(pos,num,shift) js_sw_get_bits(buf, pos, num, shift, info->bits) - -static __u64 js_sw_get_bits(unsigned char *buf, int pos, int num, char shift, char bits) -{ - __u64 data = 0; - int tri = pos % bits; /* Start position */ - int i = pos / bits; - int bit = shift; - - while (num--) { - data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ - if (tri == bits) { - i++; /* Next triplet */ - tri = 0; - } - } - - return data; -} - -/* - * js_sw_init_digital() initializes a SideWinder 3D Pro joystick - * into digital mode. - */ - -static void js_sw_init_digital(int io, int speed) -{ - int seq[] = { 140, 140+725, 140+300, 0 }; - unsigned long flags; - int i, t; - - __save_flags(flags); - __cli(); - - i = 0; - do { - outb(0xff, io); /* Trigger */ - t = (JS_SW_TIMEOUT * speed) >> 10; - while ((inb(io) & 1) && t) t--; /* Wait for axis to fall back to 0 */ - udelay(seq[i]); /* Delay magic time */ - } while (seq[++i]); - - outb(0xff, io); /* Last trigger */ - - __restore_flags(flags); -} - -/* - * js_sw_parity() computes parity of __u64 - */ - -static int js_sw_parity(__u64 t) -{ - int x = t ^ (t >> 32); - x ^= x >> 16; - x ^= x >> 8; - x ^= x >> 4; - x ^= x >> 2; - x ^= x >> 1; - return x & 1; -} - -/* - * js_sw_ccheck() checks synchronization bits and computes checksum of nibbles. - */ - -static int js_sw_check(__u64 t) -{ - char sum = 0; - - if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ - return -1; - - while (t) { /* Sum */ - sum += t & 0xf; - t >>= 4; - } - - return sum & 0xf; -} - -/* - * js_sw_parse() analyzes SideWinder joystick data, and writes the results into - * the axes and buttons arrays. - */ - -static int js_sw_parse(unsigned char *buf, struct js_sw_info *info, int **axes, int **buttons) -{ - int hat, i; - - switch (info->type) { - - case JS_SW_TYPE_3DP: - case JS_SW_TYPE_F23: - - if (js_sw_check(GB(0,64,0)) || (hat = GB(6,1,3) | GB(60,3,0)) > 8) return -1; - - axes[0][0] = GB( 3,3,7) | GB(16,7,0); - axes[0][1] = GB( 0,3,7) | GB(24,7,0); - axes[0][2] = GB(35,2,7) | GB(40,7,0); - axes[0][3] = GB(32,3,7) | GB(48,7,0); - axes[0][4] = js_sw_hat_to_axis[hat].x; - axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~(GB(37,1,8) | GB(38,1,7) | GB(8,7,0)); - - return 0; - - case JS_SW_TYPE_GP: - - for (i = 0; i < info->number * 15; i += 15) { - - if (js_sw_parity(GB(i,15,0))) return -1; - - axes[i][0] = GB(i+3,1,0) - GB(i+2,1,0); - axes[i][1] = GB(i+0,1,0) - GB(i+1,1,0); - buttons[i][0] = ~GB(i+4,10,0); - - } - - return 0; - - case JS_SW_TYPE_PP: - case JS_SW_TYPE_FFP: - - if (!js_sw_parity(GB(0,48,0)) || (hat = GB(42,4,0)) > 8) return -1; - - axes[0][0] = GB( 9,10,0); - axes[0][1] = GB(19,10,0); - axes[0][2] = GB(36, 6,0); - axes[0][3] = GB(29, 7,0); - axes[0][4] = js_sw_hat_to_axis[hat].x; - axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~GB(0,9,0); - - return 0; - - case JS_SW_TYPE_FSP: - - if (!js_sw_parity(GB(0,43,0)) || (hat = GB(28,4,0)) > 8) return -1; - - axes[0][0] = GB( 0,10,0); - axes[0][1] = GB(16,10,0); - axes[0][2] = GB(32, 6,0); - axes[0][3] = js_sw_hat_to_axis[hat].x; - axes[0][4] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~(GB(10,6,0) | GB(26,2,6) | GB(38,2,8)); - - return 0; - - case JS_SW_TYPE_FFW: - - if (!js_sw_parity(GB(0,33,0))) return -1; - - axes[0][0] = GB( 0,10,0); - axes[0][1] = GB(10, 6,0); - axes[0][2] = GB(16, 6,0); - buttons[0][0] = ~GB(22,8,0); - - return 0; - } - - return -1; -} - -/* - * js_sw_read() reads SideWinder joystick data, and reinitializes - * the joystick in case of persistent problems. This is the function that is - * called from the generic code to poll the joystick. - */ - -static int js_sw_read(void *xinfo, int **axes, int **buttons) -{ - struct js_sw_info *info = xinfo; - unsigned char buf[JS_SW_LENGTH]; - int i; - - i = js_sw_read_packet(info->io, info->speed, buf, info->length, 0); - - if (info->type <= JS_SW_TYPE_F23 && info->length == 66 && i != 66) { /* Broken packet, try to fix */ - - if (i == 64 && !js_sw_check(js_sw_get_bits(buf,0,64,0,1))) { /* Last init failed, 1 bit mode */ - printk(KERN_WARNING "joy-sidewinder: Joystick in wrong mode on %#x" - " - going to reinitialize.\n", info->io); - info->fail = JS_SW_FAIL; /* Reinitialize */ - i = 128; /* Bogus value */ - } - - if (i < 66 && GB(0,64,0) == GB(i*3-66,64,0)) /* 1 == 3 */ - i = 66; /* Everything is fine */ - - if (i < 66 && GB(0,64,0) == GB(66,64,0)) /* 1 == 2 */ - i = 66; /* Everything is fine */ - - if (i < 66 && GB(i*3-132,64,0) == GB(i*3-66,64,0)) { /* 2 == 3 */ - memmove(buf, buf + i - 22, 22); /* Move data */ - i = 66; /* Carry on */ - } - } - - if (i == info->length && !js_sw_parse(buf, info, axes, buttons)) { /* Parse data */ - - info->fail = 0; - info->ok++; - - if (info->type <= JS_SW_TYPE_F23 && info->length == 66 /* Many packets OK */ - && info->ok > JS_SW_OK) { - - printk(KERN_INFO "joy-sidewinder: No more trouble on %#x" - " - enabling optimization again.\n", info->io); - info->length = 22; - } - - return 0; - } - - info->ok = 0; - info->fail++; - - if (info->type <= JS_SW_TYPE_F23 && info->length == 22 /* Consecutive bad packets */ - && info->fail > JS_SW_BAD) { - - printk(KERN_INFO "joy-sidewinder: Many bit errors on %#x" - " - disabling optimization.\n", info->io); - info->length = 66; - } - - if (info->fail < JS_SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ - - printk(KERN_WARNING "joy-sidewinder: Too many bit errors on %#x" - " - reinitializing joystick.\n", info->io); - - if (!i && info->type <= JS_SW_TYPE_F23) { /* 3D Pro can be in analog mode */ - udelay(3 * JS_SW_TIMEOUT); - js_sw_init_digital(info->io, info->speed); - } - - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, 0); /* Read normal data packet */ - udelay(JS_SW_TIMEOUT); - js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, i); /* Read ID packet, this initializes the stick */ - - info->fail = JS_SW_FAIL; - - return -1; -} - -/* - * js_sw_init_corr() initializes the correction values for - * SideWinders. - */ - -static void __init js_sw_init_corr(int num_axes, int type, int number, struct js_corr **corr) -{ - int i, j; - - for (i = 0; i < number; i++) { - - for (j = 0; j < num_axes; j++) { - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 8; - corr[i][j].coef[0] = 511 - 32; - corr[i][j].coef[1] = 512 + 32; - corr[i][j].coef[2] = (1 << 29) / (511 - 32); - corr[i][j].coef[3] = (1 << 29) / (511 - 32); - } - - switch (type) { - - case JS_SW_TYPE_3DP: - case JS_SW_TYPE_F23: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 4; - corr[i][2].coef[0] = 255 - 16; - corr[i][2].coef[1] = 256 + 16; - corr[i][2].coef[2] = (1 << 29) / (255 - 16); - corr[i][2].coef[3] = (1 << 29) / (255 - 16); - - j = 4; - - break; - - case JS_SW_TYPE_PP: - case JS_SW_TYPE_FFP: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 0; - corr[i][2].coef[0] = 31 - 2; - corr[i][2].coef[1] = 32 + 2; - corr[i][2].coef[2] = (1 << 29) / (31 - 2); - corr[i][2].coef[3] = (1 << 29) / (31 - 2); - - corr[i][3].type = JS_CORR_BROKEN; - corr[i][3].prec = 1; - corr[i][3].coef[0] = 63 - 4; - corr[i][3].coef[1] = 64 + 4; - corr[i][3].coef[2] = (1 << 29) / (63 - 4); - corr[i][3].coef[3] = (1 << 29) / (63 - 4); - - j = 4; - - break; - - case JS_SW_TYPE_FFW: - - corr[i][0].type = JS_CORR_BROKEN; - corr[i][0].prec = 2; - corr[i][0].coef[0] = 511 - 8; - corr[i][0].coef[1] = 512 + 8; - corr[i][0].coef[2] = (1 << 29) / (511 - 8); - corr[i][0].coef[3] = (1 << 29) / (511 - 8); - - corr[i][1].type = JS_CORR_BROKEN; - corr[i][1].prec = 1; - corr[i][1].coef[0] = 63; - corr[i][1].coef[1] = 63; - corr[i][1].coef[2] = (1 << 29) / -63; - corr[i][1].coef[3] = (1 << 29) / -63; - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 1; - corr[i][2].coef[0] = 63; - corr[i][2].coef[1] = 63; - corr[i][2].coef[2] = (1 << 29) / -63; - corr[i][2].coef[3] = (1 << 29) / -63; - - j = 3; - - break; - - case JS_SW_TYPE_FSP: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 0; - corr[i][2].coef[0] = 31 - 2; - corr[i][2].coef[1] = 32 + 2; - corr[i][2].coef[2] = (1 << 29) / (31 - 2); - corr[i][2].coef[3] = (1 << 29) / (31 - 2); - - j = 3; - - break; - - default: - - j = 0; - } - - for (; j < num_axes; j++) { /* Hats & other binary axes */ - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 0; - corr[i][j].coef[0] = 0; - corr[i][j].coef[1] = 0; - corr[i][j].coef[2] = (1 << 29); - corr[i][j].coef[3] = (1 << 29); - } - } -} - -/* - * js_sw_print_packet() prints the contents of a SideWinder packet. - */ - -static void js_sw_print_packet(char *name, int length, unsigned char *buf, char bits) -{ - int i; - - printk("joy-sidewinder: %s packet, %d bits. [", name, length); - for (i = (((length + 3) >> 2) - 1); i >= 0; i--) - printk("%x", (int)js_sw_get_bits(buf, i << 2, 4, 0, bits)); - printk("]\n"); -} - -/* - * js_sw_3dp_id() translates the 3DP id into a human legible string. - * Unfortunately I don't know how to do this for the other SW types. - */ - -static void js_sw_3dp_id(unsigned char *buf, char *comment) -{ - int i; - char pnp[8], rev[9]; - - for (i = 0; i < 7; i++) /* ASCII PnP ID */ - pnp[i] = js_sw_get_bits(buf, 24+8*i, 8, 0, 1); - - for (i = 0; i < 8; i++) /* ASCII firmware revision */ - rev[i] = js_sw_get_bits(buf, 88+8*i, 8, 0, 1); - - pnp[7] = rev[8] = 0; - - sprintf(comment, " [PnP %d.%02d id %s rev %s]", - (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | /* Two 6-bit values */ - js_sw_get_bits(buf, 16, 6, 0, 1)) / 100, - (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | - js_sw_get_bits(buf, 16, 6, 0, 1)) % 100, - pnp, rev); -} - -/* - * js_sw_guess_mode() checks the upper two button bits for toggling - - * indication of that the joystick is in 3-bit mode. This is documented - * behavior for 3DP ID packet, and for example the FSP does this in - * normal packets instead. Fun ... - */ - -static int js_sw_guess_mode(unsigned char *buf, int len) -{ - int i; - unsigned char xor = 0; - for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; - return !!xor * 2 + 1; -} - -/* - * js_sw_probe() probes for SideWinder type joysticks. - */ - -static struct js_port __init *js_sw_probe(int io, struct js_port *port) -{ - struct js_sw_info info; - char *names[] = {NULL, "SideWinder 3D Pro", "Flight2000 F-23", "SideWinder GamePad", "SideWinder Precision Pro", - "SideWinder Force Feedback Pro", "SideWinder FreeStyle Pro", "SideWinder Force Feedback Wheel" }; - char axes[] = { 0, 6, 6, 2, 6, 6, 5, 3 }; - char buttons[] = { 0, 9, 9, 10, 9, 9, 10, 8 }; - int i, j, k, l, speed; - unsigned char buf[JS_SW_LENGTH]; - unsigned char idbuf[JS_SW_LENGTH]; - unsigned char m = 1; - char comment[40]; - - comment[0] = 0; - - if (check_region(io, 1)) return port; - - speed = js_sw_measure_speed(io); - - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read normal packet */ - m |= js_sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ - udelay(JS_SW_TIMEOUT); - -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 1: Mode %d. Length %d.\n", m , i); -#endif - - if (!i) { /* No data. 3d Pro analog mode? */ - js_sw_init_digital(io, speed); /* Switch to digital */ - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ - udelay(JS_SW_TIMEOUT); -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 1b: Length %d.\n", i); -#endif - if (!i) return port; /* No data -> FAIL */ - } - - j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i); /* Read ID. This initializes the stick */ - m |= js_sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ - -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2: Mode %d. ID Length %d.\n", m , j); -#endif - - if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2b: Mode %d. Length %d.\n", m , i); -#endif - if (!i) return port; - udelay(JS_SW_TIMEOUT); - j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i);/* Retry reading ID */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2c: ID Length %d.\n", j); -#endif - - } - - k = JS_SW_FAIL; /* Try JS_SW_FAIL times */ - l = 0; - - do { - k--; - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read data packet */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 3: Length %d.\n", i); -#endif - - if (i > l) { /* Longer? As we can only lose bits, it makes */ - /* no sense to try detection for a packet shorter */ - l = i; /* than the previous one */ - - info.number = 1; - info.io = io; - info.speed = speed; - info.length = i; - info.bits = m; - info.fail = 0; - info.ok = 0; - info.type = 0; - - switch (i * m) { - case 60: - info.number++; - case 45: /* Ambiguous packet length */ - if (j <= 40) { /* ID length less or eq 40 -> FSP */ - case 43: - info.type = JS_SW_TYPE_FSP; - break; - } - info.number++; - case 30: - info.number++; - case 15: - info.type = JS_SW_TYPE_GP; - break; - case 33: - case 31: - info.type = JS_SW_TYPE_FFW; - break; - case 48: /* Ambiguous */ - if (j == 14) { /* ID lenght 14*3 -> FFP */ - info.type = JS_SW_TYPE_FFP; - sprintf(comment, " [AC %s]", js_sw_get_bits(idbuf,38,1,0,3) ? "off" : "on"); - } else - info.type = JS_SW_TYPE_PP; - break; - case 198: - info.length = 22; - case 64: - info.type = JS_SW_TYPE_3DP; - if (j == 160) js_sw_3dp_id(idbuf, comment); - break; - } - } - - } while (k && !info.type); - - if (!info.type) { - printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " - "(io=%#x), contact <vojtech@suse.cz>\n", io); - js_sw_print_packet("ID", j * 3, idbuf, 3); - js_sw_print_packet("Data", i * m, buf, m); - return port; - } - -#ifdef JS_SW_DEBUG - js_sw_print_packet("ID", j * 3, idbuf, 3); - js_sw_print_packet("Data", i * m, buf, m); -#endif - - k = i; - - request_region(io, 1, "joystick (sidewinder)"); - - port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); - - for (i = 0; i < info.number; i++) - printk(KERN_INFO "js%d: %s%s at %#x [%d ns res %d-bit id %d data %d]\n", - js_register_device(port, i, axes[info.type], buttons[info.type], - names[info.type], THIS_MODULE, NULL, NULL), names[info.type], comment, io, - 1000000 / speed, m, j, k); - - js_sw_init_corr(axes[info.type], info.type, info.number, port->corr); - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_sw_init(void) -#endif -{ - int *p; - - for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port); - if (js_sw_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-sidewinder: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_sw_info *info; - - while (js_sw_port) { - for (i = 0; i < js_sw_port->ndevs; i++) - if (js_sw_port->devs[i]) - js_unregister_device(js_sw_port->devs[i]); - info = js_sw_port->info; - release_region(info->io, 1); - js_sw_port = js_unregister_port(js_sw_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-spaceball.c b/drivers/char/joystick/joy-spaceball.c deleted file mode 100644 index 3ace13642..000000000 --- a/drivers/char/joystick/joy-spaceball.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * joy-spaceball.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * Copyright (c) 1999 Joseph Krahn - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the SpaceTec SpaceBall 4000 FLX. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_SBALL 12 -#define JS_SBALL_MAX_LENGTH 128 - -/* - * List of SpaceBalls. - */ - -static struct js_port* js_sball_port = NULL; - -/* - * Per-Ball data. - */ - -struct js_sball_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_SBALL_MAX_LENGTH]; - int js; - char used; -}; - -/* - * js_sball_process_packet() decodes packets the driver receives from the - * SpaceBall. - */ - -static void js_sball_process_packet(struct js_sball_info* info) -{ - int i,b; - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - - if (info->idx < 2) return; - - switch (info->data[0]) { - - case '@': /* Reset packet */ - info->data[info->idx - 1] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - - printk(KERN_INFO "js%d: SpaceBall 4000FLX [%s] on %s%d\n", - info->js, info->data + i, info->tty->driver.name, - MINOR(info->tty->device) - info->tty->driver.minor_start); - - memset(axes[0], 0, sizeof(int) * 6); /* All axes, buttons should be zero */ - buttons[0][0] = 0; - break; - - case 'D': /* Ball data */ - if (info->idx != 16) return; - if (!info->port->devs[0]) return; - axes[0][0] = ((data[3] << 8) | data[4] ); - axes[0][1] = ((data[5] << 8) | data[6] ); - axes[0][2] = ((data[7] << 8) | data[8] ); - axes[0][3] = ((data[9] << 8) | data[10]); - axes[0][4] = ((data[11]<< 8) | data[12]); - axes[0][5] = ((data[13]<< 8) | data[14]); - for(i = 0; i < 6; i ++) if (axes[0][i] & 0x8000) axes[0][i] -= 0x10000; - break; - - case 'K': /* Button data, part1 */ - /* We can ignore this packet for the SB 4000FLX. */ - break; - - case '.': /* Button data, part2 */ - if (info->idx != 4) return; - if (!info->port->devs[0]) return; - b = (data[1] & 0xbf) << 8 | (data[2] & 0xbf); - buttons[0][0] = ((b & 0x1f80) >> 1 | (b & 0x3f)); - break; - - case '?': /* Error packet */ - info->data[info->idx - 1] = 0; - printk(KERN_ERR "joy-spaceball: Device error. [%s]\n",info->data+1); - break; - - case 'A': /* reply to A command (ID# report) */ - case 'B': /* reply to B command (beep) */ - case 'H': /* reply to H command (firmware report) */ - case 'S': /* reply to S command (single beep) */ - case 'Y': /* reply to Y command (scale flag) */ - case '"': /* reply to "n command (report info, part n) */ - break; - - case 'P': /* Pulse (update) speed */ - if (info->idx != 3) return; /* data[2],data[3] = hex digits for speed 00-FF */ - break; - - default: - printk("joy-spaceball: Unknown packet %d length %d:", data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_sball_open() is a callback from the joystick device open routine. - */ - -static int js_sball_open(struct js_dev *jd) -{ - struct js_sball_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_sball_close() is a callback from the joystick device release routine. - */ - -static int js_sball_close(struct js_dev *jd) -{ - struct js_sball_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_sball_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_sball_init_corr() initializes the correction values for the SpaceBall. - */ - -static void __init js_sball_init_corr(struct js_corr **corr) -{ - int j; - - for (j = 0; j < 3; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = 50000; - corr[0][j].coef[3] = 50000; - } - for (j = 3; j < 6; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = 300000; - corr[0][j].coef[3] = 300000; - } -} - -/* - * js_sball_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_sball_ldisc_open(struct tty_struct *tty) -{ - struct js_sball_info iniinfo; - struct js_sball_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_sball_port = js_register_port(js_sball_port, info, 1, sizeof(struct js_sball_info), NULL); - - info = js_sball_port->info; - info->port = js_sball_port; - tty->disc_data = info; - - info->js = js_register_device(js_sball_port, 0, 6, 12, "SpaceBall 4000 FLX", THIS_MODULE, js_sball_open, js_sball_close); - - js_sball_init_corr(js_sball_port->corr); - - return 0; -} - -/* - * js_sball_ldisc_close() is the opposite of js_sball_ldisc_open() - */ - -static void js_sball_ldisc_close(struct tty_struct *tty) -{ - struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_sball_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_sball_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_sball_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; - int i; - int esc_flag = 0; - -/* - * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, - * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can - * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) - * on whether the axis value is increasing, decreasing, or same as before. - * (I don't see why this is useful). - * - * There may be a nicer whay to handle the escapes, but I wanted to be sure to - * allow for an escape at the end of the buffer. - */ - for (i = 0; i < count; i++) { - if (esc_flag) { /* If the last char was an escape, overwrite it with the escaped value */ - - switch (cp[i]){ - case 'M': - case 'Q': - case 'S': - info->data[info->idx]=0x0d; - break; - case '^': /* escaped escape; leave as is */ - break; - default: - printk("joy-spaceball: Unknown escape character: %02x\n", cp[i]); - } - - esc_flag = 0; - - } else { - - if (info->idx < JS_SBALL_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - - if (cp[i] == 0x0D) { - if (info->idx) - js_sball_process_packet(info); - info->idx = 0; - } else - if (cp[i] == '^') esc_flag = 1; - - } - } -} - -/* - * js_sball_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_sball_ldisc_room(struct tty_struct *tty) -{ - return JS_SBALL_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_sball_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "spaceball", - open: js_sball_ldisc_open, - close: js_sball_ldisc_close, - receive_buf: js_sball_ldisc_receive, - receive_room: js_sball_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_sball_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_SBALL, &js_sball_ldisc)) { - printk(KERN_ERR "joy-spaceball: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_SBALL, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-spaceorb.c b/drivers/char/joystick/joy-spaceorb.c deleted file mode 100644 index 0fb4f4c71..000000000 --- a/drivers/char/joystick/joy-spaceorb.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * joy-spaceorb.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the SpaceTec SpaceOrb 360 and SpaceBall Avenger 6dof controllers. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_ORB 15 -#define JS_ORB_MAX_LENGTH 64 - -/* - * List of SpaceOrbs. - */ - -static struct js_port* js_orb_port = NULL; - -/* - * Per-Orb data. - */ - -struct js_orb_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_ORB_MAX_LENGTH]; - int js; - char used; -}; - -static unsigned char js_orb_xor[] = "SpaceWare"; - -static unsigned char *js_orb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", - "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; - -/* - * js_orb_process_packet() decodes packets the driver receives from the - * SpaceOrb. - */ - -static void js_orb_process_packet(struct js_orb_info* info) -{ - int i; - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - unsigned char c = 0; - - if (info->idx < 2) return; - for (i = 0; i < info->idx; i++) c ^= data[i]; - if (c) return; - - switch (info->data[0]) { - - case 'R': /* Reset packet */ - info->data[info->idx - 1] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - printk(KERN_INFO "js%d: SpaceOrb 360 [%s] on %s%d\n", - info->js, info->data + i, info->tty->driver.name, - MINOR(info->tty->device) - info->tty->driver.minor_start); - break; - - case 'D': /* Ball + button data */ - if (info->idx != 12) return; - if (!info->port->devs[0]) return; - for (i = 0; i < 9; i++) info->data[i+2] ^= js_orb_xor[i]; - axes[0][0] = ( data[2] << 3) | (data[ 3] >> 4); - axes[0][1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); - axes[0][2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); - axes[0][3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); - axes[0][4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); - axes[0][5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); - for(i = 0; i < 6; i ++) if (axes[0][i] & 0x200) axes[0][i] -= 1024; - buttons[0][0] = data[1]; - break; - - case 'K': /* Button data */ - if (info->idx != 5) return; - if (!info->port->devs[0]) return; - buttons[0][0] = data[2]; - break; - - case 'E': /* Error packet */ - if (info->idx != 4) return; - printk(KERN_ERR "joy-spaceorb: Device error. [ "); - for (i = 0; i < 7; i++) - if (data[1] & (1 << i)) - printk("%s ", js_orb_errors[i]); - printk("]\n"); - break; - - case 'N': /* Null region */ - if (info->idx != 3) return; - break; - - case 'P': /* Pulse (update) speed */ - if (info->idx != 4) return; - break; - - default: - printk("joy-spaceorb: Unknown packet %d length %d:", data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_orb_open() is a callback from the joystick device open routine. - */ - -static int js_orb_open(struct js_dev *jd) -{ - struct js_orb_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_orb_close() is a callback from the joystick device release routine. - */ - -static int js_orb_close(struct js_dev *jd) -{ - struct js_orb_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_orb_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_orb_init_corr() initializes the correction values for the SpaceOrb. - */ - -static void __init js_orb_init_corr(struct js_corr **corr) -{ - int j; - - for (j = 0; j < 6; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0 ; - corr[0][j].coef[1] = 0 ; - corr[0][j].coef[2] = (1 << 29) / 511; - corr[0][j].coef[3] = (1 << 29) / 511; - } -} - -/* - * js_orb_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_orb_ldisc_open(struct tty_struct *tty) -{ - struct js_orb_info iniinfo; - struct js_orb_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_orb_port = js_register_port(js_orb_port, info, 1, sizeof(struct js_orb_info), NULL); - - info = js_orb_port->info; - info->port = js_orb_port; - tty->disc_data = info; - - info->js = js_register_device(js_orb_port, 0, 6, 7, "SpaceOrb 360", THIS_MODULE, js_orb_open, js_orb_close); - - js_orb_init_corr(js_orb_port->corr); - - return 0; -} - -/* - * js_orb_ldisc_close() is the opposite of js_orb_ldisc_open() - */ - -static void js_orb_ldisc_close(struct tty_struct *tty) -{ - struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_orb_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_orb_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_orb_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) { - if (~cp[i] & 0x80) { - if (info->idx) js_orb_process_packet(info); - info->idx = 0; - } - if (info->idx < JS_ORB_MAX_LENGTH) - info->data[info->idx++] = cp[i] & 0x7f; - } -} - -/* - * js_orb_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_orb_ldisc_room(struct tty_struct *tty) -{ - return JS_ORB_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_orb_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "spaceorb", - open: js_orb_ldisc_open, - close: js_orb_ldisc_close, - receive_buf: js_orb_ldisc_receive, - receive_room: js_orb_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_orb_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_ORB, &js_orb_ldisc)) { - printk(KERN_ERR "joy-spaceorb: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_ORB, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-thrustmaster.c b/drivers/char/joystick/joy-thrustmaster.c deleted file mode 100644 index 56e043a59..000000000 --- a/drivers/char/joystick/joy-thrustmaster.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * joy-thrustmaster.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * ThrustMaster DirectConnect (BSP) joystick family. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_TM_MAX_START 400 -#define JS_TM_MAX_STROBE 45 -#define JS_TM_MAX_LENGTH 13 - -#define JS_TM_MODE_M3DI 1 -#define JS_TM_MODE_3DRP 3 -#define JS_TM_MODE_FGP 163 - -#define JS_TM_BYTE_ID 10 -#define JS_TM_BYTE_REV 11 -#define JS_TM_BYTE_DEF 12 - -static int js_tm_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_tm_port __initdata = NULL; - -static unsigned char js_tm_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; -static unsigned char js_tm_byte_d[16] = { 2, 5, 8, 9 }; - -struct js_tm_info { - int io; - unsigned char mode; -}; - -/* - * js_tm_read_packet() reads a ThrustMaster packet. - */ - -static int js_tm_read_packet(int io, unsigned char *data) -{ - unsigned int t, p; - unsigned char u, v, error; - int i, j; - unsigned long flags; - - error = 0; - i = j = 0; - p = t = JS_TM_MAX_START; - - __save_flags(flags); - __cli(); - outb(0xff,io); - - v = inb(io) >> 4; - - do { - t--; - u = v; v = inb(io) >> 4; - if (~v & u & 2) { - if (j) { - if (j < 9) { /* Data bit */ - data[i] |= (~v & 1) << (j - 1); - j++; - } else { /* Stop bit */ - error |= v & 1; - j = 0; - i++; - } - } else { /* Start bit */ - data[i] = 0; - error |= ~v & 1; - j++; - } - p = t = (p - t) << 1; - } - } while (!error && i < JS_TM_MAX_LENGTH && t > 0); - - __restore_flags(flags); - - return -(i != JS_TM_MAX_LENGTH); -} - -/* - * js_tm_read() reads and analyzes ThrustMaster joystick data. - */ - -static int js_tm_read(void *xinfo, int **axes, int **buttons) -{ - struct js_tm_info *info = xinfo; - unsigned char data[JS_TM_MAX_LENGTH]; - int i; - - if (js_tm_read_packet(info->io, data)) return -1; - if (data[JS_TM_BYTE_ID] != info->mode) return -1; - - for (i = 0; i < data[JS_TM_BYTE_DEF] >> 4; i++) axes[0][i] = data[js_tm_byte_a[i]]; - - switch (info->mode) { - - case JS_TM_MODE_M3DI: - - axes[0][4] = ((data[js_tm_byte_d[0]] >> 3) & 1) - ((data[js_tm_byte_d[0]] >> 1) & 1); - axes[0][5] = ((data[js_tm_byte_d[0]] >> 2) & 1) - ( data[js_tm_byte_d[0]] & 1); - - buttons[0][0] = ((data[js_tm_byte_d[0]] >> 6) & 0x01) | ((data[js_tm_byte_d[0]] >> 3) & 0x06) - | ((data[js_tm_byte_d[0]] >> 4) & 0x08) | ((data[js_tm_byte_d[1]] >> 2) & 0x30); - - return 0; - - case JS_TM_MODE_3DRP: - case JS_TM_MODE_FGP: - - buttons[0][0] = (data[js_tm_byte_d[0]] & 0x3f) | ((data[js_tm_byte_d[1]] << 6) & 0xc0) - | (( ((int) data[js_tm_byte_d[0]]) << 2) & 0x300); - - return 0; - - default: - - buttons[0][0] = 0; - - for (i = 0; i < (data[JS_TM_BYTE_DEF] & 0xf); i++) - buttons[0][0] |= ((int) data[js_tm_byte_d[i]]) << (i << 3); - - return 0; - - } - - return -1; -} - -/* - * js_tm_init_corr() initializes the correction values for - * ThrustMaster joysticks. - */ - -static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) -{ - int j = 0; - - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 127 - 2; - corr[0][j].coef[1] = 128 + 2; - corr[0][j].coef[2] = (1 << 29) / (127 - 4); - corr[0][j].coef[3] = (1 << 29) / (127 - 4); - } - - switch (mode) { - case JS_TM_MODE_M3DI: j = 4; break; - default: break; - } - - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); - } - -} - -/* - * js_tm_probe() probes for ThrustMaster type joysticks. - */ - -static struct js_port __init *js_tm_probe(int io, struct js_port *port) -{ - struct js_tm_info info; - struct js_rm_models { - unsigned char id; - char *name; - char axes; - char buttons; - } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 6 }, - { 3, "ThrustMaster Rage 3D Gamepad", 2, 10 }, - { 163, "Thrustmaster Fusion GamePad", 2, 10 }, - { 0, NULL, 0, 0 }}; - char name[64]; - unsigned char data[JS_TM_MAX_LENGTH]; - unsigned char a, b; - int i; - - if (check_region(io, 1)) return port; - - if (js_tm_read_packet(io, data)) return port; - - info.io = io; - info.mode = data[JS_TM_BYTE_ID]; - - if (!info.mode) return port; - - for (i = 0; models[i].id && models[i].id != info.mode; i++); - - if (models[i].id != info.mode) { - a = data[JS_TM_BYTE_DEF] >> 4; - b = (data[JS_TM_BYTE_DEF] & 0xf) << 3; - sprintf(name, "Unknown %d-axis, %d-button TM device %d", a, b, info.mode); - } else { - sprintf(name, models[i].name); - a = models[i].axes; - b = models[i].buttons; - } - - request_region(io, 1, "joystick (thrustmaster)"); - port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); - printk(KERN_INFO "js%d: %s revision %d at %#x\n", - js_register_device(port, 0, a, b, name, THIS_MODULE, NULL, NULL), name, data[JS_TM_BYTE_REV], io); - js_tm_init_corr(a, info.mode, port->axes, port->corr); - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_tm_init(void) -#endif -{ - int *p; - - for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port); - if (js_tm_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_tm_info *info; - - while (js_tm_port) { - js_unregister_device(js_tm_port->devs[0]); - info = js_tm_port->info; - release_region(info->io, 1); - js_tm_port = js_unregister_port(js_tm_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-turbografx.c b/drivers/char/joystick/joy-turbografx.c deleted file mode 100644 index c138a99d0..000000000 --- a/drivers/char/joystick/joy-turbografx.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * joy-turbografx.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Steffen Schwenke's <schwenke@burg-halle.de> TurboGraFX parallel port - * interface. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_tg, "2-8i"); -MODULE_PARM(js_tg_2, "2-8i"); -MODULE_PARM(js_tg_3, "2-8i"); - -#define JS_TG_BUTTON1 0x08 -#define JS_TG_UP 0x10 -#define JS_TG_DOWN 0x20 -#define JS_TG_LEFT 0x40 -#define JS_TG_RIGHT 0x80 - -#define JS_TG_BUTTON2 0x02 -#define JS_TG_BUTTON3 0x04 -#define JS_TG_BUTTON4 0x01 -#define JS_TG_BUTTON5 0x08 - -static struct js_port* js_tg_port __initdata = NULL; - -static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; - -struct js_tg_info { - struct pardevice *port; /* parport device */ - int sticks; /* joysticks connected */ -}; - -/* - * js_tg_read() reads and analyzes tg joystick data. - */ - -static int js_tg_read(void *xinfo, int **axes, int **buttons) -{ - struct js_tg_info *info = xinfo; - int data1, data2, i; - - for (i = 0; i < 7; i++) - if ((info->sticks >> i) & 1) { - - JS_PAR_DATA_OUT(~(1 << i), info->port); - data1 = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - data2 = JS_PAR_CTRL_IN(info->port) ^ JS_PAR_CTRL_INVERT; - - axes[i][0] = ((data1 & JS_TG_RIGHT) ? 1 : 0) - ((data1 & JS_TG_LEFT) ? 1 : 0); - axes[i][1] = ((data1 & JS_TG_DOWN ) ? 1 : 0) - ((data1 & JS_TG_UP ) ? 1 : 0); - - buttons[i][0] = ((data1 & JS_TG_BUTTON1) ? 0x01 : 0) | ((data2 & JS_TG_BUTTON2) ? 0x02 : 0) - | ((data2 & JS_TG_BUTTON3) ? 0x04 : 0) | ((data2 & JS_TG_BUTTON4) ? 0x08 : 0) - | ((data2 & JS_TG_BUTTON5) ? 0x10 : 0); - - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: race possible here. - */ - -int js_tg_open(struct js_dev *dev) -{ - struct js_tg_info *info = dev->port->info; - - if (!MOD_IN_USE) { - if (parport_claim(info->port)) return -EBUSY; - JS_PAR_CTRL_OUT(0x04, info->port); - } - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_tg_close(struct js_dev *dev) -{ - struct js_tg_info *info = dev->port->info; - - MOD_DEC_USE_COUNT; - if (!MOD_IN_USE) { - JS_PAR_CTRL_OUT(0x00, info->port); - parport_release(info->port); - } - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_tg_info *info; - int i; - - while (js_tg_port) { - for (i = 0; i < js_tg_port->ndevs; i++) - if (js_tg_port->devs[i]) - js_unregister_device(js_tg_port->devs[i]); - info = js_tg_port->info; - parport_unregister_device(info->port); - js_tg_port = js_unregister_port(js_tg_port); - } -} -#endif - -/* - * js_tg_init_corr() initializes correction values of - * tg gamepads. - */ - -static void __init js_tg_init_corr(int sticks, struct js_corr **corr) -{ - int i, j; - - for (i = 0; i < 7; i++) - if ((sticks >> i) & 1) - for (j = 0; j < 2; j++) { - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 0; - corr[i][j].coef[0] = 0; - corr[i][j].coef[1] = 0; - corr[i][j].coef[2] = (1 << 29); - corr[i][j].coef[3] = (1 << 29); - } -} - -/* - * js_tg_probe() probes for tg gamepads. - */ - -static struct js_port __init *js_tg_probe(int *config, struct js_port *port) -{ - struct js_tg_info iniinfo; - struct js_tg_info *info = &iniinfo; - struct parport *pp; - int i; - - if (config[0] < 0) return port; - - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-tg: no such parport\n"); - return port; - } - - info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info->port) - return port; - - port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read); - info = port->info; - - info->sticks = 0; - - for (i = 0; i < 7; i++) - if (config[i+1] > 0 && config[i+1] < 6) { - printk(KERN_INFO "js%d: Multisystem joystick on %s\n", - js_register_device(port, i, 2, config[i+1], "Multisystem joystick", NULL, js_tg_open, js_tg_close), - info->port->port->name); - info->sticks |= (1 << i); - } - - if (!info->sticks) { - parport_unregister_device(info->port); - return port; - } - - js_tg_init_corr(info->sticks, port->corr); - - return port; -} - -#ifndef MODULE -int __init js_tg_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; - return 1; -} -int __init js_tg_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; - return 1; -} -int __init js_tg_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; - return 1; -} -__setup("js_tg=", js_tg_setup); -__setup("js_tg_2=", js_tg_setup_2); -__setup("js_tg_3=", js_tg_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_tg_init(void) -#endif -{ - js_tg_port = js_tg_probe(js_tg, js_tg_port); - js_tg_port = js_tg_probe(js_tg_2, js_tg_port); - js_tg_port = js_tg_probe(js_tg_3, js_tg_port); - - if (js_tg_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-tg: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-warrior.c b/drivers/char/joystick/joy-warrior.c deleted file mode 100644 index 232699859..000000000 --- a/drivers/char/joystick/joy-warrior.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * joy-warrior.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the Logitech WingMan Warrior joystick. - */ - -/* - * This program is free warftware; 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_WAR 13 -#define JS_WAR_MAX_LENGTH 16 - -/* - * List of Warriors. - */ - -static struct js_port* js_war_port = NULL; - -static char js_war_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; - -/* - * Per-Warrior data. - */ - -struct js_war_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - int len; - unsigned char data[JS_WAR_MAX_LENGTH]; - char used; -}; - -/* - * js_war_process_packet() decodes packets the driver receives from the - * Warrior. It updates the data accordingly. - */ - -static void js_war_process_packet(struct js_war_info* info) -{ - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - int i; - - if (!info->idx) return; - - switch ((data[0] >> 4) & 7) { - - case 1: /* Button data */ - if (!info->port->devs[0]) return; - buttons[0][0] = ((data[3] & 0xa) >> 1) | ((data[3] & 0x5) << 1); - return; - case 3: /* XY-axis info->data */ - if (!info->port->devs[0]) return; - axes[0][0] = ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)); - axes[0][1] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); - return; - break; - case 5: /* Throttle, spinner, hat info->data */ - if (!info->port->devs[0]) return; - axes[0][2] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); - axes[0][3] = (data[3] & 2 ? 1 : 0) - (info->data[3] & 1 ? 1 : 0); - axes[0][4] = (data[3] & 8 ? 1 : 0) - (info->data[3] & 4 ? 1 : 0); - axes[0][5] = (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5); - return; - case 2: /* Static status (Send !S to get one) */ - case 4: /* Dynamic status */ - return; - default: - printk("joy-warrior: Unknown packet %d length %d:", (data[0] >> 4) & 7, info->idx); - for (i = 0; i < info->idx; i++) - printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_war_open() is a callback from the joystick device open routine. - */ - -static int js_war_open(struct js_dev *jd) -{ - struct js_war_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_war_close() is a callback from the joystick device release routine. - */ - -static int js_war_close(struct js_dev *jd) -{ - struct js_war_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_war_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_war_init_corr() initializes the correction values for the Warrior. - */ - -static void __init js_war_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 6; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = -8; - corr[0][i].coef[1] = 8; - corr[0][i].coef[2] = (1 << 29) / (128 - 64); - corr[0][i].coef[3] = (1 << 29) / (128 - 64); - } - - corr[0][2].coef[2] = (1 << 29) / (128 - 16); - corr[0][2].coef[3] = (1 << 29) / (128 - 16); - - for (i = 3; i < 5; i++) { - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } - - corr[0][5].prec = -1; - corr[0][5].coef[0] = 0; - corr[0][5].coef[1] = 0; - corr[0][5].coef[2] = (1 << 29) / 128; - corr[0][5].coef[3] = (1 << 29) / 128; -} - -/* - * js_war_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_war_ldisc_open(struct tty_struct *tty) -{ - struct js_war_info iniinfo; - struct js_war_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->len = 0; - info->used = 1; - - js_war_port = js_register_port(js_war_port, info, 1, sizeof(struct js_war_info), NULL); - - info = js_war_port->info; - info->port = js_war_port; - tty->disc_data = info; - - printk(KERN_INFO "js%d: WingMan Warrior on %s%d\n", - js_register_device(js_war_port, 0, 6, 4, "WingMan Warrior", THIS_MODULE, js_war_open, js_war_close), - tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); - - js_war_init_corr(js_war_port->corr); - - return 0; -} - -/* - * js_war_ldisc_close() is the opposite of js_war_ldisc_open() - */ - -static void js_war_ldisc_close(struct tty_struct *tty) -{ - struct js_war_info* info = (struct js_war_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_war_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_war_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_war_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_war_info* info = (struct js_war_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) { - if (cp[i] & 0x80) { - if (info->idx) - js_war_process_packet(info); - info->idx = 0; - info->len = js_war_lengths[(cp[i] >> 4) & 7]; - } - - if (info->idx < JS_WAR_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - - if (info->idx == info->len) { - if (info->idx) - js_war_process_packet(info); - info->idx = 0; - info->len = 0; - } - } -} - -/* - * js_war_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_war_ldisc_room(struct tty_struct *tty) -{ - return JS_WAR_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_war_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "warrior", - open: js_war_ldisc_open, - close: js_war_ldisc_close, - receive_buf: js_war_ldisc_receive, - receive_room: js_war_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_war_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_WAR, &js_war_ldisc)) { - printk(KERN_ERR "joy-warrior: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_WAR, NULL); -} -#endif diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c deleted file mode 100644 index c9bd56ce3..000000000 --- a/drivers/char/joystick/joystick.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * joystick.c Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is the main joystick driver for Linux. It doesn't support any - * devices directly, rather is lets you use sub-modules to do that job. See - * Documentation/joystick.txt for more info. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/devfs_fs_kernel.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/malloc.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/config.h> -#include <linux/init.h> - -/* - * Configurable parameters. - */ - -#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ - -/* - * Exported symbols. - */ - -EXPORT_SYMBOL(js_register_port); -EXPORT_SYMBOL(js_unregister_port); -EXPORT_SYMBOL(js_register_device); -EXPORT_SYMBOL(js_unregister_device); - -/* - * Buffer macros. - */ - -#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) -#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) -#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) -#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X)) - -/* - * Global variables. - */ - -static struct JS_DATA_SAVE_TYPE js_comp_glue; -static struct js_port *js_port = NULL; -static struct js_dev *js_dev = NULL; -static struct timer_list js_timer; -spinlock_t js_lock = SPIN_LOCK_UNLOCKED; -static int js_use_count = 0; - -/* - * Module info. - */ - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_SUPPORTED_DEVICE("js"); - -/* - * js_correct() performs correction of raw joystick data. - */ - -static int js_correct(int value, struct js_corr *corr) -{ - switch (corr->type) { - case JS_CORR_NONE: - break; - case JS_CORR_BROKEN: - value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : - ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : - ((corr->coef[2] * (value - corr->coef[0])) >> 14); - break; - - default: - return 0; - } - - if (value < -32767) return -32767; - if (value > 32767) return 32767; - - return value; -} - -/* - * js_button() returns value of button number i. - */ - -static inline int js_button(int *buttons, int i) -{ - return (buttons[i >> 5] >> (i & 0x1f)) & 1; -} - -/* - * js_add_event() adds an event to the buffer. This requires additional - * queue post-processing done by js_sync_buff. - */ - -static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value) -{ - jd->buff[jd->ahead].time = time; - jd->buff[jd->ahead].type = type; - jd->buff[jd->ahead].number = number; - jd->buff[jd->ahead].value = value; - if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0; -} - -/* - * js_flush_data() does the same as js_process_data, except for that it doesn't - * generate any events - it just copies the data from new to cur. - */ - -static void js_flush_data(struct js_dev *jd) -{ - int i; - - for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++) - jd->cur.buttons[i] = jd->new.buttons[i]; - for (i = 0; i < jd->num_axes; i++) - jd->cur.axes[i] = jd->new.axes[i]; -} - -/* - * js_process_data() finds changes in button states and axis positions and adds - * them as events to the buffer. - */ - -static void js_process_data(struct js_dev *jd) -{ - int i, t; - - for (i = 0; i < jd->num_buttons; i++) - if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) { - js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t); - jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f)); - } - - for (i = 0; i < jd->num_axes; i++) { - t = js_correct(jd->new.axes[i], &jd->corr[i]); - if (((jd->corr[i].prec == -1) && t) || - ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) && - (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) { - js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t); - jd->cur.axes[i] = jd->new.axes[i]; - } - } -} - -/* - * js_sync_buff() checks for all overflows caused by recent additions to the buffer. - * These happen only if some process is reading the data too slowly. It - * wakes up any process waiting for data. - */ - -static void js_sync_buff(struct js_dev *jd) -{ - struct js_list *curl = jd->list; - - if (jd->bhead != jd->ahead) { - if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) { - while (curl) { - if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) { - curl->tail = jd->ahead; - curl->startup = 0; - } - curl = curl->next; - } - jd->tail = jd->ahead; - } - jd->bhead = jd->ahead; - wake_up_interruptible(&jd->wait); - } -} - -/* - * js_do_timer() acts as an interrupt replacement. It reads the data - * from all ports and then generates events for all devices. - */ - -static void js_do_timer(unsigned long data) -{ - struct js_port *curp = js_port; - struct js_dev *curd = js_dev; - unsigned long flags; - - while (curp) { - if (curp->read) - if (curp->read(curp->info, curp->axes, curp->buttons)) - curp->fail++; - curp->total++; - curp = curp->next; - } - - spin_lock_irqsave(&js_lock, flags); - - while (curd) { - if (data) { - js_process_data(curd); - js_sync_buff(curd); - } else { - js_flush_data(curd); - } - curd = curd->next; - } - - spin_unlock_irqrestore(&js_lock, flags); - - js_timer.expires = jiffies + JS_REFRESH_TIME; - add_timer(&js_timer); -} - -/* - * js_read() copies one or more entries from jsd[].buff to user - * space. - */ - -static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct js_event *buff = (void *) buf; - struct js_list *curl; - struct js_dev *jd; - unsigned long blocks = count / sizeof(struct js_event); - int written = 0; - int new_tail, orig_tail; - int retval = 0; - unsigned long flags; - - curl = file->private_data; - jd = curl->dev; - orig_tail = curl->tail; - -/* - * Check user data. - */ - - if (!blocks) - return -EINVAL; - -/* - * Lock it. - */ - - spin_lock_irqsave(&js_lock, flags); - -/* - * Handle (non)blocking i/o. - */ - if (count != sizeof(struct JS_DATA_TYPE)) { - - if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { - - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&jd->wait, &wait); - - while (GOF(curl->tail) == jd->bhead) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - spin_unlock_irqrestore(&js_lock, flags); - schedule(); - spin_lock_irqsave(&js_lock, flags); - - } - - current->state = TASK_RUNNING; - remove_wait_queue(&jd->wait, &wait); - } - - if (retval) { - spin_unlock_irqrestore(&js_lock, flags); - return retval; - } - -/* - * Initial state. - */ - - while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) { - - struct js_event tmpevent; - - if (curl->startup < jd->num_buttons) { - tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - tmpevent.value = js_button(jd->cur.buttons, curl->startup); - tmpevent.number = curl->startup; - } else { - tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; - tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons], - &jd->corr[curl->startup - jd->num_buttons]); - tmpevent.number = curl->startup - jd->num_buttons; - } - - tmpevent.time = jiffies * (1000/HZ); - - if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - - curl->startup++; - written++; - } - -/* - * Buffer data. - */ - - while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { - - if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) - retval = -EFAULT; - - curl->tail = new_tail; - written++; - } - } - - else - -/* - * Handle version 0.x compatibility. - */ - - { - struct JS_DATA_TYPE data; - - data.buttons = jd->new.buttons[0]; - data.x = jd->num_axes < 1 ? 0 : - ((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x; - data.y = jd->num_axes < 2 ? 0 : - ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; - - retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - - curl->startup = jd->num_axes + jd->num_buttons; - curl->tail = GOB(jd->bhead); - if (!retval) retval = sizeof(struct JS_DATA_TYPE); - } - -/* - * Check main tail and move it. - */ - - if (orig_tail == jd->tail) { - new_tail = curl->tail; - curl = jd->list; - while (curl && curl->tail != jd->tail) { - if (ROT(jd->bhead, new_tail, curl->tail) || - (jd->bhead == curl->tail)) new_tail = curl->tail; - curl = curl->next; - } - if (!curl) jd->tail = new_tail; - } - - spin_unlock_irqrestore(&js_lock, flags); - - return retval ? retval : written * sizeof(struct js_event); -} - -/* - * js_poll() does select() support. - */ - -static unsigned int js_poll(struct file *file, poll_table *wait) -{ - struct js_list *curl = file->private_data; - unsigned long flags; - int retval = 0; - poll_wait(file, &curl->dev->wait, wait); - spin_lock_irqsave(&js_lock, flags); - if (GOF(curl->tail) != curl->dev->bhead || - curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM; - spin_unlock_irqrestore(&js_lock, flags); - return retval; -} - -/* - * js_ioctl handles misc ioctl calls. - */ - -static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct js_list *curl; - struct js_dev *jd; - int len; - - curl = file->private_data; - jd = curl->dev; - - switch (cmd) { - -/* - * 0.x compatibility - */ - - case JS_SET_CAL: - return copy_from_user(&js_comp_glue.JS_CORR, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_GET_CAL: - return copy_to_user((struct JS_DATA_TYPE *) arg, &js_comp_glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_SET_TIMEOUT: - return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - case JS_GET_TIMEOUT: - return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - case JS_SET_TIMELIMIT: - return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - case JS_GET_TIMELIMIT: - return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - case JS_SET_ALL: - return copy_from_user(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - case JS_GET_ALL: - return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - -/* - * 1.x ioctl calls - */ - - case JSIOCGVERSION: - return put_user(JS_VERSION, (__u32 *) arg); - case JSIOCGAXES: - return put_user(jd->num_axes, (__u8 *) arg); - case JSIOCGBUTTONS: - return put_user(jd->num_buttons, (__u8 *) arg); - case JSIOCSCORR: - return copy_from_user(jd->corr, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; - case JSIOCGCORR: - return copy_to_user((struct js_corr *) arg, jd->corr, - sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; - default: - if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - len = strlen(jd->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT; - return len; - } - } - - return -EINVAL; -} - -/* - * js_open() performs necessary initialization and adds - * an entry to the linked list. - */ - -static int js_open(struct inode *inode, struct file *file) -{ - struct js_list *curl, *new; - struct js_dev *jd = js_dev; - int i = MINOR(inode->i_rdev); - unsigned long flags; - int result; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - - - spin_lock_irqsave(&js_lock, flags); - - while (i > 0 && jd) { - jd = jd->next; - i--; - } - - spin_unlock_irqrestore(&js_lock, flags); - - if (!jd) return -ENODEV; - - if (jd->owner) - __MOD_INC_USE_COUNT(jd->owner); - if (jd->open && (result = jd->open(jd))) { - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - return result; - } - - new = kmalloc(sizeof(struct js_list), GFP_KERNEL); - if (!new) { - if (jd->close) - jd->close(jd); - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - - return -ENOMEM; - } - - spin_lock_irqsave(&js_lock, flags); - - curl = jd->list; - - jd->list = new; - jd->list->next = curl; - jd->list->dev = jd; - jd->list->startup = 0; - jd->list->tail = GOB(jd->bhead); - file->private_data = jd->list; - - spin_unlock_irqrestore(&js_lock, flags); - - if (!js_use_count++) js_do_timer(0); - - return 0; -} - -/* - * js_release() removes an entry from list and deallocates memory - * used by it. - */ - -static int js_release(struct inode *inode, struct file *file) -{ - struct js_list *curl = file->private_data; - struct js_dev *jd = curl->dev; - struct js_list **curp = &jd->list; - int new_tail; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - while (*curp && (*curp != curl)) curp = &((*curp)->next); - *curp = (*curp)->next; - - if (jd->list) - if (curl->tail == jd->tail) { - curl = jd->list; - new_tail = curl->tail; - while (curl && curl->tail != jd->tail) { - if (ROT(jd->bhead, new_tail, curl->tail) || - (jd->bhead == curl->tail)) new_tail = curl->tail; - curl = curl->next; - } - if (!curl) jd->tail = new_tail; - } - - spin_unlock_irqrestore(&js_lock, flags); - - kfree(file->private_data); - - if (!--js_use_count) del_timer(&js_timer); - - if (jd->close) - jd->close(jd); - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - - return 0; -} - -/* - * js_dump_mem() dumps all data structures in memory. - * It's used for debugging only. - */ - -struct js_port *js_register_port(struct js_port *port, - void *info, int devs, int infos, js_read_func read) -{ - struct js_port **ptrp = &js_port; - struct js_port *curp; - void *all; - int i; - unsigned long flags; - - if (!(all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL))) - return NULL; - - curp = all; - - curp->next = NULL; - curp->prev = port; - curp->read = read; - curp->ndevs = devs; - curp->fail = 0; - curp->total = 0; - - curp->devs = all += sizeof(struct js_port); - for (i = 0; i < devs; i++) curp->devs[i] = NULL; - - curp->axes = all += devs * sizeof(void*); - curp->buttons = (void*) all += devs * sizeof(void*); - curp->corr = all += devs * sizeof(void*); - - if (infos) { - curp->info = all += devs * sizeof(void*); - memcpy(curp->info, info, infos); - } else { - curp->info = NULL; - } - - spin_lock_irqsave(&js_lock, flags); - - while (*ptrp) ptrp=&((*ptrp)->next); - *ptrp = curp; - - spin_unlock_irqrestore(&js_lock, flags); - - return curp; -} - -struct js_port *js_unregister_port(struct js_port *port) -{ - struct js_port **curp = &js_port; - struct js_port *prev; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - printk("js: There were %d failures out of %d read attempts.\n", port->fail, port->total); - - while (*curp && (*curp != port)) curp = &((*curp)->next); - *curp = (*curp)->next; - - spin_unlock_irqrestore(&js_lock, flags); - - prev = port->prev; - kfree(port); - - return prev; -} - -extern struct file_operations js_fops; - -static devfs_handle_t devfs_handle = NULL; - -int js_register_device(struct js_port *port, int number, int axes, - int buttons, char *name, struct module *owner, - js_ops_func open, js_ops_func close) -{ - struct js_dev **ptrd = &js_dev; - struct js_dev *curd; - void *all; - int i = 0; - unsigned long flags; - char devfs_name[8]; - - if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + - 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + - axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL))) - return -1; - - curd = all; - - curd->next = NULL; - curd->list = NULL; - curd->port = port; - curd->open = open; - curd->close = close; - curd->owner = owner; - - init_waitqueue_head(&curd->wait); - - curd->ahead = 0; - curd->bhead = 0; - curd->tail = JS_BUFF_SIZE - 1; - curd->num_axes = axes; - curd->num_buttons = buttons; - - curd->cur.axes = all += sizeof(struct js_dev); - curd->cur.buttons = all += axes * sizeof(int); - curd->new.axes = all += (((buttons - 1) >> 5) + 1) * sizeof(int); - curd->new.buttons = all += axes * sizeof(int); - curd->corr = all += (((buttons -1 ) >> 5) + 1) * sizeof(int); - - curd->name = all += axes * sizeof(struct js_corr); - strcpy(curd->name, name); - - port->devs[number] = curd; - port->axes[number] = curd->new.axes; - port->buttons[number] = curd->new.buttons; - port->corr[number] = curd->corr; - - spin_lock_irqsave(&js_lock, flags); - - while (*ptrd) { ptrd=&(*ptrd)->next; i++; } - *ptrd = curd; - - spin_unlock_irqrestore(&js_lock, flags); - - sprintf(devfs_name, "js%d", i); - curd->devfs_handle = devfs_register(devfs_handle, devfs_name, 0, - DEVFS_FL_DEFAULT, - JOYSTICK_MAJOR, i, - S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, - &js_fops, NULL); - - return i; -} - -void js_unregister_device(struct js_dev *dev) -{ - struct js_dev **curd = &js_dev; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - while (*curd && (*curd != dev)) curd = &((*curd)->next); - *curd = (*curd)->next; - - spin_unlock_irqrestore(&js_lock, flags); - - devfs_unregister(dev->devfs_handle); - kfree(dev); -} - -/* - * The operations structure. - */ - -static struct file_operations js_fops = -{ - owner: THIS_MODULE, - read: js_read, - poll: js_poll, - ioctl: js_ioctl, - open: js_open, - release: js_release, -}; - -/* - * js_init() registers the driver and calls the probe function. - * also initializes some crucial variables. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_init(void) -#endif -{ - - if (devfs_register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { - printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); - return -EBUSY; - } - devfs_handle = devfs_mk_dir(NULL, "joysticks", 9, NULL); - - printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n", - JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); - - spin_lock_init(&js_lock); - - init_timer(&js_timer); - js_timer.function = js_do_timer; - js_timer.data = 1; - - memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE)); - js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; - js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; - -#ifndef MODULE -#ifdef CONFIG_JOY_PCI - js_pci_init(); -#endif -#ifdef CONFIG_JOY_LIGHTNING - js_l4_init(); -#endif -#ifdef CONFIG_JOY_SIDEWINDER - js_sw_init(); -#endif -#ifdef CONFIG_JOY_ASSASSIN - js_as_init(); -#endif -#ifdef CONFIG_JOY_LOGITECH - js_lt_init(); -#endif -#ifdef CONFIG_JOY_THRUSTMASTER - js_tm_init(); -#endif -#ifdef CONFIG_JOY_GRAVIS - js_gr_init(); -#endif -#ifdef CONFIG_JOY_CREATIVE - js_cr_init(); -#endif -#ifdef CONFIG_JOY_ANALOG - js_an_init(); -#endif -#ifdef CONFIG_JOY_CONSOLE - js_console_init(); -#endif -#ifdef CONFIG_JOY_DB9 - js_db9_init(); -#endif -#ifdef CONFIG_JOY_TURBOGRAFX - js_tg_init(); -#endif -#ifdef CONFIG_JOY_AMIGA - js_am_init(); -#endif -#ifdef CONFIG_JOY_MAGELLAN - js_mag_init(); -#endif -#ifdef CONFIG_JOY_WARRIOR - js_war_init(); -#endif -#ifdef CONFIG_JOY_SPACEORB - js_orb_init(); -#endif -#ifdef CONFIG_JOY_SPACEBALL - js_sball_init(); -#endif -#endif - - return 0; -} - -/* - * cleanup_module() handles module removal. - */ - -#ifdef MODULE -void cleanup_module(void) -{ - del_timer(&js_timer); - devfs_unregister(devfs_handle); - if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js")) - printk(KERN_ERR "js: can't unregister device\n"); -} -#endif - diff --git a/drivers/char/joystick/lightning.c b/drivers/char/joystick/lightning.c new file mode 100644 index 000000000..69dfd1112 --- /dev/null +++ b/drivers/char/joystick/lightning.c @@ -0,0 +1,300 @@ +/* + * $Id: lightning.c,v 1.7 2000/05/24 19:36:03 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * PDPI Lightning 4 gamecard driver for Linux. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> + +#define L4_PORT 0x201 +#define L4_SELECT_ANALOG 0xa4 +#define L4_SELECT_DIGITAL 0xa5 +#define L4_SELECT_SECONDARY 0xa6 +#define L4_CMD_ID 0x80 +#define L4_CMD_GETCAL 0x92 +#define L4_CMD_SETCAL 0x93 +#define L4_ID 0x04 +#define L4_BUSY 0x01 +#define L4_TIMEOUT 80 /* 80 us */ + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +struct l4 { + struct gameport gameport; + unsigned char port; +} *l4_port[8]; + +/* + * l4_wait_ready() waits for the L4 to become ready. + */ + +static int l4_wait_ready(void) +{ + unsigned int t; + t = L4_TIMEOUT; + while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--; + return -(t<=0); +} + +/* + * l4_cooked_read() reads data from the Lightning 4. + */ + +static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct l4 *l4 = gameport->driver; + unsigned char status; + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(l4->port & 3, L4_PORT); + + if (l4_wait_ready()) goto fail; + status = inb(L4_PORT); + + for (i = 0; i < 4; i++) + if (status & (1 << i)) { + if (l4_wait_ready()) goto fail; + axes[i] = inb(L4_PORT); + if (axes[i] > 252) axes[i] = -1; + } + + if (status & 0x10) { + if (l4_wait_ready()) goto fail; + *buttons = inb(L4_PORT) & 0x0f; + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +static int l4_open(struct gameport *gameport, int mode) +{ + struct l4 *l4 = gameport->driver; + if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED) + return -1; + outb(L4_SELECT_ANALOG, L4_PORT); + return 0; +} + +/* + * l4_getcal() reads the L4 with calibration values. + */ + +static int l4_getcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(L4_CMD_GETCAL, L4_PORT); + + if (l4_wait_ready()) goto fail; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail; + + if (l4_wait_ready()) goto fail; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) goto fail; + cal[i] = inb(L4_PORT); + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_setcal() programs the L4 with calibration values. + */ + +static int l4_setcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(L4_CMD_SETCAL, L4_PORT); + + if (l4_wait_ready()) goto fail; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail; + + if (l4_wait_ready()) goto fail; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) goto fail; + outb(cal[i], L4_PORT); + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_calibrate() calibrates the L4 for the attached device, so + * that the device's resistance fits into the L4's 8-bit range. + */ + +static int l4_calibrate(struct gameport *gameport, int *axes, int *max) +{ + int i, t; + int cal[4]; + struct l4 *l4 = gameport->driver; + + if (l4_getcal(l4->port, cal)) + return -1; + + for (i = 0; i < 4; i++) { + t = (max[i] * cal[i]) / 200; + t = (t < 1) ? 1 : ((t > 255) ? 255 : t); + axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t; + axes[i] = (axes[i] > 252) ? 252 : axes[i]; + cal[i] = t; + } + + if (l4_setcal(l4->port, cal)) + return -1; + + return 0; +} + +int __init l4_init(void) +{ + int cal[4] = {255,255,255,255}; + int i, j, rev, cards = 0; + struct gameport *gameport; + struct l4 *l4; + + if (!request_region(L4_PORT, 1, "lightning")) + return -1; + + for (i = 0; i < 2; i++) { + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + i, L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) continue; + outb(L4_CMD_ID, L4_PORT); + + if (l4_wait_ready()) continue; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + i) continue; + + if (l4_wait_ready()) continue; + if (inb(L4_PORT) != L4_ID) continue; + + if (l4_wait_ready()) continue; + rev = inb(L4_PORT); + + if (!rev) continue; + + if (!(l4_port[i * 4] = kmalloc(sizeof(struct l4) * 4, GFP_KERNEL))) { + printk(KERN_ERR "lightning: Out of memory allocating ports.\n"); + continue; + } + memset(l4_port[i * 4], 0, sizeof(struct l4) * 4); + + for (j = 0; j < 4; j++) { + + l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j; + l4->port = i * 4 + j; + + gameport = &l4->gameport; + gameport->driver = l4; + gameport->open = l4_open; + gameport->cooked_read = l4_cooked_read; + gameport->calibrate = l4_calibrate; + gameport->type = GAMEPORT_EXT; + + if (!i && !j) { + gameport->io = L4_PORT; + gameport->size = 1; + } + + if (rev > 0x28) /* on 2.9+ the setcal command works correctly */ + l4_setcal(l4->port, cal); + + gameport_register_port(gameport); + } + + printk(KERN_INFO "gameport%d,%d,%d,%d: PDPI Lightning 4 %s card v%d.%d at %#x\n", + l4_port[i * 4 + 0]->gameport.number, l4_port[i * 4 + 1]->gameport.number, + l4_port[i * 4 + 2]->gameport.number, l4_port[i * 4 + 3]->gameport.number, + i ? "secondary" : "primary", rev >> 4, rev, L4_PORT); + + cards++; + } + + outb(L4_SELECT_ANALOG, L4_PORT); + + if (!cards) { + release_region(L4_PORT, 1); + return -1; + } + + return 0; +} + +void __init l4_exit(void) +{ + int i; + int cal[4] = {59, 59, 59, 59}; + + for (i = 0; i < 8; i++) + if (l4_port[i]) { + l4_setcal(l4_port[i]->port, cal); + gameport_unregister_port(&l4_port[i]->gameport); + } + outb(L4_SELECT_ANALOG, L4_PORT); + release_region(L4_PORT, 1); +} + +module_init(l4_init); +module_exit(l4_exit); diff --git a/drivers/char/joystick/magellan.c b/drivers/char/joystick/magellan.c new file mode 100644 index 000000000..e8c77f48e --- /dev/null +++ b/drivers/char/joystick/magellan.c @@ -0,0 +1,210 @@ +/* + * $Id: magellan.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Magellan and Space Mouse 6dof controller driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +/* + * Definitions & global arrays. + */ + +#define MAGELLAN_MAX_LENGTH 32 + +static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8}; +static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; +static char *magellan_name = "LogiCad3D Magellan"; + +/* + * Per-Magellan data. + */ + +struct magellan { + struct input_dev dev; + int idx; + unsigned char data[MAGELLAN_MAX_LENGTH]; +}; + +/* + * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan + * have correct upper nibbles for the lower ones, if not, the packet will + * be thrown away. It also strips these upper halves to simplify further + * processing. + */ + +static int magellan_crunch_nibbles(unsigned char *data, int count) +{ + static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; + + do { + if (data[count] == nibbles[data[count] & 0xf]) + data[count] = data[count] & 0xf; + else + return -1; + } while (--count); + + return 0; +} + +static void magellan_process_packet(struct magellan* magellan) +{ + struct input_dev *dev = &magellan->dev; + unsigned char *data = magellan->data; + int i, t; + + if (!magellan->idx) return; + + switch (magellan->data[0]) { + + case 'd': /* Axis data */ + if (magellan->idx != 25) return; + if (magellan_crunch_nibbles(data, 24)) return; + for (i = 0; i < 6; i++) + input_report_abs(dev, magellan_axes[i], + (data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 | + data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768); + break; + + case 'k': /* Button data */ + if (magellan->idx != 4) return; + if (magellan_crunch_nibbles(data, 3)) return; + t = (data[1] << 1) | (data[2] << 5) | data[3]; + for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1); + break; + } +} + +static void magellan_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct magellan* magellan = serio->private; + + if (data == '\r') { + magellan_process_packet(magellan); + magellan->idx = 0; + } else { + if (magellan->idx < MAGELLAN_MAX_LENGTH) + magellan->data[magellan->idx++] = data; + } +} + +/* + * magellan_disconnect() is the opposite of magellan_connect() + */ + +static void magellan_disconnect(struct serio *serio) +{ + struct magellan* magellan = serio->private; + input_unregister_device(&magellan->dev); + serio_close(serio); + kfree(magellan); +} + +/* + * magellan_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Magellan, and if found, registers + * it as an input device. + */ + +static void magellan_connect(struct serio *serio, struct serio_dev *dev) +{ + struct magellan *magellan; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_MAGELLAN)) + return; + + if (!(magellan = kmalloc(sizeof(struct magellan), GFP_KERNEL))) + return; + + memset(magellan, 0, sizeof(struct magellan)); + + magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < 9; i++) + set_bit(magellan_buttons[i], &magellan->dev.keybit); + + for (i = 0; i < 6; i++) { + t = magellan_axes[i]; + set_bit(t, magellan->dev.absbit); + magellan->dev.absmin[t] = -360; + magellan->dev.absmax[t] = 360; + } + + magellan->dev.private = magellan; + magellan->dev.name = magellan_name; + magellan->dev.idbus = BUS_RS232; + magellan->dev.idvendor = SERIO_MAGELLAN; + magellan->dev.idproduct = 0x0001; + magellan->dev.idversion = 0x0100; + + serio->private = magellan; + + if (serio_open(serio, dev)) { + kfree(magellan); + return; + } + + input_register_device(&magellan->dev); + + printk(KERN_INFO "input%d: %s on serio%d\n", magellan->dev.number, magellan_name, serio->number); +} + +/* + * The serio device structure. + */ + +static struct serio_dev magellan_dev = { + interrupt: magellan_interrupt, + connect: magellan_connect, + disconnect: magellan_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init magellan_init(void) +{ + serio_register_device(&magellan_dev); + return 0; +} + +void __exit magellan_exit(void) +{ + serio_unregister_device(&magellan_dev); +} + +module_init(magellan_init); +module_exit(magellan_exit); diff --git a/drivers/char/joystick/ns558.c b/drivers/char/joystick/ns558.c new file mode 100644 index 000000000..f34a18640 --- /dev/null +++ b/drivers/char/joystick/ns558.c @@ -0,0 +1,366 @@ +/* + * $Id: ns558.c,v 1.11 2000/06/20 23:35:03 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999 Brian Gerst + * + * Sponsored by SuSE + */ + +/* + * NS558 based standard IBM game port driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> +#include <linux/isapnp.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); + +#define NS558_ISA 1 +#define NS558_PNP 2 +#define NS558_PCI 3 + +static int ns558_isa_portlist[] = { 0x201, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, + 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; + +struct ns558 { + int type; + struct pci_dev *dev; + struct ns558 *next; + struct gameport gameport; +}; + +static struct ns558 *ns558 = NULL; + +/* + * ns558_isa_probe() tries to find an isa gameport at the + * specified address, and also checks for mirrors. + * A joystick must be attached for this to work. + */ + +static struct ns558* ns558_isa_probe(int io, struct ns558 *next) +{ + int i, j, b; + unsigned char c, u, v; + struct ns558 *port; + +/* + * No one should be using this address. + */ + + if (check_region(io, 1)) + return next; + +/* + * We must not be able to write arbitrary values to the port. + * The lower two axis bits must be 1 after a write. + */ + + c = inb(io); + outb(~c & ~3, io); + if (~(u = v = inb(io)) & 3) { + outb(c, io); + return next; + } +/* + * After a trigger, there must be at least some bits changing. + */ + + for (i = 0; i < 1000; i++) v &= inb(io); + + if (u == v) { + outb(c, io); + return next; + } + wait_ms(3); +/* + * After some time (4ms) the axes shouldn't change anymore. + */ + + u = inb(io); + for (i = 0; i < 1000; i++) + if ((u ^ inb(io)) & 0xf) { + outb(c, io); + return next; + } +/* + * And now find the number of mirrors of the port. + */ + + for (i = 1; i < 5; i++) { + + if (check_region(io & (-1 << i), (1 << i))) /* Don't disturb anyone */ + break; + + outb(0xff, io & (-1 << i)); + for (j = b = 0; j < 1000; j++) + if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; + wait_ms(3); + + if (b > 300) /* We allow 30% difference */ + break; + } + + i--; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + return next; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = next; + port->type = NS558_ISA; + port->gameport.io = io; + port->gameport.size = (1 << i); + + request_region(port->gameport.io, port->gameport.size, "ns558-isa"); + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 ISA at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return port; +} + +#ifdef CONFIG_PCI +static struct pci_device_id ns558_pci_tbl[] __devinitdata = { + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB Live! gameport */ + { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, /* ESS Solo 1 */ + { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, /* S3 SonicVibes */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ns558_pci_tbl); + +static int __devinit ns558_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ioport, iolen; + int rc; + struct ns558 *port; + + rc = pci_enable_device(pdev); + if (rc) { + printk(KERN_ERR "ns558: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n", + pdev->bus->number, pdev->devfn, rc); + return rc; + } + + ioport = pci_resource_start(pdev, ent->driver_data); + iolen = pci_resource_len(pdev, ent->driver_data); + + if (!request_region(ioport, iolen, "ns558-pci")) + return -EBUSY; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + return -ENOMEM; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = ns558; + port->type = NS558_PCI; + port->gameport.io = ioport; + port->gameport.size = iolen; + port->dev = pdev; + ns558 = port; + + pdev->driver_data = port; + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 PCI at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return 0; +} + +static void __devexit ns558_pci_remove(struct pci_dev *pdev) +{ + struct ns558 *port = (struct ns558 *)pdev->driver_data; + release_region(port->gameport.io, port->gameport.size); +} + +static struct pci_driver ns558_pci_driver = { + name: "PCI Gameport", + id_table: ns558_pci_tbl, + probe: ns558_pci_probe, + remove: ns558_pci_remove, +}; +#endif /* CONFIG_PCI */ + + +#ifdef CONFIG_ISAPNP +/* + * PnP IDs: + * + * CTL00c1 - SB AWE32 PnP + * CTL00c3 - SB AWE64 PnP + * CTL00f0 - SB16 PnP / Vibra 16x + * CTL7001 - SB Vibra16C PnP + * CSC0b35 - Crystal ** doesn't have compatibility ID ** + * TER1141 - Terratec AD1818 + * YMM0800 - Yamaha OPL3-SA3 + * + * PNPb02f - Generic gameport + */ + +static struct pnp_devid { + unsigned int vendor, device; +} pnp_devids[] = { + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7002) }, + { ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0b35) }, + { ISAPNP_VENDOR('P','N','P'), ISAPNP_DEVICE(0xb02f) }, + { 0, }, +}; + +static struct ns558* ns558_pnp_probe(struct pci_dev *dev, struct ns558 *next) +{ + int ioport, iolen; + struct ns558 *port; + + if (dev->prepare && dev->prepare(dev) < 0) + return next; + + if (!(dev->resource[0].flags & IORESOURCE_IO)) { + printk(KERN_WARNING "No i/o ports on a gameport? Weird\n"); + return next; + } + + if (dev->activate && dev->activate(dev) < 0) { + printk(KERN_ERR "PnP resource allocation failed\n"); + return next; + } + + ioport = pci_resource_start(dev, 0); + iolen = pci_resource_len(dev, 0); + + if (!request_region(ioport, iolen, "ns558-pnp")) + goto deactivate; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + goto deactivate; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = next; + port->type = NS558_PNP; + port->gameport.io = ioport; + port->gameport.size = iolen; + port->dev = dev; + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 PnP at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return port; + +deactivate: + if (dev->deactivate) + dev->deactivate(dev); + return next; +} +#endif + +int __init ns558_init(void) +{ + int i = 0; +#ifdef CONFIG_ISAPNP + struct pci_dev *dev = NULL; + struct pnp_devid *devid; +#endif + +/* + * Probe for ISA ports. + */ + + while (ns558_isa_portlist[i]) + ns558 = ns558_isa_probe(ns558_isa_portlist[i++], ns558); + +/* + * Probe for PCI ports. + */ +#ifdef CONFIG_PCI + pci_register_driver(&ns558_pci_driver); +#endif + +/* + * Probe for PnP ports. + */ + +#ifdef CONFIG_ISAPNP + for (devid = pnp_devids; devid->vendor; devid++) { + while ((dev = isapnp_find_dev(NULL, devid->vendor, devid->device, dev))) { + ns558 = ns558_pnp_probe(dev, ns558); + } + } +#endif + + return -!ns558; +} + +void __exit ns558_exit(void) +{ + struct ns558 *port = ns558; + + while (port) { + gameport_unregister_port(&port->gameport); + switch (port->type) { + +#ifdef CONFIG_ISAPNP + case NS558_PNP: + if (port->dev->deactivate) + port->dev->deactivate(port->dev); + /* fall through */ +#endif + + case NS558_ISA: + release_region(port->gameport.io, port->gameport.size); + break; + + default: + break; + } + + port = port->next; + } + +#ifdef CONFIG_PCI + pci_unregister_driver(&ns558_pci_driver); +#endif +} + +module_init(ns558_init); +module_exit(ns558_exit); diff --git a/drivers/char/joystick/pcigame.c b/drivers/char/joystick/pcigame.c new file mode 100644 index 000000000..0348ba08b --- /dev/null +++ b/drivers/char/joystick/pcigame.c @@ -0,0 +1,198 @@ +/* + * $Id: pcigame.c,v 1.6 2000/05/25 12:05:24 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * Based on the work of: + * Raymond Ingles + * + * Sponsored by SuSE + */ + +/* + * Trident 4DWave and Aureal Vortex gameport driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/init.h> +#include <linux/gameport.h> + +#define PCI_VENDOR_ID_AUREAL 0x12eb + +#define PCIGAME_DATA_WAIT 20 /* 20 ms */ + +#define PCIGAME_4DWAVE 0 +#define PCIGAME_VORTEX 1 +#define PCIGAME_VORTEX2 2 + +struct pcigame_data { + int gcr; /* Gameport control register */ + int legacy; /* Legacy port location */ + int axes; /* Axes start */ + int axsize; /* Axis field size */ + int axmax; /* Axis field max value */ + int adcmode; /* Value to enable ADC mode in GCR */ +}; + +static struct pcigame_data pcigame_data[] __devinitdata = +{{ 0x00030, 0x00031, 0x00034, 2, 0xffff, 0x80 }, + { 0x1100c, 0x11008, 0x11010, 4, 0x1fff, 0x40 }, + { 0x2880c, 0x28808, 0x28810, 4, 0x1fff, 0x40 }, + { 0 }}; + +struct pcigame { + struct gameport gameport; + struct pci_dev *dev; + unsigned char *base; + struct pcigame_data *data; +}; + +static unsigned char pcigame_read(struct gameport *gameport) +{ + struct pcigame *pcigame = gameport->driver; + return readb(pcigame->base + pcigame->data->legacy); +} + +static void pcigame_trigger(struct gameport *gameport) +{ + struct pcigame *pcigame = gameport->driver; + writeb(0xff, pcigame->base + pcigame->data->legacy); +} + +static int pcigame_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct pcigame *pcigame = gameport->driver; + int i; + + *buttons = (~readb(pcigame->base + pcigame->data->legacy) >> 4) & 0xf; + + for (i = 0; i < 4; i++) { + axes[i] = readw(pcigame->base + pcigame->data->axes + i * pcigame->data->axsize); + if (axes[i] == pcigame->data->axmax) axes[i] = -1; + } + + return 0; +} + +static int pcigame_open(struct gameport *gameport, int mode) +{ + struct pcigame *pcigame = gameport->driver; + + switch (mode) { + case GAMEPORT_MODE_COOKED: + writeb(pcigame->data->adcmode, pcigame->base + pcigame->data->gcr); + wait_ms(PCIGAME_DATA_WAIT); + return 0; + case GAMEPORT_MODE_RAW: + writeb(0, pcigame->base + pcigame->data->gcr); + return 0; + default: + return -1; + } + + return 0; +} + +static int __devinit pcigame_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct pcigame *pcigame; + int i; + + if (!(pcigame = kmalloc(sizeof(struct pcigame), GFP_KERNEL))) + return -1; + memset(pcigame, 0, sizeof(struct pcigame)); + + + pcigame->data = pcigame_data + id->driver_data; + + pcigame->dev = dev; + dev->driver_data = pcigame; + + pcigame->gameport.driver = pcigame; + pcigame->gameport.type = GAMEPORT_EXT; + pcigame->gameport.fuzz = 64; + + pcigame->gameport.read = pcigame_read; + pcigame->gameport.trigger = pcigame_trigger; + pcigame->gameport.cooked_read = pcigame_cooked_read; + pcigame->gameport.open = pcigame_open; + + for (i = 0; i < 6; i++) + if (~pci_resource_flags(dev, i) & IORESOURCE_IO) + break; + + pci_enable_device(dev); + + pcigame->base = ioremap(pci_resource_start(pcigame->dev, i), + pci_resource_len(pcigame->dev, i)); + + gameport_register_port(&pcigame->gameport); + + printk(KERN_INFO "gameport%d: %s at pci%02x:%02x.%x speed %d kHz\n", + pcigame->gameport.number, dev->name, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), pcigame->gameport.speed); + + return 0; +} + +static void __devexit pcigame_remove(struct pci_dev *dev) +{ + struct pcigame *pcigame = dev->driver_data; + gameport_unregister_port(&pcigame->gameport); + iounmap(pcigame->base); + kfree(pcigame); +} + +static struct pci_device_id pcigame_id_table[] __devinitdata = +{{ PCI_VENDOR_ID_TRIDENT, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, + { PCI_VENDOR_ID_TRIDENT, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, + { PCI_VENDOR_ID_AUREAL, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX }, + { PCI_VENDOR_ID_AUREAL, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX2 }, + { 0 }}; + +static struct pci_driver pcigame_driver = { + name: "pcigame", + id_table: pcigame_id_table, + probe: pcigame_probe, + remove: pcigame_remove, +}; + +int __init pcigame_init(void) +{ + return pci_module_init(&pcigame_driver); +} + +void __exit pcigame_exit(void) +{ + pci_unregister_driver(&pcigame_driver); +} + +module_init(pcigame_init); +module_exit(pcigame_exit); diff --git a/drivers/char/joystick/serio.c b/drivers/char/joystick/serio.c new file mode 100644 index 000000000..9595f0a6c --- /dev/null +++ b/drivers/char/joystick/serio.c @@ -0,0 +1,132 @@ +/* + * $Id: serio.c,v 1.5 2000/06/04 17:44:59 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * The Serio abstraction module + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/serio.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); + +EXPORT_SYMBOL(serio_register_port); +EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_register_device); +EXPORT_SYMBOL(serio_unregister_device); +EXPORT_SYMBOL(serio_open); +EXPORT_SYMBOL(serio_close); +EXPORT_SYMBOL(serio_rescan); + +static struct serio *serio_list = NULL; +static struct serio_dev *serio_dev = NULL; +static int serio_number = 0; + +static void serio_find_dev(struct serio *serio) +{ + struct serio_dev *dev = serio_dev; + + while (dev && !serio->dev) { + if (dev->connect) + dev->connect(serio, dev); + dev = dev->next; + } +} + +void serio_rescan(struct serio *serio) +{ + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); + serio_find_dev(serio); +} + +void serio_register_port(struct serio *serio) +{ + serio->number = serio_number++; + serio->next = serio_list; + serio_list = serio; + serio_find_dev(serio); +} + +void serio_unregister_port(struct serio *serio) +{ + struct serio **serioptr = &serio_list; + + while (*serioptr && (*serioptr != serio)) serioptr = &((*serioptr)->next); + *serioptr = (*serioptr)->next; + + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); + + serio_number--; +} + +void serio_register_device(struct serio_dev *dev) +{ + struct serio *serio = serio_list; + + dev->next = serio_dev; + serio_dev = dev; + + while (serio) { + if (!serio->dev && dev->connect) + dev->connect(serio, dev); + serio = serio->next; + } +} + +void serio_unregister_device(struct serio_dev *dev) +{ + struct serio_dev **devptr = &serio_dev; + struct serio *serio = serio_list; + + while (*devptr && (*devptr != dev)) devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + while (serio) { + if (serio->dev == dev && dev->disconnect) + dev->disconnect(serio); + serio_find_dev(serio); + serio = serio->next; + } +} + +int serio_open(struct serio *serio, struct serio_dev *dev) +{ + if (serio->open(serio)) + return -1; + serio->dev = dev; + return 0; +} + +void serio_close(struct serio *serio) +{ + serio->close(serio); + serio->dev = NULL; +} diff --git a/drivers/char/joystick/serport.c b/drivers/char/joystick/serport.c new file mode 100644 index 000000000..453e674d7 --- /dev/null +++ b/drivers/char/joystick/serport.c @@ -0,0 +1,220 @@ +/* + * $Id: serport.c,v 1.4 2000/05/29 10:54:53 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module that converts a tty line into a much simpler + * 'serial io port' abstraction that the input device drivers use. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/uaccess.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/tty.h> + +struct serport { + struct tty_struct *tty; + wait_queue_head_t wait; + struct serio serio; +}; + +/* + * Callback functions from the serio code. + */ + +static int serport_serio_write(struct serio *serio, unsigned char data) +{ + struct serport *serport = serio->driver; + return -(serport->tty->driver.write(serport->tty, 0, &data, 1) != 1); +} + +static int serport_serio_open(struct serio *serio) +{ + return 0; +} + +static void serport_serio_close(struct serio *serio) +{ + struct serport *serport = serio->driver; + wake_up_interruptible(&serport->wait); +} + +/* + * serport_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. It looks for the Mag, and if found, registers + * it as a joystick device. + */ + +static int serport_ldisc_open(struct tty_struct *tty) +{ + struct serport *serport; + + MOD_INC_USE_COUNT; + + if (!(serport = kmalloc(sizeof(struct serport), GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + memset(serport, 0, sizeof(struct serport)); + + serport->tty = tty; + tty->disc_data = serport; + + serport->serio.type = SERIO_RS232; + serport->serio.write = serport_serio_write; + serport->serio.open = serport_serio_open; + serport->serio.close = serport_serio_close; + serport->serio.driver = serport; + + init_waitqueue_head(&serport->wait); + + return 0; +} + +/* + * serport_ldisc_close() is the opposite of serport_ldisc_open() + */ + +static void serport_ldisc_close(struct tty_struct *tty) +{ + struct serport *serport = (struct serport*) tty->disc_data; + kfree(serport); + MOD_DEC_USE_COUNT; +} + +/* + * serport_ldisc_receive() is called by the low level tty driver when characters + * are ready for us. We forward the characters, one by one to the 'interrupt' + * routine. + */ + +static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct serport *serport = (struct serport*) tty->disc_data; + int i; + for (i = 0; i < count; i++) + if (serport->serio.dev) + serport->serio.dev->interrupt(&serport->serio, cp[i], 0); +} + +/* + * serport_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, and 256 seems to be reasonable. + */ + +static int serport_ldisc_room(struct tty_struct *tty) +{ + return 256; +} + +/* + * serport_ldisc_read() just waits indefinitely if everything goes well. + * However, when the serio driver closes the serio port, it finishes, + * returning 0 characters. + */ + +static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char * buf, size_t nr) +{ + struct serport *serport = (struct serport*) tty->disc_data; + DECLARE_WAITQUEUE(wait, current); + char name[32]; + + sprintf(name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + serio_register_port(&serport->serio); + + printk(KERN_INFO "serio%d: Serial port %s\n", serport->serio.number, name); + + add_wait_queue(&serport->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while(serport->serio.type && !signal_pending(current)) schedule(); + + current->state = TASK_RUNNING; + remove_wait_queue(&serport->wait, &wait); + + serio_unregister_port(&serport->serio); + + return 0; +} + +/* + * serport_ldisc_ioctl() allows to set the port protocol, and device ID + */ + +static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct serport *serport = (struct serport*) tty->disc_data; + + switch (cmd) { + case SPIOCSTYPE: + return get_user(serport->serio.type, (unsigned long *) arg); + } + + return -EINVAL; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc serport_ldisc = { + name: "input", + open: serport_ldisc_open, + close: serport_ldisc_close, + read: serport_ldisc_read, + ioctl: serport_ldisc_ioctl, + receive_buf: serport_ldisc_receive, + receive_room: serport_ldisc_room, +}; + +/* + * The functions for insering/removing us as a module. + */ + +int __init serport_init(void) +{ + if (tty_register_ldisc(N_MOUSE, &serport_ldisc)) { + printk(KERN_ERR "serport.c: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +void __exit serport_exit(void) +{ + tty_register_ldisc(N_MOUSE, NULL); +} + +module_init(serport_init); +module_exit(serport_exit); diff --git a/drivers/char/joystick/sidewinder.c b/drivers/char/joystick/sidewinder.c new file mode 100644 index 000000000..861966b4e --- /dev/null +++ b/drivers/char/joystick/sidewinder.c @@ -0,0 +1,756 @@ +/* + * $Id: sidewinder.c,v 1.14 2000/05/29 11:27:55 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Microsoft SideWinder joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +/* + * These are really magic values. Changing them can make a problem go away, + * as well as break everything. + */ + +#undef SW_DEBUG + +#define SW_START 400 /* The time we wait for the first bit [400 us] */ +#define SW_STROBE 45 /* Max time per bit [45 us] */ +#define SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ +#define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ +#define SW_END 8 /* Number of bits before end of packet to kick */ +#define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ +#define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ +#define SW_OK 64 /* Number of packet read successes to switch optimization back on */ +#define SW_LENGTH 512 /* Max number of bits in a packet */ +#define SW_REFRESH HZ/50 /* Time to wait between updates of joystick data [20 ms] */ + +#ifdef SW_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +/* + * SideWinder joystick types ... + */ + +#define SW_ID_3DP 0 +#define SW_ID_GP 1 +#define SW_ID_PP 2 +#define SW_ID_FFP 3 +#define SW_ID_FSP 4 +#define SW_ID_FFW 5 + +/* + * Names, buttons, axes ... + */ + +static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro", + "Force Feedback Wheel" }; + +static char sw_abs[][7] = { + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_RX, ABS_RUDDER, ABS_THROTTLE }}; + +static char sw_bit[][7] = { + { 10, 10, 9, 10, 1, 1 }, + { 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 1, 1 }, + { 10, 7, 7, 1, 1 }}; + +static short sw_btn[][12] = { + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }, + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3 }}; + +static struct { + int x; + int y; +} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct sw { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[4]; + char name[64]; + int length; + int type; + int bits; + int number; + int fail; + int ok; + int reads; + int bads; + int used; +}; + +/* + * sw_read_packet() is a function which reads either a data packet, or an + * identification packet from a SideWinder joystick. The protocol is very, + * very, very braindamaged. Microsoft patented it in US patent #5628686. + */ + +static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id) +{ + unsigned long flags; + int timeout, bitout, sched, i, kick, start, strobe; + unsigned char pending, u, v; + + i = -id; /* Don't care about data, only want ID */ + timeout = id ? gameport_time(gameport, SW_TIMEOUT) : 0; /* Set up global timeout for ID packet */ + kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */ + start = gameport_time(gameport, SW_START); + strobe = gameport_time(gameport, SW_STROBE); + bitout = start; + pending = 0; + sched = 0; + + __save_flags(flags); /* Quiet, please */ + __cli(); + + gameport_trigger(gameport); /* Trigger */ + v = gameport_read(gameport); + + do { + bitout--; + u = v; + v = gameport_read(gameport); + } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ + + if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ + + while ((timeout > 0 || bitout > 0) && (i < length)) { + + timeout--; + bitout--; /* Decrement timers */ + sched--; + + u = v; + v = gameport_read(gameport); + + if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ + if (i >= 0) /* Want this data */ + buf[i] = v >> 5; /* Store it */ + i++; /* Advance index */ + bitout = strobe; /* Extend timeout for next bit */ + } + + if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ + sched = kick; /* Schedule second trigger */ + kick = 0; /* Don't schedule next time on falling edge */ + pending = 1; /* Mark schedule */ + } + + if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */ + gameport_trigger(gameport); /* Trigger */ + bitout = start; /* Long bit timeout */ + pending = 0; /* Unmark schedule */ + timeout = 0; /* Switch from global to bit timeouts */ + } + } + + __restore_flags(flags); /* Done - relax */ + +#ifdef SW_DEBUG + { + int j; + printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i); + for (j = 0; j < i; j++) printk("%d", buf[j]); + printk("]\n"); + } +#endif + + return i; +} + +/* + * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits) + +static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits) +{ + __u64 data = 0; + int tri = pos % bits; /* Start position */ + int i = pos / bits; + int bit = 0; + + while (num--) { + data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ + if (tri == bits) { + i++; /* Next triplet */ + tri = 0; + } + } + + return data; +} + +/* + * sw_init_digital() initializes a SideWinder 3D Pro joystick + * into digital mode. + */ + +static void sw_init_digital(struct gameport *gameport) +{ + int seq[] = { 140, 140+725, 140+300, 0 }; + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + gameport_trigger(gameport); /* Trigger */ + t = gameport_time(gameport, SW_TIMEOUT); + while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */ + udelay(seq[i]); /* Delay magic time */ + } while (seq[++i]); + + gameport_trigger(gameport); /* Last trigger */ + + __restore_flags(flags); +} + +/* + * sw_parity() computes parity of __u64 + */ + +static int sw_parity(__u64 t) +{ + int x = t ^ (t >> 32); + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; +} + +/* + * sw_ccheck() checks synchronization bits and computes checksum of nibbles. + */ + +static int sw_check(__u64 t) +{ + unsigned char sum = 0; + + if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ + return -1; + + while (t) { /* Sum */ + sum += t & 0xf; + t >>= 4; + } + + return sum & 0xf; +} + +/* + * sw_parse() analyzes SideWinder joystick data, and writes the results into + * the axes and buttons arrays. + */ + +static int sw_parse(unsigned char *buf, struct sw *sw) +{ + int hat, i, j; + struct input_dev *dev = sw->dev; + + switch (sw->type) { + + case SW_ID_3DP: + + if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8) return -1; + + input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7)); + input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7)); + input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7)); + input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 7; j++) + input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1)); + + input_report_key(dev, BTN_BASE4, !GB(38,1)); + input_report_key(dev, BTN_BASE5, !GB(37,1)); + + return 0; + + case SW_ID_GP: + + for (i = 0; i < sw->number; i ++) { + + if (sw_parity(GB(i*15,15))) return -1; + + input_report_key(dev + i, ABS_X, GB(i*15+3,1) - GB(i*15+2,1)); + input_report_key(dev + i, ABS_Y, GB(i*15+0,1) - GB(i*15+1,1)); + + for (j = 0; j < 10; j++) + input_report_key(dev, sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1)); + } + + return 0; + + case SW_ID_PP: + case SW_ID_FFP: + + if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8) return -1; + + input_report_abs(dev, ABS_X, GB( 9,10)); + input_report_abs(dev, ABS_Y, GB(19,10)); + input_report_abs(dev, ABS_RZ, GB(36, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(29, 7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 9; j++) + input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1)); + + return 0; + + case SW_ID_FSP: + + if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8) return -1; + + input_report_abs(dev, ABS_X, GB( 0,10)); + input_report_abs(dev, ABS_Y, GB(16,10)); + input_report_abs(dev, ABS_THROTTLE, GB(32, 6)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 6; j++) + input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1)); + + input_report_key(dev, BTN_TR, GB(26,1)); + input_report_key(dev, BTN_START, GB(27,1)); + input_report_key(dev, BTN_MODE, GB(38,1)); + input_report_key(dev, BTN_SELECT, GB(39,1)); + + return 0; + + case SW_ID_FFW: + + if (!sw_parity(GB(0,33))) return -1; + + input_report_abs(dev, ABS_RX, GB( 0,10)); + input_report_abs(dev, ABS_RUDDER, GB(10, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(16, 6)); + + for (j = 0; j < 8; j++) + input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1)); + + return 0; + } + + return -1; +} + +/* + * sw_read() reads SideWinder joystick data, and reinitializes + * the joystick in case of persistent problems. This is the function that is + * called from the generic code to poll the joystick. + */ + +static int sw_read(struct sw *sw) +{ + unsigned char buf[SW_LENGTH]; + int i; + + i = sw_read_packet(sw->gameport, buf, sw->length, 0); + + if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */ + + if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */ + printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on gameport%d" + " - going to reinitialize.\n", sw->gameport->number); + sw->fail = SW_FAIL; /* Reinitialize */ + i = 128; /* Bogus value */ + } + + if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */ + memmove(buf, buf + i - 22, 22); /* Move data */ + i = 66; /* Carry on */ + } + } + + if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */ + + sw->fail = 0; + sw->ok++; + + if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */ + && sw->ok > SW_OK) { + + printk(KERN_INFO "sidewinder.c: No more trouble on gameport%d" + " - enabling optimization again.\n", sw->gameport->number); + sw->length = 22; + } + + return 0; + } + + sw->ok = 0; + sw->fail++; + + if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */ + + printk(KERN_INFO "sidewinder.c: Many bit errors on gameport%d" + " - disabling optimization.\n", sw->gameport->number); + sw->length = 66; + } + + if (sw->fail < SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ + + printk(KERN_WARNING "sidewinder.c: Too many bit errors on gameport%d" + " - reinitializing joystick.\n", sw->gameport->number); + + if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */ + udelay(3 * SW_TIMEOUT); + sw_init_digital(sw->gameport); + } + + udelay(SW_TIMEOUT); + i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */ + udelay(SW_TIMEOUT); + sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */ + + sw->fail = SW_FAIL; + + return -1; +} + +static void sw_timer(unsigned long private) +{ + struct sw *sw = (void *) private; + + sw->reads++; + if (sw_read(sw)) sw->bads++; + mod_timer(&sw->timer, jiffies + SW_REFRESH); +} + +static int sw_open(struct input_dev *dev) +{ + struct sw *sw = dev->private; + if (!sw->used++) + mod_timer(&sw->timer, jiffies + SW_REFRESH); + return 0; +} + +static void sw_close(struct input_dev *dev) +{ + struct sw *sw = dev->private; + if (!--sw->used) + del_timer(&sw->timer); +} + +/* + * sw_print_packet() prints the contents of a SideWinder packet. + */ + +static void sw_print_packet(char *name, int length, unsigned char *buf, char bits) +{ + int i; + + printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length); + for (i = (((length + 3) >> 2) - 1); i >= 0; i--) + printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits)); + printk("]\n"); +} + +/* + * sw_3dp_id() translates the 3DP id into a human legible string. + * Unfortunately I don't know how to do this for the other SW types. + */ + +static void sw_3dp_id(unsigned char *buf, char *comment) +{ + int i; + char pnp[8], rev[9]; + + for (i = 0; i < 7; i++) /* ASCII PnP ID */ + pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1); + + for (i = 0; i < 8; i++) /* ASCII firmware revision */ + rev[i] = sw_get_bits(buf, 88+8*i, 8, 1); + + pnp[7] = rev[8] = 0; + + sprintf(comment, " [PnP %d.%02d id %s rev %s]", + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */ + sw_get_bits(buf, 16, 6, 1)) / 100, + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | + sw_get_bits(buf, 16, 6, 1)) % 100, + pnp, rev); +} + +/* + * sw_guess_mode() checks the upper two button bits for toggling - + * indication of that the joystick is in 3-bit mode. This is documented + * behavior for 3DP ID packet, and for example the FSP does this in + * normal packets instead. Fun ... + */ + +static int sw_guess_mode(unsigned char *buf, int len) +{ + int i; + unsigned char xor = 0; + for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; + return !!xor * 2 + 1; +} + +/* + * sw_connect() probes for SideWinder type joysticks. + */ + +static void sw_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct sw *sw; + int i, j, k, l; + unsigned char buf[SW_LENGTH]; + unsigned char idbuf[SW_LENGTH]; + unsigned char m = 1; + char comment[40]; + + comment[0] = 0; + + if (!(sw = kmalloc(sizeof(struct sw), GFP_KERNEL))) return; + memset(sw, 0, sizeof(struct sw)); + + gameport->private = sw; + + sw->gameport = gameport; + init_timer(&sw->timer); + sw->timer.data = (long) sw; + sw->timer.function = sw_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */ + m |= sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ + udelay(SW_TIMEOUT); + dbg("Init 1: Mode %d. Length %d.", m , i); + + if (!i) { /* No data. 3d Pro analog mode? */ + sw_init_digital(gameport); /* Switch to digital */ + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + udelay(SW_TIMEOUT); + dbg("Init 1b: Length %d.", i); + if (!i) goto fail2; /* No data -> FAIL */ + } + + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */ + m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ + dbg("Init 2: Mode %d. ID Length %d.", m , j); + + if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + dbg("Init 2b: Mode %d. Length %d.", m, i); + if (!i) goto fail2; + udelay(SW_TIMEOUT); + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */ + dbg("Init 2c: ID Length %d.", j); + } + + sw->type = -1; + k = SW_FAIL; /* Try SW_FAIL times */ + l = 0; + + do { + k--; + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */ + dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k); + + if (i > l) { /* Longer? As we can only lose bits, it makes */ + /* no sense to try detection for a packet shorter */ + l = i; /* than the previous one */ + + sw->number = 1; + sw->gameport = gameport; + sw->length = i; + sw->bits = m; + + dbg("Init 3a: Case %d.\n", i * m); + + switch (i * m) { + case 60: + sw->number++; + case 45: /* Ambiguous packet length */ + if (j <= 40) { /* ID length less or eq 40 -> FSP */ + case 43: + sw->type = SW_ID_FSP; + break; + } + sw->number++; + case 30: + sw->number++; + case 15: + sw->type = SW_ID_GP; + break; + case 33: + case 31: + sw->type = SW_ID_FFW; + break; + case 48: /* Ambiguous */ + if (j == 14) { /* ID length 14*3 -> FFP */ + sw->type = SW_ID_FFP; + sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on"); + } else + sw->type = SW_ID_PP; + break; + case 198: + sw->length = 22; + case 64: + sw->type = SW_ID_3DP; + if (j == 160) sw_3dp_id(idbuf, comment); + break; + } + } + + } while (k && (sw->type == -1)); + + if (sw->type == -1) { + printk(KERN_WARNING "sidewinder.c: unknown joystick device detected " + "on gameport%d, contact <vojtech@suse.cz>\n", gameport->number); + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); + goto fail2; + } + +#ifdef SW_DEBUG + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); +#endif + + k = i; + l = j; + + for (i = 0; i < sw->number; i++) { + int bits, code; + + sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]); + + sw->dev[i].private = sw; + + sw->dev[i].open = sw_open; + sw->dev[i].close = sw_close; + + sw->dev[i].name = sw->name; + sw->dev[i].idbus = BUS_GAMEPORT; + sw->dev[i].idvendor = GAMEPORT_ID_VENDOR_MICROSOFT; + sw->dev[i].idproduct = sw->type; + sw->dev[i].idversion = 0x0100; + + sw->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; (bits = sw_bit[sw->type][j]); j++) { + code = sw_abs[sw->type][j]; + set_bit(code, sw->dev[i].absbit); + sw->dev[i].absmax[code] = (1 << bits) - 1; + sw->dev[i].absmin[code] = (bits == 1) ? -1 : 0; + sw->dev[i].absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0; + if (code != ABS_THROTTLE) + sw->dev[i].absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0; + } + + for (j = 0; (code = sw_btn[sw->type][j]); j++) + set_bit(code, sw->dev[i].keybit); + + input_register_device(sw->dev + i); + printk(KERN_INFO "input%d: %s%s on gameport%d.%d [%d-bit id %d data %d]\n", + sw->dev[i].number, sw->name, comment, gameport->number, i, m, l, k); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(sw); +} + +static void sw_disconnect(struct gameport *gameport) +{ + int i; + + struct sw *sw = gameport->private; + for (i = 0; i < sw->number; i++) + input_unregister_device(sw->dev + i); + gameport_close(gameport); + kfree(sw); +} + +static struct gameport_dev sw_dev = { + connect: sw_connect, + disconnect: sw_disconnect, +}; + +int __init sw_init(void) +{ + gameport_register_device(&sw_dev); + return 0; +} + +void __exit sw_exit(void) +{ + gameport_unregister_device(&sw_dev); +} + +module_init(sw_init); +module_exit(sw_exit); diff --git a/drivers/char/joystick/spaceball.c b/drivers/char/joystick/spaceball.c new file mode 100644 index 000000000..4f5059330 --- /dev/null +++ b/drivers/char/joystick/spaceball.c @@ -0,0 +1,231 @@ +/* + * $Id: spaceball.c,v 1.6 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + * Joseph Krahn + * + * Sponsored by SuSE + */ + +/* + * SpaceTec SpaceBall 4000 FLX driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> + +/* + * Constants. + */ + +#define JS_SBALL_MAX_LENGTH 128 +static int spaceball_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char *spaceball_name = "SpaceTec SpaceBall 4000 FLX"; + +/* + * Per-Ball data. + */ + +struct spaceball { + struct input_dev dev; + struct serio *serio; + int idx; + int escape; + unsigned char data[JS_SBALL_MAX_LENGTH]; +}; + +/* + * spaceball_process_packet() decodes packets the driver receives from the + * SpaceBall. + */ + +static void spaceball_process_packet(struct spaceball* spaceball) +{ + struct input_dev *dev = &spaceball->dev; + unsigned char *data = spaceball->data; + int i, d; + + if (spaceball->idx < 2) return; + + switch (spaceball->data[0]) { + + case '@': /* Reset packet */ + spaceball->data[spaceball->idx - 1] = 0; + for (i = 1; i < spaceball->idx && spaceball->data[i] == ' '; i++); + printk(KERN_INFO "input%d: %s [%s] on serio%d\n", + spaceball->dev.number, spaceball_name, spaceball->data + i, spaceball->serio->number); + break; + + case 'D': /* Ball data */ + if (spaceball->idx != 16) return; + for (i = 0; i < 6; i++) { + d = ((data[2 * i + 3] << 8) | data[2 * i + 2]); + input_report_abs(dev, spaceball_axes[i], d - ((d & 0x8000) ? 0x10000 : 0)); + } + break; + + case '.': /* Button data, part2 */ + if (spaceball->idx != 4) return; + input_report_key(dev, BTN_LEFT, data[2] & 1); + input_report_key(dev, BTN_RIGHT, data[2] & 2); + break; + + case '?': /* Error packet */ + spaceball->data[spaceball->idx - 1] = 0; + printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1); + break; + } +} + +/* + * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, + * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can + * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) + * on whether the axis value is increasing, decreasing, or same as before. + * (I don't see why this is useful). + */ + +static void spaceball_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct spaceball *spaceball = serio->private; + + switch (data) { + case 0xd: + if (spaceball->idx) + spaceball_process_packet(spaceball); + spaceball->idx = 0; + spaceball->escape = 0; + return; + case 'M': + case 'Q': + case 'S': + if (spaceball->escape) + data = 0xd; + case '^': + spaceball->escape ^= 1; + default: + if (spaceball->escape) { + printk(KERN_WARNING "spaceball.c: Unknown escaped character: %#x\n", data); + spaceball->escape = 0; + } + if (spaceball->idx < JS_SBALL_MAX_LENGTH) + spaceball->data[spaceball->idx++] = data; + return; + } +} + +/* + * spaceball_disconnect() is the opposite of spaceball_connect() + */ + +static void spaceball_disconnect(struct serio *serio) +{ + struct spaceball* spaceball = serio->private; + input_unregister_device(&spaceball->dev); + serio_close(serio); + kfree(spaceball); +} + +/* + * spaceball_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Magellan, and if found, registers + * it as an input device. + */ + +static void spaceball_connect(struct serio *serio, struct serio_dev *dev) +{ + struct spaceball *spaceball; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_SPACEBALL)) + return; + + if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL))) + return; + memset(spaceball, 0, sizeof(struct spaceball)); + + spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + spaceball->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); + + for (i = 0; i < 6; i++) { + t = spaceball_axes[i]; + set_bit(t, spaceball->dev.absbit); + spaceball->dev.absmin[t] = i < 3 ? -10000 : -2000; + spaceball->dev.absmax[t] = i < 3 ? 10000 : 2000; + spaceball->dev.absflat[t] = i < 3 ? 50 : 10; + spaceball->dev.absfuzz[t] = i < 3 ? 12 : 2; + } + + spaceball->serio = serio; + spaceball->dev.private = spaceball; + + spaceball->dev.name = spaceball_name; + spaceball->dev.idbus = BUS_RS232; + spaceball->dev.idvendor = SERIO_SPACEBALL; + spaceball->dev.idproduct = 0x0001; + spaceball->dev.idversion = 0x0100; + + serio->private = spaceball; + + if (serio_open(serio, dev)) { + kfree(spaceball); + return; + } + + input_register_device(&spaceball->dev); +} + +/* + * The serio device structure. + */ + +static struct serio_dev spaceball_dev = { + interrupt: spaceball_interrupt, + connect: spaceball_connect, + disconnect: spaceball_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init spaceball_init(void) +{ + serio_register_device(&spaceball_dev); + return 0; +} + +void __exit spaceball_exit(void) +{ + serio_unregister_device(&spaceball_dev); +} + +module_init(spaceball_init); +module_exit(spaceball_exit); diff --git a/drivers/char/joystick/spaceorb.c b/drivers/char/joystick/spaceorb.c new file mode 100644 index 000000000..866e1ba50 --- /dev/null +++ b/drivers/char/joystick/spaceorb.c @@ -0,0 +1,225 @@ +/* + * $Id: spaceorb.c,v 1.7 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + * + * Sponsored by SuSE + */ + +/* + * SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> + +/* + * Constants. + */ + +#define SPACEORB_MAX_LENGTH 64 + +static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A, BTN_MODE}; +static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; +static char *spaceorb_name = "SpaceTec SpaceOrb 360"; + +/* + * Per-Orb data. + */ + +struct spaceorb { + struct input_dev dev; + struct serio *serio; + int idx; + unsigned char data[SPACEORB_MAX_LENGTH]; +}; + +static unsigned char spaceorb_xor[] = "SpaceWare"; + +static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", + "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; + +/* + * spaceorb_process_packet() decodes packets the driver receives from the + * SpaceOrb. + */ + +static void spaceorb_process_packet(struct spaceorb *spaceorb) +{ + struct input_dev *dev = &spaceorb->dev; + unsigned char *data = spaceorb->data; + unsigned char c = 0; + int axes[6]; + int i; + + if (spaceorb->idx < 2) return; + for (i = 0; i < spaceorb->idx; i++) c ^= data[i]; + if (c) return; + + switch (data[0]) { + + case 'R': /* Reset packet */ + spaceorb->data[spaceorb->idx - 1] = 0; + for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++); + printk(KERN_INFO "input%d: %s [%s] on serio%d\n", + spaceorb->dev.number, spaceorb_name, spaceorb->data + i, spaceorb->serio->number); + break; + + case 'D': /* Ball + button data */ + if (spaceorb->idx != 12) return; + for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i]; + axes[0] = ( data[2] << 3) | (data[ 3] >> 4); + axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); + axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); + axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); + axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); + axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); + for (i = 0; i < 6; i++) + input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0)); + for (i = 0; i < 8; i++) + input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1); + break; + + case 'K': /* Button data */ + if (spaceorb->idx != 5) return; + for (i = 0; i < 7; i++) + input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1); + + break; + + case 'E': /* Error packet */ + if (spaceorb->idx != 4) return; + printk(KERN_ERR "joy-spaceorb: Device error. [ "); + for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]); + printk("]\n"); + break; + } +} + +static void spaceorb_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct spaceorb* spaceorb = serio->private; + + if (~data & 0x80) { + if (spaceorb->idx) spaceorb_process_packet(spaceorb); + spaceorb->idx = 0; + } + if (spaceorb->idx < SPACEORB_MAX_LENGTH) + spaceorb->data[spaceorb->idx++] = data & 0x7f; +} + +/* + * spaceorb_disconnect() is the opposite of spaceorb_connect() + */ + +static void spaceorb_disconnect(struct serio *serio) +{ + struct spaceorb* spaceorb = serio->private; + input_unregister_device(&spaceorb->dev); + serio_close(serio); + kfree(spaceorb); +} + +/* + * spaceorb_connect() is the routine that is called when someone adds a + * new serio device. It looks for the SpaceOrb/Avenger, and if found, registers + * it as an input device. + */ + +static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) +{ + struct spaceorb *spaceorb; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_SPACEORB)) + return; + + if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL))) + return; + memset(spaceorb, 0, sizeof(struct spaceorb)); + + spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < 7; i++) + set_bit(spaceorb_buttons[i], &spaceorb->dev.keybit); + + for (i = 0; i < 6; i++) { + t = spaceorb_axes[i]; + set_bit(t, spaceorb->dev.absbit); + spaceorb->dev.absmin[t] = -508; + spaceorb->dev.absmax[t] = 508; + } + + spaceorb->serio = serio; + spaceorb->dev.private = spaceorb; + + spaceorb->dev.name = spaceorb_name; + spaceorb->dev.idbus = BUS_RS232; + spaceorb->dev.idvendor = SERIO_SPACEORB; + spaceorb->dev.idproduct = 0x0001; + spaceorb->dev.idversion = 0x0100; + + serio->private = spaceorb; + + if (serio_open(serio, dev)) { + kfree(spaceorb); + return; + } + + input_register_device(&spaceorb->dev); +} + +/* + * The serio device structure. + */ + +static struct serio_dev spaceorb_dev = { + interrupt: spaceorb_interrupt, + connect: spaceorb_connect, + disconnect: spaceorb_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init spaceorb_init(void) +{ + serio_register_device(&spaceorb_dev); + return 0; +} + +void __exit spaceorb_exit(void) +{ + serio_unregister_device(&spaceorb_dev); +} + +module_init(spaceorb_init); +module_exit(spaceorb_exit); diff --git a/drivers/char/joystick/tmdc.c b/drivers/char/joystick/tmdc.c new file mode 100644 index 000000000..f356f7dd5 --- /dev/null +++ b/drivers/char/joystick/tmdc.c @@ -0,0 +1,348 @@ +/* + * $Id: tmdc.c,v 1.18 2000/06/08 19:59:59 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + * + * Based on the work of: + * Trystan Larey-Williams + * + */ + +/* + * ThrustMaster DirectConnect (BSP) joystick family driver for Linux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define TMDC_MAX_START 400 /* 400 us */ +#define TMDC_MAX_STROBE 45 /* 45 us */ +#define TMDC_MAX_LENGTH 13 +#define TMDC_REFRESH_TIME HZ/50 /* 20 ms */ + +#define TMDC_MODE_M3DI 1 +#define TMDC_MODE_3DRP 3 +#define TMDC_MODE_FGP 163 + +#define TMDC_BYTE_ID 10 +#define TMDC_BYTE_REV 11 +#define TMDC_BYTE_DEF 12 + +#define TMDC_ABS 7 +#define TMDC_ABS_HAT 4 +#define TMDC_BTN_PAD 10 +#define TMDC_BTN_JOY 16 + +static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; +static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 }; + +static unsigned char tmdc_abs[TMDC_ABS] = + { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ }; +static unsigned char tmdc_abs_hat[TMDC_ABS_HAT] = + { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; +static unsigned short tmdc_btn_pad[TMDC_BTN_PAD] = + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR }; +static unsigned short tmdc_btn_joy[TMDC_BTN_JOY] = + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE, + BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; + +struct tmdc { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + char name[2][64]; + int mode[2]; + int used; + int reads; + int bads; + unsigned char exists; +}; + +/* + * tmdc_read_packet() reads a ThrustMaster packet. + */ + +static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH]) +{ + unsigned char u, v, w, x; + unsigned long flags; + int i[2], j[2], t[2], p, k; + + p = gameport_time(gameport, TMDC_MAX_STROBE); + + for (k = 0; k < 2; k++) { + t[k] = gameport_time(gameport, TMDC_MAX_START); + i[k] = j[k] = 0; + } + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + + w = gameport_read(gameport) >> 4; + + do { + x = w; + w = gameport_read(gameport) >> 4; + + for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) { + if (~v & u & 2) { + if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue; + t[k] = p; + if (j[k] == 0) { /* Start bit */ + if (~v & 1) t[k] = 0; + data[k][i[k]] = 0; j[k]++; continue; + } + if (j[k] == 9) { /* Stop bit */ + if (v & 1) t[k] = 0; + j[k] = 0; i[k]++; continue; + } + data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */ + } + t[k]--; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1); +} + +/* + * tmdc_read() reads and analyzes ThrustMaster joystick data. + */ + +static void tmdc_timer(unsigned long private) +{ + unsigned char data[2][TMDC_MAX_LENGTH]; + struct tmdc *tmdc = (void *) private; + struct input_dev *dev; + unsigned char r, bad = 0; + int i, j; + + tmdc->reads++; + + if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists) + bad = 1; + + for (j = 0; j < 2; j++) + if (r & (1 << j) & tmdc->exists) { + + if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) { + bad = 1; + continue; + } + + dev = tmdc->dev + j; + + for (i = 0; i < data[j][TMDC_BYTE_DEF] >> 4; i++) + input_report_abs(dev, tmdc_abs[i], data[j][tmdc_byte_a[i]]); + + switch (tmdc->mode[j]) { + + case TMDC_MODE_M3DI: + + i = tmdc_byte_d[0]; + + input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i] & 1)); + + for (i = 0; i < 4; i++) + input_report_key(dev, tmdc_btn_joy[i], + (data[j][tmdc_byte_d[0]] >> (i + 4)) & 1); + for (i = 0; i < 2; i++) + input_report_key(dev, tmdc_btn_joy[i + 4], + (data[j][tmdc_byte_d[1]] >> (i + 6)) & 1); + + break; + + case TMDC_MODE_3DRP: + case TMDC_MODE_FGP: + + for (i = 0; i < 10; i++) + input_report_key(dev, tmdc_btn_pad[i], + (data[j][tmdc_byte_d[i >> 3]] >> (i & 7)) & 1); + + break; + + default: + + for (i = 0; i < ((data[j][TMDC_BYTE_DEF] & 0xf) << 3) && i < TMDC_BTN_JOY; i++) + input_report_key(dev, tmdc_btn_joy[i], + (data[j][tmdc_byte_d[i >> 3]] >> (i & 7)) & 1); + + break; + + } + } + + tmdc->bads += bad; + + mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME); +} + +static int tmdc_open(struct input_dev *dev) +{ + struct tmdc *tmdc = dev->private; + if (!tmdc->used++) + mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME); + return 0; +} + +static void tmdc_close(struct input_dev *dev) +{ + struct tmdc *tmdc = dev->private; + if (!--tmdc->used) + del_timer(&tmdc->timer); +} + +/* + * tmdc_probe() probes for ThrustMaster type joysticks. + */ + +static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct tmdc *tmdc; + struct js_tm_models { + unsigned char id; + char *name; + char abs; + char hats; + char joybtn; + char padbtn; + } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 0, 6, 0 }, + { 3, "ThrustMaster Rage 3D Gamepad", 2, 0, 0, 10 }, + { 163, "Thrustmaster Fusion GamePad", 2, 0, 0, 10 }, + { 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, 0, 0 }}; + unsigned char data[2][TMDC_MAX_LENGTH]; + int i, j, m; + + if (!(tmdc = kmalloc(sizeof(struct tmdc), GFP_KERNEL))) + return; + memset(tmdc, 0, sizeof(struct tmdc)); + + gameport->private = tmdc; + + tmdc->gameport = gameport; + init_timer(&tmdc->timer); + tmdc->timer.data = (long) tmdc; + tmdc->timer.function = tmdc_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + if (!(tmdc->exists = tmdc_read_packet(gameport, data))) + goto fail2; + + for (j = 0; j < 2; j++) + if (tmdc->exists & (1 << j)) { + + tmdc->mode[j] = data[j][TMDC_BYTE_ID]; + + for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++); + + if (!models[m].id) { + models[m].abs = data[j][TMDC_BYTE_DEF] >> 4; + models[m].joybtn = (data[j][TMDC_BYTE_DEF] & 0xf) << 3; + } + + sprintf(tmdc->name[j], models[m].name, models[m].abs, models[m].joybtn, tmdc->mode[j]); + + tmdc->dev[j].private = tmdc; + tmdc->dev[j].open = tmdc_open; + tmdc->dev[j].close = tmdc_close; + + tmdc->dev[j].name = tmdc->name[j]; + tmdc->dev[j].idbus = BUS_GAMEPORT; + tmdc->dev[j].idvendor = GAMEPORT_ID_VENDOR_THRUSTMASTER; + tmdc->dev[j].idproduct = models[m].id; + tmdc->dev[j].idversion = 0x0100; + + tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) { + set_bit(tmdc_abs[i], tmdc->dev[j].absbit); + tmdc->dev[j].absmin[tmdc_abs[i]] = 8; + tmdc->dev[j].absmax[tmdc_abs[i]] = 248; + tmdc->dev[j].absfuzz[tmdc_abs[i]] = 2; + tmdc->dev[j].absflat[tmdc_abs[i]] = 4; + } + + for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++) { + set_bit(tmdc_abs_hat[i], tmdc->dev[j].absbit); + tmdc->dev[j].absmin[tmdc_abs_hat[i]] = -1; + tmdc->dev[j].absmax[tmdc_abs_hat[i]] = 1; + } + + for (i = 0; i < models[m].joybtn && i < TMDC_BTN_JOY; i++) + set_bit(tmdc_btn_joy[i], tmdc->dev[j].keybit); + + for (i = 0; i < models[m].padbtn && i < TMDC_BTN_PAD; i++) + set_bit(tmdc_btn_pad[i], tmdc->dev[j].keybit); + + input_register_device(tmdc->dev + j); + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + tmdc->dev[j].number, tmdc->name[j], gameport->number, j); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(tmdc); +} + +static void tmdc_disconnect(struct gameport *gameport) +{ + struct tmdc *tmdc = gameport->private; + int i; + for (i = 0; i < 2; i++) + if (tmdc->exists & (1 << i)) + input_unregister_device(tmdc->dev + i); + gameport_close(gameport); + kfree(tmdc); +} + +static struct gameport_dev tmdc_dev = { + connect: tmdc_connect, + disconnect: tmdc_disconnect, +}; + +int __init tmdc_init(void) +{ + gameport_register_device(&tmdc_dev); + return 0; +} + +void __exit tmdc_exit(void) +{ + gameport_unregister_device(&tmdc_dev); +} + +module_init(tmdc_init); +module_exit(tmdc_exit); diff --git a/drivers/char/joystick/turbografx.c b/drivers/char/joystick/turbografx.c new file mode 100644 index 000000000..0e2dedcd0 --- /dev/null +++ b/drivers/char/joystick/turbografx.c @@ -0,0 +1,258 @@ +/* + * $Id: turbografx.c,v 1.8 2000/05/29 20:39:38 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Based on the work of: + * Steffen Schwenke + * + * Sponsored by SuSE + */ + +/* + * TurboGraFX parallel port interface driver for Linux. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/parport.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(tgfx, "2-8i"); +MODULE_PARM(tgfx_2, "2-8i"); +MODULE_PARM(tgfx_3, "2-8i"); + +#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ + +#define TGFX_TRIGGER 0x08 +#define TGFX_UP 0x10 +#define TGFX_DOWN 0x20 +#define TGFX_LEFT 0x40 +#define TGFX_RIGHT 0x80 + +#define TGFX_THUMB 0x02 +#define TGFX_THUMB2 0x04 +#define TGFX_TOP 0x01 +#define TGFX_TOP2 0x08 + +static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; + +static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 }; +static char *tgfx_name = "TurboGraFX Multisystem joystick"; + +struct tgfx { + struct pardevice *pd; + struct timer_list timer; + struct input_dev dev[7]; + int sticks; + int used; +} *tgfx_base[3]; + +/* + * tgfx_timer() reads and analyzes TurboGraFX joystick data. + */ + +static void tgfx_timer(unsigned long private) +{ + struct tgfx *tgfx = (void *) private; + struct input_dev *dev; + int data1, data2, i; + + for (i = 0; i < 7; i++) + if (tgfx->sticks & (1 << i)) { + + dev = tgfx->dev + i; + + parport_write_data(tgfx->pd->port, ~(1 << i)); + data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; + data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */ + + input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT)); + input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP )); + + input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER)); + input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB )); + input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 )); + input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP )); + input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 )); + } + + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); +} + +static int tgfx_open(struct input_dev *dev) +{ + struct tgfx *tgfx = dev->private; + if (!tgfx->used++) { + parport_claim(tgfx->pd); + parport_write_control(tgfx->pd->port, 0x04); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + } + return 0; +} + +static void tgfx_close(struct input_dev *dev) +{ + struct tgfx *tgfx = dev->private; + if (!--tgfx->used) { + del_timer(&tgfx->timer); + parport_write_control(tgfx->pd->port, 0x00); + parport_release(tgfx->pd); + } +} + +/* + * tgfx_probe() probes for tg gamepads. + */ + +static struct tgfx __init *tgfx_probe(int *config) +{ + struct tgfx *tgfx; + struct parport *pp; + int i, j; + + if (config[0] < 0) + return NULL; + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "turbografx.c: no such parport\n"); + return NULL; + } + + if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) + return NULL; + memset(tgfx, 0, sizeof(struct tgfx)); + + tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!tgfx->pd) { + printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n"); + kfree(tgfx); + return NULL; + } + + init_timer(&tgfx->timer); + tgfx->timer.data = (long) tgfx; + tgfx->timer.function = tgfx_timer; + + tgfx->sticks = 0; + + for (i = 0; i < 7; i++) + if (config[i+1] > 0 && config[i+1] < 6) { + + tgfx->sticks |= (1 << i); + + tgfx->dev[i].private = tgfx; + tgfx->dev[i].open = tgfx_open; + tgfx->dev[i].close = tgfx_close; + + tgfx->dev[i].name = tgfx_name; + tgfx->dev[i].idbus = BUS_PARPORT; + tgfx->dev[i].idvendor = 0x0003; + tgfx->dev[i].idproduct = config[i+1]; + tgfx->dev[i].idversion = 0x0100; + + tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; j < config[i+1]; j++) + set_bit(tgfx_buttons[j], tgfx->dev[i].keybit); + + tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1; + tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1; + + input_register_device(tgfx->dev + i); + printk(KERN_INFO "input%d: %d-button Multisystem joystick on %s\n", + tgfx->dev[i].number, config[i+1], tgfx->pd->port->name); + } + + if (!tgfx->sticks) { + parport_unregister_device(tgfx->pd); + kfree(tgfx); + return NULL; + } + + return tgfx; +} + +#ifndef MODULE +int __init tgfx_setup(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx[i] = ints[i + 1]; + return 1; +} +int __init tgfx_setup_2(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx_2[i] = ints[i + 1]; + return 1; +} +int __init tgfx_setup_3(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx_3[i] = ints[i + 1]; + return 1; +} +__setup("tgfx=", tgfx_setup); +__setup("tgfx_2=", tgfx_setup_2); +__setup("tgfx_3=", tgfx_setup_3); +#endif + +int __init tgfx_init(void) +{ + tgfx_base[0] = tgfx_probe(tgfx); + tgfx_base[1] = tgfx_probe(tgfx_2); + tgfx_base[2] = tgfx_probe(tgfx_3); + + if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2]) + return 0; + + return -ENODEV; +} + +void __exit tgfx_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) + if (tgfx_base[i]) { + for (j = 0; j < 7; j++) + if (tgfx_base[i]->sticks & (1 << j)) + input_unregister_device(tgfx_base[i]->dev + j); + parport_unregister_device(tgfx_base[i]->pd); + } +} + +module_init(tgfx_init); +module_exit(tgfx_exit); diff --git a/drivers/char/joystick/warrior.c b/drivers/char/joystick/warrior.c new file mode 100644 index 000000000..7000b8560 --- /dev/null +++ b/drivers/char/joystick/warrior.c @@ -0,0 +1,212 @@ +/* + * $Id: warrior.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Logitech WingMan Warrior joystick driver for Linux + */ + +/* + * This program is free warftware; 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define WARRIOR_MAX_LENGTH 16 +static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; +static char *warrior_name = "Logitech WingMan Warrior"; + +/* + * Per-Warrior data. + */ + +struct warrior { + struct input_dev dev; + int idx, len; + unsigned char data[WARRIOR_MAX_LENGTH]; +}; + +/* + * warrior_process_packet() decodes packets the driver receives from the + * Warrior. It updates the data accordingly. + */ + +static void warrior_process_packet(struct warrior *warrior) +{ + struct input_dev *dev = &warrior->dev; + unsigned char *data = warrior->data; + + if (!warrior->idx) return; + + switch ((data[0] >> 4) & 7) { + case 1: /* Button data */ + input_report_key(dev, BTN_TRIGGER, data[3] & 1); + input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1); + input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1); + input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1); + return; + case 3: /* XY-axis info->data */ + input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5))); + input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + return; + case 5: /* Throttle, spinner, hat info->data */ + input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0)); + input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0)); + input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5)); + return; + } +} + +/* + * warrior_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void warrior_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct warrior* warrior = serio->private; + + if (data & 0x80) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = warrior_lengths[(data >> 4) & 7]; + } + + if (warrior->idx < warrior->len) + warrior->data[warrior->idx++] = data; + + if (warrior->idx == warrior->len) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = 0; + } +} + +/* + * warrior_disconnect() is the opposite of warrior_connect() + */ + +static void warrior_disconnect(struct serio *serio) +{ + struct warrior* warrior = serio->private; + input_unregister_device(&warrior->dev); + serio_close(serio); + kfree(warrior); +} + +/* + * warrior_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Warrior, and if found, registers + * it as an input device. + */ + +static void warrior_connect(struct serio *serio, struct serio_dev *dev) +{ + struct warrior *warrior; + int i; + + if (serio->type != (SERIO_RS232 | SERIO_WARRIOR)) + return; + + if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL))) + return; + + memset(warrior, 0, sizeof(struct warrior)); + + warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2); + warrior->dev.relbit[0] = BIT(REL_DIAL); + warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); + + warrior->dev.name = warrior_name; + warrior->dev.idbus = BUS_RS232; + warrior->dev.idvendor = SERIO_WARRIOR; + warrior->dev.idproduct = 0x0001; + warrior->dev.idversion = 0x0100; + + for (i = 0; i < 2; i++) { + warrior->dev.absmax[ABS_X+i] = -64; + warrior->dev.absmin[ABS_X+i] = 64; + warrior->dev.absflat[ABS_X+i] = 8; + } + + warrior->dev.absmax[ABS_THROTTLE] = -112; + warrior->dev.absmin[ABS_THROTTLE] = 112; + + for (i = 0; i < 2; i++) { + warrior->dev.absmax[ABS_HAT0X+i] = -1; + warrior->dev.absmin[ABS_HAT0X+i] = 1; + } + + warrior->dev.private = warrior; + + serio->private = warrior; + + if (serio_open(serio, dev)) { + kfree(warrior); + return; + } + + input_register_device(&warrior->dev); + + printk(KERN_INFO "input%d: Logitech WingMan Warrior on serio%d\n", warrior->dev.number, serio->number); +} + +/* + * The serio device structure. + */ + +static struct serio_dev warrior_dev = { + interrupt: warrior_interrupt, + connect: warrior_connect, + disconnect: warrior_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init warrior_init(void) +{ + serio_register_device(&warrior_dev); + return 0; +} + +void __exit warrior_exit(void) +{ + serio_unregister_device(&warrior_dev); +} + +module_init(warrior_init); +module_exit(warrior_exit); diff --git a/drivers/char/lp.c b/drivers/char/lp.c index c2df7ea18..bbbcad9a7 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -675,9 +675,9 @@ static int lp_register(int nr, struct parport *port) lp_reset(nr); sprintf (name, "%d", nr); - devfs_register (devfs_handle, name, 0, + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, LP_MAJOR, nr, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &lp_fops, NULL); printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 40e6c7ba6..3b5a2c497 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -18,7 +18,6 @@ #include <linux/mman.h> #include <linux/random.h> #include <linux/init.h> -#include <linux/joystick.h> #include <linux/raw.h> #include <linux/capability.h> @@ -608,9 +607,9 @@ void __init memory_devfs_register (void) int i; for (i=0; i<(sizeof(list)/sizeof(*list)); i++) - devfs_register (NULL, list[i].name, 0, DEVFS_FL_NONE, + devfs_register (NULL, list[i].name, DEVFS_FL_NONE, MEM_MAJOR, list[i].minor, - list[i].mode | S_IFCHR, 0, 0, + list[i].mode | S_IFCHR, list[i].fops, NULL); } @@ -654,13 +653,6 @@ int __init chr_dev_init(void) #ifdef CONFIG_SPARCAUDIO sparcaudio_init(); #endif -#ifdef CONFIG_JOYSTICK - /* - * Some joysticks only appear when the sound card they are - * connected to is configured. Keep the sound/joystick ordering. - */ - js_init(); -#endif #if CONFIG_QIC02_TAPE qic02_tape_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index df1e97494..c26a58eea 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -193,9 +193,9 @@ int misc_register(struct miscdevice * misc) if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL); misc->devfs_handle = - devfs_register (devfs_handle, misc->name, 0, DEVFS_FL_NONE, + devfs_register (devfs_handle, misc->name, DEVFS_FL_NONE, MISC_MAJOR, misc->minor, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, misc->fops, NULL); /* diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 819ed0b1a..af52cf98f 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -665,7 +665,7 @@ static int __init ppdev_init (void) devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL); devfs_register_series (devfs_handle, "%u", PARPORT_MAX, DEVFS_FL_DEFAULT, PP_MAJOR, 0, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &pp_fops, NULL); printk (KERN_INFO PP_VERSION "\n"); diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index fdaeeaea0..ba1b9caa9 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3194,7 +3194,7 @@ int __init stl_init(void) devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &stl_fsiomem, NULL); /* diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 133f19aac..09b502a72 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -2905,37 +2905,37 @@ int __init qic02_tape_init(void) #endif return -ENODEV; } - devfs_register (NULL, "ntpqic11", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic11", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 2, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic11", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic11", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 3, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic24", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic24", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 4, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic24", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic24", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 5, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic120", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic120", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 6, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic120", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic120", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 7, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic150", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic150", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 8, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic150", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic150", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 9, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); init_waitqueue_head(&qic02_tape_transfer); /* prepare timer */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 8a03fed26..613b2f967 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1971,8 +1971,6 @@ void tty_register_devfs (struct tty_driver *driver, unsigned int flags, { #ifdef CONFIG_DEVFS_FS umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR; - uid_t uid = 0; - gid_t gid = 0; struct tty_struct tty; char buf[32]; @@ -1996,14 +1994,11 @@ void tty_register_devfs (struct tty_driver *driver, unsigned int flags, } # ifdef CONFIG_UNIX98_PTYS if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) && - (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) { - uid = current->uid; - gid = current->gid; - } + (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) + flags |= DEVFS_FL_CURRENT_OWNER; # endif - devfs_register (NULL, tty_name (&tty, buf), 0,flags | DEVFS_FL_DEFAULT, - driver->major, minor, mode, uid, gid, - &tty_fops, NULL); + devfs_register (NULL, tty_name (&tty, buf), flags | DEVFS_FL_DEFAULT, + driver->major, minor, mode, &tty_fops, NULL); #endif /* CONFIG_DEVFS_FS */ } diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 5138c76b8..67ff8d856 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -476,12 +476,12 @@ void vcs_make_devfs (unsigned int index, int unregister) } else { - devfs_register (devfs_handle, name + 1, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, name + 1, DEVFS_FL_DEFAULT, VCS_MAJOR, index + 1, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); - devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, VCS_MAJOR, index + 129, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); } #endif /* CONFIG_DEVFS_FS */ } @@ -496,12 +496,12 @@ int __init vcs_init(void) printk("unable to get major %d for vcs device", VCS_MAJOR); devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL); - devfs_register (devfs_handle, "0", 1, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, "0", DEVFS_FL_DEFAULT, VCS_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); - devfs_register (devfs_handle, "a", 1, DEVFS_FL_DEFAULT, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, "a", DEVFS_FL_DEFAULT, VCS_MAJOR, 128, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); return error; } diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index ababd833f..4a3bfb859 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -62,7 +62,7 @@ LIST_HEAD(videodev_proc_list); #ifdef CONFIG_VIDEO_BT848 -extern int tuner_init_module(struct video_init *); +extern int i2c_tuner_init(struct video_init *); #endif #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); @@ -79,7 +79,7 @@ extern int init_zoran_cards(struct video_init *); static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 - {"i2c-tuner", tuner_init_module}, + {"i2c-tuner", i2c_tuner_init}, #endif #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, @@ -286,6 +286,14 @@ static int videodev_proc_read(char *page, char **start, off_t off, PRINT_VID_TYPE(VID_TYPE_MJPEG_ENCODER); out += sprintf (out, "\n"); out += sprintf (out, "hardware : 0x%x\n", vfd->hardware); +#if 0 + out += sprintf (out, "channels : %d\n", d->vcap.channels); + out += sprintf (out, "audios : %d\n", d->vcap.audios); + out += sprintf (out, "maxwidth : %d\n", d->vcap.maxwidth); + out += sprintf (out, "maxheight : %d\n", d->vcap.maxheight); + out += sprintf (out, "minwidth : %d\n", d->vcap.minwidth); + out += sprintf (out, "minheight : %d\n", d->vcap.minheight); +#endif skip: len = out - page; @@ -351,6 +359,8 @@ static void videodev_proc_create_dev (struct video_device *vfd, char *name) d->vdev = vfd; strcpy (d->name, name); + /* How can I get capability information ? */ + list_add (&d->proc_list, &videodev_proc_list); } @@ -459,9 +469,9 @@ int video_register_device(struct video_device *vfd, int type) * has serious privacy issues. */ vfd->devfs_handle = - devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT, + devfs_register (NULL, name, DEVFS_FL_DEFAULT, VIDEO_MAJOR, vfd->minor, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &video_fops, NULL); #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) @@ -564,4 +574,3 @@ EXPORT_SYMBOL(video_unregister_device); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers"); - diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index ab4d3afbb..53f6b5a9e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -2199,9 +2199,9 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots) if (!CDROM_CONFIG_FLAGS (drive)->close_tray) devinfo->mask |= CDC_CLOSE_TRAY; - devinfo->de = devfs_register(drive->de, "cd", 2, DEVFS_FL_DEFAULT, + devinfo->de = devfs_register(drive->de, "cd", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, ide_fops, NULL); return register_cdrom(devinfo); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 09e1b12fb..911234014 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -5987,14 +5987,14 @@ int idetape_init (void) idetape_setup (drive, tape, minor); idetape_chrdevs[minor].drive = drive; tape->de_r = - devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT, + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &idetape_fops, NULL); tape->de_n = - devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT, + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor + 128, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &idetape_fops, NULL); devfs_register_tape (tape->de_r); supported++; failed--; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 932c6f114..b9aa05d4d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -805,7 +805,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err) spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); HWGROUP(drive)->rq = NULL; - rq->rq_status = RQ_INACTIVE; + blkdev_release_request(rq); spin_unlock_irqrestore(&io_request_lock, flags); if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 972019295..8169df482 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -2053,11 +2053,11 @@ int capi_init(void) devfs_register_series (NULL, "capi/r%u", CAPINC_NR_PORTS, DEVFS_FL_DEFAULT, capi_rawmajor, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &capinc_raw_fops, NULL); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - devfs_register (NULL, "isdn/capi20", 0, DEVFS_FL_DEFAULT, - capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + devfs_register (NULL, "isdn/capi20", DEVFS_FL_DEFAULT, + capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &capi_fops, NULL); printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index aca716184..fa5a3d844 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -2581,14 +2581,14 @@ static void isdn_register_devfs(int k) sprintf (buf, "isdn%d", k); dev->devfs_handle_isdnX[k] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, - ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, 0, 0, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, &isdn_fops, NULL); sprintf (buf, "isdnctrl%d", k); dev->devfs_handle_isdnctrlX[k] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR, - 0, 0, &isdn_fops, NULL); + &isdn_fops, NULL); } static void isdn_unregister_devfs(int k) @@ -2610,19 +2610,19 @@ static void isdn_init_devfs(void) sprintf (buf, "ippp%d", i); dev->devfs_handle_ipppX[i] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_PPP + i, - 0600 | S_IFCHR, 0, 0, &isdn_fops, NULL); + 0600 | S_IFCHR, &isdn_fops, NULL); } # endif dev->devfs_handle_isdninfo = - devfs_register (devfs_handle, "isdninfo", 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, "isdninfo", DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR, - 0, 0, &isdn_fops, NULL); + &isdn_fops, NULL); dev->devfs_handle_isdnctrl = - devfs_register (devfs_handle, "isdnctrl", 0, DEVFS_FL_DEFAULT, - ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 0, 0, + devfs_register (devfs_handle, "isdnctrl", DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, &isdn_fops, NULL); } diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 2fc74f806..ff6f18ee1 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -674,8 +674,8 @@ void adbdev_init() if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops)) printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); else - devfs_register (NULL, "adb", 0, DEVFS_FL_NONE, + devfs_register (NULL, "adb", DEVFS_FL_DEFAULT, ADB_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &adb_fops, NULL); } diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 7592e0bc8..5084ea70e 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -197,7 +197,7 @@ endmenu bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then - dep_tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX $CONFIG_FDDI + tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX tristate ' SysKonnect FDDI PCI support' CONFIG_SKFP fi diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index d0f4257da..b425513b0 100644 --- a/drivers/net/dmfe.c +++ b/drivers/net/dmfe.c @@ -1,8 +1,9 @@ /* - dmfe.c: Version 1.28 01/18/2000 + dmfe.c: Version 1.30 06/11/2000 A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. Copyright (C) 1997 Sten Wang + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -46,20 +47,29 @@ Made it compile in 2.3 (device to net_device) Alan Cox <alan@redhat.com> : + Cleaned up for kernel merge. Removed the back compatibility support Reformatted, fixing spelling etc as I went Removed IRQ 0-15 assumption - + + Jeff Garzik <jgarzik@mandrakesoft.com> : + Updated to use new PCI driver API. + Resource usage cleanups. + Report driver version to user. + TODO - + + Implement pci_driver::suspend() and pci_driver::resume() + power management methods. + Check and fix on 64bit and big endian boxes. - Sort out the PCI latency. - - (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. - - Cleaned up for kernel merge by Alan Cox (alan@redhat.com) + + Test and make sure PCI latency is now correct for all cases. + */ +#define DMFE_VERSION "1.30 (June 11, 2000)" + #include <linux/module.h> #include <linux/kernel.h> @@ -142,7 +152,9 @@ #define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US; -#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define CHK_IO_SIZE(pci_dev, dev_rev) \ + __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) /* Structure/enum declaration ------------------------------- */ @@ -164,7 +176,7 @@ struct rx_desc { struct dmfe_board_info { u32 chip_id; /* Chip vendor/Device ID */ - u32 chip_revesion; /* Chip revesion */ + u32 chip_revision; /* Chip revision */ struct net_device *next_dev; /* next device */ struct pci_dev *net_dev; /* PCI device */ @@ -221,7 +233,6 @@ enum dmfe_CR6_bits { /* Global variable declaration ----------------------------- */ static int dmfe_debug = 0; static unsigned char dmfe_media_mode = 8; -static struct net_device *dmfe_root_dev = NULL; /* First device */ static u32 dmfe_cr6_user_set = 0; /* For module input parameter */ @@ -299,7 +310,6 @@ static unsigned long CrcTable[256] = }; /* function declaration ------------------------------------- */ -static int dmfe_probe(void); static int dmfe_open(struct net_device *); static int dmfe_start_xmit(struct sk_buff *, struct net_device *); static int dmfe_stop(struct net_device *); @@ -333,111 +343,117 @@ static unsigned long cal_CRC(unsigned char *, unsigned int, u8); * Search DM910X board, allocate space and register it */ -static int __init dmfe_probe(void) + +static int __init dmfe_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { unsigned long pci_iobase; - u16 dm9102_count = 0; u8 pci_irqline; - static int index = 0; /* For multiple call */ struct dmfe_board_info *db; /* Point a board information structure */ int i; - struct pci_dev *net_dev = NULL; struct net_device *dev; + u32 dev_rev; DMFE_DBUG(0, "dmfe_probe()", 0); - if (!pci_present()) - return -ENODEV; + pci_iobase = pci_resource_start(pdev, 0); + pci_irqline = pdev->irq; - index = 0; - while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev))) - { - u32 pci_id; - u32 dev_rev; + /* Interrupt check */ + if (pci_irqline == 0) { + printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", + pci_irqline); + goto err_out; + } - index++; - if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC) - continue; + /* iobase check */ + if (pci_iobase == 0) { + printk(KERN_ERR "dmfe: I/O base is zero\n"); + goto err_out; + } - if ((net_dev->device != PCI_DM9102_ID) && (net_dev->device != PCI_DM9132_ID)) - continue; + /* Enable Master/IO access, Disable memory access */ + if (pci_enable_device(pdev)) + goto err_out; + pci_set_master(pdev); - pci_iobase = pci_resource_start (net_dev, 0); - pci_irqline = net_dev->irq; - - /* Enable Master/IO access, Disable memory access */ - - if (pci_enable_device(net_dev)) - continue; - pci_set_master(net_dev); - - /* Set Latency Timer 80h */ - - /* FIXME: setting values > 32 breaks some SiS 559x stuff. - Need a PCI quirk.. */ - - pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80); +#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ - /* Read Chip revesion */ - pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev); - - /* IO range check */ - if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) { - continue; - } - /* Interrupt check */ - if (pci_irqline == 0) { - printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline); - continue; - } + /* Set Latency Timer 80h */ + /* FIXME: setting values > 32 breaks some SiS 559x stuff. + Need a PCI quirk.. */ - /* Found DM9102 card and PCI resource allocated OK */ - dm9102_count++; /* Found a DM9102 card */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); +#endif - /* Init network device */ - dev = init_etherdev(NULL, sizeof(*db)); - if (dev == NULL) - continue; - - db = dev->priv; + /* Read Chip revision */ + pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); + + /* Init network device */ + dev = init_etherdev(NULL, sizeof(*db)); + if (dev == NULL) + goto err_out; - memset(db, 0, sizeof(*db)); - db->next_dev = dmfe_root_dev; - dmfe_root_dev = dev; + /* IO range check */ + if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) { + printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", + pci_iobase, CHK_IO_SIZE(pdev, dev_rev)); + goto err_out_netdev; + } - db->chip_id = pci_id; /* keep Chip vandor/Device ID */ - db->ioaddr = pci_iobase; - db->chip_revesion = dev_rev; + db = dev->priv; + pdev->driver_data = dev; - db->net_dev = net_dev; + db->chip_id = ent->driver_data; + db->ioaddr = pci_iobase; + db->chip_revision = dev_rev; - dev->base_addr = pci_iobase; - dev->irq = pci_irqline; - dev->open = &dmfe_open; - dev->hard_start_xmit = &dmfe_start_xmit; - dev->stop = &dmfe_stop; - dev->get_stats = &dmfe_get_stats; - dev->set_multicast_list = &dmfe_set_filter_mode; - dev->do_ioctl = &dmfe_do_ioctl; + db->net_dev = pdev; - request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name); + dev->base_addr = pci_iobase; + dev->irq = pci_irqline; + dev->open = &dmfe_open; + dev->hard_start_xmit = &dmfe_start_xmit; + dev->stop = &dmfe_stop; + dev->get_stats = &dmfe_get_stats; + dev->set_multicast_list = &dmfe_set_filter_mode; + dev->do_ioctl = &dmfe_do_ioctl; - /* read 64 word srom data */ - for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i); + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i); - /* Set Node address */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = db->srom[20 + i]; + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + return 0; + +err_out_netdev: + unregister_netdev(dev); + kfree(dev); +err_out: + return -ENODEV; +} - } - if (!dm9102_count) - printk(KERN_WARNING "dmfe: Can't find DM910X board\n"); +static void __exit dmfe_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct dmfe_board_info *db; + + DMFE_DBUG(0, "dmfe_remove_one()", 0); + + db = dev->priv; + + unregister_netdev(dev); + release_region(dev->base_addr, CHK_IO_SIZE(pdev, db->chip_revision)); + kfree(dev); /* free board information */ - return dm9102_count ? 0 : -ENODEV; + DMFE_DBUG(0, "dmfe_remove_one() exit", 0); } + /* * Open the interface. * The interface is opened whenever "ifconfig" actives it. @@ -482,7 +498,7 @@ static int dmfe_open(struct net_device *dev) db->in_reset_state = 0; db->rx_error_cnt = 0; - if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) { + if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revision >= 0x02000030)) { //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */ //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */ db->cr0_data = 0xc00000; /* TX/RX desc burst mode */ @@ -933,8 +949,8 @@ static void dmfe_timer(unsigned long data) else tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ - if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) || - ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) { + if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revision == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && (db->chip_revision == 0x02000010))) { /* DM9102A Chip */ if (tmp_cr12 & 2) tmp_cr12 = 0x0; /* Link failed */ @@ -1532,6 +1548,21 @@ unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag) } +static struct pci_device_id dmfe_pci_tbl[] __initdata = { + { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); + +static struct pci_driver dmfe_driver = { + name: "dmfe", + id_table: dmfe_pci_tbl, + probe: dmfe_init_one, + remove: dmfe_remove_one, +}; + MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); MODULE_PARM(debug, "i"); @@ -1546,6 +1577,8 @@ MODULE_PARM(chkmode, "i"); static int __init dmfe_init_module(void) { + int rc; + DMFE_DBUG(0, "init_module() ", debug); if (debug) @@ -1565,32 +1598,26 @@ static int __init dmfe_init_module(void) break; } - return dmfe_probe(); /* search board and register */ + rc = pci_register_driver(&dmfe_driver); + if (rc < 0) + return rc; + if (rc > 0) { + printk (KERN_INFO "Davicom DM91xx net driver loaded, version " + DMFE_VERSION "\n"); + return 0; + } + return -ENODEV; } /* * Description: * when user used rmmod to delete module, system invoked clean_module() - * to un-register device. + * to un-register all registered services. */ static void __exit dmfe_cleanup_module(void) { - struct net_device *next_dev; - struct dmfe_board_info *db; - - DMFE_DBUG(0, "clean_module()", 0); - - while (dmfe_root_dev) { - db = dmfe_root_dev->priv; - next_dev = db->next_dev; - unregister_netdev(dmfe_root_dev); - release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion)); - kfree(db); /* free board information */ - kfree(dmfe_root_dev); /* free device structure */ - dmfe_root_dev = next_dev; - } - DMFE_DBUG(0, "clean_module() exit", 0); + pci_unregister_driver(&dmfe_driver); } module_init(dmfe_init_module); diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index bec5eb631..ccb0d42de 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -110,12 +110,12 @@ int __init dummy_init(struct net_device *dev) static int dummy_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats; - dev_kfree_skb(skb); stats = (struct net_device_stats *)dev->priv; stats->tx_packets++; stats->tx_bytes+=skb->len; + dev_kfree_skb(skb); return 0; } diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index bb6314456..03944f066 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -941,12 +941,7 @@ static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; irda_task_execute(self, __irport_change_speed, NULL, NULL, (void *) irq->ifr_baudrate); diff --git a/drivers/net/irda/irtty.c b/drivers/net/irda/irtty.c index c174dfa71..1fd3acda9 100644 --- a/drivers/net/irda/irtty.c +++ b/drivers/net/irda/irtty.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Tue Apr 25 21:17:49 2000 + * Modified at: Sat Mar 11 07:43:30 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> @@ -962,12 +962,7 @@ static int irtty_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; irda_task_execute(self, irtty_change_speed, NULL, NULL, (void *) irq->ifr_baudrate); diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 4fe1a764b..df9c63621 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -56,7 +56,6 @@ #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> -#include <asm/hardirq.h> #include <linux/pm.h> @@ -1948,15 +1947,8 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) { - IRDA_DEBUG(0, __FUNCTION__ "(), not capable sysadm\n"); + if (!capable(CAP_NET_ADMIN)) return -EPERM; - } nsc_ircc_change_speed(self, irq->ifr_baudrate); break; case SIOCSMEDIABUSY: /* Set media busy */ diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c index 1f1f2cb14..387208e2f 100644 --- a/drivers/net/irda/toshoboe.c +++ b/drivers/net/irda/toshoboe.c @@ -603,12 +603,7 @@ static int toshoboe_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; /* toshoboe_setbaud(self, irq->ifr_baudrate); */ /* Just change speed once - inserted by Paul Bristow */ @@ -722,7 +717,7 @@ toshoboe_open (struct pci_dev *pci_dev) self->open = 0; self->stopped = 0; self->pdev = pci_dev; - self->base = pci_resource_start (pci_dev, 0); + self->base = pci_dev->resource[0].start; self->io.sir_base = self->base; self->io.irq = pci_dev->irq; @@ -905,6 +900,7 @@ toshoboe_gotosleep (struct toshoboe_cb *self) static void toshoboe_wakeup (struct toshoboe_cb *self) { + struct net_device *dev = self->netdev; unsigned long flags; if (!self->stopped) @@ -956,26 +952,36 @@ int __init toshoboe_init (void) struct pci_dev *pci_dev = NULL; int found = 0; - while ((pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, - PCI_DEVICE_ID_FIR701, pci_dev)) != NULL) { - if (pci_enable_device(pci_dev)) - continue; + do + { + pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, + PCI_DEVICE_ID_FIR701, pci_dev); + if (pci_dev) + { printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n", - pci_resource_start (pci_dev, 0), + pci_dev->resource[0].start, pci_dev->irq); if (!toshoboe_open (pci_dev)) found++; - } + } + + } + while (pci_dev); + if (found) + { return 0; + } return -ENODEV; } +#ifdef MODULE -static void __exit toshoboe_cleanup (void) +static void +toshoboe_cleanup (void) { int i; @@ -991,8 +997,19 @@ static void __exit toshoboe_cleanup (void) } -#ifdef MODULE -module_init(toshoboe_init); -#endif -module_exit(toshoboe_cleanup); +int +init_module (void) +{ + return toshoboe_init (); +} + + +void +cleanup_module (void) +{ + toshoboe_cleanup (); +} + + +#endif diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index c262c7e30..2456e012d 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -255,12 +255,13 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, dev->get_stats = w83977af_net_get_stats; rtnl_lock(); - err = register_netdevice(dev); + err = register_netdev(dev); rtnl_unlock(); if (err) { - ERROR(__FUNCTION__ "(), register_netdevice() failed!\n"); + ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); return -1; } + MESSAGE("IrDA: Registered device %s\n", dev->name); return 0; @@ -301,7 +302,7 @@ static int w83977af_close(struct w83977af_ir *self) /* Release the PORT that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.fir_base); + self->io.fir_base); release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) @@ -1331,12 +1332,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; w83977af_change_speed(self, irq->ifr_baudrate); break; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index b9d5ebfc3..e43e3b394 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -718,9 +718,9 @@ int __init ppp_init(void) err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); - devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register(NULL, "ppp", DEVFS_FL_DEFAULT, PPP_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &ppp_device_fops, NULL); return 0; diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 6885d5fb1..c27d89ca9 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -398,7 +398,7 @@ static int __init cosa_init(void) devfs_handle = devfs_mk_dir (NULL, "cosa", 4, NULL); devfs_register_series (devfs_handle, "%u", nr_cards, DEVFS_FL_DEFAULT, cosa_major, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &cosa_fops, NULL); if (!nr_cards) { printk(KERN_WARNING "cosa: no devices found.\n"); diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c index a945167c5..36d0a5d6a 100644 --- a/drivers/pcmcia/yenta.c +++ b/drivers/pcmcia/yenta.c @@ -127,6 +127,8 @@ static int yenta_get_status(pci_socket_t *socket, unsigned int *value) val = (state & CB_3VCARD) ? SS_3VCARD : 0; val |= (state & CB_XVCARD) ? SS_XVCARD : 0; + val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD + | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; if (state & CB_CBCARD) { val |= SS_CARDBUS; @@ -556,30 +558,6 @@ static void yenta_clear_maps(pci_socket_t *socket) } } -/* - * Many chipsets (all TI chips?) seem to have - * problems sensing the power state of the card - * that was inserted at chip init time, so force - * it if necessary.. - */ -static void yenta_power_sense(pci_socket_t *socket) -{ - u32 status = cb_readl(socket, CB_SOCKET_STATE); - - /* - * Nothing inserted, nothing to sense.. - * ..or sense status already available. - */ - if (status & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) - return; - - /* - * Ho humm. It reports a card, but it doesn't report - * any voltages. Need to redo the VS test.. - */ - cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); -} - /* Called at resume and initialization events */ static int yenta_init(pci_socket_t *socket) { @@ -620,7 +598,8 @@ static int yenta_init(pci_socket_t *socket) exca_writeb(socket, I365_GBLCTL, 0x00); exca_writeb(socket, I365_GENCTL, 0x00); - yenta_power_sense(socket); + /* Redo card voltage interrogation */ + cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); yenta_clear_maps(socket); return 0; diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index 497879619..2115cdac9 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -500,6 +500,7 @@ static void __init isapnp_add_irq_resource(struct pci_dev *dev, int dependent, int size) { unsigned char tmp[3]; + int i; struct isapnp_irq *irq, *ptr; isapnp_peek(tmp, size); @@ -526,6 +527,9 @@ static void __init isapnp_add_irq_resource(struct pci_dev *dev, ptr->next = irq; else (*res)->irq = irq; + for (i=0; i<16; i++) + if (irq->map & i) + pcibios_penalize_isa_irq(i); } /* @@ -1603,6 +1607,14 @@ static int isapnp_check_interrupt(struct isapnp_cfgtmp *cfg, int irq, int idx) return 1; } } +#ifdef CONFIG_PCI + if (!isapnp_skip_pci_scan) { + pci_for_each_dev(dev) { + if (dev->irq == irq) + return 1; + } + } +#endif if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL)) return 1; free_irq(irq, NULL); @@ -2070,45 +2082,6 @@ static void isapnp_free_all_resources(void) #endif } -static int __init isapnp_do_reserve_irq(int irq) -{ - int i; - - if (irq < 0 || irq > 15) - return -EINVAL; - for (i = 0; i < 16; i++) { - if (isapnp_reserve_irq[i] == irq) - return 0; - } - for (i = 0; i < 16; i++) { - if (isapnp_reserve_irq[i] < 0) { - isapnp_reserve_irq[i] = irq; -#ifdef ISAPNP_DEBUG - printk("isapnp: IRQ %i is reserved now.\n", irq); -#endif - return 0; - } - } - return -ENOMEM; -} - -#ifdef CONFIG_PCI - -static void __init isapnp_pci_init(void) -{ - struct pci_dev *dev; - - pci_for_each_dev(dev) { -#ifdef ISAPNP_DEBUG - printk("isapnp: PCI: reserved IRQ: %i\n", dev->irq); -#endif - if (dev->irq > 0) - isapnp_do_reserve_irq(dev->irq); - } -} - -#endif /* CONFIG_PCI */ - EXPORT_SYMBOL(isapnp_cards); EXPORT_SYMBOL(isapnp_devices); EXPORT_SYMBOL(isapnp_present); @@ -2200,10 +2173,6 @@ int __init isapnp_init(void) } else { printk("isapnp: No Plug & Play card found\n"); } -#ifdef CONFIG_PCI - if (!isapnp_skip_pci_scan) - isapnp_pci_init(); -#endif #ifdef CONFIG_PROC_FS isapnp_proc_init(); #endif diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 79e8f27dc..55e27c7d8 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -792,11 +792,10 @@ register_dasd_last (int di) dasd_info[di]->info.devno,'\0' ); dasd_info[di] -> devfs_entry = devfs_register ( NULL /* dir */, - name, strlen(name), - 0 /* flags */, + name, + DEVFS_FL_DEFAULT /* flags */, DASD_MAJOR, minor, 0755 /* mode */, - 0 /* uid */ , 0 /* gid */, &dasd_device_operations, (void *)dasd_info[di]); } diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index 31856af31..586945b10 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -1,4 +1,4 @@ -/* $Id: audio.c,v 1.51 2000/06/19 06:24:47 davem Exp $ +/* $Id: audio.c,v 1.52 2000/06/22 11:42:27 davem Exp $ * drivers/sbus/audio/audio.c * * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) @@ -2010,9 +2010,9 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex) for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) { sparcaudio_mkname (name_buf, dev_list[i].name, dev); minor = (dev << SPARCAUDIO_DEVICE_SHIFT) | dev_list[i].minor; - devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE, + devfs_register (devfs_handle, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, minor, S_IFCHR | dev_list[i].mode, - 0, 0, &sparcaudio_fops, NULL); + &sparcaudio_fops, NULL); } /* Setup the circular queues of output and input buffers diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index ba6810017..6dfc4c132 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -1031,7 +1031,7 @@ int __init bpp_init(void) } devfs_handle = devfs_mk_dir (NULL, "bpp", 3, NULL); devfs_register_series (devfs_handle, "%u", BPP_NO, DEVFS_FL_DEFAULT, - BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &bpp_fops, NULL); return 0; diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index d31ef5193..458fe20af 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -271,7 +271,7 @@ static loff_t jsf_lseek(struct file * file, loff_t offset, int orig) } /* - * P3: OS SIMM Cannot be read in other size but a 32bits word. + * OS SIMM Cannot be read in other size but a 32bits word. */ static ssize_t jsf_read(struct file * file, char * buf, size_t togo, loff_t *ppos) @@ -647,8 +647,7 @@ int jsfd_init(void) { int i; if (jsf0.base == 0) { - printk("jsfd_init: no flash\n"); /* P3 */ - return -EIO; + return -ENXIO; } if (register_blkdev(JSFD_MAJOR, "jsfd", &jsfd_fops)) { @@ -657,8 +656,6 @@ int jsfd_init(void) { return -EIO; } - printk("jsfd0: at major %d\n", MAJOR_NR); /* P3 */ - blksize_size[JSFD_MAJOR] = jsfd_blksizes; blk_size[JSFD_MAJOR] = jsfd_sizes; diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 5f7ea6923..8004569ab 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -1605,9 +1605,9 @@ void __init keyboard_zsinit(void (*put_char)(unsigned char)) spin_unlock_irq(&sunkbd_lock); /* Register the /dev/kbd interface */ - devfs_register (NULL, "kbd", 0, DEVFS_FL_NONE, + devfs_register (NULL, "kbd", DEVFS_FL_DEFAULT, KBD_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &kbd_fops, NULL); if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ printk ("Could not register /dev/kbd device\n"); diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index dee47f98b..589b787d0 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -166,9 +166,9 @@ int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) return -EIO; sprintf (devname, "%d", instance); - dev->de = devfs_register (devfs_handle, devname, 0, DEVFS_FL_DEFAULT, + dev->de = devfs_register (devfs_handle, devname, DEVFS_FL_DEFAULT, VFC_MAJOR, instance, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &vfc_fops, NULL); return 0; } diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 67648bc75..d9f952ca6 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -243,6 +243,8 @@ #include <linux/blk.h> #include <linux/tqueue.h> #include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/smp.h> #include "sd.h" #include "scsi.h" #include "hosts.h" @@ -264,7 +266,7 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -#define AIC7XXX_C_VERSION "5.2.0" +#define AIC7XXX_C_VERSION "5.2.1" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -281,45 +283,12 @@ # define FALSE 0 #endif -/* - * We need the bios32.h file if we are kernel version 2.1.92 or less. The - * full set of pci_* changes wasn't in place until 2.1.93 - */ - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92) -# if defined(__sparc_v9__) || defined(__powerpc__) -# error "PPC and Sparc platforms are only support under 2.1.92 and above" -# endif -# include <linux/bios32.h> -#endif - -#if defined(__powerpc__) +#if defined(__powerpc__) || defined(__i386) # define MMAPIO -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("eieio" ::: "memory") -#elif defined(__i386__) -# define MMAPIO -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("lock ; addl $0,0(%%esp)": : :"memory") -#elif defined(__alpha__) -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("mb": : :"memory") #endif -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) -# include <linux/spinlock.h> -# include <linux/smp.h> -# define cpuid smp_processor_id() # if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) +# define cpuid smp_processor_id() # define DRIVER_LOCK_INIT \ spin_lock_init(&p->spin_lock); # define DRIVER_LOCK \ @@ -337,17 +306,6 @@ # define DRIVER_LOCK # define DRIVER_UNLOCK # endif -#else -# define cpuid 0 -# define DRIVER_LOCK_INIT -# define DRIVER_LOCK \ - save_flags(cpu_flags); \ - cli(); -# define DRIVER_UNLOCK \ - restore_flags(cpu_flags); -# define le32_to_cpu(x) (x) -# define cpu_to_le32(x) (x) -#endif /* * You can try raising me if tagged queueing is enabled, or lowering @@ -359,13 +317,6 @@ #define AIC7XXX_CMDS_PER_DEVICE 8 #endif -/* Set this to the delay in seconds after SCSI bus reset. */ -#ifdef CONFIG_AIC7XXX_RESET_DELAY -#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY -#else -#define AIC7XXX_RESET_DELAY 5 -#endif - /* * Control collection of SCSI transfer statistics for the /proc filesystem. * @@ -377,28 +328,6 @@ #endif /* - * NOTE: Uncommenting the define below no longer has any effect, the - * tagged queue value array is always active now. I've added - * a setup option to set this particular array and I'm hoping - * insmod will be smart enough to set it properly as well. It's - * by use of this array that a person can enable tagged queueing. - * The DEFAULT_TAG_COMMANDS define has been changed to disable - * tagged queueing by default, so if your devices can handle tagged - * queueing you will need to add a line to their lilo.conf file like: - * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}" - * which will result in the first four devices on the first two - * controllers being set to a tagged queue depth of 32. - * - * Set this for defining the number of tagged commands on a device - * by device, and controller by controller basis. The first set - * of tagged commands will be used for the first detected aic7xxx - * controller, the second set will be used for the second detected - * aic7xxx controller, and so on. These values will *only* be used - * for targets that are tagged queueing capable; these values will - * be ignored in all other cases. The tag_commands is an array of - * 16 to allow for wide and twin adapters. Twin adapters will use - * indexes 0-7 for channel 0, and indexes 8-15 for channel 1. - * * *** Determining commands per LUN *** * * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its @@ -408,7 +337,6 @@ * use an internal queue depth of 3, with no more than one of those * three commands active at one time. */ -/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */ typedef struct { @@ -453,8 +381,8 @@ typedef struct /* * NOTE: The below structure is for reference only, the actual structure - * to modify in order to change things is located around line - * number 1305 + * to modify in order to change things is found after this fake one. + * adapter_tag_info_t aic7xxx_tag_info[] = { {DEFAULT_TAG_COMMANDS}, @@ -509,6 +437,7 @@ static const char *board_names[] = { "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */ + "Adaptec AHA-2940UW Pro Ultra SCSI host adapter", /* AIC_7887 */ "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AHA-293X Ultra2 SCSI host adapter", /* AIC_7890 */ @@ -565,7 +494,7 @@ static const char *board_names[] = { * AIC-7770 I/O range to reserve for a card */ #define MINREG 0xC00 -#define MAXREG 0xCBF +#define MAXREG 0xCFF #define INTDEF 0x5C /* Interrupt Definition Register */ @@ -828,7 +757,8 @@ typedef enum { * and what flags weren't. This way, I could clean up the flag usage on * a use by use basis. Doug Ledford */ - AHC_NO_STPWR = 0x00040000, + AHC_MOTHERBOARD = 0x00020000, + AHC_NO_STPWEN = 0x00040000, AHC_RESET_DELAY = 0x00080000, AHC_A_SCANNED = 0x00100000, AHC_B_SCANNED = 0x00200000, @@ -1056,11 +986,8 @@ struct aic7xxx_host { struct timer_list dev_timer; unsigned long dev_expires[MAX_TARGETS]; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) spinlock_t spin_lock; volatile unsigned char cpu_lock_count[NR_CPUS]; -#endif - Scsi_Cmnd *dev_dtr_cmnd[MAX_TARGETS]; @@ -1384,9 +1311,7 @@ static int aic7xxx_seltime = 0x10; */ #ifdef MODULE static char * aic7xxx = NULL; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18) MODULE_PARM(aic7xxx, "s"); -#endif /* * Just in case someone uses commas to separate items on the insmod @@ -1444,32 +1369,6 @@ static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer); * ***************************************************************************/ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) -static inline void -mdelay(int milliseconds) -{ - int i; - - for(i=0; i<milliseconds; i++) - udelay(1000); -} - -static inline int -time_after_eq(unsigned long a, unsigned long b) -{ - return((long)((a) - (b)) >= 0L); -} - -static inline int -timer_pending(struct timer_list *timer) -{ - return( timer->prev != NULL ); -} - -#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 - -#endif - static inline unsigned char aic_inb(struct aic7xxx_host *p, long port) { @@ -1483,7 +1382,6 @@ aic_inb(struct aic7xxx_host *p, long port) { x = inb(p->base + port); } - mb(); return(x); #else return(inb(p->base + port)); @@ -1497,14 +1395,17 @@ aic_outb(struct aic7xxx_host *p, unsigned char val, long port) if(p->maddr) { writeb(val, p->maddr + port); + mb(); /* locked operation in order to force CPU ordering */ + readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */ } else { outb(val, p->base + port); + mb(); /* locked operation in order to force CPU ordering */ } - mb(); #else outb(val, p->base + port); + mb(); /* locked operation in order to force CPU ordering */ #endif } @@ -1642,7 +1543,7 @@ aic7xxx_setup(char *s) } else if (!strncmp(p, "verbose", n)) { - *(options[i].flag) = 0xff09; + *(options[i].flag) = 0xff29; } else { @@ -1669,7 +1570,7 @@ __setup("aic7xxx=", aic7xxx_setup); * is important since the sequencer can disable pausing for critical * sections. *-F*************************************************************************/ -static inline void +static void pause_sequencer(struct aic7xxx_host *p) { aic_outb(p, p->pause, HCNTRL); @@ -1677,6 +1578,10 @@ pause_sequencer(struct aic7xxx_host *p) { ; } + if(p->features & AHC_ULTRA2) + { + aic_inb(p, CCSCBCTL); + } } /*+F************************************************************************* @@ -1687,7 +1592,7 @@ pause_sequencer(struct aic7xxx_host *p) * Unpause the sequencer. Unremarkable, yet done often enough to * warrant an easy way to do it. *-F*************************************************************************/ -static inline void +static void unpause_sequencer(struct aic7xxx_host *p, int unpause_always) { if (unpause_always || @@ -1706,7 +1611,7 @@ unpause_sequencer(struct aic7xxx_host *p, int unpause_always) * Restart the sequencer program from address zero. This assumes * that the sequencer is already paused. *-F*************************************************************************/ -static inline void +static void restart_sequencer(struct aic7xxx_host *p) { aic_outb(p, 0, SEQADDR0); @@ -1787,7 +1692,6 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, struct ins_format1 *fmt1_ins; struct ins_format3 *fmt3_ins; unsigned char opcode; - volatile unsigned char hcntrl; instr = *(union ins_formats*) &seqprog[instrptr * 4]; @@ -1889,14 +1793,10 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, } } aic_outb(p, (instr.integer & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); - udelay(50); + udelay(10); break; default: @@ -2151,7 +2051,7 @@ aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period, * Use async transfers for this target */ *options = 0; - *period = 0; + *period = 255; syncrate = NULL; } return (syncrate); @@ -2950,6 +2850,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) #define WIDE_INQUIRY_BITS 0x60 #define SYNC_INQUIRY_BITS 0x10 #define SCSI_VERSION_BITS 0x07 +#define SCSI_DT_BIT 0x04 if ( (buffer[7] & WIDE_INQUIRY_BITS) && (p->features & AHC_WIDE) ) { @@ -2968,48 +2869,66 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) AHC_TRANS_CUR) ); unpause_sequencer(p, FALSE); } - if (buffer[7] & SYNC_INQUIRY_BITS) + if ( (buffer[7] & SYNC_INQUIRY_BITS) && + p->transinfo[tindex].user_offset ) { - p->needsdtr |= (1<<tindex); - p->needsdtr_copy |= (1<<tindex); - p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; - if (p->transinfo[tindex].user_offset) + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + if ( (((buffer[2] & SCSI_VERSION_BITS) == 3) || + (buffer[56] & SCSI_DT_BIT) || + (p->dev_flags[tindex] & DEVICE_SCSI_3) ) && + (p->transinfo[tindex].user_period <= 9) && + (p->transinfo[tindex].user_options) ) { - if (p->features & AHC_ULTRA2) - p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; - else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) - p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; - else - p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + p->needppr |= (1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->dev_flags[tindex] |= DEVICE_SCSI_3; + } + else + { + p->needsdtr |= (1<<tindex); + p->needsdtr_copy |= (1<<tindex); + p->transinfo[tindex].goal_period = + MAX(10, p->transinfo[tindex].goal_period); + p->transinfo[tindex].goal_options = 0; } } else { p->needsdtr &= ~(1<<tindex); p->needsdtr_copy &= ~(1<<tindex); - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_offset = 0; p->transinfo[tindex].goal_options = 0; } - if ( (buffer[2] & SCSI_VERSION_BITS) == 3 ) + /* + * This is needed to work around a sequencer bug for now. Regardless + * of the controller in use, if we have a Quantum drive, we need to + * limit the speed to 80MByte/sec. As soon as I get a fixed version + * of the sequencer, this code will get yanked. + */ + if(!strncmp(buffer + 8, "QUANTUM", 7) && + p->transinfo[tindex].goal_options ) { - p->dev_flags[tindex] |= DEVICE_SCSI_3; - /* - * OK, we are a SCSI 3 device and we are in need of negotiation. - * Use PPR messages instead of WDTR and SDTR messages. - */ - if ( (p->needsdtr & (1<<tindex)) || - (p->needwdtr & (1<<tindex)) ) - { - p->needppr |= (1<<tindex); - p->needppr_copy |= (1<<tindex); - } - p->needwdtr &= ~(1<<tindex); - p->needwdtr_copy &= ~(1<<tindex); - p->needsdtr &= ~(1<<tindex); - p->needsdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_period = + MAX(p->transinfo[tindex].goal_period, 10); + p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needsdtr_copy |= (1<<tindex); + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); } /* * Get the INQUIRY checksum. We use this on Ultra 160/m @@ -3039,6 +2958,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) #undef WIDE_INQUIRY_BITS #undef SYNC_INQUIRY_BITS #undef SCSI_VERSION_BITS +#undef SCSI_DT_BIT } } else if ((scb->flags & SCB_MSGOUT_BITS) != 0) @@ -4170,12 +4090,7 @@ aic7xxx_pci_intr(struct aic7xxx_host *p) { unsigned char status1; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS + 1, &status1); -#endif if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" @@ -4196,12 +4111,7 @@ aic7xxx_pci_intr(struct aic7xxx_host *p) printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " "PERR#\n", p->host_no, -1, -1, -1); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS + 1, status1); -#endif if (status1 & (DPR|RMA|RTA)) aic_outb(p, CLRPARERR, CLRINT); @@ -4226,11 +4136,7 @@ aic7xxx_timer(struct aic7xxx_host *p) unsigned long cpu_flags = 0; struct aic7xxx_scb *scb; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - DRIVER_LOCK -#else spin_lock_irqsave(&io_request_lock, cpu_flags); -#endif p->dev_timer_active &= ~(0x01 << MAX_TARGETS); if ( (p->dev_timer_active & (0x01 << p->scsi_id)) && time_after_eq(jiffies, p->dev_expires[p->scsi_id]) ) @@ -4287,11 +4193,7 @@ aic7xxx_timer(struct aic7xxx_host *p) } aic7xxx_run_waiting_queues(p); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - DRIVER_UNLOCK -#else spin_unlock_irqrestore(&io_request_lock, cpu_flags); -#endif } /*+F************************************************************************* @@ -4673,7 +4575,9 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) * As per the draft specs, any device capable of supporting any of * the option values other than 0 are not allowed to reject the * PPR message. Instead, they must negotiate out what they do - * support instead of rejecting our offering. + * support instead of rejecting our offering or else they cause + * a parity error during msg_out phase to signal that they don't + * like our settings. */ p->needppr &= ~target_mask; p->needppr_copy &= ~target_mask; @@ -4813,39 +4717,36 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) case CHECK_CONDITION: if ( !(scb->flags & SCB_SENSE) ) { - unsigned char *sense_buffer; - /* - * XXX - How do we save the residual (if there is one). - */ - if ( hscb->residual_SG_segment_count != 0 ) - aic7xxx_calculate_residual(p, scb); - /* - * Send a sense command to the requesting target. + * Send a sense command to the requesting target. * XXX - revisit this and get rid of the memcopys. - */ + */ memcpy(scb->sense_cmd, &generic_sense[0], sizeof(generic_sense)); scb->sense_cmd[1] = (cmd->lun << 5); scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - sense_buffer = cmd->sense_buffer; scb->sg_list[0].length = cpu_to_le32(sizeof(cmd->sense_buffer)); + scb->sg_list[0].address = + cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer, + sizeof(cmd->sense_buffer), + PCI_DMA_FROMDEVICE)); /* * XXX - We should allow disconnection, but can't as it * might allow overlapped tagged commands. */ - /* hscb->control &= DISCENB; */ + /* hscb->control &= DISCENB; */ hscb->control = 0; hscb->target_status = 0; hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list)); - hscb->data_count = scb->sg_list[0].length; hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd)); + hscb->data_count = scb->sg_list[0].length; + hscb->data_pointer = scb->sg_list[0].address; hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); hscb->residual_SG_segment_count = 0; hscb->residual_data_count[0] = 0; @@ -4855,53 +4756,6 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) scb->sg_count = hscb->SG_segment_count = 1; scb->sg_length = sizeof(cmd->sense_buffer); scb->tag_action = 0; - /* - * This problem could be caused if the target has lost power - * or found some other way to loose the negotiation settings, - * so if needed, we'll re-negotiate while doing the sense cmd. - * However, if this SCB already was attempting to negotiate, - * then we assume this isn't the problem and skip this part. - */ - if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && - (p->dev_flags[tindex] & DEVICE_SCANNED) && - !(p->dtr_pending & target_mask) ) - { - p->needppr |= (p->needppr_copy & target_mask); - p->needwdtr |= (p->needwdtr_copy & target_mask); - p->needsdtr |= (p->needsdtr_copy & target_mask); - } - else if ( scb->cmd == p->dev_dtr_cmnd[tindex] ) - { - /* - * This is already a negotiation command, so we must have - * already done PPR, WDTR or SDTR. Since our negotiation - * could have gotten rejected, we don't really know the - * full state of things. Don't do anything here, and allow - * the negotiation_complete() handler to do the right - * thing. - */ - - /* - * This is the important part though. We are getting sense - * info back from this device. It's going into a fake - * command. We need to put that into the real command - * instead so that the mid level SCSI code can act upon it. - * So, when we set up these fake commands, the next pointer - * is used to point to the real command. Use that to change - * the address of our sense_buffer[] to the real command. - * However, don't do this if the real command is also a - * TEST_UNIT_READY as it will most likely pull down its own - * SENSE information anyway. - */ - if (cmd->next->cmnd[0] != TEST_UNIT_READY) - sense_buffer = cmd->next->sense_buffer; - } - scb->sg_list[0].address = - cpu_to_le32(pci_map_single(p->pdev, sense_buffer, - sizeof(cmd->sense_buffer), - PCI_DMA_FROMDEVICE)); - hscb->data_pointer = scb->sg_list[0].address; - scb->flags |= SCB_SENSE; /* * Ensure the target is busy since this will be an @@ -4924,7 +4778,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic7xxx_error(cmd) = DID_OK; break; } /* first time sense, no errors */ - aic7xxx_error(cmd) = DID_OK; + aic7xxx_error(cmd) = DID_ERROR; scb->flags &= ~SCB_SENSE; break; @@ -5171,51 +5025,14 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) } else if (scb->flags & SCB_MSGOUT_PPR) { - unsigned int max_sync, period; - unsigned char options = 0; - - if (p->features & AHC_ULTRA2) - { - if ( (aic_inb(p, SBLKCTL) & ENAB40) && - !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) - { - if( (p->features & AHC_ULTRA3) && - (p->dev_flags[tindex] & DEVICE_SCSI_3) && - (p->transinfo[tindex].goal_width == - MSG_EXT_WDTR_BUS_16_BIT) && - (p->transinfo[tindex].goal_options != 0) ) - { - max_sync = AHC_SYNCRATE_ULTRA3; - options = p->transinfo[tindex].goal_options; - } - else - { - max_sync = AHC_SYNCRATE_ULTRA2; - } - } - else - { - max_sync = AHC_SYNCRATE_ULTRA; - } - } - else if (p->features & AHC_ULTRA) - { - max_sync = AHC_SYNCRATE_ULTRA; - } - else - { - max_sync = AHC_SYNCRATE_FAST; - } - period = p->transinfo[tindex].goal_period; - aic7xxx_find_syncrate(p, &period, max_sync, &options); - p->transinfo[tindex].goal_period = period; - p->transinfo[tindex].goal_options = options; if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n", - p->host_no, CTL_OF_SCB(scb), period, + p->host_no, CTL_OF_SCB(scb), + p->transinfo[tindex].goal_period, p->transinfo[tindex].goal_offset, - p->transinfo[tindex].goal_width, options); + p->transinfo[tindex].goal_width, + p->transinfo[tindex].goal_options); } aic7xxx_construct_ppr(p, scb); } @@ -5261,8 +5078,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no, - CTL_OF_SCB(scb), - p->transinfo[tindex].goal_period, + CTL_OF_SCB(scb), period, p->transinfo[tindex].goal_offset); } aic7xxx_construct_sdtr(p, period, @@ -5375,15 +5191,15 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT); resid_dcnt = aic_inb(p, SCB_RESID_DCNT) | (aic_inb(p, SCB_RESID_DCNT + 1) << 8) | - (aic_inb(p, SCB_RESID_DCNT + 2) << 24); - index = scb->sg_count - resid_sgcnt; + (aic_inb(p, SCB_RESID_DCNT + 2) << 16); + index = scb->sg_count - (resid_sgcnt + 1); native_addr = le32_to_cpu(scb->sg_list[index].address); native_length = le32_to_cpu(scb->sg_list[index].length); /* * Make sure this is a valid sg_seg for the given pointer */ if(cur_addr < native_addr || - cur_addr > (native_addr + native_length)) + cur_addr > (native_addr + native_length + 1)) { printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n", p->host_no, CTL_OF_SCB(scb), cur_addr); @@ -5400,16 +5216,31 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) p->host_no, CTL_OF_SCB(scb), le32_to_cpu(scb->sg_list[index + 1].address), le32_to_cpu(scb->sg_list[index + 1].length)); + printk(WARN_LEAD " cur_address:0x%x resid_dcnt:0x%06x\n", + p->host_no, CTL_OF_SCB(scb), + cur_addr, resid_dcnt); break; } - /* - * If our current address matches the sg_seg->address then we - * have to back up the sg array to the previous segment and set - * it up to have only one byte of transfer left to go. - */ - if(cur_addr == native_addr) + if( (resid_sgcnt == 0) && + ((resid_dcnt == 0) || (resid_dcnt == 0xffffff))) + { + /* + * We are at the end of the transfer and this is about a byte + * we ignored already (because the sequencer knew this was + * the last segment and set the adapter to ignore any wide + * residue bytes that might come through, which is only done + * on the last scatter gather segment of transfers). + */ + break; + } + else if(cur_addr == native_addr) { + /* + * If our current address matches the sg_seg->address then we + * have to back up the sg array to the previous segment and set + * it up to have only one byte of transfer left to go. + */ if(index == 0) { printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been " @@ -5439,39 +5270,6 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); - /* - * The sequencer actually wants to find the new address and byte - * count in the SHCNT and SHADDR register sets. These registers - * are a shadow of the regular HCNT and HADDR registers. On the - * Ultra2 controllers, these registers are read only and the way - * we have to set their values is to put the values we want into - * the HCNT and HADDR registers and then output PRELOADEN into - * the DFCNTRL register which causes the card to latch the current - * values in the HADDR and HCNT registers and drop it through to - * the shadow registers. On older cards we copy them directly - * across by hand. - */ - if(p->features & AHC_ULTRA2) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - i=0; - udelay(1); - while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - udelay(1); - } - } - else - { - aic_outb(p, 1, STCNT); - aic_outb(p, 0, STCNT + 1); - aic_outb(p, 0, STCNT + 2); - aic_outb(p, cur_addr & 0xff, SHADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); - } } else { @@ -5491,28 +5289,46 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); - if(p->features & AHC_ULTRA2) + } + /* + * The sequencer actually wants to find the new address and byte + * count in the SHCNT and SHADDR register sets. These registers + * are a shadow of the regular HCNT and HADDR registers. On the + * Ultra2 controllers, these registers are read only and the way + * we have to set their values is to put the values we want into + * the HCNT and HADDR registers and then output PRELOADEN into + * the DFCNTRL register which causes the card to latch the current + * values in the HADDR and HCNT registers and drop it through to + * the shadow registers. On older cards we copy them directly + * across by hand. + */ + if(p->features & AHC_ULTRA2) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - i=0; udelay(1); - while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - udelay(1); - } } - else + aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000)) { - aic_outb(p, resid_dcnt & 0xff, STCNT); - aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); - aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); - aic_outb(p, cur_addr & 0xff, SHADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + udelay(1); } } + else + { + aic_outb(p, resid_dcnt & 0xff, STCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); + aic_outb(p, cur_addr & 0xff, SHADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + } } break; @@ -5921,6 +5737,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) reply = TRUE; scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_PPR; + p->dev_flags[tindex] |= DEVICE_SCSI_3; if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) { /* @@ -5954,7 +5771,6 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) p->transinfo[tindex].user_width; p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; - p->needppr_copy |= target_mask; } if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { @@ -5988,6 +5804,25 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) break; } } + if ( (p->transinfo[tindex].goal_period > 9) || + (p->transinfo[tindex].goal_options == 0) ) + { + scb->flags &= ~SCB_MSGOUT_BITS; + reject = TRUE; + reply = FALSE; + p->needppr &= ~(1 << tindex); + p->needppr_copy &= ~(1 << tindex); + if ( p->transinfo[tindex].goal_offset ) + { + p->needsdtr |= (1 << tindex); + p->needsdtr_copy |= (1 << tindex); + } + if ( p->transinfo[tindex].goal_width ) + { + p->needwdtr |= (1 << tindex); + p->needwdtr_copy |= (1 << tindex); + } + } } else { @@ -5995,7 +5830,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { default: { - reply = TRUE; + reject = TRUE; if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) || (aic7xxx_verbose > 0xffff)) ) @@ -6021,27 +5856,18 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) } } - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); - syncrate = aic7xxx_find_syncrate(p, &period, maxsync, - &new_trans_options); - aic7xxx_validate_offset(p, syncrate, &offset, bus_width); - aic7xxx_set_syncrate(p, syncrate, target, channel, period, - offset, new_trans_options, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); - - if( (offset != saved_offset) || - (trans_options != new_trans_options) || - ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) != - (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) ) + if ( !reject ) { aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + syncrate = aic7xxx_find_syncrate(p, &period, maxsync, + &new_trans_options); + aic7xxx_validate_offset(p, syncrate, &offset, bus_width); aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset, new_trans_options, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); - reply = TRUE; + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); } + p->dtr_pending &= ~target_mask; p->needppr &= ~target_mask; if(reply) @@ -6445,15 +6271,33 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) } } /* - * Restarting the sequencer will stop the selection and make sure devices - * are allowed to reselect in. + * Keep the sequencer from trying to restart any selections + */ + aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ); + /* + * Make sure the data bits on the bus are released + * Don't do this on 7770 chipsets, it makes them give us + * a BRKADDRINT and kills the card. */ - aic_outb(p, 0, SCSISEQ); + if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI ) + aic_outb(p, 0, SCSIBUSL); + + /* + * Delay for the selection timeout delay period then stop the selection + */ + udelay(301); aic_outb(p, CLRSELINGO, CLRSINT0); + /* + * Clear out all the interrupt status bits + */ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); + /* + * Restarting the sequencer will stop the selection and make sure devices + * are allowed to reselect in. + */ restart_sequencer(p); unpause_sequencer(p, TRUE); } @@ -6519,7 +6363,9 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) * A parity error has occurred during a data * transfer phase. Flag it and continue. */ - if( (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && (lastphase == P_DATAIN) ) + if( (p->features & AHC_ULTRA3) && + (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && + (lastphase == P_DATAIN) ) { printk(WARN_LEAD "CRC error during %s phase.\n", p->host_no, CTL_OF_SCB(scb), phase); @@ -6544,13 +6390,50 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) p->host_no, CTL_OF_SCB(scb)); } } - else + else if( (lastphase == P_MESGOUT) && + (cmd == p->dev_dtr_cmnd[tindex]) && + (scb->flags & SCB_MSGOUT_PPR) ) { - printk(WARN_LEAD "Parity error during %s phase.\n", - p->host_no, CTL_OF_SCB(scb), phase); + /* + * As per the draft specs, any device capable of supporting any of + * the option values other than 0 are not allowed to reject the + * PPR message. Instead, they must negotiate out what they do + * support instead of rejecting our offering or else they cause + * a parity error during msg_out phase to signal that they don't + * like our settings. + */ + p->needppr &= ~(1 << tindex); + p->needppr_copy &= ~(1 << tindex); + aic7xxx_set_width(p, scb->cmd->target, scb->cmd->channel, scb->cmd->lun, + MSG_EXT_WDTR_BUS_8_BIT, + (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE)); + aic7xxx_set_syncrate(p, NULL, scb->cmd->target, scb->cmd->channel, 0, 0, + 0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + p->transinfo[tindex].goal_options = 0; + p->dtr_pending &= ~(1 << tindex); + scb->flags &= ~SCB_MSGOUT_BITS; + if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) + { + printk(INFO_LEAD "parity error during PPR message, reverting " + "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb)); + } + if ( p->transinfo[tindex].goal_width ) + { + p->needwdtr |= (1 << tindex); + p->needwdtr_copy |= (1 << tindex); + } + if ( p->transinfo[tindex].goal_offset ) + { + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->transinfo[tindex].goal_period = 10; + } + p->needsdtr |= (1 << tindex); + p->needsdtr_copy |= (1 << tindex); + } + scb = NULL; } - - if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR) + else if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR) { struct aic7xxx_syncrate *syncrate; unsigned int period = p->transinfo[tindex].cur_period; @@ -6562,6 +6445,8 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) * instead of slowing down if those exist. That's hard to do with simple * checksums though. */ + printk(WARN_LEAD "Parity error during %s phase.\n", + p->host_no, CTL_OF_SCB(scb), phase); if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL) { syncrate++; @@ -6569,20 +6454,59 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) { p->transinfo[tindex].goal_period = syncrate->period; - if( !(syncrate->sxfr_ultra2 & 0x40) ) + if( p->transinfo[tindex].goal_period > 9 ) { p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); + if (p->transinfo[tindex].goal_width) + { + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); + } + } + } + else if (p->transinfo[tindex].goal_width) + { + p->transinfo[tindex].goal_width = 0; + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = + p->transinfo[tindex].user_options; + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->needppr |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + } + else + { + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); } } else { p->transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_options = 0; + p->transinfo[tindex].goal_width = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); } - p->needppr |= (p->needppr_copy & (1<<tindex)); - p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); - p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); } p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR; } @@ -6600,6 +6524,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) if (mesg_out != MSG_NOOP) { aic_outb(p, mesg_out, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO); scb = NULL; } aic_outb(p, CLRSCSIPERR, CLRSINT1); @@ -6791,7 +6716,7 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) { struct aic7xxx_scb *scb = NULL; Scsi_Cmnd *cmd; - unsigned char scb_index; + unsigned char scb_index, tindex; #ifdef AIC7XXX_VERBOSE_DEBUGGING if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) @@ -6817,23 +6742,21 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) scb_index = p->qoutfifo[p->qoutfifonext]; p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; if ( scb_index >= p->scb_data->numscbs ) - scb = NULL; - else - scb = p->scb_data->scb_array[scb_index]; - if (scb == NULL) { printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, -1, -1, -1, scb_index); continue; } - else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + scb = p->scb_data->scb_array[scb_index]; + if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, (unsigned long) scb->cmd); continue; } - else if (scb->flags & SCB_QUEUED_ABORT) + tindex = TARGET_INDEX(scb->cmd); + if (scb->flags & SCB_QUEUED_ABORT) { pause_sequencer(p); if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && @@ -6856,6 +6779,43 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) */ scb->flags &= ~(SCB_ABORT|SCB_RESET); } + else if (scb->flags & SCB_SENSE) + { + char *buffer = &scb->cmd->sense_buffer[0]; + if (scb->cmd == p->dev_dtr_cmnd[tindex]) + { + struct aic7xxx_scb *old_scb; + /* + * We have valid sense data, send it back immediately. + */ + old_scb = p->scb_data->scb_array[scb->cmd->next->tag]; + *old_scb->cmd->sense_buffer = *scb->cmd->sense_buffer; + old_scb->hscb->target_status = scb->hscb->target_status; + old_scb->cmd->result = scb->hscb->target_status; + old_scb->cmd->result |= (DID_ERROR << 16); + aic7xxx_status(old_scb->cmd) = scb->hscb->target_status; + scbq_remove(&p->waiting_scbs, old_scb); + scbq_remove(&p->delayed_scbs[tindex], old_scb); + scb->cmd->next = NULL; + aic7xxx_done(p, scb); + aic7xxx_done(p, old_scb); + continue; + } + else if (buffer[12] == 0x47 || buffer[12] == 0x54) + { + /* + * SCSI errors, run domain validation and re-run negotiation + */ + p->needdv |= (1<<tindex); + /* + * Signal that we need to re-negotiate things, this also gets us our + * INQUIRY command to re-checksum off of. + */ + p->needppr |= (p->needppr_copy & (1<<tindex)); + p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); + p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); + } + } switch (status_byte(scb->hscb->target_status)) { case QUEUE_FULL: @@ -6985,6 +6945,13 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) if (intstat & SEQINT) { + /* + * Read the CCSCBCTL register to work around a bug in the Ultra2 cards + */ + if(p->features & AHC_ULTRA2) + { + aic_inb(p, CCSCBCTL); + } aic7xxx_handle_seqint(p, intstat); } @@ -7019,10 +6986,10 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) p = (struct aic7xxx_host *)dev_id; if(!p) return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95) spin_lock_irqsave(&io_request_lock, cpu_flags); if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags)) { + spin_unlock_irqrestore(&io_request_lock, cpu_flags); return; } do @@ -7033,21 +7000,6 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) aic7xxx_run_waiting_queues(p); clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); -#else - if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags)) - { - return; - } - DRIVER_LOCK - do - { - aic7xxx_isr(irq, dev_id, regs); - } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); - DRIVER_UNLOCK - aic7xxx_done_cmds_complete(p); - aic7xxx_run_waiting_queues(p); - clear_bit(AHC_IN_ISR_BIT, (int *)&p->flags); -#endif } /*+F************************************************************************* @@ -7067,7 +7019,7 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) * with queue depths for individual devices. It also allows tagged * queueing to be [en|dis]abled for a specific adapter. *-F*************************************************************************/ -static void +static int aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) { int default_depth = 3; @@ -7077,6 +7029,14 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) tindex = device->id | (device->channel << 3); target_mask = (1 << tindex); + if (p->dev_max_queue_depth[tindex] > 1) + { + /* + * We've already scanned this device, leave it alone + */ + return(p->dev_max_queue_depth[tindex]); + } + device->queue_depth = default_depth; p->dev_temp_queue_depth[tindex] = 1; p->dev_max_queue_depth[tindex] = 1; @@ -7145,6 +7105,7 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) } } } + return(p->dev_max_queue_depth[tindex]); } /*+F************************************************************************* @@ -7172,8 +7133,7 @@ aic7xxx_select_queue_depth(struct Scsi_Host *host, { if (device->host == host) { - aic7xxx_device_queue_depth(p, device); - scbnum += device->queue_depth; + scbnum += aic7xxx_device_queue_depth(p, device); } } while (scbnum > p->scb_data->numscbs) @@ -7481,7 +7441,6 @@ release_seeprom(struct aic7xxx_host *p) */ CLOCK_PULSE(p); aic_outb(p, 0, SEECTL); - CLOCK_PULSE(p); } /*+F************************************************************************* @@ -7757,8 +7716,6 @@ write_brdctl(struct aic7xxx_host *p, unsigned char value) } } -#undef CLOCK_PULSE - /*+F************************************************************************* * Function: * aic785x_cable_detect @@ -7773,16 +7730,64 @@ aic785x_cable_detect(struct aic7xxx_host *p, int *int_50, unsigned char brdctl; aic_outb(p, BRDRW | BRDCS, BRDCTL); - udelay(1); + CLOCK_PULSE(p); aic_outb(p, 0, BRDCTL); - udelay(1); + CLOCK_PULSE(p); brdctl = aic_inb(p, BRDCTL); - udelay(1); + CLOCK_PULSE(p); *int_50 = !(brdctl & BRDDAT5); *ext_present = !(brdctl & BRDDAT6); *eeprom = (aic_inb(p, SPIOCAP) & EEPROM); } +#undef CLOCK_PULSE + +/*+F************************************************************************* + * Function: + * aic2940_uwpro_cable_detect + * + * Description: + * Detect the cables that are present on the 2940-UWPro cards + * + * NOTE: This functions assumes the SEEPROM will have already been aquired + * prior to invocation of this function. + *-F*************************************************************************/ +static void +aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68, + int *ext_68, int *eeprom) +{ + unsigned char brdctl; + + /* + * First read the status of our cables. Set the rom bank to + * 0 since the bank setting serves as a multiplexor for the + * cable detection logic. BRDDAT5 controls the bank switch. + */ + write_brdctl(p, 0); + + /* + * Now we read the state of the internal 68 connector. BRDDAT6 + * is don't care, BRDDAT7 is internal 68. The cable is + * present if the bit is 0 + */ + brdctl = read_brdctl(p); + *int_68 = !(brdctl & BRDDAT7); + + /* + * Set the bank bit in brdctl and then read the external cable state + * and the EEPROM status + */ + write_brdctl(p, BRDDAT5); + brdctl = read_brdctl(p); + + *ext_68 = !(brdctl & BRDDAT6); + *eeprom = !(brdctl & BRDDAT7); + + /* + * We're done, the calling function will release the SEEPROM for us + */ +} + /*+F************************************************************************* * Function: * aic787x_cable_detect @@ -7887,59 +7892,182 @@ configure_termination(struct aic7xxx_host *p) max_target = 8; aic_outb(p, SEEMS | SEECS, SEECTL); sxfrctl1 &= ~STPWEN; - if ( (p->adapter_control & CFAUTOTERM) || - (p->features & AHC_NEW_AUTOTERM) ) + /* + * The termination/cable detection logic is split into three distinct + * groups. Ultra2 and later controllers, 2940UW-Pro controllers, and + * older 7850, 7860, 7870, 7880, and 7895 controllers. Each has its + * own unique way of detecting their cables and writing the results + * back to the card. + */ + if (p->features & AHC_ULTRA2) { - if ( (p->adapter_control & CFAUTOTERM) && - !(p->features & AHC_NEW_AUTOTERM) ) + /* + * As long as user hasn't overridden term settings, always check the + * cable detection logic + */ + if (aic7xxx_override_term == -1) { - printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", - p->host_no); - printk(KERN_INFO "(scsi%d) Please verify driver detected settings are " - "correct.\n", p->host_no); - printk(KERN_INFO "(scsi%d) If not, then please properly set the device " - "termination\n", p->host_no); - printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A " - "when prompted\n", p->host_no); - printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); + aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, + &enableLVD_low, &enableLVD_high, + &eprom_present); + } + + /* + * If the user is overriding settings, then they have been preserved + * to here as fake adapter_control entries. Parse them and allow + * them to override the detected settings (if we even did detection). + */ + if (!(p->adapter_control & CFSEAUTOTERM)) + { + enableSE_low = (p->adapter_control & CFSTERM); + enableSE_high = (p->adapter_control & CFWSTERM); + } + if (!(p->adapter_control & CFAUTOTERM)) + { + enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); } - /* Configure auto termination. */ - if (p->features & AHC_NEW_AUTOTERM) + /* + * Now take those settings that we have and translate them into the + * values that must be written into the registers. + * + * Flash Enable = BRDDAT7 + * Secondary High Term Enable = BRDDAT6 + * Secondary Low Term Enable = BRDDAT5 + * LVD/Primary High Term Enable = BRDDAT4 + * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1 + */ + if (enableLVD_low != 0) { - if (aic7xxx_override_term == -1) - aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, - &enableLVD_low, &enableLVD_high, - &eprom_present); - if (!(p->adapter_control & CFSEAUTOTERM)) + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_LVD; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination " + "Enabled\n", p->host_no); + } + + if (enableLVD_high != 0) + { + brddat |= BRDDAT4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination " + "Enabled\n", p->host_no); + } + + if (enableSE_low != 0) + { + brddat |= BRDDAT5; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Secondary Low byte termination " + "Enabled\n", p->host_no); + } + + if (enableSE_high != 0) + { + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Secondary High byte termination " + "Enabled\n", p->host_no); + } + } + else if (p->features & AHC_NEW_AUTOTERM) + { + /* + * The 50 pin connector termination is controlled by STPWEN in the + * SXFRCTL1 register. Since the Adaptec docs typically say the + * controller is not allowed to be in the middle of a cable and + * this is the only connection on that stub of the bus, there is + * no need to even check for narrow termination, it's simply + * always on. + */ + sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n", + p->host_no); + + if (p->adapter_control & CFAUTOTERM) + { + aic2940_uwpro_wide_cable_detect(p, &internal68_present, + &external_present, + &eprom_present); + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", p->host_no, + "Don't Care", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, + eprom_present ? "is" : "is not"); + if (internal68_present && external_present) { - enableSE_low = (p->adapter_control & CFSTERM); - enableSE_high = (p->adapter_control & CFWSTERM); + brddat = 0; + p->flags &= ~AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n", + p->host_no); } - if (!(p->adapter_control & CFAUTOTERM)) + else { - enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); + brddat = BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n", + p->host_no); } - internal50_present = 0; - internal68_present = 1; - external_present = 1; - } - else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) - { - aic787x_cable_detect(p, &internal50_present, &internal68_present, - &external_present, &eprom_present); } else { - aic785x_cable_detect(p, &internal50_present, &external_present, - &eprom_present); + /* + * The termination of the Wide channel is done more like normal + * though, and the setting of this termination is done by writing + * either a 0 or 1 to BRDDAT6 of the BRDDAT register + */ + if (p->adapter_control & CFWSTERM) + { + brddat = BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n", + p->host_no); + } + else + { + brddat = 0; + } } - - if (max_target <= 8) - internal68_present = 0; - - if ( !(p->features & AHC_NEW_AUTOTERM) ) + } + else + { + if (p->adapter_control & CFAUTOTERM) { + if (p->flags & AHC_MOTHERBOARD) + { + printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", + p->host_no); + printk(KERN_INFO "(scsi%d) Please verify driver detected settings " + "are correct.\n", p->host_no); + printk(KERN_INFO "(scsi%d) If not, then please properly set the " + "device termination\n", p->host_no); + printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting " + "CTRL-A when prompted\n", p->host_no); + printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); + } + /* Configure auto termination. */ + + if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) + { + aic787x_cable_detect(p, &internal50_present, &internal68_present, + &external_present, &eprom_present); + } + else + { + aic785x_cable_detect(p, &internal50_present, &external_present, + &eprom_present); + } + + if (max_target <= 8) + internal68_present = 0; + if (max_target > 8) { printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " @@ -7955,100 +8083,73 @@ configure_termination(struct aic7xxx_host *p) internal50_present ? "YES" : "NO", external_present ? "YES" : "NO"); } - } - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, - eprom_present ? "is" : "is not"); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, + eprom_present ? "is" : "is not"); - /* - * Now set the termination based on what we found. BRDDAT6 - * controls wide termination enable. - * Flash Enable = BRDDAT7 - * SE High Term Enable = BRDDAT6 - * SE Low Term Enable = BRDDAT5 (7890) - * LVD High Term Enable = BRDDAT4 (7890) - */ - if ( !(p->features & AHC_NEW_AUTOTERM) && - (internal50_present && internal68_present && external_present) ) - { - printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", - p->host_no); - printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " - "in use at a time!\n", p->host_no); /* - * Force termination (low and high byte) on. This is safer than - * leaving it completely off, especially since this message comes - * most often from motherboard controllers that don't even have 3 - * connectors, but instead are failing the cable detection. + * Now set the termination based on what we found. BRDDAT6 + * controls wide termination enable. + * Flash Enable = BRDDAT7 + * SE High Term Enable = BRDDAT6 */ - internal50_present = external_present = 0; - enableSE_high = enableSE_low = 1; - } - - if ((max_target > 8) && - ((external_present == 0) || (internal68_present == 0) || - (enableSE_high != 0))) - { - brddat |= BRDDAT6; - p->flags |= AHC_TERM_ENB_SE_HIGH; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + if (internal50_present && internal68_present && external_present) + { + printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", p->host_no); - } + printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " + "in use at a time!\n", p->host_no); + /* + * Force termination (low and high byte) on. This is safer than + * leaving it completely off, especially since this message comes + * most often from motherboard controllers that don't even have 3 + * connectors, but instead are failing the cable detection. + */ + internal50_present = external_present = 0; + enableSE_high = enableSE_low = 1; + } - if ( (((internal50_present ? 1 : 0) + - (internal68_present ? 1 : 0) + - (external_present ? 1 : 0)) <= 1) || - (enableSE_low != 0) ) - { - if (p->features & AHC_NEW_AUTOTERM) - brddat |= BRDDAT5; - else - sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_SE_LOW; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", - p->host_no); - } + if ((max_target > 8) && + ((external_present == 0) || (internal68_present == 0)) ) + { + brddat |= BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); + } - if (enableLVD_low != 0) - { - sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_LVD; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n", - p->host_no); - } - - if (enableLVD_high != 0) - { - brddat |= BRDDAT4; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n", - p->host_no); + if ( ((internal50_present ? 1 : 0) + + (internal68_present ? 1 : 0) + + (external_present ? 1 : 0)) <= 1 ) + { + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_SE_LOW; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); + } } - } - else - { - if (p->adapter_control & CFSTERM) + else /* p->adapter_control & CFAUTOTERM */ { - if (p->features & AHC_NEW_AUTOTERM) - brddat |= BRDDAT5; - else + if (p->adapter_control & CFSTERM) + { sxfrctl1 |= STPWEN; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", - p->host_no); - } + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); + } - if (p->adapter_control & CFWSTERM) - { - brddat |= BRDDAT6; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", - p->host_no); + if (p->adapter_control & CFWSTERM) + { + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); + } } } + aic_outb(p, sxfrctl1, SXFRCTL1); write_brdctl(p, brddat); release_seeprom(p); @@ -8236,23 +8337,8 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, } aic_outb(p, 0, SEQ_FLAGS); - /* - * We are starting to do real work on the card....it's possible we could - * generate some spurious interrupts at this point, especially in the - * event of a PCI error or some such. If there are other devices already - * registered on the same interrupt as us, this could cause the machine - * to lock up. So, we disable the interrupt this card is on until we - * finish our card setup. We only need to do this for modules, if we are - * compiled into the kernel then interrupts are already off during this - * part of the code. - */ -#ifdef MODULE - disable_irq(p->irq); -#endif - detect_maxscb(p); - printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -8277,12 +8363,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, { unsigned char devconfig; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - DEVCONFIG, &devconfig); -#endif if ( (aic7xxx_stpwlev >> p->instance) & 0x01 ) { devconfig |= STPWLEVEL; @@ -8295,12 +8376,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, if (aic7xxx_verbose & VERBOSE_PROBE2) printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no); } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_byte(p->pdev, DEVCONFIG, devconfig); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - DEVCONFIG, devconfig); -#endif } } #endif @@ -8404,7 +8480,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, * we won't have a power source for the SCSI termination, which means * we'll see infinite incoming bus resets. */ - if(p->flags & AHC_NO_STPWR) + if(p->flags & AHC_NO_STPWEN) aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1); else aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); @@ -8484,9 +8560,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk("(scsi%d) Unable to allocate hardware SCB array; " "failing detection.\n", p->host_no); aic_outb(p, 0, SIMODE1); -#ifdef MODULE - enable_irq(p->irq); -#endif p->irq = 0; return(0); } @@ -8670,9 +8743,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring " "controller.\n", p->host_no, p->irq); aic_outb(p, 0, SIMODE1); -#ifdef MODULE - enable_irq(p->irq); -#endif p->irq = 0; return (0); } @@ -8682,10 +8752,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, p->host_no, -1, -1 , -1); aic7xxx_clear_intstat(p); -#ifdef MODULE - enable_irq(p->irq); -#endif - unpause_sequencer(p, /* unpause_always */ TRUE); return (found); @@ -8715,10 +8781,10 @@ aic7xxx_chip_reset(struct aic7xxx_host *p) * In the future, we may call this function as a last resort for * error handling. Let's be nice and not do any unecessary delays. */ - wait = 1000; /* 1 second (1000 * 1 msec) */ + wait = 1000; /* 1 msec (1000 * 1 msec) */ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) { - udelay(1); /* 1 msec */ + udelay(1); /* 1 usec */ } pause_sequencer(p); @@ -8794,7 +8860,7 @@ aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp) p->orderedtag = 0; for (i=0; i<MAX_TARGETS; i++) { - p->transinfo[i].goal_period = 0; + p->transinfo[i].goal_period = 255; p->transinfo[i].goal_offset = 0; p->transinfo[i].goal_options = 0; p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT; @@ -9369,11 +9435,7 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * PCI-bus probe. */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (pci_present()) -#else - if (pcibios_present()) -#endif { struct { @@ -9412,7 +9474,7 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_AIC7860_FE, 7, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, AHC_AIC7860_FE, 7, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860, @@ -9420,7 +9482,8 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_AIC7860_FE, 8, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 9, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, + AHC_AIC7870_FE, 9, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10, @@ -9437,7 +9500,8 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 14, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, + AHC_AIC7880_FE, 14, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15, @@ -9460,106 +9524,93 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 18, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7895_FE, 19, + AHC_AIC7895_FE, 20, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 20, + AHC_AIC7890_FE, 21, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 20, + AHC_AIC7890_FE, 21, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 21, + AHC_AIC7890_FE, 22, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 22, + AHC_AIC7890_FE, 23, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 23, + AHC_AIC7896_FE, 24, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 24, + AHC_AIC7896_FE, 25, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 25, + AHC_AIC7896_FE, 26, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWR, - AHC_AIC7860_FE, 26, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN, + AHC_AIC7860_FE, 27, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, }; unsigned short command; unsigned int devconfig, i, oldverbose; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) struct pci_dev *pdev = NULL; -#else - int index; - unsigned int piobase, mmapbase; - unsigned char pci_bus, pci_devfn, pci_irq; -#endif for (i = 0; i < NUMBER(aic_pdevs); i++) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pdev = NULL; while ((pdev = pci_find_device(aic_pdevs[i].vendor_id, aic_pdevs[i].device_id, pdev))) { if (pci_enable_device(pdev)) continue; -#else - index = 0; - while (!(pcibios_find_device(aic_pdevs[i].vendor_id, - aic_pdevs[i].device_id, - index++, &pci_bus, &pci_devfn)) ) { -#endif if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */ { if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2)) @@ -9583,7 +9634,6 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Read sundry information from PCI BIOS. */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) temp_p->irq = pdev->irq; temp_p->pdev = pdev; temp_p->pci_bus = pdev->bus->number; @@ -9595,7 +9645,8 @@ aic7xxx_detect(Scsi_Host_Template *template) { if ( ((current_p->pci_bus == temp_p->pci_bus) && (current_p->pci_device_fn == temp_p->pci_device_fn)) || - (current_p->base == temp_p->base) ) + (temp_p->base && (current_p->base == temp_p->base)) || + (temp_p->mbase && (current_p->mbase == temp_p->mbase)) ) { /* duplicate PCI entry, skip it */ kfree(temp_p); @@ -9635,69 +9686,8 @@ aic7xxx_detect(Scsi_Host_Template *template) devconfig |= 0x80000040; pci_write_config_dword(pdev, DEVCONFIG, devconfig); #endif /* AIC7XXX_STRICT_PCI_SETUP */ -#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ - temp_p->pci_bus = pci_bus; - temp_p->pci_device_fn = pci_devfn; - pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE, - &pci_irq); - temp_p->irq = pci_irq; - pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0, - &piobase); - temp_p->base = piobase; - pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_1, - &mmapbase); - temp_p->mbase = mmapbase; - temp_p->base &= PCI_BASE_ADDRESS_IO_MASK; - temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK; - current_p = list_p; - while(current_p) - { - if ( ((current_p->pci_bus == temp_p->pci_bus) && - (current_p->pci_device_fn == temp_p->pci_device_fn)) || - (current_p->base == temp_p->base) ) - { - /* duplicate PCI entry, skip it */ - kfree(temp_p); - temp_p = NULL; - } - current_p = current_p->next; - } - if ( temp_p == NULL ) - continue; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d/%d\n", - board_names[aic_pdevs[i].board_name_index], - temp_p->pci_bus, - PCI_SLOT(temp_p->pci_device_fn), - PCI_FUNC(temp_p->pci_device_fn)); - pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command); - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", - (int)command); - } -#ifdef AIC7XXX_STRICT_PCI_SETUP - command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | - PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; -#else - command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; -#endif - command &= ~PCI_COMMAND_INVALIDATE; - if (aic7xxx_pci_parity == 0) - command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); - pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command); -#ifdef AIC7XXX_STRICT_PCI_SETUP - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig); - } - devconfig |= 0x80000040; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig); -#endif /* AIC7XXX_STRICT_PCI_SETUP */ -#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ - if(check_region(temp_p->base, MAXREG - MINREG)) + if(temp_p->base && check_region(temp_p->base, MAXREG - MINREG)) { printk("aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], @@ -9728,7 +9718,7 @@ aic7xxx_detect(Scsi_Host_Template *template) } #ifdef MMAPIO - if ( !(temp_p->flags & AHC_MULTI_CHANNEL) || + if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) || ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) && (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) ) { @@ -9736,11 +9726,7 @@ aic7xxx_detect(Scsi_Host_Template *template) base = temp_p->mbase & PAGE_MASK; page_offset = temp_p->mbase - base; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) temp_p->maddr = ioremap_nocache(base, page_offset + 256); -#else - temp_p->maddr = vremap(base, page_offset + 256); -#endif if(temp_p->maddr) { temp_p->maddr += page_offset; @@ -9760,12 +9746,20 @@ aic7xxx_detect(Scsi_Host_Template *template) PCI_FUNC(temp_p->pci_device_fn)); printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to " "Programmed I/O.\n"); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); -#else - vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); -#endif temp_p->maddr = 0; + if(temp_p->base == 0) + { + printk("aic7xxx: <%s> at PCI %d/%d/%d\n", + board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: Controller disabled by BIOS, ignoring.\n"); + kfree(temp_p); + temp_p = NULL; + continue; + } } } } @@ -9774,7 +9768,8 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Lock out other contenders for our i/o space. */ - request_region(temp_p->base, MAXREG - MINREG, "aic7xxx"); + if(temp_p->base) + request_region(temp_p->base, MAXREG - MINREG, "aic7xxx"); /* * We HAVE to make sure the first pause_sequencer() and all other @@ -9826,12 +9821,7 @@ aic7xxx_detect(Scsi_Host_Template *template) * bit of DEVCONFIG */ aic_outb(temp_p, sxfrctl1, SXFRCTL1); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pcibios_write_config_dword(temp_p->pci_bus, temp_p->pci_device_fn, - DEVCONFIG, devconfig); -#else pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig); -#endif sxfrctl1 &= STPWEN; /* @@ -9866,7 +9856,6 @@ aic7xxx_detect(Scsi_Host_Template *template) case AHC_AIC7895: /* 7895 */ case AHC_AIC7896: /* 7896/7 */ case AHC_AIC7899: /* 7899 */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (PCI_FUNC(pdev->devfn) != 0) { temp_p->flags |= AHC_CHNLB; @@ -9882,25 +9871,6 @@ aic7xxx_detect(Scsi_Host_Template *template) devconfig |= SCBSIZE32; pci_write_config_dword(pdev, DEVCONFIG, devconfig); } -#else - if (PCI_FUNC(temp_p->pci_device_fn) != 0) - { - temp_p->flags |= AHC_CHNLB; - } - /* - * The 7895 is the only chipset that sets the SCBSIZE32 param - * in the DEVCONFIG register. The Ultra2 chipsets use - * the DSCOMMAND0 register instead. - */ - if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) - { - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); - devconfig |= SCBSIZE32; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); - } -#endif break; default: break; @@ -9965,12 +9935,7 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Check the rev of the chipset before we change DSCOMMAND0 */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_dword(pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); -#endif if ((devconfig & 0xff) >= 1) { aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | @@ -10040,12 +10005,7 @@ aic7xxx_detect(Scsi_Host_Template *template) case AHC_AIC7895: case AHC_AIC7896: case AHC_AIC7899: -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_dword(pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); -#endif if (temp_p->features & AHC_ULTRA2) { if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) && @@ -10087,12 +10047,7 @@ aic7xxx_detect(Scsi_Host_Template *template) "but not enabled\n"); } } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_dword(pdev, DEVCONFIG, devconfig); -#else - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); -#endif if ( (temp_p->flags & AHC_EXTERNAL_SRAM) && (temp_p->flags & AHC_CHNLB) ) aic_outb(temp_p, 1, CCSCBBADDR); @@ -10687,20 +10642,59 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) { p->transinfo[tindex].goal_period = syncrate->period; - if( !(syncrate->sxfr_ultra2 & 0x40) ) + if( p->transinfo[tindex].goal_period > 9 ) { p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); + if (p->transinfo[tindex].goal_width) + { + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); + } + } + } + else if (p->transinfo[tindex].goal_width) + { + p->transinfo[tindex].goal_width = 0; + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = + p->transinfo[tindex].user_options; + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->needppr |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + } + else + { + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); } } else { p->transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_options = 0; + p->transinfo[tindex].goal_width = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); } - p->needppr |= (p->needppr_copy & (1<<tindex)); - p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); - p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); } p->needdv &= ~(1<<tindex); } @@ -10751,8 +10745,16 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) * and we didn't follow up with SDTR yet, then this will get it started. * For all other cases, this should work out to be a no-op, unless we are * doing domain validation and happen to need a new negotiation command. + * + * In case we don't want this to go any further, the cmdcmplt interrupt + * handler will NULL out the cmd->next entry so that the real SCSI command + * can be sent back to the mid layer code with SENSE data intact. We'll + * finish things up when the cmd gets sent back down to us, so no worries. */ - aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex); + if(cmd->next) + { + aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex); + } return; } @@ -10874,13 +10876,13 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, */ hscb->control = 0; scb->tag_action = 0; + cmd->tag = hscb->tag; if (p->discenable & mask) { hscb->control |= DISCENB; if ( (p->tagenable & mask) && (cmd->cmnd[0] != TEST_UNIT_READY) ) { - cmd->tag = hscb->tag; p->dev_commands_sent[tindex]++; if (p->dev_commands_sent[tindex] < 200) { @@ -11322,13 +11324,6 @@ aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - int i, mask, found, need_tag; - struct aic7xxx_scb *scb; - unsigned char qinpos, hscbp; - - found = FALSE; -#endif printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION, UTS_RELEASE); @@ -11341,121 +11336,7 @@ aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) disable_irq(p->irq); aic7xxx_print_card(p); aic7xxx_print_scratch_ram(p); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - for(i=0; i<MAX_TARGETS; i++) - { - if(p->dev_flags[i] & DEVICE_PRESENT) - { - mask = (0x01 << i); - printk(INFO_LEAD "dev_flags=0x%x, Pending:%c, PPR:%c/%c, WDTR:%c/%c, " - "SDTR:%c/%c, q_depth=%d:%d\n", - p->host_no, 0, i, 0, p->dev_flags[i], - (p->dtr_pending & mask) ? 'Y' : 'N', - (p->needppr & mask) ? 'Y' : 'N', - (p->needppr_copy & mask) ? 'Y' : 'N', - (p->needwdtr & mask) ? 'Y' : 'N', - (p->needwdtr_copy & mask) ? 'Y' : 'N', - (p->needsdtr & mask) ? 'Y' : 'N', - (p->needsdtr_copy & mask) ? 'Y' : 'N', - p->dev_active_cmds[i], - p->dev_max_queue_depth[i] ); - printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0, - aic_inb(p, TARG_SCSIRATE + i)); - if (p->features & AHC_ULTRA2) - printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i)); - printk("\n"); - } - } - /* - * Search for this command and see if we can't track it down, it's the - * one causing the timeout. Print out this command first, then all other - * active commands afterwords. - */ - need_tag = -1; - if ( cmd ) - { - scb = p->scb_data->scb_array[aic7xxx_position(cmd)]; - if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) ) - { - printk("Timed out command is scb #%d:\n", scb->hscb->tag); - printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, - scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, - (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); - need_tag = scb->hscb->tag; - if (scb->flags & SCB_WAITINGQ) found=TRUE; - } - } - printk("QINFIFO: (TAG) "); - qinpos = aic_inb(p, QINPOS); - while ( qinpos != p->qinfifonext ) - { - if (p->qinfifo[qinpos] == need_tag) - found=TRUE; - printk("%d ", p->qinfifo[qinpos++]); - } - printk("\n"); - printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR), - aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) ); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, WAITING_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - } - printk("\n"); - printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, DISCONNECTED_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - } - printk("\n"); - printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, FREE_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - } - printk("\n"); - - if (found == FALSE) - { - /* - * We haven't found the offending SCB yet, and it should be around - * somewhere, so go look for it in the cards SCBs. - */ - printk("SCBPTR CONTROL TAG NEXT\n"); - for(i=0; i<p->scb_data->maxhscbs; i++) - { - aic_outb(p, i, SCBPTR); - printk(" %3d %02x %02x %02x\n", i, - aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG), - aic_inb(p, SCB_NEXT)); - } - } - - - for (i=0; i < p->scb_data->numscbs; i++) - { - scb = p->scb_data->scb_array[i]; - if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) ) - { - printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, - scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, - (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); - } - } -#endif - sti(); + spin_unlock_irq(&io_request_lock); for(;;) barrier(); } @@ -11501,7 +11382,6 @@ aic7xxx_abort(Scsi_Cmnd *cmd) * we are following a straight code path without entering the scheduler * code. */ - pause_sequencer(p); while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR)) { @@ -12164,15 +12044,12 @@ aic7xxx_release(struct Scsi_Host *host) if(p->irq) free_irq(p->irq, p); - release_region(p->base, MAXREG - MINREG); + if(p->base) + release_region(p->base, MAXREG - MINREG); #ifdef MMAPIO if(p->maddr) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - vfree((void *) (((unsigned long) p->maddr) & PAGE_MASK)); -#else iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK)); -#endif } #endif /* MMAPIO */ prev = NULL; @@ -12243,31 +12120,7 @@ aic7xxx_print_card(struct aic7xxx_host *p) 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f, 0xe0, 0xf1, 0xf4, 0xfc} }, }; -#ifdef CONFIG_PCI - static struct register_ranges cards_ns[] = { - { 0, {0,} }, /* none */ - { 0, {0,} }, /* 7771 */ - { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, - 0x3c, 0x41, 0x43, 0x47} }, - { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, - 0x3c, 0x41, 0x43, 0x47} }, - { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} }, - { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} }, - { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} }, - { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47, - 0xdc, 0xe3} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} } - }; -#endif chip = p->chip & AHC_CHIPID_MASK; - /* - * Let's run through the PCI space first.... - */ printk("%s at ", board_names[p->board_name_index]); switch(p->chip & ~AHC_CHIPID_MASK) @@ -12285,38 +12138,8 @@ aic7xxx_print_card(struct aic7xxx_host *p) break; } -#ifdef CONFIG_PCI - { - unsigned char temp; - - printk("PCI Dump:\n"); - k=0; - for(i=0; i<cards_ns[chip].num_ranges; i++) - { - for(j = cards_ns[chip].range_val[ i * 2 ]; - j <= cards_ns[chip].range_val[ i * 2 + 1 ] ; - j++) - { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_read_config_byte(p->pdev, j, &temp); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp); -#endif - printk("%02x:%02x ", j, temp); - if(++k == 13) - { - printk("\n"); - k = 0; - } - } - } - } - if(k != 0) - printk("\n"); -#endif /* CONFIG_PCI */ - /* - * Now the registers on the card.... + * the registers on the card.... */ printk("Card Dump:\n"); k = 0; @@ -12336,21 +12159,6 @@ aic7xxx_print_card(struct aic7xxx_host *p) } if(k != 0) printk("\n"); - if (p->flags & AHC_SEEPROM_FOUND) - { - unsigned short *sc1; - sc1 = (unsigned short *)&p->sc; - - printk("SEEPROM dump.\n"); - for(i=1; i<=32; i++) - { - printk("0x%04x", sc1[i-1]); - if ( (i % 8) == 0 ) - printk("\n"); - else - printk(" "); - } - } /* * If this was an Ultra2 controller, then we just hosed the card in terms diff --git a/drivers/scsi/aic7xxx.h b/drivers/scsi/aic7xxx.h index ffcedd96d..de438cac7 100644 --- a/drivers/scsi/aic7xxx.h +++ b/drivers/scsi/aic7xxx.h @@ -23,21 +23,7 @@ #ifndef _aic7xxx_h #define _aic7xxx_h -#define AIC7XXX_H_VERSION "3.2.4" - -#ifndef LINUX_VERSION_CODE -#include <linux/version.h> -#endif - -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) -#endif - -#if defined(__i386__) -# define AIC7XXX_BIOSPARAM aic7xxx_biosparam -#else -# define AIC7XXX_BIOSPARAM NULL -#endif +#define AIC7XXX_H_VERSION "5.2.0" /* * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields @@ -61,7 +47,7 @@ abort: aic7xxx_abort, \ reset: aic7xxx_reset, \ slave_attach: NULL, \ - bios_param: AIC7XXX_BIOSPARAM, \ + bios_param: aic7xxx_biosparam, \ can_queue: 255, /* max simultaneous cmds */\ this_id: -1, /* scsi id of host adapter */\ sg_tablesize: 0, /* max scatter-gather cmds */\ diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq index de3afbf92..15c887167 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.seq +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -60,11 +60,7 @@ reset: clr SCSISIGO; /* De-assert BSY */ and SXFRCTL1, ~BITBUCKET; /* Always allow reselection */ - if ((p->flags & AHC_TARGETMODE) != 0) { - mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; - } else { - mvi SCSISEQ, ENRSELI|ENAUTOATNP; - } + mvi SCSISEQ, ENRSELI|ENAUTOATNP; if ((p->features & AHC_CMD_CHAN) != 0) { /* Ensure that no DMA operations are in progress */ @@ -182,6 +178,15 @@ initialize_scsiid: and SCSIID, OID; /* Clear old target */ or SCSIID, A; } + mov SCSIDATL, ALLZEROS; /* clear out the latched */ + /* data register, this */ + /* fixes a bug on some */ + /* controllers where the */ + /* last byte written to */ + /* this register can leak */ + /* onto the data bus at */ + /* bad times, such as during */ + /* selection timeouts */ mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; /* @@ -227,118 +232,6 @@ ndx_dtr_2: selection: test SSTAT0,SELDO jnz select_out; -select_in: - if ((p->flags & AHC_TARGETMODE) != 0) { - test SSTAT0, TARGET jz initiator_reselect; - /* - * We've just been selected. Assert BSY and - * setup the phase for receiving the messages - * from the target. - */ - mvi SCSISIGO, P_MESGOUT|BSYO; - mvi CLRSINT0, CLRSELDO; - - /* - * If ATN isn't asserted, go directly to bus free. - */ - test SCSISIGI, ATNI jz target_busfree; - - /* - * Setup the DMA for sending the identify and - * command information. - */ - mov A, TMODE_CMDADDR_NEXT; - mvi DINDEX, HADDR; - mvi TMODE_CMDADDR call set_32byte_addr; - mvi DFCNTRL, FIFORESET; - - clr SINDEX; - /* Watch ATN closely now */ -message_loop: - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - and SXFRCTL0, ~SPIOEN; - mov DINDEX, SCSIDATL; - mov DFDAT, DINDEX; - inc SINDEX; - - /* Message Testing... */ - test DINDEX, MSG_IDENTIFYFLAG jz . + 2; - mov ARG_1, DINDEX; - - test SCSISIGI, ATNI jnz message_loop; - add A, -4, SINDEX; - jc target_cmdphase; - mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ - -target_cmdphase: - add HCNT[0], 1, A; - clr HCNT[1]; - clr HCNT[2]; - mvi SCSISIGO, P_COMMAND|BSYO; - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - mov A, SCSIDATL; - mov DFDAT, A; /* Store for host */ - - /* - * Determine the number of bytes to read - * based on the command group code. Count is - * one less than the total since we've already - * fetched the first byte. - */ - clr SINDEX; - shr A, CMD_GROUP_CODE_SHIFT; - add SEQADDR0, A; - - add SINDEX, CMD_GROUP0_BYTE_DELTA; - nop; /* Group 1 and 2 are the same */ - add SINDEX, CMD_GROUP2_BYTE_DELTA; - nop; /* Group 3 is reserved */ - add SINDEX, CMD_GROUP4_BYTE_DELTA; - add SINDEX, CMD_GROUP5_BYTE_DELTA; - /* Group 6 and 7 are not handled yet */ - - mov A, SINDEX; - add HCNT[0], A; - -command_loop: - test SSTAT0, SPIORDY jz .; - cmp SINDEX, 1 jne . + 2; - and SXFRCTL0, ~SPIOEN; /* Last Byte */ - mov DFDAT, SCSIDATL; - dec SINDEX; - test SINDEX, 0xFF jnz command_loop; - - or DFCNTRL, HDMAEN|FIFOFLUSH; - - call dma_finish; - - test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; - - mvi SCSISIGO, P_MESGIN|BSYO; - - or SXFRCTL0, SPIOEN; - - mvi MSG_DISCONNECT call target_outb; - -selectin_post: - inc TMODE_CMDADDR_NEXT; - cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; - clr TMODE_CMDADDR_NEXT; - mvi QOUTFIFO, SCB_LIST_NULL; - mvi INTSTAT,CMDCMPLT; - - test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; - - /* Busy loop on something then go to data or status phase */ - -target_busfree: - clr SCSISIGO; - jmp poll_for_work; - - } - /* * Reselection has been initiated by a target. Make a note that we've been * reselected, but haven't seen an IDENTIFY message from the target yet. @@ -444,13 +337,14 @@ clear_target_state: * STCNT may have been cleared, so restore it from the residual field. */ data_phase_reinit: - if ((p->features & AHC_CMD_CHAN) != 0) { - if ((p->features & AHC_ULTRA2) != 0) { - bmov HADDR, SHADDR, 4; - bmov HCNT, SCB_RESID_DCNT, 3; - } + if ((p->features & AHC_ULTRA2) != 0) { + bmov HADDR, SHADDR, 4; + bmov HCNT, SCB_RESID_DCNT, 3; + } + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { bmov STCNT, SCB_RESID_DCNT, 3; - } else { + } + if ((p->features & AHC_CMD_CHAN) == 0) { mvi DINDEX, STCNT; mvi SCB_RESID_DCNT call bcopy_3; } @@ -677,10 +571,13 @@ ultra2_dmafifoflush: test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, MREQPEND jnz .; ultra2_dmahalt: - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - and DFCNTRL, ~SCSIEN; - test DFCNTRL, SCSIEN jnz .; + test SCSIOFFSET, 0x7f jnz ultra2_shutdown; +ultra2_await_nreq: + test SCSISIGI, REQI jz ultra2_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz ultra2_await_nreq; +ultra2_shutdown: + and DFCNTRL, ~(HDMAEN|SCSIEN); + test DFCNTRL, (HDMAEN|SCSIEN) jnz .; bmov SCB_RESID_DCNT, STCNT, 3; mov SCB_RESID_SGCNT, SG_COUNT; or SXFRCTL0, CLRSTCNT|CLRCHN; @@ -719,10 +616,11 @@ p_command_dma_loop: test SSTAT0, SDONE jnz p_command_ultra2_dma_done; test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */ p_command_ultra2_dma_done: - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - and DFCNTRL, ~SCSIEN; - test DFCNTRL, SCSIEN jnz .; + test SCSISIGI, REQI jz p_command_ultra2_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz p_command_ultra2_dma_done; +p_command_ultra2_shutdown: + and DFCNTRL, ~(HDMAEN|SCSIEN); + test DFCNTRL, (HDMAEN|SCSIEN) jnz .; or SXFRCTL0, CLRSTCNT|CLRCHN; } jmp ITloop; @@ -1069,25 +967,12 @@ mesgin_wide_residue: mvi ARG_1 call inb_next; /* ACK the wide_residue and get */ /* the size byte */ /* - * See if we'll ignore this wide residue (because it's an overrun byte) - */ - if ((p->features & AHC_ULTRA2) != 0) { - test SSTAT2, WIDE_RES jnz mesgin_done; - } else { - test SCB_RESID_SGCNT,0xff jnz wide_residue_int; - test SCB_RESID_DCNT[0],0xff jnz wide_residue_int; - test SCB_RESID_DCNT[1],0xff jnz wide_residue_int; - test SCB_RESID_DCNT[2],0xff jnz wide_residue_int; - jmp mesgin_done; - } -wide_residue_int: -/* * In order for this to be reliable, we have to do all sorts of horrible * magic in terms of resetting the datafifo and reloading the shadow layer * with the correct new values (so that a subsequent save data pointers * message will do the right thing). We let the kernel do that work. */ - mvi INTSTAT,WIDE_RESIDUE; + mvi INTSTAT, WIDE_RESIDUE; jmp mesgin_done; /* @@ -1136,17 +1021,6 @@ inb_first: inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ -if ((p->flags & AHC_TARGETMODE) != 0) { - /* - * Send a byte to an initiator in Automatic PIO mode. - * SPIOEN must be on prior to calling this routine. - */ -target_outb: - mov SCSIDATL, SINDEX; - test SSTAT0, SPIORDY jz .; - ret; -} - mesgin_phasemis: /* * We expected to receive another byte, but the target changed phase @@ -1191,6 +1065,12 @@ dma_fifoempty: * actually off first lest we get an ILLSADDR. */ dma_dmadone: + cmp LASTPHASE, P_COMMAND je dma_await_nreq; + test SCSIRATE, 0x0f jnz dma_shutdown; +dma_await_nreq: + test SCSISIGI, REQI jz dma_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz dma_await_nreq; +dma_shutdown: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: /* diff --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c index ec80af90d..7320eb6b6 100644 --- a/drivers/scsi/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx_proc.c @@ -172,7 +172,6 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, #else size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n"); #endif - size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY); size += sprintf(BLS, "\n"); size += sprintf(BLS, "Adapter Configuration:\n"); size += sprintf(BLS, " SCSI Adapter: %s\n", diff --git a/drivers/scsi/aic7xxx_seq.c b/drivers/scsi/aic7xxx_seq.c index 511bfcb73..047a34de0 100644 --- a/drivers/scsi/aic7xxx_seq.c +++ b/drivers/scsi/aic7xxx_seq.c @@ -4,34 +4,33 @@ static unsigned char seqprog[] = { 0xff, 0x6a, 0x06, 0x08, 0x7f, 0x02, 0x04, 0x08, - 0x32, 0x6a, 0x00, 0x00, 0x12, 0x6a, 0x00, 0x00, 0xff, 0x6a, 0xd6, 0x09, 0xff, 0x6a, 0xdc, 0x09, - 0x00, 0x65, 0x42, 0x59, + 0x00, 0x65, 0xca, 0x58, 0xf7, 0x01, 0x02, 0x08, 0xff, 0x4e, 0xc8, 0x08, 0xbf, 0x60, 0xc0, 0x08, 0x60, 0x0b, 0x86, 0x68, - 0x40, 0x00, 0x0e, 0x68, + 0x40, 0x00, 0x0c, 0x68, 0x08, 0x1f, 0x3e, 0x10, 0x60, 0x0b, 0x86, 0x68, - 0x40, 0x00, 0x0e, 0x68, + 0x40, 0x00, 0x0c, 0x68, 0x08, 0x1f, 0x3e, 0x10, - 0xff, 0x3e, 0x4a, 0x60, - 0x40, 0xfa, 0x12, 0x78, + 0xff, 0x3e, 0x48, 0x60, + 0x40, 0xfa, 0x10, 0x78, 0xff, 0xf6, 0xd4, 0x08, 0x01, 0x4e, 0x9c, 0x18, 0x40, 0x60, 0xc0, 0x00, - 0x00, 0x4d, 0x12, 0x70, + 0x00, 0x4d, 0x10, 0x70, 0x01, 0x4e, 0x9c, 0x18, 0xbf, 0x60, 0xc0, 0x08, - 0x00, 0x6a, 0xbe, 0x5c, + 0x00, 0x6a, 0x3e, 0x5c, 0xff, 0x4e, 0xc8, 0x18, - 0x02, 0x6a, 0xd4, 0x5b, + 0x02, 0x6a, 0x54, 0x5b, 0xff, 0x52, 0x20, 0x09, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x52, 0x4a, 0x5c, + 0x00, 0x52, 0xca, 0x5b, 0x03, 0xb0, 0x52, 0x31, 0xff, 0xb0, 0x52, 0x09, 0xff, 0xb1, 0x54, 0x09, @@ -40,8 +39,8 @@ static unsigned char seqprog[] = { 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, 0xff, 0x3e, 0x20, 0x09, - 0x00, 0x65, 0x50, 0x58, - 0x00, 0x65, 0x0e, 0x40, + 0x00, 0x65, 0x4e, 0x58, + 0x00, 0x65, 0x0c, 0x40, 0xf7, 0x1f, 0xca, 0x08, 0x08, 0xa1, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, @@ -52,6 +51,7 @@ static unsigned char seqprog[] = { 0xf0, 0xa1, 0xc8, 0x08, 0x0f, 0x05, 0x0a, 0x08, 0x00, 0x05, 0x0a, 0x00, + 0xff, 0x6a, 0x0c, 0x08, 0x5a, 0x6a, 0x00, 0x04, 0x12, 0x65, 0x02, 0x00, 0x31, 0x6a, 0xca, 0x00, @@ -69,74 +69,14 @@ static unsigned char seqprog[] = { 0xff, 0x6c, 0x0a, 0x08, 0x20, 0x64, 0xca, 0x18, 0xff, 0x6c, 0x08, 0x0c, - 0x40, 0x0b, 0x0e, 0x69, - 0x80, 0x0b, 0x00, 0x79, - 0xa4, 0x6a, 0x06, 0x00, - 0x40, 0x6a, 0x16, 0x00, - 0x10, 0x03, 0xfc, 0x78, - 0xff, 0x50, 0xc8, 0x08, - 0x88, 0x6a, 0xcc, 0x00, - 0x49, 0x6a, 0x3a, 0x5c, - 0x01, 0x6a, 0x26, 0x01, - 0xff, 0x6a, 0xca, 0x08, - 0x08, 0x01, 0x02, 0x00, - 0x02, 0x0b, 0x9c, 0x78, - 0xf7, 0x01, 0x02, 0x08, - 0xff, 0x06, 0xcc, 0x08, - 0xff, 0x66, 0x32, 0x09, - 0x01, 0x65, 0xca, 0x18, - 0x80, 0x66, 0xaa, 0x78, - 0xff, 0x66, 0xa2, 0x08, - 0x10, 0x03, 0x9a, 0x68, - 0xfc, 0x65, 0xc8, 0x18, - 0x00, 0x65, 0xb2, 0x48, - 0xff, 0x6a, 0x32, 0x01, - 0x01, 0x64, 0x18, 0x19, - 0xff, 0x6a, 0x1a, 0x09, - 0xff, 0x6a, 0x1c, 0x09, - 0x84, 0x6a, 0x06, 0x00, - 0x08, 0x01, 0x02, 0x00, - 0x02, 0x0b, 0xbc, 0x78, - 0xff, 0x06, 0xc8, 0x08, - 0xff, 0x64, 0x32, 0x09, - 0xff, 0x6a, 0xca, 0x08, - 0x5b, 0x64, 0xc8, 0x28, - 0x00, 0x62, 0xc4, 0x18, - 0xfc, 0x65, 0xca, 0x18, - 0xff, 0x6a, 0xd4, 0x08, - 0xfa, 0x65, 0xca, 0x18, - 0xff, 0x6a, 0xd4, 0x08, - 0x04, 0x65, 0xca, 0x18, - 0x0b, 0x65, 0xca, 0x18, - 0xff, 0x65, 0xc8, 0x08, - 0x00, 0x8c, 0x18, 0x19, - 0x02, 0x0b, 0xd8, 0x78, - 0x01, 0x65, 0xde, 0x60, - 0xf7, 0x01, 0x02, 0x08, - 0xff, 0x06, 0x32, 0x09, - 0xff, 0x65, 0xca, 0x18, - 0xff, 0x65, 0xd8, 0x68, - 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x40, 0x51, 0xf0, 0x78, - 0xe4, 0x6a, 0x06, 0x00, - 0x08, 0x01, 0x02, 0x00, - 0x04, 0x6a, 0x6c, 0x5b, - 0x01, 0x50, 0xa0, 0x18, - 0x00, 0x50, 0xf6, 0xe0, - 0xff, 0x6a, 0xa0, 0x08, - 0xff, 0x6a, 0x3a, 0x01, - 0x02, 0x6a, 0x22, 0x01, - 0x40, 0x51, 0xfc, 0x68, - 0xff, 0x6a, 0x06, 0x08, - 0x00, 0x65, 0x0e, 0x40, + 0x40, 0x0b, 0x96, 0x68, 0x20, 0x6a, 0x16, 0x00, 0xf0, 0x19, 0x6e, 0x08, 0x08, 0x6a, 0x18, 0x00, 0x08, 0x11, 0x22, 0x00, 0x08, 0x6a, 0x66, 0x58, 0x08, 0x6a, 0x68, 0x00, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x12, 0x6a, 0x00, 0x00, 0x40, 0x6a, 0x16, 0x00, 0xff, 0x3e, 0x20, 0x09, @@ -147,21 +87,21 @@ static unsigned char seqprog[] = { 0x08, 0x6a, 0x66, 0x58, 0x80, 0x6a, 0x68, 0x00, 0x80, 0x36, 0x6c, 0x00, - 0x00, 0x65, 0x1e, 0x5c, + 0x00, 0x65, 0x9e, 0x5b, 0xff, 0x3d, 0xc8, 0x08, - 0xbf, 0x64, 0x5a, 0x79, - 0x80, 0x64, 0x22, 0x72, - 0xa0, 0x64, 0x52, 0x72, - 0xc0, 0x64, 0x4a, 0x72, - 0xe0, 0x64, 0x92, 0x72, + 0xbf, 0x64, 0xe2, 0x78, + 0x80, 0x64, 0xac, 0x71, + 0xa0, 0x64, 0xdc, 0x71, + 0xc0, 0x64, 0xd4, 0x71, + 0xe0, 0x64, 0x1c, 0x72, 0x01, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0xf7, 0x11, 0x22, 0x08, - 0x00, 0x65, 0x42, 0x59, + 0x00, 0x65, 0xca, 0x58, 0xff, 0x06, 0xd4, 0x08, 0xf7, 0x01, 0x02, 0x08, - 0x09, 0x0c, 0x3c, 0x79, - 0x08, 0x0c, 0x0e, 0x68, + 0x09, 0x0c, 0xc4, 0x78, + 0x08, 0x0c, 0x0c, 0x68, 0x01, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0x26, 0x09, 0x02, 0x6a, 0x08, 0x30, @@ -173,25 +113,25 @@ static unsigned char seqprog[] = { 0x03, 0xa9, 0x18, 0x31, 0x03, 0xa9, 0x10, 0x30, 0x08, 0x6a, 0xcc, 0x00, - 0xa9, 0x6a, 0x34, 0x5c, - 0x00, 0x65, 0x7a, 0x41, + 0xa9, 0x6a, 0xb4, 0x5b, + 0x00, 0x65, 0x02, 0x41, 0xa8, 0x6a, 0x6a, 0x00, 0x79, 0x6a, 0x6a, 0x00, - 0x40, 0x3d, 0x62, 0x69, + 0x40, 0x3d, 0xea, 0x68, 0x04, 0x35, 0x6a, 0x00, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0x0e, 0x5b, 0x80, 0x6a, 0xd4, 0x01, - 0x10, 0x36, 0x4e, 0x69, + 0x10, 0x36, 0xd6, 0x68, 0x10, 0x36, 0x6c, 0x00, 0x07, 0xac, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x05, 0xa3, 0x70, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0x2c, 0x5c, - 0x00, 0x65, 0x26, 0x5c, + 0xac, 0x6a, 0xac, 0x5b, + 0x00, 0x65, 0xa6, 0x5b, 0x38, 0x6a, 0xcc, 0x00, - 0xa3, 0x6a, 0x30, 0x5c, - 0xff, 0x38, 0x8a, 0x69, + 0xa3, 0x6a, 0xb0, 0x5b, + 0xff, 0x38, 0x12, 0x69, 0x80, 0x02, 0x04, 0x00, 0xe7, 0x35, 0x6a, 0x08, 0x03, 0x69, 0x18, 0x31, @@ -199,338 +139,334 @@ static unsigned char seqprog[] = { 0xff, 0x6a, 0x10, 0x00, 0xff, 0x6a, 0x12, 0x00, 0xff, 0x6a, 0x14, 0x00, - 0x01, 0x38, 0x90, 0x61, + 0x01, 0x38, 0x18, 0x61, 0xbf, 0x35, 0x6a, 0x08, 0x02, 0x6a, 0xf8, 0x01, 0xff, 0x69, 0xca, 0x08, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x94, 0x69, - 0x04, 0x0b, 0xa0, 0x69, - 0x10, 0x0c, 0x96, 0x79, - 0x04, 0x0b, 0xa0, 0x69, + 0x04, 0x0b, 0x1c, 0x69, + 0x04, 0x0b, 0x28, 0x69, + 0x10, 0x0c, 0x1e, 0x79, + 0x04, 0x0b, 0x28, 0x69, 0xff, 0x6a, 0xca, 0x08, - 0x00, 0x35, 0x76, 0x5b, - 0x80, 0x02, 0xf4, 0x69, - 0xff, 0x65, 0xe4, 0x79, + 0x00, 0x35, 0xee, 0x5a, + 0x80, 0x02, 0x7c, 0x69, + 0xff, 0x65, 0x6c, 0x79, 0xff, 0x38, 0x70, 0x18, - 0xff, 0x38, 0xe4, 0x79, - 0x80, 0xea, 0xc0, 0x61, + 0xff, 0x38, 0x6c, 0x79, + 0x80, 0xea, 0x48, 0x61, 0xef, 0x38, 0xc8, 0x18, 0x80, 0x6a, 0xc8, 0x00, - 0x00, 0x65, 0xb2, 0x49, + 0x00, 0x65, 0x3a, 0x49, 0x33, 0x38, 0xc8, 0x28, 0xff, 0x64, 0xd0, 0x09, 0x04, 0x39, 0xc0, 0x31, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xb8, 0x79, + 0x80, 0xeb, 0x40, 0x79, 0xf7, 0xeb, 0xd6, 0x09, - 0x08, 0xeb, 0xbc, 0x69, + 0x08, 0xeb, 0x44, 0x69, 0x01, 0x6a, 0xd6, 0x01, 0x08, 0xe9, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0x39, 0x6a, 0x32, 0x5c, + 0x39, 0x6a, 0xb2, 0x5b, 0x08, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x88, 0x6a, 0xa0, 0x5c, - 0x00, 0x65, 0x26, 0x5c, + 0x00, 0x65, 0x30, 0x5c, + 0x88, 0x6a, 0x20, 0x5c, + 0x00, 0x65, 0xa6, 0x5b, 0xff, 0x6a, 0xc8, 0x08, 0x08, 0x39, 0x72, 0x18, 0x00, 0x3a, 0x74, 0x20, - 0x01, 0x0c, 0xdc, 0x79, - 0x10, 0x0c, 0x7a, 0x79, + 0x01, 0x0c, 0x64, 0x79, + 0x10, 0x0c, 0x02, 0x79, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0xe2, 0x69, - 0x00, 0x65, 0xfc, 0x59, + 0x04, 0x0b, 0x6a, 0x69, + 0x00, 0x65, 0x84, 0x59, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0xff, 0x08, 0x52, 0x09, 0xff, 0x09, 0x54, 0x09, 0xff, 0x0a, 0x56, 0x09, 0xff, 0x38, 0x50, 0x09, - 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0xfc, 0x59, + 0x00, 0x65, 0xaa, 0x40, + 0x00, 0x65, 0x84, 0x59, 0x7f, 0x02, 0x04, 0x08, 0xe1, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, - 0x04, 0x93, 0x12, 0x6a, + 0x00, 0x65, 0xaa, 0x40, + 0x04, 0x93, 0x9a, 0x69, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x00, 0x6a, + 0x20, 0x93, 0x88, 0x69, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x10, 0x94, 0x10, 0x6a, - 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x14, 0x6a, - 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x18, 0x6a, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x10, 0x94, 0x98, 0x69, + 0x7f, 0x05, 0xa0, 0x69, + 0x02, 0x03, 0xa0, 0x79, + 0x11, 0x0c, 0x9c, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x28, 0x93, 0xa2, 0x69, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0x12, 0x01, 0x02, 0x00, 0xff, 0x6a, 0xd4, 0x0c, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0x0e, 0x5b, 0x05, 0xb4, 0x10, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xb4, 0x6a, 0x30, 0x5c, + 0xb4, 0x6a, 0xb0, 0x5b, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, - 0x00, 0x65, 0x26, 0x5c, - 0x3d, 0x6a, 0x76, 0x5b, + 0x00, 0x65, 0xa6, 0x5b, + 0x3d, 0x6a, 0xee, 0x5a, 0xac, 0x6a, 0x26, 0x01, - 0x04, 0x0b, 0x38, 0x6a, - 0x04, 0x0b, 0x3e, 0x6a, - 0x10, 0x0c, 0x3a, 0x7a, - 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x40, 0x6a, - 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x44, 0x6a, + 0x04, 0x0b, 0xc2, 0x69, + 0x04, 0x0b, 0xc8, 0x69, + 0x10, 0x0c, 0xc4, 0x79, + 0x02, 0x03, 0xcc, 0x79, + 0x11, 0x0c, 0xc8, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x28, 0x93, 0xce, 0x69, 0x12, 0x01, 0x02, 0x00, - 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0xaa, 0x40, + 0x00, 0x65, 0x0e, 0x5b, 0xff, 0x06, 0x44, 0x09, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x34, 0xca, 0x08, - 0x80, 0x65, 0x76, 0x62, + 0x80, 0x65, 0x00, 0x62, 0x0f, 0xa1, 0xca, 0x08, 0x07, 0xa1, 0xca, 0x08, 0x40, 0xa0, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, 0x80, 0x65, 0xca, 0x00, - 0x80, 0xa0, 0x66, 0x7a, + 0x80, 0xa0, 0xf0, 0x79, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x78, 0x42, - 0x20, 0xa0, 0x7e, 0x7a, + 0x00, 0x65, 0x02, 0x42, + 0x20, 0xa0, 0x08, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x86, 0x62, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0x10, 0x62, 0x23, 0xa0, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x86, 0x62, - 0x00, 0xb9, 0x7e, 0x42, - 0xff, 0x65, 0x7e, 0x62, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0x10, 0x62, + 0x00, 0xb9, 0x08, 0x42, + 0xff, 0x65, 0x08, 0x62, 0xa1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x10, 0x51, 0x86, 0x72, + 0x10, 0x51, 0x10, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x50, 0x72, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0xda, 0x71, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x34, 0xa6, 0x08, - 0x80, 0x34, 0x8e, 0x62, + 0x80, 0x34, 0x18, 0x62, 0x7f, 0xa0, 0x40, 0x09, 0x08, 0x6a, 0x68, 0x00, - 0x00, 0x65, 0x22, 0x41, - 0x64, 0x6a, 0x66, 0x5b, - 0x80, 0x64, 0x04, 0x6b, - 0x04, 0x64, 0xe6, 0x72, - 0x02, 0x64, 0xec, 0x72, - 0x00, 0x6a, 0xae, 0x72, - 0x03, 0x64, 0x00, 0x73, - 0x01, 0x64, 0xe2, 0x72, - 0x07, 0x64, 0x42, 0x73, - 0x08, 0x64, 0xaa, 0x72, - 0x23, 0x64, 0x46, 0x73, + 0x00, 0x65, 0xaa, 0x40, + 0x64, 0x6a, 0xe4, 0x5a, + 0x80, 0x64, 0x8e, 0x6a, + 0x04, 0x64, 0x70, 0x72, + 0x02, 0x64, 0x76, 0x72, + 0x00, 0x6a, 0x38, 0x72, + 0x03, 0x64, 0x8a, 0x72, + 0x01, 0x64, 0x6c, 0x72, + 0x07, 0x64, 0xcc, 0x72, + 0x08, 0x64, 0x34, 0x72, + 0x23, 0x64, 0xd0, 0x72, 0x11, 0x6a, 0x22, 0x01, - 0x07, 0x6a, 0x58, 0x5b, + 0x07, 0x6a, 0xd6, 0x5a, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x22, 0x41, - 0xff, 0xa8, 0xb2, 0x6a, - 0xff, 0xa2, 0xca, 0x7a, + 0x00, 0x65, 0xaa, 0x40, + 0xff, 0xa8, 0x3c, 0x6a, + 0xff, 0xa2, 0x54, 0x7a, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, - 0xff, 0xa2, 0xca, 0x7a, + 0x00, 0xb9, 0xca, 0x5b, + 0xff, 0xa2, 0x54, 0x7a, 0x71, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x40, 0x51, 0xca, 0x62, + 0x40, 0x51, 0x54, 0x62, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, + 0x00, 0xb9, 0xca, 0x5b, 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, - 0x00, 0x65, 0x50, 0x58, - 0x00, 0x65, 0x34, 0x41, - 0x20, 0xa0, 0xd2, 0x6a, + 0x00, 0x65, 0x4e, 0x58, + 0x00, 0x65, 0xbc, 0x40, + 0x20, 0xa0, 0x5c, 0x6a, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xf4, 0x5b, - 0xff, 0x6a, 0x0a, 0x5c, + 0x00, 0x6a, 0x74, 0x5b, + 0xff, 0x6a, 0x8a, 0x5b, 0xff, 0xf8, 0xc8, 0x08, 0xff, 0x4f, 0xc8, 0x08, - 0x01, 0x6a, 0xf4, 0x5b, - 0x00, 0xb9, 0x0a, 0x5c, + 0x01, 0x6a, 0x74, 0x5b, + 0x00, 0xb9, 0x8a, 0x5b, 0x01, 0x4f, 0x9e, 0x18, 0x02, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xb8, 0x5c, - 0x00, 0x65, 0x34, 0x41, + 0x00, 0x65, 0x38, 0x5c, + 0x00, 0x65, 0xbc, 0x40, 0x41, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x04, 0xa0, 0x40, 0x01, - 0x00, 0x65, 0xd0, 0x5c, - 0x00, 0x65, 0x34, 0x41, - 0x10, 0x36, 0xaa, 0x7a, + 0x00, 0x65, 0x50, 0x5c, + 0x00, 0x65, 0xbc, 0x40, + 0x10, 0x36, 0x34, 0x7a, 0x05, 0x38, 0x46, 0x31, 0x04, 0x14, 0x58, 0x31, 0x03, 0xa9, 0x60, 0x31, 0xa3, 0x6a, 0xcc, 0x00, - 0x38, 0x6a, 0x30, 0x5c, + 0x38, 0x6a, 0xb0, 0x5b, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0x32, 0x5c, - 0xa9, 0x6a, 0x34, 0x5c, - 0x00, 0x65, 0xaa, 0x42, + 0x14, 0x6a, 0xb2, 0x5b, + 0xa9, 0x6a, 0xb4, 0x5b, + 0x00, 0x65, 0x34, 0x42, 0xef, 0x36, 0x6c, 0x08, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, 0x0f, 0x64, 0xc8, 0x08, 0x07, 0x64, 0xc8, 0x08, 0x00, 0x37, 0x6e, 0x00, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0xc4, 0x5b, - 0xff, 0x51, 0x16, 0x73, - 0x20, 0x36, 0x20, 0x7b, - 0x00, 0x90, 0xb2, 0x5b, - 0x00, 0x65, 0x22, 0x43, + 0x00, 0x65, 0x44, 0x5b, + 0xff, 0x51, 0xa0, 0x72, + 0x20, 0x36, 0xaa, 0x7a, + 0x00, 0x90, 0x32, 0x5b, + 0x00, 0x65, 0xac, 0x42, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xe0, 0x3d, 0x3c, 0x63, - 0x20, 0x12, 0x3c, 0x63, - 0x51, 0x6a, 0x5c, 0x5b, - 0x00, 0x65, 0xac, 0x5b, + 0x00, 0x65, 0x9e, 0x5b, + 0xe0, 0x3d, 0xc6, 0x62, + 0x20, 0x12, 0xc6, 0x62, + 0x51, 0x6a, 0xda, 0x5a, + 0x00, 0x65, 0x2c, 0x5b, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0xa1, 0x34, 0x63, - 0x04, 0xa0, 0x34, 0x7b, + 0x00, 0xa1, 0xbe, 0x62, + 0x04, 0xa0, 0xbe, 0x7a, 0xfb, 0xa0, 0x40, 0x09, 0x80, 0x36, 0x6c, 0x00, - 0x80, 0xa0, 0xaa, 0x7a, + 0x80, 0xa0, 0x34, 0x7a, 0x7f, 0xa0, 0x40, 0x09, - 0xff, 0x6a, 0x58, 0x5b, - 0x00, 0x65, 0xaa, 0x42, - 0x04, 0xa0, 0x3a, 0x7b, - 0x00, 0x65, 0xd0, 0x5c, - 0x00, 0x65, 0x3c, 0x43, - 0x00, 0x65, 0xb8, 0x5c, + 0xff, 0x6a, 0xd6, 0x5a, + 0x00, 0x65, 0x34, 0x42, + 0x04, 0xa0, 0xc4, 0x7a, + 0x00, 0x65, 0x50, 0x5c, + 0x00, 0x65, 0xc6, 0x42, + 0x00, 0x65, 0x38, 0x5c, 0x31, 0x6a, 0x22, 0x01, - 0x0c, 0x6a, 0x58, 0x5b, - 0x00, 0x65, 0xaa, 0x42, + 0x0c, 0x6a, 0xd6, 0x5a, + 0x00, 0x65, 0x34, 0x42, 0x61, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xaa, 0x42, - 0x51, 0x6a, 0x5c, 0x5b, - 0x20, 0x0d, 0xaa, 0x6a, - 0xff, 0xa8, 0x54, 0x6b, - 0xff, 0xa9, 0x54, 0x6b, - 0xff, 0xaa, 0x54, 0x6b, - 0xff, 0xab, 0x54, 0x6b, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, + 0x51, 0x6a, 0xda, 0x5a, 0x51, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x65, 0x68, 0x0c, 0xff, 0x06, 0xd4, 0x08, - 0x01, 0x0c, 0x5e, 0x7b, - 0x04, 0x0c, 0x60, 0x6b, + 0x01, 0x0c, 0xdc, 0x7a, + 0x04, 0x0c, 0xde, 0x6a, 0xe0, 0x03, 0x7a, 0x08, - 0xe0, 0x3d, 0x72, 0x63, + 0xe0, 0x3d, 0xea, 0x62, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x12, 0xda, 0x0c, 0xff, 0x06, 0xd4, 0x0c, - 0xff, 0x65, 0x0c, 0x08, - 0x02, 0x0b, 0x6e, 0x7b, - 0xff, 0x6a, 0xd4, 0x0c, 0xd1, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0xff, 0x65, 0x26, 0x09, - 0x01, 0x0b, 0x86, 0x6b, - 0x10, 0x0c, 0x78, 0x7b, - 0x04, 0x0b, 0x80, 0x6b, + 0x01, 0x0b, 0xfe, 0x6a, + 0x10, 0x0c, 0xf0, 0x7a, + 0x04, 0x0b, 0xf8, 0x6a, 0xff, 0x6a, 0xca, 0x08, - 0x04, 0x93, 0x84, 0x6b, - 0x01, 0x94, 0x82, 0x7b, - 0x10, 0x94, 0x84, 0x6b, + 0x04, 0x93, 0xfc, 0x6a, + 0x01, 0x94, 0xfa, 0x7a, + 0x10, 0x94, 0xfc, 0x6a, + 0x80, 0x3d, 0x02, 0x73, + 0x0f, 0x04, 0x06, 0x6b, + 0x02, 0x03, 0x06, 0x7b, + 0x11, 0x0c, 0x02, 0x7b, 0xc7, 0x93, 0x26, 0x09, 0xff, 0x99, 0xd4, 0x08, - 0x38, 0x93, 0x88, 0x6b, + 0x38, 0x93, 0x08, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0x36, 0x8c, 0x6b, + 0x80, 0x36, 0x0c, 0x6b, 0x21, 0x6a, 0x22, 0x05, 0xff, 0x65, 0x20, 0x09, - 0xff, 0x51, 0x9a, 0x63, + 0xff, 0x51, 0x1a, 0x63, 0xff, 0x37, 0xc8, 0x08, - 0xa1, 0x6a, 0xa6, 0x43, + 0xa1, 0x6a, 0x26, 0x43, 0xff, 0x51, 0xc8, 0x08, - 0xb9, 0x6a, 0xa6, 0x43, + 0xb9, 0x6a, 0x26, 0x43, 0xff, 0x90, 0xa4, 0x08, - 0xff, 0xba, 0xaa, 0x73, + 0xff, 0xba, 0x2a, 0x73, 0xff, 0xba, 0x20, 0x09, 0xff, 0x65, 0xca, 0x18, - 0x00, 0x6c, 0x9e, 0x63, + 0x00, 0x6c, 0x1e, 0x63, 0xff, 0x90, 0xca, 0x0c, 0xff, 0x6a, 0xca, 0x04, - 0x20, 0x36, 0xbe, 0x7b, - 0x00, 0x90, 0x92, 0x5b, - 0xff, 0x65, 0xbe, 0x73, - 0xff, 0x52, 0xbc, 0x73, + 0x20, 0x36, 0x3e, 0x7b, + 0x00, 0x90, 0x12, 0x5b, + 0xff, 0x65, 0x3e, 0x73, + 0xff, 0x52, 0x3c, 0x73, 0xff, 0xba, 0xcc, 0x08, 0xff, 0x52, 0x20, 0x09, 0xff, 0x66, 0x74, 0x09, 0xff, 0x65, 0x20, 0x0d, 0xff, 0xba, 0x7e, 0x0c, - 0x00, 0x6a, 0xbe, 0x5c, + 0x00, 0x6a, 0x3e, 0x5c, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x51, 0x4a, 0x44, - 0xff, 0x3f, 0x18, 0x74, + 0x00, 0x51, 0xca, 0x43, + 0xff, 0x3f, 0x98, 0x73, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3f, 0x92, 0x5b, - 0xff, 0x65, 0x18, 0x74, + 0x00, 0x3f, 0x12, 0x5b, + 0xff, 0x65, 0x98, 0x73, 0x20, 0x36, 0x6c, 0x00, - 0x20, 0xa0, 0xd2, 0x6b, + 0x20, 0xa0, 0x52, 0x6b, 0xff, 0xb9, 0xa2, 0x0c, 0xff, 0x6a, 0xa2, 0x04, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xde, 0x7b, + 0x80, 0xeb, 0x5e, 0x7b, 0x01, 0x6a, 0xd6, 0x01, 0x01, 0xe9, 0xa4, 0x34, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, + 0x00, 0x65, 0x30, 0x5c, 0xff, 0x99, 0xa4, 0x0c, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x0e, 0x7c, + 0x80, 0xee, 0x8e, 0x7b, 0xff, 0x6a, 0xdc, 0x0d, 0xff, 0x65, 0x32, 0x09, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x44, + 0x00, 0x65, 0x30, 0x44, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xd4, 0x5b, + 0x00, 0x6a, 0x54, 0x5b, 0xff, 0x52, 0xa2, 0x0c, - 0x01, 0x0c, 0x1e, 0x7c, - 0x04, 0x0c, 0x1e, 0x6c, + 0x01, 0x0c, 0x9e, 0x7b, + 0x04, 0x0c, 0x9e, 0x6b, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7a, 0x0c, 0xff, 0x8c, 0x10, 0x08, @@ -553,29 +489,29 @@ static unsigned char seqprog[] = { 0x00, 0x6c, 0xda, 0x24, 0xff, 0x65, 0xc8, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x3a, 0x5c, + 0x41, 0x6a, 0xba, 0x5b, 0xff, 0x90, 0xe2, 0x09, 0x20, 0x6a, 0xd0, 0x01, - 0x04, 0x35, 0x5c, 0x7c, + 0x04, 0x35, 0xdc, 0x7b, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0x58, 0x64, - 0x00, 0x65, 0x68, 0x44, + 0xdc, 0xee, 0xd8, 0x63, + 0x00, 0x65, 0xe8, 0x43, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x62, 0x7c, + 0x80, 0xee, 0xe2, 0x7b, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0x66, 0x64, + 0xd8, 0xee, 0xe6, 0x63, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0x6a, 0x6c, + 0x18, 0xee, 0xea, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x3a, 0x5c, + 0x41, 0x6a, 0xba, 0x5b, 0x20, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x35, 0x94, 0x6c, + 0x04, 0x35, 0x14, 0x6c, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, 0xff, 0x6c, 0x32, 0x09, @@ -586,14 +522,14 @@ static unsigned char seqprog[] = { 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, - 0x00, 0x65, 0x80, 0x64, + 0x00, 0x65, 0x00, 0x64, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x04, 0x35, 0x8c, 0x7b, - 0xa0, 0x6a, 0xa0, 0x5c, - 0x00, 0x65, 0xa2, 0x5c, - 0x00, 0x65, 0xa2, 0x5c, - 0x00, 0x65, 0xa2, 0x44, + 0x00, 0x65, 0x30, 0x5c, + 0x04, 0x35, 0x0c, 0x7b, + 0xa0, 0x6a, 0x20, 0x5c, + 0x00, 0x65, 0x22, 0x5c, + 0x00, 0x65, 0x22, 0x5c, + 0x00, 0x65, 0x22, 0x44, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, @@ -602,19 +538,19 @@ static unsigned char seqprog[] = { 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x0c, - 0x08, 0x94, 0xb0, 0x7c, + 0x08, 0x94, 0x30, 0x7c, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0xb4, 0x6c, + 0x08, 0x93, 0x34, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0xff, 0x40, 0x74, 0x09, 0xff, 0x90, 0x80, 0x08, 0xff, 0x6a, 0x72, 0x05, - 0xff, 0x40, 0xcc, 0x64, - 0xff, 0x3f, 0xc4, 0x64, + 0xff, 0x40, 0x4c, 0x64, + 0xff, 0x3f, 0x44, 0x64, 0xff, 0x6a, 0xca, 0x04, 0xff, 0x3f, 0x20, 0x09, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, + 0x00, 0xb9, 0xca, 0x5b, 0xff, 0xba, 0x7e, 0x0c, 0xff, 0x40, 0x20, 0x09, 0xff, 0xba, 0x80, 0x0c, @@ -622,20 +558,12 @@ static unsigned char seqprog[] = { 0xff, 0x90, 0x7e, 0x0c, }; -static int aic7xxx_patch13_func(struct aic7xxx_host *p); - -static int -aic7xxx_patch13_func(struct aic7xxx_host *p) -{ - return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); -} - static int aic7xxx_patch12_func(struct aic7xxx_host *p); static int aic7xxx_patch12_func(struct aic7xxx_host *p) { - return ((p->features & AHC_CMD_CHAN) == 0); + return ((p->features & AHC_WIDE) != 0); } static int aic7xxx_patch11_func(struct aic7xxx_host *p); @@ -643,7 +571,7 @@ static int aic7xxx_patch11_func(struct aic7xxx_host *p); static int aic7xxx_patch11_func(struct aic7xxx_host *p) { - return ((p->features & AHC_WIDE) != 0); + return ((p->features & AHC_ULTRA2) == 0); } static int aic7xxx_patch10_func(struct aic7xxx_host *p); @@ -651,7 +579,7 @@ static int aic7xxx_patch10_func(struct aic7xxx_host *p); static int aic7xxx_patch10_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA2) == 0); + return ((p->features & AHC_CMD_CHAN) == 0); } static int aic7xxx_patch9_func(struct aic7xxx_host *p); @@ -659,7 +587,7 @@ static int aic7xxx_patch9_func(struct aic7xxx_host *p); static int aic7xxx_patch9_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA) != 0); + return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); } static int aic7xxx_patch8_func(struct aic7xxx_host *p); @@ -667,7 +595,7 @@ static int aic7xxx_patch8_func(struct aic7xxx_host *p); static int aic7xxx_patch8_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA2) != 0); + return ((p->features & AHC_ULTRA) != 0); } static int aic7xxx_patch7_func(struct aic7xxx_host *p); @@ -675,7 +603,7 @@ static int aic7xxx_patch7_func(struct aic7xxx_host *p); static int aic7xxx_patch7_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_PAGESCBS) == 0); + return ((p->features & AHC_ULTRA2) != 0); } static int aic7xxx_patch6_func(struct aic7xxx_host *p); @@ -683,7 +611,7 @@ static int aic7xxx_patch6_func(struct aic7xxx_host *p); static int aic7xxx_patch6_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_PAGESCBS) != 0); + return ((p->flags & AHC_PAGESCBS) == 0); } static int aic7xxx_patch5_func(struct aic7xxx_host *p); @@ -691,7 +619,7 @@ static int aic7xxx_patch5_func(struct aic7xxx_host *p); static int aic7xxx_patch5_func(struct aic7xxx_host *p) { - return ((p->features & AHC_QUEUE_REGS) != 0); + return ((p->flags & AHC_PAGESCBS) != 0); } static int aic7xxx_patch4_func(struct aic7xxx_host *p); @@ -699,7 +627,7 @@ static int aic7xxx_patch4_func(struct aic7xxx_host *p); static int aic7xxx_patch4_func(struct aic7xxx_host *p) { - return ((p->features & AHC_TWIN) != 0); + return ((p->features & AHC_QUEUE_REGS) != 0); } static int aic7xxx_patch3_func(struct aic7xxx_host *p); @@ -707,7 +635,7 @@ static int aic7xxx_patch3_func(struct aic7xxx_host *p); static int aic7xxx_patch3_func(struct aic7xxx_host *p) { - return ((p->features & AHC_QUEUE_REGS) == 0); + return ((p->features & AHC_TWIN) != 0); } static int aic7xxx_patch2_func(struct aic7xxx_host *p); @@ -715,7 +643,7 @@ static int aic7xxx_patch2_func(struct aic7xxx_host *p); static int aic7xxx_patch2_func(struct aic7xxx_host *p) { - return ((p->features & AHC_CMD_CHAN) != 0); + return ((p->features & AHC_QUEUE_REGS) == 0); } static int aic7xxx_patch1_func(struct aic7xxx_host *p); @@ -723,7 +651,7 @@ static int aic7xxx_patch1_func(struct aic7xxx_host *p); static int aic7xxx_patch1_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_TARGETMODE) != 0); + return ((p->features & AHC_CMD_CHAN) != 0); } static int aic7xxx_patch0_func(struct aic7xxx_host *p); @@ -740,84 +668,78 @@ struct sequencer_patch { skip_instr :10, skip_patch :12; } sequencer_patches[] = { - { aic7xxx_patch1_func, 2, 1, 2 }, - { aic7xxx_patch0_func, 3, 1, 1 }, - { aic7xxx_patch2_func, 4, 2, 1 }, - { aic7xxx_patch3_func, 8, 1, 1 }, - { aic7xxx_patch3_func, 9, 1, 1 }, - { aic7xxx_patch4_func, 12, 4, 1 }, - { aic7xxx_patch5_func, 17, 3, 2 }, - { aic7xxx_patch0_func, 20, 4, 1 }, - { aic7xxx_patch6_func, 24, 1, 1 }, - { aic7xxx_patch7_func, 27, 1, 1 }, - { aic7xxx_patch2_func, 30, 1, 2 }, - { aic7xxx_patch0_func, 31, 3, 1 }, - { aic7xxx_patch4_func, 40, 4, 1 }, - { aic7xxx_patch8_func, 44, 3, 2 }, - { aic7xxx_patch0_func, 47, 3, 1 }, - { aic7xxx_patch9_func, 52, 7, 1 }, - { aic7xxx_patch4_func, 60, 3, 1 }, - { aic7xxx_patch8_func, 63, 2, 1 }, - { aic7xxx_patch1_func, 68, 60, 1 }, - { aic7xxx_patch8_func, 162, 1, 2 }, - { aic7xxx_patch0_func, 163, 2, 1 }, - { aic7xxx_patch2_func, 167, 3, 3 }, - { aic7xxx_patch8_func, 167, 2, 1 }, - { aic7xxx_patch0_func, 170, 2, 1 }, - { aic7xxx_patch8_func, 173, 1, 2 }, - { aic7xxx_patch0_func, 174, 1, 1 }, - { aic7xxx_patch2_func, 178, 1, 1 }, - { aic7xxx_patch2_func, 181, 3, 2 }, - { aic7xxx_patch0_func, 184, 5, 1 }, - { aic7xxx_patch2_func, 192, 2, 3 }, - { aic7xxx_patch8_func, 192, 1, 1 }, - { aic7xxx_patch0_func, 194, 3, 1 }, - { aic7xxx_patch10_func, 198, 1, 2 }, - { aic7xxx_patch0_func, 199, 1, 1 }, - { aic7xxx_patch8_func, 200, 7, 2 }, - { aic7xxx_patch0_func, 207, 1, 1 }, - { aic7xxx_patch2_func, 212, 14, 3 }, - { aic7xxx_patch10_func, 225, 1, 1 }, - { aic7xxx_patch0_func, 226, 9, 1 }, - { aic7xxx_patch8_func, 240, 2, 1 }, - { aic7xxx_patch8_func, 242, 1, 1 }, - { aic7xxx_patch10_func, 243, 6, 3 }, - { aic7xxx_patch2_func, 243, 2, 2 }, - { aic7xxx_patch0_func, 245, 4, 1 }, - { aic7xxx_patch8_func, 250, 1, 1 }, - { aic7xxx_patch8_func, 254, 19, 1 }, - { aic7xxx_patch2_func, 274, 3, 3 }, - { aic7xxx_patch10_func, 276, 1, 1 }, - { aic7xxx_patch0_func, 277, 5, 1 }, - { aic7xxx_patch10_func, 282, 1, 2 }, - { aic7xxx_patch0_func, 283, 9, 1 }, - { aic7xxx_patch11_func, 299, 1, 2 }, - { aic7xxx_patch0_func, 300, 1, 1 }, - { aic7xxx_patch5_func, 361, 1, 2 }, - { aic7xxx_patch0_func, 362, 1, 1 }, - { aic7xxx_patch3_func, 365, 1, 1 }, - { aic7xxx_patch2_func, 375, 3, 2 }, - { aic7xxx_patch0_func, 378, 5, 1 }, - { aic7xxx_patch11_func, 386, 1, 2 }, - { aic7xxx_patch0_func, 387, 1, 1 }, - { aic7xxx_patch6_func, 392, 1, 1 }, - { aic7xxx_patch8_func, 420, 1, 2 }, - { aic7xxx_patch0_func, 421, 5, 1 }, - { aic7xxx_patch1_func, 438, 3, 1 }, - { aic7xxx_patch10_func, 443, 11, 1 }, - { aic7xxx_patch2_func, 491, 7, 2 }, - { aic7xxx_patch0_func, 498, 8, 1 }, - { aic7xxx_patch2_func, 507, 4, 2 }, - { aic7xxx_patch0_func, 511, 6, 1 }, - { aic7xxx_patch2_func, 517, 4, 2 }, - { aic7xxx_patch0_func, 521, 3, 1 }, - { aic7xxx_patch12_func, 531, 10, 1 }, - { aic7xxx_patch2_func, 550, 17, 4 }, - { aic7xxx_patch13_func, 558, 4, 2 }, - { aic7xxx_patch0_func, 562, 2, 1 }, - { aic7xxx_patch0_func, 567, 33, 1 }, - { aic7xxx_patch12_func, 600, 4, 1 }, - { aic7xxx_patch6_func, 604, 2, 1 }, - { aic7xxx_patch6_func, 607, 9, 1 }, + { aic7xxx_patch1_func, 3, 2, 1 }, + { aic7xxx_patch2_func, 7, 1, 1 }, + { aic7xxx_patch2_func, 8, 1, 1 }, + { aic7xxx_patch3_func, 11, 4, 1 }, + { aic7xxx_patch4_func, 16, 3, 2 }, + { aic7xxx_patch0_func, 19, 4, 1 }, + { aic7xxx_patch5_func, 23, 1, 1 }, + { aic7xxx_patch6_func, 26, 1, 1 }, + { aic7xxx_patch1_func, 29, 1, 2 }, + { aic7xxx_patch0_func, 30, 3, 1 }, + { aic7xxx_patch3_func, 39, 4, 1 }, + { aic7xxx_patch7_func, 43, 3, 2 }, + { aic7xxx_patch0_func, 46, 3, 1 }, + { aic7xxx_patch8_func, 52, 7, 1 }, + { aic7xxx_patch3_func, 60, 3, 1 }, + { aic7xxx_patch7_func, 63, 2, 1 }, + { aic7xxx_patch7_func, 102, 1, 2 }, + { aic7xxx_patch0_func, 103, 2, 1 }, + { aic7xxx_patch7_func, 107, 2, 1 }, + { aic7xxx_patch9_func, 109, 1, 1 }, + { aic7xxx_patch10_func, 110, 2, 1 }, + { aic7xxx_patch7_func, 113, 1, 2 }, + { aic7xxx_patch0_func, 114, 1, 1 }, + { aic7xxx_patch1_func, 118, 1, 1 }, + { aic7xxx_patch1_func, 121, 3, 2 }, + { aic7xxx_patch0_func, 124, 5, 1 }, + { aic7xxx_patch1_func, 132, 2, 3 }, + { aic7xxx_patch7_func, 132, 1, 1 }, + { aic7xxx_patch0_func, 134, 3, 1 }, + { aic7xxx_patch11_func, 138, 1, 2 }, + { aic7xxx_patch0_func, 139, 1, 1 }, + { aic7xxx_patch7_func, 140, 7, 2 }, + { aic7xxx_patch0_func, 147, 1, 1 }, + { aic7xxx_patch1_func, 152, 14, 3 }, + { aic7xxx_patch11_func, 165, 1, 1 }, + { aic7xxx_patch0_func, 166, 9, 1 }, + { aic7xxx_patch7_func, 180, 2, 1 }, + { aic7xxx_patch7_func, 182, 1, 1 }, + { aic7xxx_patch11_func, 183, 6, 3 }, + { aic7xxx_patch1_func, 183, 2, 2 }, + { aic7xxx_patch0_func, 185, 4, 1 }, + { aic7xxx_patch7_func, 190, 1, 1 }, + { aic7xxx_patch7_func, 194, 20, 1 }, + { aic7xxx_patch1_func, 215, 3, 3 }, + { aic7xxx_patch11_func, 217, 1, 1 }, + { aic7xxx_patch0_func, 218, 5, 1 }, + { aic7xxx_patch11_func, 223, 1, 2 }, + { aic7xxx_patch0_func, 224, 9, 1 }, + { aic7xxx_patch12_func, 240, 1, 2 }, + { aic7xxx_patch0_func, 241, 1, 1 }, + { aic7xxx_patch4_func, 302, 1, 2 }, + { aic7xxx_patch0_func, 303, 1, 1 }, + { aic7xxx_patch2_func, 306, 1, 1 }, + { aic7xxx_patch1_func, 316, 3, 2 }, + { aic7xxx_patch0_func, 319, 5, 1 }, + { aic7xxx_patch12_func, 327, 1, 2 }, + { aic7xxx_patch0_func, 328, 1, 1 }, + { aic7xxx_patch5_func, 333, 1, 1 }, + { aic7xxx_patch11_func, 375, 15, 1 }, + { aic7xxx_patch1_func, 427, 7, 2 }, + { aic7xxx_patch0_func, 434, 8, 1 }, + { aic7xxx_patch1_func, 443, 4, 2 }, + { aic7xxx_patch0_func, 447, 6, 1 }, + { aic7xxx_patch1_func, 453, 4, 2 }, + { aic7xxx_patch0_func, 457, 3, 1 }, + { aic7xxx_patch10_func, 467, 10, 1 }, + { aic7xxx_patch1_func, 486, 17, 4 }, + { aic7xxx_patch9_func, 494, 4, 2 }, + { aic7xxx_patch0_func, 498, 2, 1 }, + { aic7xxx_patch0_func, 503, 33, 1 }, + { aic7xxx_patch10_func, 536, 4, 1 }, + { aic7xxx_patch5_func, 540, 2, 1 }, + { aic7xxx_patch5_func, 543, 9, 1 }, }; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 6ae59e9dd..b7d3b9cc1 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -176,6 +176,8 @@ extern void scsi_old_times_out(Scsi_Cmnd * SCpnt); */ void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) { blk_init_queue(&SDpnt->request_queue, scsi_request_fn); + blk_queue_headactive(&SDpnt->request_queue, 0); + SDpnt->request_queue.queuedata = (void *) SDpnt; } #ifdef MODULE @@ -2567,7 +2569,6 @@ static void scsi_dump_status(int level) } } } - printk("wait_for_request = %p\n", &wait_for_request); #endif /* CONFIG_SCSI_LOGGING */ /* } */ } #endif /* CONFIG_PROC_FS */ @@ -2728,8 +2729,6 @@ Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt) scsi_build_commandblocks(SDpnt); scsi_initialize_queue(SDpnt, SHpnt); - blk_queue_headactive(&SDpnt->request_queue, 0); - SDpnt->request_queue.queuedata = (void *) SDpnt; SDpnt->online = TRUE; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d4a1fd5fb..042493e3e 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -855,10 +855,9 @@ void scsi_request_fn(request_queue_t * q) * if the device itself is blocked, or if the host is fully * occupied. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } + /* * To start with, we keep looping until the queue is empty, or until * the host is no longer able to accept any more requests. @@ -869,10 +868,8 @@ void scsi_request_fn(request_queue_t * q) * released the lock and grabbed it again, so each time * we need to check to see if the queue is plugged or not. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } /* * If the device cannot accept another request, then quit. @@ -1019,8 +1016,7 @@ void scsi_request_fn(request_queue_t * q) * We have copied the data out of the request block - it is now in * a field in SCpnt. Release the request block. */ - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + blkdev_release_request(req); } /* * Now it is finally safe to release the lock. We are diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 56ef67cb2..152f8afc3 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -271,8 +271,8 @@ void scan_scsis(struct Scsi_Host *shpnt, if (SDpnt) { memset(SDpnt, 0, sizeof(Scsi_Device)); /* - * Register the queue for the device. All I/O requests will come - * in through here. We also need to register a pointer to + * Register the queue for the device. All I/O requests will + * come in through here. We also need to register a pointer to * ourselves, since the queue handler won't know what device * the queue actually represents. We could look it up, but it * is pointless work. diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 71b93e379..4bfdc59f4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1230,9 +1230,9 @@ static int sg_attach(Scsi_Device * scsidp) sdp->detached = 0; sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k); - sdp->de = devfs_register (scsidp->de, "generic", 7, DEVFS_FL_NONE, + sdp->de = devfs_register (scsidp->de, "generic", DEVFS_FL_DEFAULT, SCSI_GENERIC_MAJOR, k, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, &sg_fops, NULL); sg_template.nr_dev++; sg_dev_arr[k] = sdp; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index e568c6d1b..cd3f7a488 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -777,9 +777,9 @@ void sr_finish() sprintf(name, "sr%d", i); strcpy(scsi_CDs[i].cdi.name, name); scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", 2, + devfs_register (scsi_CDs[i].device->de, "cd", DEVFS_FL_DEFAULT, MAJOR_NR, i, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); register_cdrom(&scsi_CDs[i].cdi); } @@ -857,7 +857,6 @@ void cleanup_module(void) } blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); blk_size[MAJOR_NR] = NULL; read_ahead[MAJOR_NR] = 0; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index a9d08c165..bdc3e8002 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3509,17 +3509,17 @@ static int st_attach(Scsi_Device * SDp) /* Rewind entry */ sprintf (name, "mt%s", formats[mode]); tpnt->de_r[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5), S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &st_fops, NULL); + &st_fops, NULL); /* No-rewind entry */ sprintf (name, "mt%sn", formats[mode]); tpnt->de_n[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5) + 128, S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &st_fops, NULL); + &st_fops, NULL); } devfs_register_tape (tpnt->de_r[0]); tpnt->device = SDp; diff --git a/drivers/sgi/char/shmiq.c b/drivers/sgi/char/shmiq.c index 18009a65a..5df43bf8b 100644 --- a/drivers/sgi/char/shmiq.c +++ b/drivers/sgi/char/shmiq.c @@ -457,11 +457,11 @@ shmiq_init (void) { printk ("SHMIQ setup\n"); devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops); - devfs_register (NULL, "shmiq", 0, DEVFS_FL_DEFAULT, - SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + devfs_register (NULL, "shmiq", DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &shmiq_fops, NULL); devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT, SHMIQ_MAJOR, 1, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &shmiq_fops, NULL); } diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index da0cfe51c..9ec821b91 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -175,9 +175,9 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f sprintf (name_buf, "%s", name); else sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); - s->de = devfs_register (devfs_handle, name_buf, 0, + s->de = devfs_register (devfs_handle, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, - S_IFCHR | mode, 0, 0, fops, NULL); + S_IFCHR | mode, fops, NULL); return r; } diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 5018ad1e7..aa7b82280 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -565,9 +565,9 @@ static void soundcard_register_devfs (int do_register) for (j = 0; j < num || j == 0; j++) { soundcard_make_name (name_buf, dev_list[i].name, j); if (do_register) - devfs_register (NULL, name_buf, 0, DEVFS_FL_NONE, + devfs_register (NULL, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), - S_IFCHR | dev_list[i].mode, 0, 0, + S_IFCHR | dev_list[i].mode, &oss_sound_fops, NULL); else { devfs_handle_t de; diff --git a/drivers/telephony/ixj.h b/drivers/telephony/ixj.h index d88013c2f..3559cc5c0 100644 --- a/drivers/telephony/ixj.h +++ b/drivers/telephony/ixj.h @@ -879,7 +879,6 @@ typedef struct { typedef struct { struct phone_device p; - struct semaphore mutex; unsigned int board; unsigned int DSPbase; unsigned int XILINXbase; diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 042f742da..5032a9b75 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -70,7 +70,10 @@ comment 'USB HID' dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB fi dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB - dep_tristate ' Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB + dep_tristate ' I-Force joysticks/wheels' CONFIG_INPUT_IFORCE_USB $CONFIG_USB + if [ "$CONFIG_INPUT_IFORCE_USB" != "n" ]; then + define_tristate CONFIG_INPUT_IFORCE $CONFIG_INPUT_IFORCE_USB + fi dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ec8687ae6..7d95c0c98 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -61,7 +61,7 @@ obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o obj-$(CONFIG_USB_HID) += hid.o input.o obj-$(CONFIG_USB_KBD) += usbkbd.o input.o obj-$(CONFIG_USB_WACOM) += wacom.o input.o -obj-$(CONFIG_USB_WMFORCE) += wmforce.o input.o +obj-$(CONFIG_INPUT_IFORCE) += iforce.o input.o obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o input.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o input.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o input.o diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index 235385515..d6cedee9b 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -1044,7 +1044,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg) kfree (buf); return -EFAULT; } else - memset (arg, size, 0); + memset (arg, 0, size); } /* ioctl to device */ diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c index 3e21f6cb5..7eca5e304 100644 --- a/drivers/usb/evdev.c +++ b/drivers/usb/evdev.c @@ -1,5 +1,5 @@ /* - * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $ + * $Id: evdev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -39,7 +39,7 @@ #include <linux/input.h> struct evdev { - int used; + int exist; int open; int minor; struct input_handle handle; @@ -99,13 +99,14 @@ static int evdev_release(struct inode * inode, struct file * file) listptr = &((*listptr)->next); *listptr = (*listptr)->next; - if (!--list->evdev->open) - input_close_device(&list->evdev->handle); - - if (!--list->evdev->used) { - input_unregister_minor(list->evdev->devfs); - evdev_table[list->evdev->minor] = NULL; - kfree(list->evdev); + if (!--list->evdev->open) { + if (list->evdev->exist) { + input_close_device(&list->evdev->handle); + } else { + input_unregister_minor(list->evdev->devfs); + evdev_table[list->evdev->minor] = NULL; + kfree(list->evdev); + } } kfree(list); @@ -121,9 +122,8 @@ static int evdev_open(struct inode * inode, struct file * file) if (i > EVDEV_MINORS || !evdev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) { + if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) return -ENOMEM; - } memset(list, 0, sizeof(struct evdev_list)); list->evdev = evdev_table[i]; @@ -132,10 +132,9 @@ static int evdev_open(struct inode * inode, struct file * file) file->private_data = list; - list->evdev->used++; - if (!list->evdev->open++) - input_open_device(&list->evdev->handle); + if (list->evdev->exist) + input_open_device(&list->evdev->handle); return 0; } @@ -303,7 +302,7 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct evdev->handle.handler = handler; evdev->handle.private = evdev; - evdev->used = 1; + evdev->exist = 1; evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); @@ -316,10 +315,11 @@ static void evdev_disconnect(struct input_handle *handle) { struct evdev *evdev = handle->private; - if (evdev->open) - input_close_device(handle); + evdev->exist = 0; - if (!--evdev->used) { + if (evdev->open) { + input_close_device(handle); + } else { input_unregister_minor(evdev->devfs); evdev_table[evdev->minor] = NULL; kfree(evdev); diff --git a/drivers/usb/iforce.c b/drivers/usb/iforce.c new file mode 100644 index 000000000..a61b7801f --- /dev/null +++ b/drivers/usb/iforce.c @@ -0,0 +1,335 @@ +/* + * $Id: iforce.c,v 1.7 2000/06/04 14:03:36 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * USB/RS232 I-Force joysticks and wheels. + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/serio.h> +#include <linux/config.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 + +#define IFORCE_MAX_LENGTH 16 + +#if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE) +#define IFORCE_232 +#endif +#if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE) +#define IFORCE_USB +#endif + +struct iforce { + signed char data[IFORCE_MAX_LENGTH]; + struct input_dev dev; + struct urb irq; + int open; + int idx, pkt, len, id; + unsigned char csum; +}; + +static struct { + __s32 x; + __s32 y; +} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *iforce_name = "I-Force joystick/wheel"; + +static void iforce_process_packet(struct input_dev *dev, unsigned char id, int idx, unsigned char *data) +{ + switch (id) { + + case 1: /* joystick position data */ + case 3: /* wheel position data */ + + if (id == 1) { + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); + input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); + } else { + input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_GAS, 255 - data[2]); + input_report_abs(dev, ABS_BRAKE, 255 - data[3]); + } + + input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); + + input_report_key(dev, BTN_TRIGGER, data[5] & 0x01); + input_report_key(dev, BTN_TOP, data[5] & 0x02); + input_report_key(dev, BTN_THUMB, data[5] & 0x04); + input_report_key(dev, BTN_TOP2, data[5] & 0x08); + input_report_key(dev, BTN_BASE, data[5] & 0x10); + input_report_key(dev, BTN_BASE2, data[5] & 0x20); + input_report_key(dev, BTN_BASE3, data[5] & 0x40); + input_report_key(dev, BTN_BASE4, data[5] & 0x80); + input_report_key(dev, BTN_BASE5, data[6] & 0x01); + input_report_key(dev, BTN_A, data[6] & 0x02); + input_report_key(dev, BTN_B, data[6] & 0x04); + input_report_key(dev, BTN_C, data[6] & 0x08); + break; + + case 2: /* force feedback effect status */ + break; + } +} + + +static int iforce_open(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + + if (dev->idbus == BUS_USB && !iforce->open++) + if (usb_submit_urb(&iforce->irq)) + return -EIO; + + return 0; +} + +static void iforce_close(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + + if (dev->idbus == BUS_USB && !--iforce->open) + usb_unlink_urb(&iforce->irq); +} + +static void iforce_input_setup(struct iforce *iforce) +{ + int i; + + iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + iforce->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | + BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); + iforce->dev.keybit[LONG(BTN_GAMEPAD)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C); + iforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) + | BIT(ABS_WHEEL) | BIT(ABS_GAS) | BIT(ABS_BRAKE); + + for (i = ABS_X; i <= ABS_Y; i++) { + iforce->dev.absmax[i] = 1920; + iforce->dev.absmin[i] = -1920; + iforce->dev.absflat[i] = 128; + iforce->dev.absfuzz[i] = 16; + } + + for (i = ABS_THROTTLE; i <= ABS_RUDDER; i++) { + iforce->dev.absmax[i] = 255; + iforce->dev.absmin[i] = 0; + } + + for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { + iforce->dev.absmax[i] = 1; + iforce->dev.absmin[i] = -1; + } + + iforce->dev.private = iforce; + iforce->dev.open = iforce_open; + iforce->dev.close = iforce_close; + + input_register_device(&iforce->dev); +} + +#ifdef IFORCE_USB + +static void iforce_usb_irq(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce_process_packet(&iforce->dev, iforce->data[0], 8, iforce->data + 1); +} + +static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct iforce *iforce; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || + dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) + return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return NULL; + memset(iforce, 0, sizeof(struct iforce)); + + iforce->dev.name = iforce_name; + iforce->dev.idbus = BUS_USB; + iforce->dev.idvendor = dev->descriptor.idVendor; + iforce->dev.idproduct = dev->descriptor.idProduct; + iforce->dev.idversion = dev->descriptor.bcdDevice; + + FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + iforce->data, 8, iforce_usb_irq, iforce, endpoint->bInterval); + + iforce_input_setup(iforce); + + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + iforce->dev.number, iforce_name, dev->bus->busnum, dev->devnum, ifnum); + + return iforce; +} + +static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) +{ + struct iforce *iforce = ptr; + usb_unlink_urb(&iforce->irq); + input_unregister_device(&iforce->dev); + kfree(iforce); +} + +static struct usb_driver iforce_usb_driver = { + name: "iforce", + probe: iforce_usb_probe, + disconnect: iforce_usb_disconnect, +}; + +#endif + +#ifdef IFORCE_232 + +static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct iforce* iforce = serio->private; + + if (!iforce->pkt) { + if (data != 0x2b) { + return; + } + iforce->pkt = 1; + return; + } + + if (!iforce->id) { + if (data > 3) { + iforce->pkt = 0; + return; + } + iforce->id = data; + return; + } + + if (!iforce->len) { + if (data > IFORCE_MAX_LENGTH) { + iforce->pkt = 0; + iforce->id = 0; + return; + } + iforce->len = data; + return; + } + + if (iforce->idx < iforce->len) { + iforce->csum += iforce->data[iforce->idx++] = data; + return; + } + + if (iforce->idx == iforce->len) { + iforce_process_packet(&iforce->dev, iforce->id, iforce->idx, iforce->data); + iforce->pkt = 0; + iforce->id = 0; + iforce->len = 0; + iforce->idx = 0; + iforce->csum = 0; + } +} + +static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +{ + struct iforce *iforce; + + if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) + return; + + if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return; + memset(iforce, 0, sizeof(struct iforce)); + + iforce->dev.name = iforce_name; + iforce->dev.idbus = BUS_RS232; + iforce->dev.idvendor = SERIO_IFORCE; + iforce->dev.idproduct = 0x0001; + iforce->dev.idversion = 0x0100; + + serio->private = iforce; + + if (serio_open(serio, dev)) { + kfree(iforce); + return; + } + + iforce_input_setup(iforce); + + printk(KERN_INFO "input%d: %s on serio%d\n", + iforce->dev.number, iforce_name, serio->number); +} + +static void iforce_serio_disconnect(struct serio *serio) +{ + struct iforce* iforce = serio->private; + input_unregister_device(&iforce->dev); + serio_close(serio); + kfree(iforce); +} + +static struct serio_dev iforce_serio_dev = { + interrupt: iforce_serio_irq, + connect: iforce_serio_connect, + disconnect: iforce_serio_disconnect, +}; + +#endif + +static int __init iforce_init(void) +{ +#ifdef IFORCE_USB + usb_register(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_register_device(&iforce_serio_dev); +#endif + return 0; +} + +static void __exit iforce_exit(void) +{ +#ifdef IFORCE_USB + usb_deregister(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_unregister_device(&iforce_serio_dev); +#endif +} + +module_init(iforce_init); +module_exit(iforce_exit); diff --git a/drivers/usb/input.c b/drivers/usb/input.c index 66a3d2911..4fb2985af 100644 --- a/drivers/usb/input.c +++ b/drivers/usb/input.c @@ -386,8 +386,8 @@ devfs_handle_t input_register_minor(char *name, int minor, int minor_base) { char devfs_name[16]; sprintf(devfs_name, name, minor); - return devfs_register(input_devfs_handle, devfs_name, 0, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, - S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, &input_fops, NULL); + return devfs_register(input_devfs_handle, devfs_name, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, + S_IFCHR | S_IRUGO | S_IWUSR, &input_fops, NULL); } void input_unregister_minor(devfs_handle_t handle) diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c index 0f0bb2773..5349d515c 100644 --- a/drivers/usb/joydev.c +++ b/drivers/usb/joydev.c @@ -1,7 +1,7 @@ /* - * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $ + * $Id: joydev.c,v 1.11 2000/06/23 09:23:00 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke * * Joystick device driver for the input driver suite. @@ -50,7 +50,7 @@ #define JOYDEV_BUFFER_SIZE 64 struct joydev { - int used; + int exist; int open; int minor; struct input_handle handle; @@ -66,6 +66,7 @@ struct joydev { __u16 keypam[KEY_MAX - BTN_MISC]; __u8 absmap[ABS_MAX]; __u8 abspam[ABS_MAX]; + __s16 abs[ABS_MAX]; }; struct joydev_list { @@ -121,7 +122,9 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne case EV_ABS: event.type = JS_EVENT_AXIS; event.number = joydev->absmap[code]; - event.value = joydev_correct(value, &joydev->corr[event.number]); + event.value = joydev_correct(value, joydev->corr + event.number); + if (event.value == joydev->abs[event.number]) return; + joydev->abs[event.number] = event.value; break; default: @@ -165,13 +168,14 @@ static int joydev_release(struct inode * inode, struct file * file) listptr = &((*listptr)->next); *listptr = (*listptr)->next; - if (!--list->joydev->open) - input_close_device(&list->joydev->handle); - - if (!--list->joydev->used) { - input_unregister_minor(list->joydev->devfs); - joydev_table[list->joydev->minor] = NULL; - kfree(list->joydev); + if (!--list->joydev->open) { + if (list->joydev->exist) { + input_close_device(&list->joydev->handle); + } else { + input_unregister_minor(list->joydev->devfs); + joydev_table[list->joydev->minor] = NULL; + kfree(list->joydev); + } } kfree(list); @@ -187,9 +191,8 @@ static int joydev_open(struct inode *inode, struct file *file) if (i > JOYDEV_MINORS || !joydev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) { + if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) return -ENOMEM; - } memset(list, 0, sizeof(struct joydev_list)); list->joydev = joydev_table[i]; @@ -198,10 +201,9 @@ static int joydev_open(struct inode *inode, struct file *file) file->private_data = list; - list->joydev->used++; - if (!list->joydev->open++) - input_open_device(&list->joydev->handle); + if (list->joydev->exist) + input_open_device(&list->joydev->handle); return 0; } @@ -228,8 +230,8 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); - data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; - data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y; + data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; + data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) return -EFAULT; @@ -274,13 +276,12 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p if (list->startup < joydev->nkey) { event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - event.value = !!test_bit(joydev->keypam[list->startup], input->key); event.number = list->startup; + event.value = !!test_bit(joydev->keypam[event.number], input->key); } else { event.type = JS_EVENT_AXIS | JS_EVENT_INIT; - event.value = joydev_correct(input->abs[joydev->abspam[list->startup - joydev->nkey]], - &joydev->corr[list->startup - joydev->nkey]); event.number = list->startup - joydev->nkey; + event.value = joydev->abs[event.number]; } if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) @@ -407,7 +408,7 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev->handle.handler = handler; joydev->handle.private = joydev; - joydev->used = 1; + joydev->exist = 1; for (i = 0; i < ABS_MAX; i++) if (test_bit(i, dev->absbit)) { @@ -442,6 +443,8 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + + joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); } joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); @@ -455,10 +458,11 @@ static void joydev_disconnect(struct input_handle *handle) { struct joydev *joydev = handle->private; - if (joydev->open) - input_close_device(handle); + joydev->exist = 0; - if (!--joydev->used) { + if (joydev->open) { + input_close_device(handle); + } else { input_unregister_minor(joydev->devfs); joydev_table[joydev->minor] = NULL; kfree(joydev); diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c index 399d2a0c0..5c871a88d 100644 --- a/drivers/usb/mousedev.c +++ b/drivers/usb/mousedev.c @@ -1,5 +1,5 @@ /* - * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $ + * $Id: mousedev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -47,7 +47,7 @@ #endif struct mousedev { - int used; + int exist; int open; int minor; wait_queue_head_t wait; @@ -172,22 +172,30 @@ static int mousedev_release(struct inode * inode, struct file * file) struct input_handle *handle = mousedev_handler.handle; while (handle) { struct mousedev *mousedev = handle->private; - if (!mousedev->open) - input_close_device(handle); + if (!mousedev->open) { + if (mousedev->exist) { + input_close_device(&mousedev->handle); + } else { + input_unregister_minor(mousedev->devfs); + mousedev_table[mousedev->minor] = NULL; + kfree(mousedev); + } + } handle = handle->hnext; } } else { - if (!mousedev_mix.open) - input_close_device(&list->mousedev->handle); + if (!mousedev_mix.open) { + if (list->mousedev->exist) { + input_close_device(&list->mousedev->handle); + } else { + input_unregister_minor(list->mousedev->devfs); + mousedev_table[list->mousedev->minor] = NULL; + kfree(list->mousedev); + } + } } } - if (!--list->mousedev->used) { - input_unregister_minor(list->mousedev->devfs); - mousedev_table[list->mousedev->minor] = NULL; - kfree(list->mousedev); - } - kfree(list); return 0; @@ -210,20 +218,20 @@ static int mousedev_open(struct inode * inode, struct file * file) mousedev_table[i]->list = list; file->private_data = list; - list->mousedev->used++; - if (!list->mousedev->open++) { if (list->mousedev->minor == MOUSEDEV_MIX) { struct input_handle *handle = mousedev_handler.handle; while (handle) { struct mousedev *mousedev = handle->private; if (!mousedev->open) - input_open_device(handle); + if (mousedev->exist) + input_open_device(handle); handle = handle->hnext; } } else { - if (!mousedev_mix.open) - input_open_device(&list->mousedev->handle); + if (!mousedev_mix.open) + if (list->mousedev->exist) + input_open_device(&list->mousedev->handle); } } @@ -402,7 +410,7 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru memset(mousedev, 0, sizeof(struct mousedev)); init_waitqueue_head(&mousedev->wait); - mousedev->used = 1; + mousedev->exist = 1; mousedev->minor = minor; mousedev_table[minor] = mousedev; @@ -424,10 +432,13 @@ static void mousedev_disconnect(struct input_handle *handle) { struct mousedev *mousedev = handle->private; - if (mousedev->open || mousedev_mix.open) - input_close_device(handle); + mousedev->exist = 0; - if (!--mousedev->used) { + if (mousedev->open) { + input_close_device(handle); + } else { + if (mousedev_mix.open) + input_close_device(handle); input_unregister_minor(mousedev->devfs); mousedev_table[mousedev->minor] = NULL; kfree(mousedev); @@ -449,7 +460,7 @@ static int __init mousedev_init(void) memset(&mousedev_mix, 0, sizeof(struct mousedev)); init_waitqueue_head(&mousedev_mix.wait); mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.used = 1; + mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 80b82bba2..8c588f79e 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,11 +1,37 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** ** -** Distribute under GPL version 2 or later. +** ChangeLog: +** .... Most of the time spend reading sources & docs. +** v0.2.x First official release for the Linux kernel. +** v0.3.0 Beutified and structured, some bugs fixed. +** v0.3.x URBifying bulk requests and bugfixing. First relatively +** stable release. Still can touch device's registers only +** from top-halves. +** v0.4.0 Control messages remained unurbified are now URBs. +** Now we can touch the HW at any time. */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> @@ -16,7 +42,7 @@ #include <linux/usb.h> -static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.4.0 2000/06/15 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; #define PEGASUS_MTU 1500 @@ -24,17 +50,39 @@ static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko #define SROM_WRITE 0x01 #define SROM_READ 0x02 #define PEGASUS_TX_TIMEOUT (HZ*5) +#define PEGASUS_CTRL_TIMEOUT 1000 #define PEGASUS_RESET 1 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) +enum pegasus_registers { + EthCtrl0 = 0, + EthCtrl1 = 1, + EthCtrl2 = 2, + EthID = 0x10, + EpromOffset = 0x20, + EpromData = 0x21, /* 0x21 low, 0x22 high byte */ + EpromCtrl = 0x23, + PhyAddr = 0x25, + PhyData = 0x26, /* 0x26 low, 0x27 high byte */ + PhyCtrl = 0x28, + UsbStst = 0x2a, + EthTxStat0 = 0x2b, + EthTxStat1 = 0x2c, + EthRxStat = 0x2d, + Gpio0 = 0x7e, + Gpio1 = 0x7f, +}; + + struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; int flags; - spinlock_t pegasus_lock; - struct urb rx_urb, tx_urb, intr_urb; + spinlock_t pegasus_lock, ctrl_lock; + struct urb rx_urb, tx_urb, intr_urb, ctrl_urb; + devrequest dr; unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(intr_buff[8]); @@ -64,7 +112,7 @@ static struct usb_eth_dev usb_dev_id[] = { {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, - {"D-Link DU-10", 0x07b8, 0xabc1, NULL}, + {"D-Link DU-E10", 0x07b8, 0xabc1, NULL}, {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, {"Linksys USB100TX", 0x066b, 0x2203, NULL}, {"Linksys USB100TX", 0x066b, 0x2204, NULL}, @@ -78,23 +126,120 @@ static struct usb_eth_dev usb_dev_id[] = { }; -#define pegasus_get_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ); -#define pegasus_set_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); -#define pegasus_set_register(dev, indx, value) \ - { __u8 __data = value; \ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);} + +static void pegasus_ctrl_end( urb_t *urb ) +{ + if ( urb->status ) + warn("ctrl_urb end status %d", urb->status); +} -static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) +static int pegasus_ctrl_timeout( urb_t *ctrl_urb ) +{ + int timeout=0; + + while ( ctrl_urb->status == -EINPROGRESS ) { + if ( timeout++ < PEGASUS_CTRL_TIMEOUT ) { + udelay(100); + continue; + } + err("ctrl urb busy %d", ctrl_urb->status); + usb_unlink_urb( ctrl_urb ); + return ctrl_urb->status; + } + return 0; +} + + +static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0xc0; + pegasus->dr.request = 0xf0; + pegasus->dr.value = 0x0; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + data, size, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRLs %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_set_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0x40; + pegasus->dr.request = 0xf1; + pegasus->dr.value = 0x0; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + data, size, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRL %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_set_register( struct pegasus *pegasus, __u16 indx,__u8 data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0x40; + pegasus->dr.request = 0xf1; + pegasus->dr.value = data; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + &data, 1, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRL %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_read_phy_word(struct pegasus *pegasus, __u8 index, __u16 *regdata) { int i; __u8 data[4] = { 1, 0, 0, 0x40 + index }; - pegasus_set_registers(dev, 0x25, 4, data); + pegasus_set_registers(pegasus, PhyAddr, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x26, 3, data); + pegasus_get_registers(pegasus, PhyData, 3, data); if (data[2] & 0x80) { *regdata = *(__u16 *)(data); return 0; @@ -107,14 +252,14 @@ static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regd } -static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) +static int pegasus_write_phy_word(struct pegasus *pegasus, __u8 index, __u16 regdata) { int i; __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index }; - pegasus_set_registers(dev, 0x25, 4, data); + pegasus_set_registers(pegasus, PhyAddr, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x28, 1, data); + pegasus_get_registers(pegasus, PhyCtrl, 1, data); if (data[0] & 0x80) return 0; udelay(100); @@ -125,51 +270,51 @@ static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regd } -static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) +static int pegasus_rw_eprom_word(struct pegasus *pegasus, __u8 index, __u16 *retdata, __u8 direction) { int i; __u8 data[4] = { index, 0, 0, direction }; - pegasus_set_registers(dev, 0x20, 4, data); + pegasus_set_registers(pegasus, EpromOffset, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x23, 1, data); + pegasus_get_registers(pegasus, EpromCtrl, 1, data); if (data[0] & 4) { - pegasus_get_registers(dev, 0x21, 2, data); + pegasus_get_registers(pegasus, EpromData, 2, data); *retdata = *(__u16 *)data; return 0; } } - warn("pegasus_rw_srom_word() failed"); + warn("pegasus_rw_eprom_word() failed"); return 1; } -static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) +static int pegasus_get_node_id(struct pegasus *pegasus, __u8 *id) { int i; for (i = 0; i < 3; i++) - if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ)) + if (pegasus_rw_eprom_word(pegasus,i,(__u16 *)&id[i*2],SROM_READ)) return 1; return 0; } -static int pegasus_reset_mac(struct usb_device *dev) +static int pegasus_reset_mac(struct pegasus *pegasus) { __u8 data = 0x8; int i; - pegasus_set_register(dev, 1, data); + pegasus_set_register(pegasus, EthCtrl1, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 1, 1, &data); + pegasus_get_registers(pegasus, EthCtrl1, 1, &data); if (~data & 0x08) { if (loopback & 1) return 0; if (loopback & 2) - pegasus_write_phy_word(dev, 0, 0x4000); - pegasus_set_register(dev, 0x7e, 0x24); - pegasus_set_register(dev, 0x7e, 0x27); + pegasus_write_phy_word(pegasus, 0, 0x4000); + pegasus_set_register(pegasus, Gpio0, 0x24); + pegasus_set_register(pegasus, Gpio0, 0x27); return 0; } } @@ -183,22 +328,22 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) __u16 partmedia, temp; __u8 node_id[6]; __u8 data[4]; + struct pegasus *pegasus = dev->priv; - if (pegasus_get_node_id(usb, node_id)) + if (pegasus_get_node_id(pegasus, node_id)) return 1; - pegasus_set_registers(usb, 0x10, 6, node_id); + pegasus_set_registers(pegasus, EthID, 6, node_id); memcpy(dev->dev_addr, node_id, 6); - if (pegasus_read_phy_word(usb, 1, &temp)) + if (pegasus_read_phy_word(pegasus, 1, &temp)) return 2; if ((~temp & 4) && !loopback) { - warn("%s: link NOT established (0x%x), check the cable.", + warn("%s: link NOT established (0x%x) - check the cable.", dev->name, temp); - /* return 3; FIXME */ } - if (pegasus_read_phy_word(usb, 5, &partmedia)) + if (pegasus_read_phy_word(pegasus, 5, &partmedia)) return 4; if ((partmedia & 0x1f) != 1) { @@ -210,7 +355,7 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); data[2] = (loopback & 1) ? 0x09 : 0x01; - pegasus_set_registers(usb, 0, 3, data); + pegasus_set_registers(pegasus, EthCtrl0, 3, data); return 0; } @@ -281,24 +426,26 @@ static void pegasus_write_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; - spin_lock(&pegasus->pegasus_lock); if (urb->status) info("%s: TX status %d", pegasus->net->name, urb->status); +#if 1 /* Should be fixed */ + if (urb->status == -ETIMEDOUT) + pegasus_reset_mac(pegasus); +#endif netif_wake_queue(pegasus->net); - - spin_unlock(&pegasus->pegasus_lock); } static void pegasus_tx_timeout(struct net_device *net) { struct pegasus *pegasus = net->priv; - warn("%s: Tx timed out. Reseting...", net->name); + usb_unlink_urb(&pegasus->tx_urb); + warn("%s: Tx timed out. Reseting...", net->name); + pegasus_reset_mac( pegasus ); pegasus->stats.tx_errors++; net->trans_start = jiffies; - pegasus->flags |= PEGASUS_RESET; netif_wake_queue(net); } @@ -372,6 +519,8 @@ static int pegasus_close(struct net_device *net) netif_stop_queue(net); + if ( pegasus->ctrl_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->ctrl_urb); if ( pegasus->rx_urb.status == -EINPROGRESS ) usb_unlink_urb(&pegasus->rx_urb); if ( pegasus->tx_urb.status == -EINPROGRESS ) @@ -394,12 +543,12 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) case SIOCDEVPRIVATE: data[0] = 1; case SIOCDEVPRIVATE+1: - pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + pegasus_read_phy_word(pegasus, data[1] & 0x1f, &data[3]); return 0; case SIOCDEVPRIVATE+2: if (!capable(CAP_NET_ADMIN)) return -EPERM; - pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + pegasus_write_phy_word(pegasus, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -410,19 +559,23 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) static void pegasus_set_rx_mode(struct net_device *net) { struct pegasus *pegasus = net->priv; + __u8 tmp; netif_stop_queue(net); if (net->flags & IFF_PROMISC) { info("%s: Promiscuous mode enabled", net->name); -/* pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */ + pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); + pegasus_set_register(pegasus, EthCtrl2, tmp | 4); } else if ((net->mc_count > multicast_filter_limit) || (net->flags & IFF_ALLMULTI)) { - pegasus_set_register(pegasus->usb, 0, 0xfa); - pegasus_set_register(pegasus->usb, 2, 0); + pegasus_set_register(pegasus, EthCtrl0, 0xfa); + pegasus_set_register(pegasus, EthCtrl2, 0); info("%s set allmulti", net->name); } else { info("%s: set Rx mode", net->name); + pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); + pegasus_set_register(pegasus, EthCtrl2, tmp & ~4); } netif_wake_queue(net); @@ -464,12 +617,6 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) } memset(pegasus, 0, sizeof(struct pegasus)); - if (pegasus_reset_mac(dev)) { - err("can't reset MAC"); - kfree(pegasus); - return NULL; - } - net = init_etherdev(0, 0); net->priv = pegasus; net->open = pegasus_open; @@ -485,6 +632,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) pegasus->usb = dev; pegasus->net = net; pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED; + pegasus->ctrl_lock = SPIN_LOCK_UNLOCKED; FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, @@ -495,7 +643,12 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); - + if (pegasus_reset_mac(pegasus)) { + err("can't reset MAC"); + kfree(pegasus); + return NULL; + } + printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); return pegasus; @@ -515,7 +668,9 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) dev_close(pegasus->net); unregister_netdev(pegasus->net); - + + if ( pegasus->ctrl_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->ctrl_urb); if ( pegasus->rx_urb.status == -EINPROGRESS ) usb_unlink_urb(&pegasus->rx_urb); if ( pegasus->tx_urb.status == -EINPROGRESS ) diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index b2e18bd8e..d60fd8e33 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/23/2000) gkh + * Cleaned up debugging statements in a quest to find UHCI timeout bug. + * * (05/22/2000) gkh * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be * removed from the individual device source files. @@ -358,7 +361,7 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor) int i, j; int good_spot; - dbg("get_free_serial %d", num_ports); + dbg(__FUNCTION__ " %d", num_ports); *minor = 0; for (i = 0; i < SERIAL_TTY_MINORS; ++i) { @@ -373,14 +376,14 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor) continue; if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) { - err("Out of memory"); + err(__FUNCTION__ " - Out of memory"); return NULL; } memset(serial, 0, sizeof(struct usb_serial)); serial->magic = USB_SERIAL_MAGIC; serial_table[i] = serial; *minor = i; - dbg("minor base = %d", *minor); + dbg(__FUNCTION__ " - minor base = %d", *minor); for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) serial_table[i] = serial; return serial; @@ -393,7 +396,7 @@ static void return_serial (struct usb_serial *serial) { int i; - dbg("return_serial"); + dbg(__FUNCTION__); if (serial == NULL) return; @@ -418,7 +421,7 @@ int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *da // dbg("ezusb_writememory %x, %d", address, length); if (!transfer_buffer) { - err("ezusb_writememory: kmalloc(%d) failed.", length); + err(__FUNCTION__ " - kmalloc(%d) failed.", length); return -ENOMEM; } memcpy (transfer_buffer, data, length); @@ -431,10 +434,10 @@ int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *da int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) { int response; - dbg("ezusb_set_reset: %d", reset_bit); + dbg(__FUNCTION__ " - %d", reset_bit); response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0); if (response < 0) { - err("ezusb_set_reset %d failed", reset_bit); + err(__FUNCTION__ "- %d failed", reset_bit); } return response; } @@ -451,7 +454,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) struct usb_serial_port *port; int portNumber; - dbg("serial_open"); + dbg(__FUNCTION__); /* initialize the pointer incase something fails */ tty->driver_data = NULL; @@ -459,7 +462,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) /* get the serial object associated with this tty pointer */ serial = get_serial_by_minor (MINOR(tty->device)); - if (serial_paranoia_check (serial, "serial_open")) { + if (serial_paranoia_check (serial, __FUNCTION__)) { return -ENODEV; } @@ -481,16 +484,16 @@ static int serial_open (struct tty_struct *tty, struct file * filp) static void serial_close(struct tty_struct *tty, struct file * filp) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_close"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not opened"); + dbg (__FUNCTION__ " - port not opened"); return; } @@ -506,16 +509,16 @@ static void serial_close(struct tty_struct *tty, struct file * filp) static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_write"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_write port %d, %d byte(s)", port->number, count); + dbg(__FUNCTION__ " - port %d, %d byte(s)", port->number, count); if (!port->active) { - dbg ("port not opened"); + dbg (__FUNCTION__ " - port not opened"); return -EINVAL; } @@ -531,16 +534,16 @@ static int serial_write (struct tty_struct * tty, int from_user, const unsigned static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_write_room"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_write_room port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -EINVAL; } @@ -556,14 +559,14 @@ static int serial_write_room (struct tty_struct *tty) static int serial_chars_in_buffer (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_chars_in_buffer"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -EINVAL; } @@ -579,16 +582,16 @@ static int serial_chars_in_buffer (struct tty_struct *tty) static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_throttle"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_throttle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -604,16 +607,16 @@ static void serial_throttle (struct tty_struct * tty) static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_unthrottle"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_unthrottle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -629,16 +632,16 @@ static void serial_unthrottle (struct tty_struct * tty) static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_ioctl"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_ioctl port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -ENODEV; } @@ -654,16 +657,16 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in static void serial_set_termios (struct tty_struct *tty, struct termios * old) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_set_termios"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_set_termios port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -679,16 +682,16 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_break"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_break port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -708,10 +711,10 @@ static int generic_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; - dbg("generic_open port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (port->active) { - dbg ("device already open"); + dbg (__FUNCTION__ " - device already open"); return -EINVAL; } port->active = 1; @@ -720,7 +723,7 @@ static int generic_open (struct usb_serial_port *port, struct file *filp) if (serial->num_bulk_in) { /*Start reading from the device*/ if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); } return (0); @@ -731,7 +734,7 @@ static void generic_close (struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial = port->serial; - dbg("generic_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); /* shutdown any bulk reads that might be going on */ if (serial->num_bulk_out) { @@ -749,22 +752,33 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns { struct usb_serial *serial = port->serial; - dbg("generic_serial_write port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (count == 0) { - dbg("write request of 0 bytes"); + dbg(__FUNCTION__ " - write request of 0 bytes"); return (0); } /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) { - dbg ("already writing"); + dbg (__FUNCTION__ " - already writing"); return (0); } count = (count > port->bulk_out_size) ? port->bulk_out_size : count; +#ifdef DEBUG + { + int i; + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count); + for (i = 0; i < count; ++i) { + printk ("%.2x ", buf[i]); + } + printk ("\n"); + } +#endif + if (from_user) { copy_from_user(port->write_urb->transfer_buffer, buf, count); } @@ -776,7 +790,7 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns port->write_urb->transfer_buffer_length = count; if (usb_submit_urb(port->write_urb)) - dbg("usb_submit_urb(write bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed"); return (count); } @@ -791,14 +805,14 @@ static int generic_write_room (struct usb_serial_port *port) struct usb_serial *serial = port->serial; int room; - dbg("generic_write_room port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) room = 0; else room = port->bulk_out_size; - dbg("generic_write_room returns %d", room); + dbg(__FUNCTION__ " returns %d", room); return (room); } @@ -810,7 +824,7 @@ static int generic_chars_in_buffer (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - dbg("generic_chars_in_buffer port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) { @@ -825,23 +839,25 @@ static int generic_chars_in_buffer (struct usb_serial_port *port) static void generic_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = get_usb_serial (port, "generic_read_bulk_callback"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int i; + dbg (__FUNCTION__ " - enter"); + if (!serial) { return; } if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); + dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status); return; } #ifdef DEBUG if (urb->actual_length) { - printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } @@ -859,8 +875,10 @@ static void generic_read_bulk_callback (struct urb *urb) /* Continue trying to always read */ if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); + dbg(__FUNCTION__ " - failed resubmitting read urb"); + dbg (__FUNCTION__ " - exit"); + return; } @@ -868,15 +886,17 @@ static void generic_read_bulk_callback (struct urb *urb) static void generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = get_usb_serial (port, "generic_write_bulk_callback"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; + dbg (__FUNCTION__ " - enter"); + if (!serial) { return; } if (urb->status) { - dbg("nonzero write bulk status received: %d", urb->status); + dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status); return; } @@ -886,6 +906,8 @@ static void generic_write_bulk_callback (struct urb *urb) wake_up_interruptible(&tty->write_wait); + dbg (__FUNCTION__ " - exit"); + return; } @@ -1255,7 +1277,7 @@ int usb_serial_init(void) serial_tty_driver.init_termios = tty_std_termios; serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; if (tty_register_driver (&serial_tty_driver)) { - err("failed to register tty driver"); + err(__FUNCTION__ " - failed to register tty driver"); return -1; } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5cfb4c42a..8be2dd351 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/23/2000) gkh + * Cleaned up debugging statements in a quest to find UHCI timeout bug. + * * (04/27/2000) Ryan VanderBijl * Fixed memory leak in visor_close * @@ -80,10 +83,10 @@ struct usb_serial_device_type handspring_device = { ******************************************************************************/ static int visor_open (struct usb_serial_port *port, struct file *filp) { - dbg("visor_open port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (port->active) { - dbg ("device already open"); + dbg (__FUNCTION__ " - device already open"); return -EINVAL; } @@ -91,7 +94,7 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) /*Start reading from the device*/ if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); return (0); } @@ -102,10 +105,10 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) struct usb_serial *serial = port->serial; unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL); - dbg("visor_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!transfer_buffer) { - err("visor_close: kmalloc(%d) failed.", 0x12); + err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); } else { /* send a shutdown message to the device */ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, @@ -122,7 +125,7 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) static void visor_throttle (struct usb_serial_port *port) { - dbg("visor_throttle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); usb_unlink_urb (port->read_urb); @@ -132,10 +135,10 @@ static void visor_throttle (struct usb_serial_port *port) static void visor_unthrottle (struct usb_serial_port *port) { - dbg("visor_unthrottle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (usb_unlink_urb (port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); return; } @@ -148,20 +151,20 @@ static int visor_startup (struct usb_serial *serial) unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL); if (!transfer_buffer) { - err("visor_startup: kmalloc(%d) failed.", 256); + err(__FUNCTION__ " - kmalloc(%d) failed.", 256); return -ENOMEM; } - dbg("visor_startup"); + dbg(__FUNCTION__); - dbg("visor_setup: Set config to 1"); + dbg(__FUNCTION__ " - Set config to 1"); usb_set_configuration (serial->dev, 1); /* send a get connection info request */ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); if (response < 0) { - err("visor_startup: error getting connection information"); + err(__FUNCTION__ " - error getting connection information"); } else { struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer; char *string; @@ -195,7 +198,7 @@ static int visor_startup (struct usb_serial *serial) response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300); if (response < 0) { - err("visor_startup: error getting bytes available request"); + err(__FUNCTION__ " - error getting bytes available request"); } kfree (transfer_buffer); diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index b9cead4fe..6d88d360b 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -1,11 +1,16 @@ /* Driver for USB Mass Storage compliant devices * - * Initial work by: - * (c) 1999 Michael Gee (michael@linuxspecific.com) + * $Id: usb-storage.c,v 1.11 2000/06/20 03:19:31 mdharm Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * * This driver is based on the 'USB Mass Storage Class' document. This * describes in detail the protocol used to communicate with such * devices. Clearly, the designers had SCSI and ATAPI commands in @@ -22,6 +27,20 @@ * * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more * information about this driver. + * + * 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, 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> @@ -88,6 +107,7 @@ struct us_data { char *protocol_name; u8 subclass; u8 protocol; + u8 max_lun; /* information about the device -- only good if device is attached */ u8 ifnum; /* interface number */ @@ -536,8 +556,9 @@ static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us) /* save the old command */ memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE); + /* set the command and the LUN */ srb->cmnd[0] = REQUEST_SENSE; - srb->cmnd[1] = 0; + srb->cmnd[1] = old_cmnd[1] & 0xE0; srb->cmnd[2] = 0; srb->cmnd[3] = 0; srb->cmnd[4] = 18; @@ -791,7 +812,7 @@ static int Bulk_max_lun(struct us_data *us) result, data); /* if we have a successful request, return the result */ - if (!result) + if (result == 1) return data; /* if we get a STALL, clear the stall */ @@ -839,6 +860,9 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_clear_halt(us->pusb_dev, pipe); + } else if (result) { + /* unknown error -- we've got a problem */ + return USB_STOR_TRANSPORT_ERROR; } /* if the command transfered well, then we go to the data stage */ @@ -976,25 +1000,17 @@ static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) srb->cmnd[0] = srb->cmnd[0] | 0x20; break; } /* end switch on cmnd[0] */ + + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); /* send the command to the transport layer */ invoke_transport(srb, us); - /* Fix the MODE_SENSE data if we translated the command - */ - if (old_cmnd == MODE_SENSE) { - unsigned char *dta = (unsigned char *)us->srb->request_buffer; - - /* FIXME: we need to compress the entire data structure here - */ - dta[0] = dta[1]; /* data len */ - dta[1] = dta[2]; /* med type */ - dta[2] = dta[3]; /* dev-spec prm */ - dta[3] = dta[7]; /* block desc len */ - printk (KERN_DEBUG USB_STORAGE - "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", - dta[0], dta[1], dta[2], dta[3]); - } + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); /* Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us @@ -1084,24 +1100,16 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) break; } /* end switch on cmnd[0] */ + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); + /* send the command to the transport layer */ invoke_transport(srb, us); - /* Fix the MODE_SENSE data here if we had to translate the command - */ - if (old_cmnd == MODE_SENSE) { - unsigned char *dta = (unsigned char *)us->srb->request_buffer; - - /* FIXME: we need to compress the entire data structure here - */ - dta[0] = dta[1]; /* data len */ - dta[1] = dta[2]; /* med type */ - dta[2] = dta[3]; /* dev-spec prm */ - dta[3] = dta[7]; /* block desc len */ - printk (KERN_DEBUG USB_STORAGE - "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", - dta[0], dta[1], dta[2], dta[3]); - } + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); /* Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us @@ -1310,9 +1318,8 @@ static int us_release(struct Scsi_Host *psh) down(&(us->notify)); /* free the data structure we were using */ - US_DEBUGP("-- freeing private host data structure\n"); + US_DEBUGP("-- freeing URB\n"); kfree(us->current_urb); - kfree(us); (struct us_data*)psh->hostdata[0] = NULL; /* we always have a successful release */ @@ -1536,11 +1543,10 @@ static int usb_stor_control_thread(void * __us) switch (action) { case US_ACT_COMMAND: - /* reject if target != 0 or if single-lun device - * and LUN != 0 + /* reject if target != 0 or if LUN is higher than + * the maximum known LUN */ - if (us->srb->target || - ((us->flags & US_FL_SINGLE_LUN) && us->srb->lun)) { + if (us->srb->target || (us->srb->lun > us->max_lun)) { US_DEBUGP("Bad device number (%d/%d)\n", us->srb->target, us->srb->lun); @@ -1623,32 +1629,37 @@ static int usb_stor_control_thread(void * __us) /* This is the list of devices we recognize, along with their flag data */ static struct us_unusual_dev us_unusual_dev_list[] = { - { 0x03f0, 0x0107, 0x0200, - "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, - { 0x04e6, 0x0001, 0x0200, - "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x04e6, 0x0002, 0x0100, - "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0006, 0x0100, - "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x057b, 0x0000, 0x0114, - "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x059b, 0x0030, 0x0100, - "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN}, - { 0x0693, 0x0002, 0x0100, - "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK, - US_FL_ALT_LENGTH}, - { 0x0781, 0x0001, 0x0200, - "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, - US_FL_SINGLE_LUN | US_FL_START_STOP}, - { 0x0781, 0x0002, 0x0009, - "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, - US_FL_SINGLE_LUN | US_FL_IGNORE_SER}, - { 0x07af, 0x0005, 0x0100, - "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x0000, 0x0000, 0x0, - "", 0, 0, 0} -}; + { 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus", + US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120", + US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, 0x0100, "Shuttle eUSB MMC Adapter", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x054c, 0x0010, 0x0210, 0x0210, "Sony DSC-S30", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, + { 0x054c, 0x002d, 0x0100, 0x0100, "Sony Memorystick MSAC-US1", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, + { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CBI, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara FlashGate SmartMedia", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk ImageMate (SDDR-01)", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP}, + { 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk Imagemate (SDDR-31)", + US_SC_SCSI, US_PR_BULK, US_FL_IGNORE_SER}, + { 0x07af, 0x0005, 0x0100, 0x0100, "Microtech USB-SCSI-HD50", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x05ab, 0x0031, 0x0100, 0x0100, "In-System USB/IDE Bridge", + US_SC_8070, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara Flashgate", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0 }}; /* Search our ususual device list, based on vendor/product combinations * to see if we can support this device. Returns a pointer to a structure @@ -1667,7 +1678,8 @@ static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, while ((ptr->idVendor != 0x0000) && !((ptr->idVendor == idVendor) && (ptr->idProduct == idProduct) && - (ptr->bcdDevice == bcdDevice))) + (ptr->bcdDeviceMin <= bcdDevice) && + (ptr->bcdDeviceMax >= bcdDevice))) ptr++; /* if the search ended because we hit the end record, we failed */ @@ -1968,20 +1980,21 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->transport_name = "Control/Bulk"; ss->transport = CB_transport; ss->transport_reset = CB_reset; + ss->max_lun = 7; break; case US_PR_CBI: ss->transport_name = "Control/Bulk/Interrupt"; ss->transport = CBI_transport; ss->transport_reset = CB_reset; + ss->max_lun = 7; break; case US_PR_BULK: ss->transport_name = "Bulk"; ss->transport = Bulk_transport; ss->transport_reset = Bulk_reset; - /* FIXME: for testing purposes only */ - Bulk_max_lun(ss); + ss->max_lun = Bulk_max_lun(ss); break; default: @@ -1994,6 +2007,10 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } US_DEBUGP("Transport: %s\n", ss->transport_name); + /* fix for single-lun devices */ + if (ss->flags & US_FL_SINGLE_LUN) + ss->max_lun = 0; + switch (ss->subclass) { case US_SC_RBC: ss->protocol_name = "Reduced Block Commands (RBC)"; @@ -2134,6 +2151,576 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) up(&(ss->dev_semaphore)); } +/************************************************************** + **************************************************************/ + +#define USB_STOR_SCSI_SENSE_HDRSZ 4 +#define USB_STOR_SCSI_SENSE_10_HDRSZ 8 + +struct usb_stor_scsi_sense_hdr +{ + __u8* dataLength; + __u8* mediumType; + __u8* devSpecParms; + __u8* blkDescLength; +}; + +typedef struct usb_stor_scsi_sense_hdr Usb_Stor_Scsi_Sense_Hdr; + +union usb_stor_scsi_sense_hdr_u +{ + Usb_Stor_Scsi_Sense_Hdr hdr; + __u8* array[USB_STOR_SCSI_SENSE_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_u Usb_Stor_Scsi_Sense_Hdr_u; + +struct usb_stor_scsi_sense_hdr_10 +{ + __u8* dataLengthMSB; + __u8* dataLengthLSB; + __u8* mediumType; + __u8* devSpecParms; + __u8* reserved1; + __u8* reserved2; + __u8* blkDescLengthMSB; + __u8* blkDescLengthLSB; +}; + +typedef struct usb_stor_scsi_sense_hdr_10 Usb_Stor_Scsi_Sense_Hdr_10; + +union usb_stor_scsi_sense_hdr_10_u +{ + Usb_Stor_Scsi_Sense_Hdr_10 hdr; + __u8* array[USB_STOR_SCSI_SENSE_10_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_10_u Usb_Stor_Scsi_Sense_Hdr_10_u; + +void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* , Usb_Stor_Scsi_Sense_Hdr_u*, + Usb_Stor_Scsi_Sense_Hdr_10_u*, int* ); +void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ); + +int +usb_stor_scsiSense10to6( Scsi_Cmnd* the10 ) +{ + __u8 *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int sgLength=0; + +#if 0 + /* Make sure we get a MODE_SENSE_10 command */ + if ( the10->cmnd[0] != MODE_SENSE_10 ) + { + printk( KERN_ERR USB_STORAGE + "Scsi_Cmnd was not a MODE_SENSE_10.\n" ); + return -1; + } + + /* Now start to format the output */ + the10->cmnd[0] = MODE_SENSE; +#endif + US_DEBUGP("-- converting 10 byte sense data to 6 byte\n"); + the10->cmnd[0] = the10->cmnd[0] & 0xBF; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the10, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the10Locations.hdr.dataLengthLSB; + outputBufferSize += USB_STOR_SCSI_SENSE_HDRSZ; + + /* Check to see if we need to truncate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE_10 buffer into MODE_SENSE.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Data length */ + if ( *the10Locations.hdr.dataLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.dataLength = 0xff; + } + else + { + *the6Locations.hdr.dataLength = *the10Locations.hdr.dataLengthLSB; + } + + /* Medium type and DevSpecific parms */ + *the6Locations.hdr.mediumType = *the10Locations.hdr.mediumType; + *the6Locations.hdr.devSpecParms = *the10Locations.hdr.devSpecParms; + + /* Block descriptor length */ + if ( *the10Locations.hdr.blkDescLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.blkDescLength = 0xff; + } + else + { + *the6Locations.hdr.blkDescLength = *the10Locations.hdr.blkDescLengthLSB; + } + + if ( the10->use_sg == 0 ) + { + buffer = the10->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + outputBufferSize - USB_STOR_SCSI_SENSE_HDRSZ ); + /* initialise last bytes left in buffer due to smaller header */ + memset( &(buffer[outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ)]), + 0, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the10->request_buffer; + /* scan through this scatterlist and figure out starting positions */ + for ( i=0; i < the10->use_sg; i++) + { + sgLength = sg[i].length; + for ( j=0; j<sgLength; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + db=i; + di=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=sgLength; + i=the10->use_sg; + } + element++; + } + } + + /* Now we know where to start the copy from */ + element = USB_STOR_SCSI_SENSE_HDRSZ; + while ( element < outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + /* check limits */ + if ( sb >= the10->use_sg || + si >= sg[sb].length || + db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + + /* get next source */ + if ( sg[sb].length-1 == si ) + { + sb++; + si=0; + } + else + { + si++; + } + + element++; + } + /* zero the remaining bytes */ + while ( element < outputBufferSize ) + { + /* check limits */ + if ( db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = 0; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + element++; + } + } + + /* All done any everything was fine */ + return 0; +} + +int +usb_stor_scsiSense6to10( Scsi_Cmnd* the6 ) +{ + /* will be used to store part of buffer */ + __u8 tempBuffer[USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ], + *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int lsb=0,lsi=0,ldb=0,ldi=0; + +#if 0 + /* Make sure we get a MODE_SENSE command */ + if ( the6->cmnd[0] != MODE_SENSE ) + { + printk( KERN_ERR USB_STORAGE + "Scsi_Cmnd was not MODE_SENSE.\n" ); + return -1; + } + + /* Now start to format the output */ + the6->cmnd[0] = MODE_SENSE_10; +#endif + US_DEBUGP("-- converting 6 byte sense data to 10 byte\n"); + the6->cmnd[0] = the6->cmnd[0] | 0x40; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the6, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the6Locations.hdr.dataLength; + outputBufferSize += USB_STOR_SCSI_SENSE_10_HDRSZ; + + /* Check to see if we need to trucate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE into MODE_SENSE_10 buffer.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Block descriptor length - save these before overwriting */ + tempBuffer[2] = *the10Locations.hdr.blkDescLengthMSB; + tempBuffer[3] = *the10Locations.hdr.blkDescLengthLSB; + *the10Locations.hdr.blkDescLengthLSB = *the6Locations.hdr.blkDescLength; + *the10Locations.hdr.blkDescLengthMSB = 0; + + /* reserved - save these before overwriting */ + tempBuffer[0] = *the10Locations.hdr.reserved1; + tempBuffer[1] = *the10Locations.hdr.reserved2; + *the10Locations.hdr.reserved1 = *the10Locations.hdr.reserved2 = 0; + + /* Medium type and DevSpecific parms */ + *the10Locations.hdr.devSpecParms = *the6Locations.hdr.devSpecParms; + *the10Locations.hdr.mediumType = *the6Locations.hdr.mediumType; + + /* Data length */ + *the10Locations.hdr.dataLengthLSB = *the6Locations.hdr.dataLength; + *the10Locations.hdr.dataLengthMSB = 0; + + if ( !the6->use_sg ) + { + buffer = the6->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + outputBufferSize-USB_STOR_SCSI_SENSE_10_HDRSZ ); + /* Put the first four bytes (after header) in place */ + memcpy( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + tempBuffer, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the6->request_buffer; + /* scan through this scatterlist and figure out ending positions */ + for ( i=0; i < the6->use_sg; i++) + { + for ( j=0; j<sg[i].length; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + ldb=i; + ldi=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + lsb=i; + lsi=j; + /* we've found both sets now, exit loops */ + j=sg[i].length; + i=the6->use_sg; + break; + } + element++; + } + } + /* scan through this scatterlist and figure out starting positions */ + element = length-1; + /* destination is the last element */ + db=the6->use_sg-1; + di=sg[db].length-1; + for ( i=the6->use_sg-1; i >= 0; i--) + { + for ( j=sg[i].length-1; j>=0; j-- ) + { + /* get to end of header and find source for copy */ + if ( element == length - 1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=-1; + i=-1; + } + element--; + } + } + /* Now we know where to start the copy from */ + element = length-1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ); + while ( element >= USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* check limits */ + if ( ( sb <= lsb && si < lsi ) || + ( db <= ldb && di < ldi ) ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + + /* get next source */ + if ( si == 0 ) + { + sb--; + si=sg[sb].length-1; + } + else + { + si--; + } + + element--; + } + /* copy the remaining four bytes */ + while ( element >= USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* check limits */ + if ( db <= ldb && di < ldi ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + element--; + } + } + + /* All done and everything was fine */ + return 0; +} + +void +usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* srb, Usb_Stor_Scsi_Sense_Hdr_u* the6, + Usb_Stor_Scsi_Sense_Hdr_10_u* the10, + int* length_p ) + +{ + int i = 0, j=0, element=0; + struct scatterlist *sg = 0; + int length = 0; + __u8* buffer=0; + + /* are we scatter-gathering? */ + if ( srb->use_sg != 0 ) + { + /* loop over all the scatter gather structures and + * get pointer to the data members in the headers + * (also work out the length while we're here) + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + { + length += sg[i].length; + /* We only do the inner loop for the headers */ + if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* scan through this scatterlist */ + for ( j=0; j<sg[i].length; j++ ) + { + if ( element < USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* fill in the pointers for both header types */ + the6->array[element] = &(sg[i].address[j]); + the10->array[element] = &(sg[i].address[j]); + } + else if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* only the longer headers still cares now */ + the10->array[element] = &(sg[i].address[j]); + } + /* increase element counter */ + element++; + } + } + } + } + else + { + length = srb->request_bufflen; + buffer = srb->request_buffer; + if ( length < USB_STOR_SCSI_SENSE_10_HDRSZ ) + printk( KERN_ERR USB_STORAGE + "Buffer length smaller than header!!" ); + for( i=0; i<USB_STOR_SCSI_SENSE_10_HDRSZ; i++ ) + { + if ( i < USB_STOR_SCSI_SENSE_HDRSZ ) + { + the6->array[i] = &(buffer[i]); + the10->array[i] = &(buffer[i]); + } + else + { + the10->array[i] = &(buffer[i]); + } + } + } + + /* Set value of length passed in */ + *length_p = length; +} + +void +usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ) +{ + int i=0, bufferSize = cmd->request_bufflen; + __u8* buffer = cmd->request_buffer; + struct scatterlist* sg = (struct scatterlist*)cmd->request_buffer; + + printk( KERN_ERR "Dumping information about %p.\n", cmd ); + printk( KERN_ERR "cmd->cmnd[0] value is %d.\n", cmd->cmnd[0] ); + printk( KERN_ERR "(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n", + MODE_SENSE, MODE_SENSE_10 ); + + printk( KERN_ERR "buffer is %p with length %d.\n", buffer, bufferSize ); + for ( i=0; i<bufferSize; i+=16 ) + { + printk( KERN_ERR "%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", + buffer[i], + buffer[i+1], + buffer[i+2], + buffer[i+3], + buffer[i+4], + buffer[i+5], + buffer[i+6], + buffer[i+7], + buffer[i+8], + buffer[i+9], + buffer[i+10], + buffer[i+11], + buffer[i+12], + buffer[i+13], + buffer[i+14], + buffer[i+15] ); + } + + printk( KERN_ERR "Buffer has %d scatterlists.\n", cmd->use_sg ); + for ( i=0; i<cmd->use_sg; i++ ) + { + printk( KERN_ERR "Length of scatterlist %d is %d.\n", i, sg[i].length ); + printk( KERN_ERR "%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", + sg[i].address[0], + sg[i].address[1], + sg[i].address[2], + sg[i].address[3], + sg[i].address[4], + sg[i].address[5], + sg[i].address[6], + sg[i].address[7], + sg[i].address[8], + sg[i].address[9], + sg[i].address[10], + sg[i].address[11], + sg[i].address[12], + sg[i].address[13], + sg[i].address[14], + sg[i].address[15] ); + } +} + +/************************************************************** + **************************************************************/ /*********************************************************************** * Initialization and registration @@ -2183,6 +2770,11 @@ void __exit usb_stor_exit(void) US_DEBUGP("-- calling scsi_unregister_module()\n"); scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt)); + /* Now that scsi_unregister_module is done with the host + * template, we can free the us_data structure (the host + * template is inline in this structure). */ + kfree (us_list); + /* advance the list pointer */ us_list = next; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 6e54b059b..82475f094 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -139,7 +139,8 @@ struct us_unusual_dev { /* we search the list based on these parameters */ __u16 idVendor; __u16 idProduct; - __u16 bcdDevice; + __u16 bcdDeviceMin; + __u16 bcdDeviceMax; /* the list specifies these parameters */ const char* name; diff --git a/drivers/usb/wmforce.c b/drivers/usb/wmforce.c deleted file mode 100644 index dff963b74..000000000 --- a/drivers/usb/wmforce.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ - * - * Copyright (c) 2000 Vojtech Pavlik - * - * USB Logitech WingMan Force joystick support - * - * Sponsored by SuSE - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/kernel.h> -#include <linux/malloc.h> -#include <linux/input.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/usb.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 - -struct wmforce { - signed char data[8]; - struct input_dev dev; - struct urb irq; - int open; -}; - -static struct { - __s32 x; - __s32 y; -} wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -static char *wmforce_name = "Logitech WingMan Force"; - -static void wmforce_irq(struct urb *urb) -{ - struct wmforce *wmforce = urb->context; - unsigned char *data = wmforce->data; - struct input_dev *dev = &wmforce->dev; - - if (urb->status) return; - - if (data[0] != 1) { - if (data[0] != 2) - warn("received unknown report #%d", data[0]); - return; - } - - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[2] << 8) | data[1])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[4] << 8) | data[3])); - input_report_abs(dev, ABS_THROTTLE, data[5]); - input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x); - input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y); - - input_report_key(dev, BTN_TRIGGER, data[6] & 0x01); - input_report_key(dev, BTN_TOP, data[6] & 0x02); - input_report_key(dev, BTN_THUMB, data[6] & 0x04); - input_report_key(dev, BTN_TOP2, data[6] & 0x08); - input_report_key(dev, BTN_BASE, data[6] & 0x10); - input_report_key(dev, BTN_BASE2, data[6] & 0x20); - input_report_key(dev, BTN_BASE3, data[6] & 0x40); - input_report_key(dev, BTN_BASE4, data[6] & 0x80); - input_report_key(dev, BTN_BASE5, data[7] & 0x01); -} - -static int wmforce_open(struct input_dev *dev) -{ - struct wmforce *wmforce = dev->private; - - if (wmforce->open++) - return 0; - - if (usb_submit_urb(&wmforce->irq)) - return -EIO; - - return 0; -} - -static void wmforce_close(struct input_dev *dev) -{ - struct wmforce *wmforce = dev->private; - - if (!--wmforce->open) - usb_unlink_urb(&wmforce->irq); -} - -static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum) -{ - struct usb_endpoint_descriptor *endpoint; - struct wmforce *wmforce; - int i; - - if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || - dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) - return NULL; - - endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - - if (!(wmforce = kmalloc(sizeof(struct wmforce), GFP_KERNEL))) return NULL; - memset(wmforce, 0, sizeof(struct wmforce)); - - wmforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - wmforce->dev.keybit[LONG(BTN_JOYSTICK)] = BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | - BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); - wmforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); - - for (i = ABS_X; i <= ABS_Y; i++) { - wmforce->dev.absmax[i] = 1920; - wmforce->dev.absmin[i] = -1920; - wmforce->dev.absflat[i] = 128; - } - - wmforce->dev.absmax[ABS_THROTTLE] = 255; - wmforce->dev.absmin[ABS_THROTTLE] = 0; - - for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { - wmforce->dev.absmax[i] = 1; - wmforce->dev.absmin[i] = -1; - } - - wmforce->dev.private = wmforce; - wmforce->dev.open = wmforce_open; - wmforce->dev.close = wmforce_close; - - wmforce->dev.name = wmforce_name; - wmforce->dev.idbus = BUS_USB; - wmforce->dev.idvendor = dev->descriptor.idVendor; - wmforce->dev.idproduct = dev->descriptor.idProduct; - wmforce->dev.idversion = dev->descriptor.bcdDevice; - - FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), - wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval); - - input_register_device(&wmforce->dev); - - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum); - - return wmforce; -} - -static void wmforce_disconnect(struct usb_device *dev, void *ptr) -{ - struct wmforce *wmforce = ptr; - usb_unlink_urb(&wmforce->irq); - input_unregister_device(&wmforce->dev); - kfree(wmforce); -} - -static struct usb_driver wmforce_driver = { - name: "wmforce", - probe: wmforce_probe, - disconnect: wmforce_disconnect, -}; - -static int __init wmforce_init(void) -{ - usb_register(&wmforce_driver); - return 0; -} - -static void __exit wmforce_exit(void) -{ - usb_deregister(&wmforce_driver); -} - -module_init(wmforce_init); -module_exit(wmforce_exit); diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 83e5dace8..6529b53c5 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -120,6 +120,7 @@ if [ "$CONFIG_FB" = "y" ]; then tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY tristate ' ATI Rage 128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 bool ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX + tristate ' SIS 630/540 display support (EXPERIMENTAL)' CONFIG_FB_SIS fi fi if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index ee1248a23..864e30f0c 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -654,8 +654,8 @@ register_framebuffer(struct fb_info *fb_info) } sprintf (name_buf, "%d", i); fb_info->devfs_handle = - devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE, - FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT, + FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, &fb_fops, NULL); return 0; diff --git a/drivers/video/sisfb.c b/drivers/video/sisfb.c index 7b83f69c4..d42448fbf 100644 --- a/drivers/video/sisfb.c +++ b/drivers/video/sisfb.c @@ -43,9 +43,10 @@ #define FALSE 0 #define TRUE 1 -/* Draw Function */ +/* Draw Function #define FBIOGET_GLYPH 0x4620 #define FBIOGET_HWCINFO 0x4621 +*/ #define BR(x) (0x8200 | (x) << 2) #define BITBLT 0x00000000 @@ -115,7 +116,8 @@ #define MMIO_SIZE 0x20000 /* 128K MMIO capability */ #define MAX_ROM_SCAN 0x10000 -#define RESERVED_MEM_SIZE 0x400000 /* 4M */ +#define RESERVED_MEM_SIZE_4M 0x400000 /* 4M */ +#define RESERVED_MEM_SIZE_8M 0x800000 /* 8M */ /* Mode set stuff */ #define DEFAULT_MODE 0 @@ -173,9 +175,9 @@ static struct board { const char *name; } dev_list[] = { { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_300, "SIS 300"}, { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_540, "SIS 540"}, { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_630, "SIS 630"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_300, "SIS 300"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_540, "SIS 540"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, "SIS 630"}, { 0, 0, NULL} }; @@ -1020,8 +1022,11 @@ static int sisfb_heap_init(void) struct OH *poh; u8 jTemp, tq_state; - heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE; - //heap_start = (unsigned long)ivideo.video_vbase + (video_size - RESERVED_MEM_SIZE); + if(ivideo.video_size > 0x800000) /* video ram is large than 8M */ + heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE_8M; + else + heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE_4M; + heap_end = (unsigned long) ivideo.video_vbase + ivideo.video_size; heap_size = heap_end - heap_start; @@ -1398,6 +1403,7 @@ static u32 get_reg3(u16 port) static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo) { +#if 0 unsigned char ModeID; u16 modeidlength; u16 usModeIDOffset; @@ -1411,6 +1417,8 @@ static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo) ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); } return (modeidlength); +#endif + return(10); } static int search_modeID(unsigned long ROMAddr, u16 ModeNo) @@ -2467,7 +2475,11 @@ static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, strcpy(fix->id, fb_info.modename); fix->smem_start = ivideo.video_base; - fix->smem_len = RESERVED_MEM_SIZE; /* reserved for Xserver */ + if(ivideo.video_size > 0x800000) + fix->smem_len = RESERVED_MEM_SIZE_8M; /* reserved for Xserver */ + else + fix->smem_len = RESERVED_MEM_SIZE_4M; /* reserved for Xserver */ + fix->type = video_type; fix->type_aux = 0; if (ivideo.video_bpp == 8) diff --git a/fs/Config.in b/fs/Config.in index 8bdb7a176..730afdb94 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -78,7 +78,6 @@ if [ "$CONFIG_NET" = "y" ]; then dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET dep_mbool ' Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD - dep_mbool ' Provide NFS server over TCP support (DEVELOPER-ONLY)' CONFIG_NFSD_TCP $CONFIG_NFSD $CONFIG_EXPERIMENTAL if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_tristate CONFIG_SUNRPC y diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index f36495259..9e1a59ed2 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -73,7 +73,7 @@ int adfs_bmap(struct inode *inode, int block); #endif struct inode *adfs_iget(struct super_block *sb, struct object_info *obj); void adfs_read_inode(struct inode *inode); -void adfs_write_inode(struct inode *inode, int unused); +void adfs_write_inode(struct inode *inode); int adfs_notify_change(struct dentry *dentry, struct iattr *attr); /* map.c */ diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 5dacc56d6..fa3655a12 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -349,7 +349,7 @@ out: * The adfs-specific inode data has already been updated by * adfs_notify_change() */ -void adfs_write_inode(struct inode *inode, int unused) +void adfs_write_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct object_info obj; diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 84ea2adf8..1551e613c 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -191,7 +191,7 @@ affs_read_inode(struct inode *inode) } void -affs_write_inode(struct inode *inode, int unused) +affs_write_inode(struct inode *inode) { struct buffer_head *bh; struct file_end *file_end; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 5f41c53ac..2b47ab674 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -84,7 +84,7 @@ static void bfs_read_inode(struct inode * inode) brelse(bh); } -static void bfs_write_inode(struct inode * inode, int unused) +static void bfs_write_inode(struct inode * inode) { unsigned long ino = inode->i_ino; kdev_t dev = inode->i_dev; diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index bcdadf1d7..ade9091a5 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -406,7 +406,7 @@ beyond_if: regs->gp = ex.a_gpvalue; #endif start_thread(regs, ex.a_entry, current->mm->start_stack); - if (current->ptrace&PT_PTRACED) + if (current->ptrace & PT_PTRACED) send_sig(SIGTRAP, current, 0); return 0; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index cd6a76271..9fd867d0e 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -674,8 +674,9 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) interpreter, &interp_load_addr); - allow_write_access(interpreter); + lock_kernel(); fput(interpreter); + unlock_kernel(); kfree(elf_interpreter); if (elf_entry == ~0UL) { @@ -754,7 +755,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) #endif start_thread(regs, elf_entry, bprm->p); - if (current->ptrace&PT_PTRACED) + if (current->ptrace & PT_PTRACED) send_sig(SIGTRAP, current, 0); retval = 0; out: @@ -762,8 +763,9 @@ out: /* error cleanup */ out_free_dentry: - allow_write_access(interpreter); + lock_kernel(); fput(interpreter); + unlock_kernel(); out_free_interp: if (elf_interpreter) kfree(elf_interpreter); diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index 75f0abc59..1b18094eb 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -43,7 +43,6 @@ static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) } bprm->sh_bang++; /* Well, the bang-shell is implicit... */ - allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index f9c30df1b..0d44c3d4e 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -201,7 +201,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (!fmt) goto _ret; - allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 3d5023e2d..dc78f8389 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -29,7 +29,6 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs) */ bprm->sh_bang++; - allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/block_dev.c b/fs/block_dev.c index 29972c8ca..c455a735d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -313,7 +313,7 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) * since the vma has no handle. */ -static int block_fsync(struct file *filp, struct dentry *dentry, int datasync) +static int block_fsync(struct file *filp, struct dentry *dentry) { return fsync_dev(dentry->d_inode->i_rdev); } @@ -597,8 +597,6 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int kind) ret = bdev->bd_op->open(fake_inode, &fake_file); if (!ret) atomic_inc(&bdev->bd_openers); - else if (!atomic_read(&bdev->bd_openers)) - bdev->bd_op = NULL; iput(fake_inode); } } @@ -619,8 +617,6 @@ int blkdev_open(struct inode * inode, struct file * filp) ret = bdev->bd_op->open(inode,filp); if (!ret) atomic_inc(&bdev->bd_openers); - else if (!atomic_read(&bdev->bd_openers)) - bdev->bd_op = NULL; } up(&bdev->bd_sem); return ret; diff --git a/fs/buffer.c b/fs/buffer.c index 432877cdd..47d690fa4 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -68,8 +68,6 @@ static char buffersize_index[65] = * lru_list_lock > hash_table_lock > free_list_lock > unused_list_lock */ -#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_inode_buffers) - /* * Hash table gook.. */ @@ -325,7 +323,7 @@ asmlinkage long sys_sync(void) * filp may be NULL if called via the msync of a vma. */ -int file_fsync(struct file *filp, struct dentry *dentry, int datasync) +int file_fsync(struct file *filp, struct dentry *dentry) { struct inode * inode = dentry->d_inode; struct super_block * sb; @@ -334,7 +332,7 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync) lock_kernel(); /* sync the inode to buffers */ - write_inode_now(inode, 0); + write_inode_now(inode); /* sync the superblock to buffers */ sb = inode->i_sb; @@ -362,7 +360,12 @@ asmlinkage long sys_fsync(unsigned int fd) goto out; dentry = file->f_dentry; + if (!dentry) + goto out_putf; + inode = dentry->d_inode; + if (!inode) + goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) @@ -370,7 +373,7 @@ asmlinkage long sys_fsync(unsigned int fd) /* We need to protect against concurrent writers.. */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry, 0); + err = file->f_op->fsync(file, dentry); up(&inode->i_sem); out_putf: @@ -392,14 +395,20 @@ asmlinkage long sys_fdatasync(unsigned int fd) goto out; dentry = file->f_dentry; + if (!dentry) + goto out_putf; + inode = dentry->d_inode; + if (!inode) + goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out_putf; + /* this needs further work, at the moment it is identical to fsync() */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry, 1); + err = file->f_op->fsync(file, dentry); up(&inode->i_sem); out_putf: @@ -526,7 +535,8 @@ static void put_last_free(struct buffer_head * bh) * As we don't lock buffers (unless we are reading them, that is), * something might happen to it while we sleep (ie a read-error * will force it bad). This shouldn't really happen currently, but - * the code is ready. */ + * the code is ready. + */ struct buffer_head * get_hash_table(kdev_t dev, int block, int size) { struct buffer_head **head = &hash(dev, block); @@ -564,42 +574,6 @@ unsigned int get_hardblocksize(kdev_t dev) return 0; } -void buffer_insert_inode_queue(struct buffer_head *bh, struct inode *inode) -{ - spin_lock(&lru_list_lock); - if (bh->b_inode) - list_del(&bh->b_inode_buffers); - bh->b_inode = inode; - list_add(&bh->b_inode_buffers, &inode->i_dirty_buffers); - spin_unlock(&lru_list_lock); -} - -/* The caller must have the lru_list lock before calling the - remove_inode_queue functions. */ -static void __remove_inode_queue(struct buffer_head *bh) -{ - bh->b_inode = NULL; - list_del(&bh->b_inode_buffers); -} - -static inline void remove_inode_queue(struct buffer_head *bh) -{ - if (bh->b_inode) - __remove_inode_queue(bh); -} - -int inode_has_buffers(struct inode *inode) -{ - int ret; - - spin_lock(&lru_list_lock); - ret = !list_empty(&inode->i_dirty_buffers); - spin_unlock(&lru_list_lock); - - return ret; -} - - /* If invalidate_buffers() will trash dirty buffers, it means some kind of fs corruption is going on. Trashing dirty data always imply losing information that was supposed to be just stored on the physical layer @@ -827,137 +801,6 @@ still_busy: return; } - -/* - * Synchronise all the inode's dirty buffers to the disk. - * - * We have conflicting pressures: we want to make sure that all - * initially dirty buffers get waited on, but that any subsequently - * dirtied buffers don't. After all, we don't want fsync to last - * forever if somebody is actively writing to the file. - * - * Do this in two main stages: first we copy dirty buffers to a - * temporary inode list, queueing the writes as we go. Then we clean - * up, waiting for those writes to complete. - * - * During this second stage, any subsequent updates to the file may end - * up refiling the buffer on the original inode's dirty list again, so - * there is a chance we will end up with a buffer queued for write but - * not yet completed on that list. So, as a final cleanup we go through - * the osync code to catch these locked, dirty buffers without requeuing - * any newly dirty buffers for write. - */ - -int fsync_inode_buffers(struct inode *inode) -{ - struct buffer_head *bh; - struct inode tmp; - int err = 0, err2; - - INIT_LIST_HEAD(&tmp.i_dirty_buffers); - - spin_lock(&lru_list_lock); - - while (!list_empty(&inode->i_dirty_buffers)) { - bh = BH_ENTRY(inode->i_dirty_buffers.next); - list_del(&bh->b_inode_buffers); - if (!buffer_dirty(bh) && !buffer_locked(bh)) - bh->b_inode = NULL; - else { - bh->b_inode = &tmp; - list_add(&bh->b_inode_buffers, &tmp.i_dirty_buffers); - atomic_inc(&bh->b_count); - if (buffer_dirty(bh)) { - spin_unlock(&lru_list_lock); - ll_rw_block(WRITE, 1, &bh); - spin_lock(&lru_list_lock); - } - } - } - - while (!list_empty(&tmp.i_dirty_buffers)) { - bh = BH_ENTRY(tmp.i_dirty_buffers.prev); - remove_inode_queue(bh); - spin_unlock(&lru_list_lock); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - err = -EIO; - brelse(bh); - spin_lock(&lru_list_lock); - } - - spin_unlock(&lru_list_lock); - err2 = osync_inode_buffers(inode); - - if (err) - return err; - else - return err2; -} - - -/* - * osync is designed to support O_SYNC io. It waits synchronously for - * all already-submitted IO to complete, but does not queue any new - * writes to the disk. - * - * To do O_SYNC writes, just queue the buffer writes with ll_rw_block as - * you dirty the buffers, and then use osync_inode_buffers to wait for - * completion. Any other dirty buffers which are not yet queued for - * write will not be flushed to disk by the osync. - */ - -int osync_inode_buffers(struct inode *inode) -{ - struct buffer_head *bh; - struct list_head *list; - int err = 0; - - spin_lock(&lru_list_lock); - - repeat: - - for (list = inode->i_dirty_buffers.prev; - bh = BH_ENTRY(list), list != &inode->i_dirty_buffers; - list = bh->b_inode_buffers.prev) { - if (buffer_locked(bh)) { - atomic_inc(&bh->b_count); - spin_unlock(&lru_list_lock); - wait_on_buffer(bh); - brelse(bh); - if (!buffer_uptodate(bh)) - err = -EIO; - spin_lock(&lru_list_lock); - goto repeat; - } - } - - spin_unlock(&lru_list_lock); - return err; -} - - -/* - * Invalidate any and all dirty buffers on a given inode. We are - * probably unmounting the fs, but that doesn't mean we have already - * done a sync(). Just drop the buffers from the inode list. - */ - -void invalidate_inode_buffers(struct inode *inode) -{ - struct list_head *list, *next; - - spin_lock(&lru_list_lock); - list = inode->i_dirty_buffers.next; - while (list != &inode->i_dirty_buffers) { - next = list->next; - remove_inode_queue(BH_ENTRY(list)); - list = next; - } - spin_unlock(&lru_list_lock); -} - - /* * Ok, this is getblk, and it isn't very clear, again to hinder * race-conditions. Most of the code is seldom used, (ie repeating), @@ -1089,8 +932,6 @@ static void __refile_buffer(struct buffer_head *bh) __remove_from_lru_list(bh, bh->b_list); bh->b_list = dispose; __insert_into_lru_list(bh, dispose); - if (dispose == BUF_CLEAN) - remove_inode_queue(bh); } } @@ -1127,7 +968,6 @@ void __bforget(struct buffer_head * buf) if (!atomic_dec_and_test(&buf->b_count) || buffer_locked(buf)) goto in_use; __hash_unlink(buf); - remove_inode_queue(buf); write_unlock(&hash_table_lock); __remove_from_lru_list(buf, buf->b_list); spin_unlock(&lru_list_lock); @@ -1228,8 +1068,6 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, */ static __inline__ void __put_unused_buffer_head(struct buffer_head * bh) { - if (bh->b_inode) - BUG(); if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) { kmem_cache_free(bh_cachep, bh); } else { @@ -1443,46 +1281,6 @@ static void unmap_buffer(struct buffer_head * bh) } } -/** - * discard_buffer - discard that buffer without doing any IO - * @bh: buffer to discard - * - * This function removes a buffer from all the queues, without doing - * any IO, we are not interested in the contents of the buffer. This - * function can block if the buffer is locked. - */ -static inline struct buffer_head *discard_buffer(struct buffer_head * bh) -{ - struct buffer_head *next; - - if (bh->b_dev == B_FREE) - BUG(); - - next = bh->b_this_page; - - unmap_buffer(bh); - - spin_lock(&lru_list_lock); - write_lock(&hash_table_lock); - spin_lock(&unused_list_lock); - - if (atomic_read(&bh->b_count)) - BUG(); - - __hash_unlink(bh); - write_unlock(&hash_table_lock); - - remove_inode_queue(bh); - __remove_from_lru_list(bh, bh->b_list); - spin_unlock(&lru_list_lock); - - __put_unused_buffer_head(bh); - spin_unlock(&unused_list_lock); - - return next; -} - - /* * We don't have to release all buffers here, but * we have to be sure that no dirty buffer is left @@ -1515,43 +1313,24 @@ int block_flushpage(struct page *page, unsigned long offset) bh = next; } while (bh != head); - return 1; -} - -/** - * block_destroy_buffers - Will destroy the contents of all the - * buffers in this page - * @page: page to examine the buffers - * - * This function destroy all the buffers in one page without making - * any IO. The function can block due to the fact that discad_bufferr - * can block. - */ -void block_destroy_buffers(struct page *page) -{ - struct buffer_head *bh, *head; - - if (!PageLocked(page)) - BUG(); - if (!page->buffers) - return; - - head = page->buffers; - bh = head; - do { - /* We need to get the next buffer from discard buffer - * because discard buffer can block and anybody else - * can change the buffer list under our feet. - */ - bh = discard_buffer(bh); - }while (bh != head); - - /* Wake up anyone waiting for buffer heads */ - wake_up(&buffer_wait); + /* + * subtle. We release buffer-heads only if this is + * the 'final' flushpage. We have invalidated the get_block + * cached value unconditionally, so real IO is not + * possible anymore. + * + * If the free doesn't work out, the buffers can be + * left around - they just turn into anonymous buffers + * instead. + */ + if (!offset) { + if (!try_to_free_buffers(page, 0)) { + atomic_inc(&buffermem_pages); + return 0; + } + } - /* And free the page */ - page->buffers = NULL; - page_cache_release(page); + return 1; } static void create_empty_buffers(struct page *page, struct inode *inode, unsigned long blocksize) @@ -1654,7 +1433,7 @@ static int __block_prepare_write(struct inode *inode, struct page *page, unsigned long block; int err = 0; unsigned blocksize, bbits; - struct buffer_head *bh, *head, *wait[MAX_BUF_PER_PAGE], **wait_bh=wait; + struct buffer_head *bh, *head, *wait[2], **wait_bh=wait; char *kaddr = (char *)kmap(page); blocksize = inode->i_sb->s_blocksize; @@ -1728,7 +1507,6 @@ static int __block_commit_write(struct inode *inode, struct page *page, } else { set_bit(BH_Uptodate, &bh->b_state); if (!atomic_set_buffer_dirty(bh)) { - buffer_insert_inode_queue(bh, inode); __mark_dirty(bh, 0); need_balance_dirty = 1; } @@ -2021,7 +1799,6 @@ static int do_kio(int rw, int nr, struct buffer_head *bh[], int size) } spin_unlock(&unused_list_lock); - wake_up(&buffer_wait); return iosize; } @@ -2158,8 +1935,6 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], __put_unused_buffer_head(bh[bhind]); } spin_unlock(&unused_list_lock); - wake_up(&buffer_wait); - goto finished; } @@ -2337,12 +2112,6 @@ out: } /* - * Can the buffer be thrown out? - */ -#define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected)) -#define buffer_busy(bh) (atomic_read(&(bh)->b_count) | ((bh)->b_state & BUFFER_BUSY_BITS)) - -/* * Sync all the buffers on one page.. * * If we have old buffers that are locked, we'll @@ -2352,7 +2121,7 @@ out: * This all is required so that we can free up memory * later. */ -static int sync_page_buffers(struct buffer_head *bh, int wait) +static void sync_page_buffers(struct buffer_head *bh, int wait) { struct buffer_head * tmp = bh; @@ -2365,19 +2134,15 @@ static int sync_page_buffers(struct buffer_head *bh, int wait) } else if (buffer_dirty(p)) ll_rw_block(WRITE, 1, &p); } while (tmp != bh); - - do { - struct buffer_head *p = tmp; - tmp = tmp->b_this_page; - if (buffer_busy(p)) - return 0; - } while (tmp != bh); - - /* Success. Now try_to_free_buffers can free the page. */ - return 1; } /* + * Can the buffer be thrown out? + */ +#define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock) | (1<<BH_Protected)) +#define buffer_busy(bh) (atomic_read(&(bh)->b_count) | ((bh)->b_state & BUFFER_BUSY_BITS)) + +/* * try_to_free_buffers() checks if all the buffers on this particular page * are unused, and free's the page if so. * @@ -2393,7 +2158,6 @@ int try_to_free_buffers(struct page * page, int wait) struct buffer_head * tmp, * bh = page->buffers; int index = BUFSIZE_INDEX(bh->b_size); -again: spin_lock(&lru_list_lock); write_lock(&hash_table_lock); spin_lock(&free_list[index].lock); @@ -2415,10 +2179,8 @@ again: /* The buffer can be either on the regular * queues or on the free list.. */ - if (p->b_dev != B_FREE) { - remove_inode_queue(p); + if (p->b_dev != B_FREE) __remove_from_queues(p); - } else __remove_from_free_list(p, index); __put_unused_buffer_head(p); @@ -2441,8 +2203,7 @@ busy_buffer_page: spin_unlock(&free_list[index].lock); write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); - if (sync_page_buffers(bh, wait)) - goto again; + sync_page_buffers(bh, wait); return 0; } @@ -2738,7 +2499,7 @@ asmlinkage long sys_bdflush(int func, long data) * the syscall above, but now we launch it ourselves internally with * kernel_thread(...) directly after the first thread in init/main.c */ -int bdflush(void *sem) +int bdflush(void * unused) { struct task_struct *tsk = current; int flushed; @@ -2760,8 +2521,6 @@ int bdflush(void *sem) recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); - up((struct semaphore *)sem); - for (;;) { CHECK_EMERGENCY_SYNC @@ -2796,7 +2555,7 @@ int bdflush(void *sem) * You don't need to change your userspace configuration since * the userspace `update` will do_exit(0) at the first sys_bdflush(). */ -int kupdate(void *sem) +int kupdate(void * unused) { struct task_struct * tsk = current; int interval; @@ -2812,8 +2571,6 @@ int kupdate(void *sem) recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); - up((struct semaphore *)sem); - for (;;) { /* update interval */ interval = bdf_prm.b_un.interval; @@ -2847,11 +2604,8 @@ int kupdate(void *sem) static int __init bdflush_init(void) { - DECLARE_MUTEX_LOCKED(sem); - kernel_thread(bdflush, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - down(&sem); - kernel_thread(kupdate, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - down(&sem); + kernel_thread(bdflush, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + kernel_thread(kupdate, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); return 0; } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 0faf29663..e949f7986 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -55,7 +55,7 @@ static void coda_prepare_fakefile(struct inode *coda_inode, struct dentry *open_dentry); static int coda_venus_readdir(struct file *filp, void *dirent, filldir_t filldir); -int coda_fsync(struct file *, struct dentry *dentry, int); +int coda_fsync(struct file *, struct dentry *dentry); int coda_hasmknod = 0; diff --git a/fs/coda/file.c b/fs/coda/file.c index 704b4d00b..9aecd716a 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -40,7 +40,7 @@ coda_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) } /* exported from this file (used for dirs) */ -int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) { struct inode *inode = coda_dentry->d_inode; struct dentry cont_dentry; @@ -60,10 +60,10 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) cont_dentry.d_inode = (struct inode *)inode->i_mapping->host; down(&cont_dentry.d_inode->i_sem); - result = file_fsync(NULL, &cont_dentry, datasync); + result = file_fsync(NULL, &cont_dentry); up(&cont_dentry.d_inode->i_sem); - if ( !datasync && result == 0 ) { + if ( result == 0 ) { lock_kernel(); result = venus_fsync(inode->i_sb, coda_i2f(inode)); unlock_kernel(); diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 582ea7000..45025e871 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -417,7 +417,7 @@ int init_coda_psdev(void) devfs_handle = devfs_mk_dir (NULL, "coda", 4, NULL); devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE, CODA_PSDEV_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &coda_psdev_fops, NULL); memset(&coda_upc_comm, 0, sizeof(coda_upc_comm)); memset(&coda_super_info, 0, sizeof(coda_super_info)); diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 9f1e8b06f..0d3d15177 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -451,6 +451,31 @@ Added CONFIG_DEVFS_MOUNT. Work sponsored by SGI. v0.96 + 20000608 Richard Gooch <rgooch@atnf.csiro.au> + Disabled multi-mount capability (use VFS bindings instead). + Work sponsored by SGI. + v0.97 + 20000610 Richard Gooch <rgooch@atnf.csiro.au> + Switched to FS_SINGLE to disable multi-mounts. + 20000612 Richard Gooch <rgooch@atnf.csiro.au> + Removed module support. + Removed multi-mount code. + Removed compatibility macros: VFS has changed too much. + Work sponsored by SGI. + v0.98 + 20000614 Richard Gooch <rgooch@atnf.csiro.au> + Merged devfs inode into devfs entry. + Work sponsored by SGI. + v0.99 + 20000619 Richard Gooch <rgooch@atnf.csiro.au> + Removed dead code in <devfs_register> which used to call + <free_dentries>. + Work sponsored by SGI. + v0.100 + 20000621 Richard Gooch <rgooch@atnf.csiro.au> + Changed interface to <devfs_register>. + Work sponsored by SGI. + v0.101 */ #include <linux/types.h> #include <linux/errno.h> @@ -485,29 +510,9 @@ #include <asm/bitops.h> #include <asm/atomic.h> -#define DEVFS_VERSION "0.96 (20000430)" +#define DEVFS_VERSION "0.101 (20000621)" -#ifndef DEVFS_NAME -# define DEVFS_NAME "devfs" -#endif - -/* Compatibility for 2.2.x kernel series */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)) -# define init_waitqueue_head(p) init_waitqueue(p) -# define DECLARE_WAITQUEUE(wait, p) struct wait_queue wait = {p, NULL} -typedef struct wait_queue *wait_queue_head_t; -#endif -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,6)) -# define D_ALLOC_ROOT(inode) d_alloc_root (inode, NULL) -#else -# define D_ALLOC_ROOT(inode) d_alloc_root (inode) -#endif -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)) -# define SETUP_STATIC -# define __setup(a,b) -#else -# define SETUP_STATIC static -#endif +#define DEVFS_NAME "devfs" #define INODE_TABLE_INC 250 #define FIRST_INODE 1 @@ -604,6 +609,19 @@ struct fifo_type gid_t gid; }; +struct devfs_inode /* This structure is for "persistent" inode storage */ +{ + time_t atime; + time_t mtime; + time_t ctime; + unsigned int ino; /* Inode number as seen in the VFS */ + struct dentry *dentry; + umode_t mode; + uid_t uid; + gid_t gid; + nlink_t nlink; +}; + struct devfs_entry { void *info; @@ -619,8 +637,7 @@ struct devfs_entry struct devfs_entry *next; /* Next entry in the parent directory */ struct devfs_entry *parent; /* The parent directory */ struct devfs_entry *slave; /* Another entry to unregister */ - struct devfs_inode *first_inode; - struct devfs_inode *last_inode; + struct devfs_inode inode; umode_t mode; unsigned short namelen; /* I think 64k+ filenames are a way off... */ unsigned char registered:1; @@ -634,26 +651,6 @@ struct devfs_entry /* The root of the device tree */ static struct devfs_entry *root_entry = NULL; -struct devfs_inode /* This structure is for "persistent" inode storage */ -{ - time_t atime; - time_t mtime; - time_t ctime; - unsigned int ino; /* Inode number as seen in the VFS */ - struct devfs_entry *de; - struct fs_info *fs_info; - struct devfs_inode *prev; /* This pair are used to associate a list of */ - struct devfs_inode *next; /* inodes (one per FS) for a devfs entry */ - struct dentry *dentry; -#ifdef CONFIG_DEVFS_TUNNEL - struct dentry *covered; -#endif - umode_t mode; - uid_t uid; - gid_t gid; - nlink_t nlink; -}; - struct devfsd_buf_entry { void *data; @@ -667,7 +664,7 @@ struct fs_info /* This structure is for each mounted devfs */ { unsigned int num_inodes; /* Number of inodes created */ unsigned int table_size; /* Size of the inode pointer table */ - struct devfs_inode **table; + struct devfs_entry **table; struct super_block *sb; volatile struct devfsd_buf_entry *devfsd_buffer; volatile unsigned int devfsd_buf_in; @@ -680,23 +677,15 @@ struct fs_info /* This structure is for each mounted devfs */ atomic_t devfsd_overrun_count; wait_queue_head_t devfsd_wait_queue; wait_queue_head_t revalidate_wait_queue; - struct fs_info *prev; - struct fs_info *next; - unsigned char require_explicit:1; }; -static struct fs_info *first_fs = NULL; -static struct fs_info *last_fs = NULL; +static struct fs_info fs_info; static unsigned int next_devnum_char = MIN_DEVNUM; static unsigned int next_devnum_block = MIN_DEVNUM; static const int devfsd_buf_size = PAGE_SIZE / sizeof(struct devfsd_buf_entry); #ifdef CONFIG_DEVFS_DEBUG -# ifdef MODULE -unsigned int devfs_debug = DEBUG_NONE; -# else static unsigned int devfs_debug_init __initdata = DEBUG_NONE; static unsigned int devfs_debug = DEBUG_NONE; -# endif #endif #ifdef CONFIG_DEVFS_MOUNT @@ -770,15 +759,40 @@ static struct devfs_entry *search_for_entry_in_dir (struct devfs_entry *parent, static struct devfs_entry *create_entry (struct devfs_entry *parent, const char *name,unsigned int namelen) { - struct devfs_entry *new; + struct devfs_entry *new, **table; + /* First ensure table size is enough */ + if (fs_info.num_inodes >= fs_info.table_size) + { + if ( ( table = kmalloc (sizeof *table * + (fs_info.table_size + INODE_TABLE_INC), + GFP_KERNEL) ) == NULL ) return NULL; + fs_info.table_size += INODE_TABLE_INC; +#ifdef CONFIG_DEVFS_DEBUG + if (devfs_debug & DEBUG_I_CREATE) + printk ("%s: create_entry(): grew inode table to: %u entries\n", + DEVFS_NAME, fs_info.table_size); +#endif + if (fs_info.table) + { + memcpy (table, fs_info.table, sizeof *table *fs_info.num_inodes); + kfree (fs_info.table); + } + fs_info.table = table; + } if ( name && (namelen < 1) ) namelen = strlen (name); if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL ) return NULL; + /* Magic: this will set the ctime to zero, thus subsequent lookups will + trigger the call to <update_devfs_inode_from_entry> */ memset (new, 0, sizeof *new + namelen); new->parent = parent; if (name) memcpy (new->name, name, namelen); new->namelen = namelen; + new->inode.ino = fs_info.num_inodes + FIRST_INODE; + new->inode.nlink = 1; + fs_info.table[fs_info.num_inodes] = new; + ++fs_info.num_inodes; if (parent == NULL) return new; new->prev = parent->u.dir.last; /* Insert into the parent directory's list of children */ @@ -788,6 +802,36 @@ static struct devfs_entry *create_entry (struct devfs_entry *parent, return new; } /* End Function create_entry */ +static void update_devfs_inode_from_entry (struct devfs_entry *de) +{ + if (de == NULL) return; + if ( S_ISDIR (de->mode) ) + { + de->inode.mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + de->inode.uid = 0; + de->inode.gid = 0; + } + else if ( S_ISLNK (de->mode) ) + { + de->inode.mode = S_IFLNK | S_IRUGO | S_IXUGO; + de->inode.uid = 0; + de->inode.gid = 0; + } + else if ( S_ISFIFO (de->mode) ) + { + de->inode.mode = de->mode; + de->inode.uid = de->u.fifo.uid; + de->inode.gid = de->u.fifo.gid; + } + else + { + if (de->u.fcb.auto_owner) + de->inode.mode = (de->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + else de->inode.mode = de->mode; + de->inode.uid = de->u.fcb.default_uid; + de->inode.gid = de->u.fcb.default_gid; + } +} /* End Function update_devfs_inode_from_entry */ /** * get_root_entry - Get the root devfs entry. @@ -804,6 +848,8 @@ static struct devfs_entry *get_root_entry (void) if ( ( root_entry = create_entry (NULL, NULL, 0) ) == NULL ) return NULL; root_entry->registered = TRUE; root_entry->mode = S_IFDIR; + /* Force an inode update, because lookup() is never done for the root */ + update_devfs_inode_from_entry (root_entry); /* And create the entry for ".devfsd" */ if ( ( new = create_entry (root_entry, ".devfsd", 0) ) == NULL ) return NULL; @@ -1002,7 +1048,7 @@ static struct devfs_entry *find_entry (devfs_handle_t dir, return find_by_dev (root_entry, major, minor, type); } /* End Function find_entry */ -static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) +static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode) { struct fs_info *fs_info; @@ -1012,7 +1058,7 @@ static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) if (fs_info == NULL) return NULL; if (inode->i_ino - FIRST_INODE >= fs_info->num_inodes) return NULL; return fs_info->table[inode->i_ino - FIRST_INODE]; -} /* End Function get_devfs_inode_from_vfs_inode */ +} /* End Function get_devfs_entry_from_vfs_inode */ /** @@ -1022,21 +1068,17 @@ static struct devfs_inode *get_devfs_inode_from_vfs_inode (struct inode *inode) static void free_dentries (struct devfs_entry *de) { - struct devfs_inode *di; struct dentry *dentry; - for (di = de->first_inode; di != NULL; di = di->next) + dentry = de->inode.dentry; + if (dentry != NULL) { - dentry = di->dentry; - if (dentry != NULL) - { - dget (dentry); - di->dentry = NULL; - /* Forcefully remove the inode */ - if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; - d_drop (dentry); - dput (dentry); - } + dget (dentry); + de->inode.dentry = NULL; + /* Forcefully remove the inode */ + if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; + d_drop (dentry); + dput (dentry); } } /* End Function free_dentries */ @@ -1155,14 +1197,9 @@ static int devfsd_notify_one (void *data, unsigned int type, umode_t mode, static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) { - struct fs_info *fs_info; - - for (fs_info = first_fs; fs_info != NULL; fs_info = fs_info->next) - { - if (devfsd_notify_one (de, type, de->mode, current->euid, - current->egid, fs_info) && wait) - wait_for_devfsd_finished (fs_info); - } + if (devfsd_notify_one (de, type, de->mode, current->euid, + current->egid, &fs_info) && wait) + wait_for_devfsd_finished (&fs_info); } /* End Function devfsd_notify */ @@ -1171,15 +1208,10 @@ static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) * @dir: The handle to the parent devfs directory entry. If this is %NULL the * new name is relative to the root of the devfs. * @name: The name of the entry. - * @namelen: The number of characters in @name, not including a %NULL - * terminator. If this is 0, then @name must be %NULL-terminated and the - * length is computed internally. * @flags: A set of bitwise-ORed flags (DEVFS_FL_*). * @major: The major number. Not needed for regular files. * @minor: The minor number. Not needed for regular files. * @mode: The default file mode. - * @uid: The default UID of the file. - * @guid: The default GID of the file. * @ops: The &file_operations or &block_device_operations structure. * This must not be externally deallocated. * @info: An arbitrary pointer which will be written to the @private_data @@ -1191,12 +1223,10 @@ static void devfsd_notify (struct devfs_entry *de, unsigned int type, int wait) * On failure %NULL is returned. */ -devfs_handle_t devfs_register (devfs_handle_t dir, - const char *name, unsigned int namelen, +devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, unsigned int flags, unsigned int major, unsigned int minor, - umode_t mode, uid_t uid, gid_t gid, - void *ops, void *info) + umode_t mode, void *ops, void *info) { int is_new; struct devfs_entry *de; @@ -1231,7 +1261,6 @@ devfs_handle_t devfs_register (devfs_handle_t dir, DEVFS_NAME, name); return NULL; } - if (namelen < 1) namelen = strlen (name); if ( S_ISCHR (mode) && (flags & DEVFS_FL_AUTO_DEVNUM) ) { if (next_devnum_char >= MAX_DEVNUM) @@ -1256,7 +1285,8 @@ devfs_handle_t devfs_register (devfs_handle_t dir, minor = next_devnum_block & 0xff; ++next_devnum_block; } - de = search_for_entry (dir, name, namelen, TRUE, TRUE, &is_new, FALSE); + de = search_for_entry (dir, name, strlen (name), TRUE, TRUE, &is_new, + FALSE); if (de == NULL) { printk ("%s: devfs_register(): could not create entry: \"%s\"\n", @@ -1284,8 +1314,6 @@ devfs_handle_t devfs_register (devfs_handle_t dir, DEVFS_NAME, name); return NULL; } - /* If entry already exists free any dentries associated with it */ - if (de->registered) free_dentries (de); } de->registered = TRUE; if ( S_ISCHR (mode) || S_ISBLK (mode) ) @@ -1302,8 +1330,16 @@ devfs_handle_t devfs_register (devfs_handle_t dir, } de->info = info; de->mode = mode; - de->u.fcb.default_uid = uid; - de->u.fcb.default_gid = gid; + if (flags & DEVFS_FL_CURRENT_OWNER) + { + de->u.fcb.default_uid = current->uid; + de->u.fcb.default_gid = current->gid; + } + else + { + de->u.fcb.default_uid = 0; + de->u.fcb.default_gid = 0; + } de->registered = TRUE; de->u.fcb.ops = ops; de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE; @@ -1687,13 +1723,9 @@ int devfs_get_maj_min (devfs_handle_t de, unsigned int *major, devfs_handle_t devfs_get_handle_from_inode (struct inode *inode) { - struct devfs_inode *di; - if (!inode || !inode->i_sb) return NULL; if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL; - di = get_devfs_inode_from_vfs_inode (inode); - if (!di) return NULL; - return di->de; + return get_devfs_entry_from_vfs_inode (inode); } /* End Function devfs_get_handle_from_inode */ @@ -1756,19 +1788,14 @@ void *devfs_get_ops (devfs_handle_t de) int devfs_set_file_size (devfs_handle_t de, unsigned long size) { - struct devfs_inode *di; - if (de == NULL) return -EINVAL; if (!de->registered) return -EINVAL; if ( !S_ISREG (de->mode) ) return -EINVAL; if (de->u.fcb.u.file.size == size) return 0; de->u.fcb.u.file.size = size; - for (di = de->first_inode; di != NULL; di = di->next) - { - if (di->dentry == NULL) continue; - if (di->dentry->d_inode == NULL) continue; - di->dentry->d_inode->i_size = size; - } + if (de->inode.dentry == NULL) return 0; + if (de->inode.dentry->d_inode == NULL) return 0; + de->inode.dentry->d_inode->i_size = size; return 0; } /* End Function devfs_set_file_size */ @@ -1974,18 +2001,16 @@ int devfs_unregister_blkdev (unsigned int major, const char *name) return unregister_blkdev (major, name); } /* End Function devfs_unregister_blkdev */ -#ifndef MODULE - /** * devfs_setup - Process kernel boot options. * @str: The boot options after the "devfs=". */ -SETUP_STATIC int __init devfs_setup (char *str) +static int __init devfs_setup (char *str) { while ( (*str != '\0') && !isspace (*str) ) { -# ifdef CONFIG_DEVFS_DEBUG +#ifdef CONFIG_DEVFS_DEBUG if (strncmp (str, "dall", 4) == 0) { devfs_debug_init |= DEBUG_ALL; @@ -2037,7 +2062,7 @@ SETUP_STATIC int __init devfs_setup (char *str) str += 8; } else -# endif /* CONFIG_DEVFS_DEBUG */ +#endif /* CONFIG_DEVFS_DEBUG */ if (strncmp (str, "show", 4) == 0) { boot_options |= OPTION_SHOW; @@ -2068,8 +2093,6 @@ SETUP_STATIC int __init devfs_setup (char *str) __setup("devfs=", devfs_setup); -#endif /* !MODULE */ - EXPORT_SYMBOL(devfs_register); EXPORT_SYMBOL(devfs_unregister); EXPORT_SYMBOL(devfs_mk_symlink); @@ -2094,101 +2117,6 @@ EXPORT_SYMBOL(devfs_register_blkdev); EXPORT_SYMBOL(devfs_unregister_chrdev); EXPORT_SYMBOL(devfs_unregister_blkdev); -#ifdef CONFIG_DEVFS_DEBUG -MODULE_PARM(devfs_debug, "i"); -#endif - -static void update_devfs_inode_from_entry (struct devfs_inode *di) -{ - if (di == NULL) return; - if (di->de == NULL) - { - printk ("%s: update_devfs_inode_from_entry(): NULL entry\n", - DEVFS_NAME); - return; - } - if ( S_ISDIR (di->de->mode) ) - { - di->mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - di->uid = 0; - di->gid = 0; - } - else if ( S_ISLNK (di->de->mode) ) - { - di->mode = S_IFLNK | S_IRUGO | S_IXUGO; - di->uid = 0; - di->gid = 0; - } - else if ( S_ISFIFO (di->de->mode) ) - { - di->mode = di->de->mode; - di->uid = di->de->u.fifo.uid; - di->gid = di->de->u.fifo.gid; - } - else - { - if (di->de->u.fcb.auto_owner) - di->mode = (di->de->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; - else di->mode = di->de->mode; - di->uid = di->de->u.fcb.default_uid; - di->gid = di->de->u.fcb.default_gid; - } -} /* End Function update_devfs_inode_from_entry */ - - -/** - * create_devfs_inode - Create a devfs inode entry. - * @de: The devfs entry to associate the new inode with. - * @fs_info: The FS info. - * - * Returns a pointer to the devfs inode on success, else %NULL. - */ - -static struct devfs_inode *create_devfs_inode (struct devfs_entry *de, - struct fs_info *fs_info) -{ - struct devfs_inode *di, **table; - - /* First ensure table size is enough */ - if (fs_info->num_inodes >= fs_info->table_size) - { - if ( ( table = kmalloc (sizeof *table * - (fs_info->table_size + INODE_TABLE_INC), - GFP_KERNEL) ) == NULL ) return NULL; - fs_info->table_size += INODE_TABLE_INC; -#ifdef CONFIG_DEVFS_DEBUG - if (devfs_debug & DEBUG_I_CREATE) - printk ("%s: create_devfs_inode(): grew inode table to: %u entries\n", - DEVFS_NAME, fs_info->table_size); -#endif - if (fs_info->table) - { - memcpy (table, fs_info->table, sizeof *table *fs_info->num_inodes); - kfree (fs_info->table); - } - fs_info->table = table; - } - if ( ( di = kmalloc (sizeof *di, GFP_KERNEL) ) == NULL ) return NULL; - memset (di, 0, sizeof *di); - di->ino = fs_info->num_inodes + FIRST_INODE; - di->nlink = 1; - fs_info->table[fs_info->num_inodes] = di; - ++fs_info->num_inodes; - di->de = de; - di->fs_info = fs_info; - di->prev = de->last_inode; - if (de->first_inode == NULL) de->first_inode = di; - else de->last_inode->next = di; - de->last_inode = di; - update_devfs_inode_from_entry (di); -#ifdef CONFIG_DEVFS_DEBUG - if (devfs_debug & DEBUG_I_CREATE) - printk ("%s: create_devfs_inode(): new di(%u): %p\n", - DEVFS_NAME, di->ino, di); -#endif - return di; -} /* End Function create_devfs_inode */ - /** * try_modload - Notify devfsd of an inode lookup. @@ -2224,34 +2152,6 @@ static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info, return 0; } /* End Function try_modload */ -static void delete_fs (struct fs_info *fs_info) -{ - unsigned int count; - struct devfs_inode *di; - struct devfs_entry *de; - - if (fs_info == NULL) return; - for (count = 0; count < fs_info->num_inodes; ++count) - { - /* Unhook this inode from the devfs tree */ - di = fs_info->table[count]; - de = di->de; - if (di->prev == NULL) de->first_inode = di->next; - else di->prev->next = di->next; - if (di->next == NULL) de->last_inode = di->prev; - else di->next->prev = di->prev; - memset (di, 0, sizeof *di); - kfree (di); - } - if (fs_info->table) kfree (fs_info->table); - if (fs_info->prev == NULL) first_fs = fs_info->next; - else fs_info->prev->next = fs_info->next; - if (fs_info->next == NULL) last_fs = fs_info->prev; - else fs_info->next->prev = fs_info->prev; - memset (fs_info, 0, sizeof *fs_info); - kfree (fs_info); -} /* End Function delete_fs */ - /** * check_disc_changed - Check if a removable disc was changed. @@ -2345,19 +2245,19 @@ static struct inode_operations devfs_symlink_iops; static void devfs_read_inode (struct inode *inode) { - struct devfs_inode *di; + struct devfs_entry *de; - di = get_devfs_inode_from_vfs_inode (inode); - if (di == NULL) + de = get_devfs_entry_from_vfs_inode (inode); + if (de == NULL) { - printk ("%s: read_inode(%d): VFS inode: %p NO devfs_inode\n", + printk ("%s: read_inode(%d): VFS inode: %p NO devfs_entry\n", DEVFS_NAME, (int) inode->i_ino, inode); return; } #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_READ) - printk ("%s: read_inode(%d): VFS inode: %p devfs_inode: %p\n", - DEVFS_NAME, (int) inode->i_ino, inode, di); + printk ("%s: read_inode(%d): VFS inode: %p devfs_entry: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, de); #endif inode->i_size = 0; inode->i_blocks = 0; @@ -2365,39 +2265,39 @@ static void devfs_read_inode (struct inode *inode) inode->i_op = &devfs_iops; inode->i_fop = &devfs_fops; inode->i_rdev = NODEV; - if ( S_ISCHR (di->mode) ) + if ( S_ISCHR (de->inode.mode) ) { - inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, - di->de->u.fcb.u.device.minor); + inode->i_rdev = MKDEV (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor); } - else if ( S_ISBLK (di->mode) ) + else if ( S_ISBLK (de->inode.mode) ) { - inode->i_rdev = MKDEV (di->de->u.fcb.u.device.major, - di->de->u.fcb.u.device.minor); + inode->i_rdev = MKDEV (de->u.fcb.u.device.major, + de->u.fcb.u.device.minor); inode->i_bdev = bdget (inode->i_rdev); if (inode->i_bdev) { - if (!inode->i_bdev->bd_op && di->de->u.fcb.ops) - inode->i_bdev->bd_op = di->de->u.fcb.ops; + if (!inode->i_bdev->bd_op && de->u.fcb.ops) + inode->i_bdev->bd_op = de->u.fcb.ops; } else printk ("%s: read_inode(%d): no block device from bdget()\n", DEVFS_NAME, (int) inode->i_ino); } - else if ( S_ISFIFO (di->mode) ) inode->i_fop = &def_fifo_fops; - else if ( S_ISREG (di->mode) ) inode->i_size = di->de->u.fcb.u.file.size; - else if ( S_ISDIR (di->mode) ) inode->i_op = &devfs_dir_iops; - else if ( S_ISLNK (di->mode) ) + else if ( S_ISFIFO (de->inode.mode) ) inode->i_fop = &def_fifo_fops; + else if ( S_ISREG (de->inode.mode) ) inode->i_size = de->u.fcb.u.file.size; + else if ( S_ISDIR (de->inode.mode) ) inode->i_op = &devfs_dir_iops; + else if ( S_ISLNK (de->inode.mode) ) { inode->i_op = &devfs_symlink_iops; - inode->i_size = di->de->u.symlink.length; - } - inode->i_mode = di->mode; - inode->i_uid = di->uid; - inode->i_gid = di->gid; - inode->i_atime = di->atime; - inode->i_mtime = di->mtime; - inode->i_ctime = di->ctime; - inode->i_nlink = di->nlink; + inode->i_size = de->u.symlink.length; + } + inode->i_mode = de->inode.mode; + inode->i_uid = de->inode.uid; + inode->i_gid = de->inode.gid; + inode->i_atime = de->inode.atime; + inode->i_mtime = de->inode.mtime; + inode->i_ctime = de->inode.ctime; + inode->i_nlink = de->inode.nlink; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_READ) printk ("%s: mode: 0%o uid: %d gid: %d\n", @@ -2409,7 +2309,7 @@ static void devfs_read_inode (struct inode *inode) static void devfs_write_inode (struct inode *inode, int unused) { int index; - struct devfs_inode *di; + struct devfs_entry *de; struct fs_info *fs_info = inode->i_sb->u.generic_sbp; if (inode->i_ino < FIRST_INODE) return; @@ -2420,57 +2320,43 @@ static void devfs_write_inode (struct inode *inode, int unused) DEVFS_NAME, inode->i_ino); return; } - di = fs_info->table[index]; + de = fs_info->table[index]; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_WRITE) { - printk ("%s: write_inode(%d): VFS inode: %p devfs_inode: %p\n", - DEVFS_NAME, (int) inode->i_ino, inode, di); + printk ("%s: write_inode(%d): VFS inode: %p devfs_entry: %p\n", + DEVFS_NAME, (int) inode->i_ino, inode, de); printk ("%s: mode: 0%o uid: %d gid: %d\n", DEVFS_NAME, (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid); } #endif - di->mode = inode->i_mode; - di->uid = inode->i_uid; - di->gid = inode->i_gid; - di->atime = inode->i_atime; - di->mtime = inode->i_mtime; - di->ctime = inode->i_ctime; + de->inode.mode = inode->i_mode; + de->inode.uid = inode->i_uid; + de->inode.gid = inode->i_gid; + de->inode.atime = inode->i_atime; + de->inode.mtime = inode->i_mtime; + de->inode.ctime = inode->i_ctime; } /* End Function devfs_write_inode */ static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr) { int retval; - struct devfs_inode *di; + struct devfs_entry *de; struct inode *inode = dentry->d_inode; struct fs_info *fs_info = inode->i_sb->u.generic_sbp; - di = get_devfs_inode_from_vfs_inode (inode); - if (di == NULL) return -ENODEV; + de = get_devfs_entry_from_vfs_inode (inode); + if (de == NULL) return -ENODEV; retval = inode_change_ok (inode, iattr); if (retval != 0) return retval; inode_setattr (inode, iattr); if ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) - devfsd_notify_one (di->de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, + devfsd_notify_one (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info); return 0; } /* End Function devfs_notify_change */ -static void devfs_put_super (struct super_block *sb) -{ - struct fs_info *fs_info = sb->u.generic_sbp; - -#ifdef CONFIG_DEVFS_DEBUG - if (devfs_debug & DEBUG_S_PUT) - printk ("%s: put_super(): devfs ptr: %p\n", DEVFS_NAME, fs_info); -#endif -#ifdef CONFIG_DEVFS_TUNNEL - dput (fs_info->table[0]->covered); -#endif - delete_fs (fs_info); -} /* End Function devfs_put_super */ - static int devfs_statfs (struct super_block *sb, struct statfs *buf) { buf->f_type = DEVFS_SUPER_MAGIC; @@ -2486,7 +2372,6 @@ static struct super_operations devfs_sops = { read_inode: devfs_read_inode, write_inode: devfs_write_inode, - put_super: devfs_put_super, statfs: devfs_statfs, }; @@ -2501,21 +2386,22 @@ static struct super_operations devfs_sops = */ static struct inode *get_vfs_inode (struct super_block *sb, - struct devfs_inode *di, + struct devfs_entry *de, struct dentry *dentry) { struct inode *inode; - if (di->dentry != NULL) + if (de->inode.dentry != NULL) { - printk ("%s: get_vfs_inode(%u): old di->dentry: %p \"%s\" new dentry: %p \"%s\"\n", - DEVFS_NAME, di->ino, di->dentry, di->dentry->d_name.name, + printk ("%s: get_vfs_inode(%u): old de->inode.dentry: %p \"%s\" new dentry: %p \"%s\"\n", + DEVFS_NAME, de->inode.ino, + de->inode.dentry, de->inode.dentry->d_name.name, dentry, dentry->d_name.name); - printk (" old inode: %p\n", di->dentry->d_inode); + printk (" old inode: %p\n", de->inode.dentry->d_inode); return NULL; } - if ( ( inode = iget (sb, di->ino) ) == NULL ) return NULL; - di->dentry = dentry; + if ( ( inode = iget (sb, de->inode.ino) ) == NULL ) return NULL; + de->inode.dentry = dentry; return inode; } /* End Function get_vfs_inode */ @@ -2533,7 +2419,6 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) int err, count; int stored = 0; struct fs_info *fs_info; - struct devfs_inode *di; struct devfs_entry *parent, *de; struct inode *inode = file->f_dentry->d_inode; @@ -2548,8 +2433,7 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) return -ENOTDIR; } fs_info = inode->i_sb->u.generic_sbp; - di = get_devfs_inode_from_vfs_inode (file->f_dentry->d_inode); - parent = di->de; + parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode); if ( (long) file->f_pos < 0 ) return -EINVAL; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_F_READDIR) @@ -2579,38 +2463,13 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) count = file->f_pos - 2; for (de = parent->u.dir.first; (de != NULL) && (count > 0); de = de->next) - { - if ( IS_HIDDEN (de) ) continue; - if (!fs_info->require_explicit) - { - --count; - continue; - } - /* Must search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - { - if (fs_info == di->fs_info) break; - } - if (di != NULL) --count; - } + if ( !IS_HIDDEN (de) ) --count; /* Now add all remaining entries */ for (; de != NULL; de = de->next) { if ( IS_HIDDEN (de) ) continue; - /* Must search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - { - if (fs_info == di->fs_info) break; - } - if (di == NULL) - { - if (fs_info->require_explicit) continue; - /* Have to create the inode right now to get the inum */ - di = create_devfs_inode (de, fs_info); - if (di == NULL) return -ENOMEM; - } err = (*filldir) (dirent, de->name, de->namelen, - file->f_pos, di->ino); + file->f_pos, de->inode.ino); if (err == -EINVAL) break; if (err < 0) return err; file->f_pos++; @@ -2625,16 +2484,15 @@ static int devfs_open (struct inode *inode, struct file *file) { int err; struct fcb_type *df; - struct devfs_inode *di; - struct dentry *dentry = file->f_dentry; + struct devfs_entry *de; struct fs_info *fs_info = inode->i_sb->u.generic_sbp; - di = get_devfs_inode_from_vfs_inode (inode); - if (di == NULL) return -ENODEV; - if ( S_ISDIR (di->de->mode) ) return 0; - df = &di->de->u.fcb; - if (!di->de->registered) return -ENODEV; - file->private_data = di->de->info; + de = get_devfs_entry_from_vfs_inode (inode); + if (de == NULL) return -ENODEV; + if ( S_ISDIR (de->mode) ) return 0; + df = &de->u.fcb; + if (!de->registered) return -ENODEV; + file->private_data = de->info; if ( S_ISBLK (inode->i_mode) ) { file->f_op = &def_blk_fops; @@ -2651,21 +2509,20 @@ static int devfs_open (struct inode *inode, struct file *file) } if (err < 0) return err; /* Open was successful */ - df->open = TRUE; - if (dentry->d_count != 1) return 0; /* No fancy operations */ - /* This is the first open */ + if (df->open) return 0; + df->open = TRUE; /* This is the first open */ if (df->auto_owner) { /* Change the ownership/protection */ - di->mode = (di->mode & ~S_IALLUGO) | (di->de->mode & S_IRWXUGO); - di->uid = current->euid; - di->gid = current->egid; - inode->i_mode = di->mode; - inode->i_uid = di->uid; - inode->i_gid = di->gid; + de->inode.mode = (de->inode.mode & ~S_IALLUGO) |(de->mode & S_IRWXUGO); + de->inode.uid = current->euid; + de->inode.gid = current->egid; + inode->i_mode = de->inode.mode; + inode->i_uid = de->inode.uid; + inode->i_gid = de->inode.gid; } if (df->aopen_notify) - devfsd_notify_one (di->de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode, + devfsd_notify_one (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode, current->euid, current->egid, fs_info); return 0; } /* End Function devfs_open */ @@ -2705,21 +2562,17 @@ static void devfs_d_release (struct dentry *dentry) static void devfs_d_iput (struct dentry *dentry, struct inode *inode) { - struct devfs_inode *di; + struct devfs_entry *de; - di = get_devfs_inode_from_vfs_inode (inode); + de = get_devfs_entry_from_vfs_inode (inode); #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_D_IPUT) - printk ("%s: d_iput(): dentry: %p inode: %p di: %p di->dentry: %p\n", - DEVFS_NAME, dentry, inode, di, di->dentry); + printk ("%s: d_iput(): dentry: %p inode: %p de: %p de->dentry: %p\n", + DEVFS_NAME, dentry, inode, de, de->inode.dentry); #endif - if (di->dentry == dentry) + if (de->inode.dentry == dentry) { - di->dentry = NULL; -#ifdef CONFIG_DEVFS_TUNNEL - dput (di->covered); - di->covered = NULL; -#endif + de->inode.dentry = NULL; } iput (inode); } /* End Function devfs_d_iput */ @@ -2751,7 +2604,7 @@ static struct dentry_operations devfs_wait_dops = static int devfs_d_delete (struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct devfs_inode *di; + struct devfs_entry *de; struct fs_info *fs_info; if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops; @@ -2766,29 +2619,28 @@ static int devfs_d_delete (struct dentry *dentry) return 1; } fs_info = inode->i_sb->u.generic_sbp; - di = get_devfs_inode_from_vfs_inode (inode); + de = get_devfs_entry_from_vfs_inode (inode); #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_D_DELETE) - printk ("%s: d_delete(): dentry: %p inode: %p devfs_inode: %p\n", - DEVFS_NAME, dentry, inode, di); + printk ("%s: d_delete(): dentry: %p inode: %p devfs_entry: %p\n", + DEVFS_NAME, dentry, inode, de); #endif - if (di == NULL) return 0; - if (di->de == NULL) return 0; - if ( !S_ISCHR (di->mode) && !S_ISBLK (di->mode) && !S_ISREG (di->mode) ) + if (de == NULL) return 0; + if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) ) return 0; - if (!di->de->u.fcb.open) return 0; - di->de->u.fcb.open = FALSE; - if (di->de->u.fcb.aopen_notify) - devfsd_notify_one (di->de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, + if (!de->u.fcb.open) return 0; + de->u.fcb.open = FALSE; + if (de->u.fcb.aopen_notify) + devfsd_notify_one (de, DEVFSD_NOTIFY_CLOSE, inode->i_mode, current->euid, current->egid, fs_info); - if (!di->de->u.fcb.auto_owner) return 0; + if (!de->u.fcb.auto_owner) return 0; /* Change the ownership/protection back */ - di->mode = (di->mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; - di->uid = di->de->u.fcb.default_uid; - di->gid = di->de->u.fcb.default_gid; - inode->i_mode = di->mode; - inode->i_uid = di->uid; - inode->i_gid = di->gid; + de->inode.mode = (de->inode.mode & ~S_IALLUGO) | S_IRUGO | S_IWUGO; + de->inode.uid = de->u.fcb.default_uid; + de->inode.gid = de->u.fcb.default_gid; + inode->i_mode = de->inode.mode; + inode->i_uid = de->inode.uid; + inode->i_gid = de->inode.gid; return 0; } /* End Function devfs_d_delete */ @@ -2802,7 +2654,6 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) { if ( !dentry->d_inode && is_devfsd_or_child (fs_info) ) { - struct devfs_inode *di = NULL; struct inode *inode; #ifdef CONFIG_DEVFS_DEBUG @@ -2816,36 +2667,27 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) printk ("%s: d_revalidate(): dentry: %p name: \"%s\" by: \"%s\"\n", DEVFS_NAME, dentry, txt, current->comm); #endif - if (de) - { - /* Search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - if (di->fs_info == fs_info) break; - } if (de == NULL) { devfs_handle_t parent; - struct devfs_inode *pi; - pi = get_devfs_inode_from_vfs_inode (dir); - parent = pi->de; + parent = get_devfs_entry_from_vfs_inode (dir); de = search_for_entry_in_dir (parent, dentry->d_name.name, dentry->d_name.len, FALSE); } if (de == NULL) return 1; /* Create an inode, now that the driver information is available */ - if (di == NULL) di = create_devfs_inode (de, fs_info); - else if (de->no_persistence) update_devfs_inode_from_entry (di); - else if (di->ctime == 0) update_devfs_inode_from_entry (di); - else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); - if (di == NULL) return 1; - if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + if (de->no_persistence) update_devfs_inode_from_entry (de); + else if (de->inode.ctime == 0) update_devfs_inode_from_entry (de); + else de->inode.mode = + (de->mode & ~S_IALLUGO) | (de->inode.mode & S_IALLUGO); + if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) return 1; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_LOOKUP) - printk ("%s: d_revalidate(): new VFS inode(%u): %p devfs_inode: %p\n", - DEVFS_NAME, di->ino, inode, di); + printk ("%s: d_revalidate(): new VFS inode(%u): %p devfs_entry: %p\n", + DEVFS_NAME, de->inode.ino, inode, de); #endif d_instantiate (dentry, inode); return 1; @@ -2861,8 +2703,6 @@ static int devfs_d_revalidate_wait (struct dentry *dentry, int flags) static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) { struct fs_info *fs_info; - struct devfs_inode *di = NULL; - struct devfs_inode *pi; struct devfs_entry *parent, *de; struct inode *inode; char txt[STRING_LENGTH]; @@ -2887,32 +2727,13 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) #endif fs_info = dir->i_sb->u.generic_sbp; /* First try to get the devfs entry for this directory */ - pi = get_devfs_inode_from_vfs_inode (dir); - if (pi == NULL) return ERR_PTR (-EINVAL); - parent = pi->de; + parent = get_devfs_entry_from_vfs_inode (dir); + if (parent == NULL) return ERR_PTR (-EINVAL); if (!parent->registered) return ERR_PTR (-ENOENT); /* Try to reclaim an existing devfs entry */ de = search_for_entry_in_dir (parent, dentry->d_name.name, dentry->d_name.len, FALSE); - if (de) - { - /* Search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - if (di->fs_info == fs_info) break; - } - if (fs_info->require_explicit) - { - if (di == NULL) - { - /* Make the dentry negative so a subsequent operation can deal - with it (for the benefit of mknod()). Leaving the dentry - unhashed will cause <lock_parent> to fail which in turns causes - <do_mknod> to fail */ - d_add (dentry, NULL); - return NULL; - } - } if ( ( (de == NULL) || !de->registered ) && (parent->u.dir.num_removable > 0) && get_removable_partition (parent, dentry->d_name.name, @@ -2961,17 +2782,16 @@ static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry) d_add (dentry, NULL); /* Open the floodgates */ } /* Create an inode, now that the driver information is available */ - if (di == NULL) di = create_devfs_inode (de, fs_info); - else if (de->no_persistence) update_devfs_inode_from_entry (di); - else if (di->ctime == 0) update_devfs_inode_from_entry (di); - else di->mode = (de->mode & ~S_IALLUGO) | (di->mode & S_IALLUGO); - if (di == NULL) return ERR_PTR (-ENOMEM); - if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + if (de->no_persistence) update_devfs_inode_from_entry (de); + else if (de->inode.ctime == 0) update_devfs_inode_from_entry (de); + else de->inode.mode = + (de->mode & ~S_IALLUGO) | (de->inode.mode & S_IALLUGO); + if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) return ERR_PTR (-ENOMEM); #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_LOOKUP) - printk ("%s: lookup(): new VFS inode(%u): %p devfs_inode: %p\n", - DEVFS_NAME, di->ino, inode, di); + printk ("%s: lookup(): new VFS inode(%u): %p devfs_entry: %p\n", + DEVFS_NAME, de->inode.ino, inode, de); #endif d_instantiate (dentry, inode); /* Unlock directory semaphore, which will release any waiters. They will @@ -2998,7 +2818,7 @@ static int devfs_link (struct dentry *old_dentry, struct inode *dir, static int devfs_unlink (struct inode *dir, struct dentry *dentry) { - struct devfs_inode *di; + struct devfs_entry *de; #ifdef CONFIG_DEVFS_DEBUG char txt[STRING_LENGTH]; @@ -3014,12 +2834,12 @@ static int devfs_unlink (struct inode *dir, struct dentry *dentry) if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; if (!dentry || !dentry->d_inode) return -ENOENT; - di = get_devfs_inode_from_vfs_inode (dentry->d_inode); - if (di == NULL) return -ENOENT; - if (!di->de->registered) return -ENOENT; - di->de->registered = FALSE; - di->de->hide = TRUE; - free_dentries (di->de); + de = get_devfs_entry_from_vfs_inode (dentry->d_inode); + if (de == NULL) return -ENOENT; + if (!de->registered) return -ENOENT; + de->registered = FALSE; + de->hide = TRUE; + free_dentries (de); return 0; } /* End Function devfs_unlink */ @@ -3028,17 +2848,14 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry, { int err; struct fs_info *fs_info; - struct devfs_inode *pi; - struct devfs_inode *di = NULL; struct devfs_entry *parent, *de; struct inode *inode; if ( !dir || !S_ISDIR (dir->i_mode) ) return -ENOTDIR; fs_info = dir->i_sb->u.generic_sbp; /* First try to get the devfs entry for this directory */ - pi = get_devfs_inode_from_vfs_inode (dir); - if (pi == NULL) return -EINVAL; - parent = pi->de; + parent = get_devfs_entry_from_vfs_inode (dir); + if (parent == NULL) return -EINVAL; if (!parent->registered) return -ENOENT; err = devfs_mk_symlink (parent, dentry->d_name.name, dentry->d_name.len, DEVFS_FL_NONE, symname, 0, &de, NULL); @@ -3048,27 +2865,20 @@ static int devfs_symlink (struct inode *dir, struct dentry *dentry, DEVFS_NAME, err); #endif if (err < 0) return err; - /* Search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - { - if (di->fs_info == fs_info) break; - } - if (di == NULL) di = create_devfs_inode (de, fs_info); - if (di == NULL) return -ENOMEM; - di->mode = de->mode; - di->atime = CURRENT_TIME; - di->mtime = CURRENT_TIME; - di->ctime = CURRENT_TIME; - if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + de->inode.mode = de->mode; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) return -ENOMEM; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_DISABLED) printk ("%s: symlink(): new VFS inode(%u): %p dentry: %p\n", - DEVFS_NAME, di->ino, inode, dentry); + DEVFS_NAME, de->inode.ino, inode, dentry); #endif de->hide = FALSE; d_instantiate (dentry, inode); - devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info); return 0; } /* End Function devfs_symlink */ @@ -3077,8 +2887,6 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) { int is_new; struct fs_info *fs_info; - struct devfs_inode *di = NULL; - struct devfs_inode *pi; struct devfs_entry *parent, *de; struct inode *inode; @@ -3087,9 +2895,8 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) fs_info = dir->i_sb->u.generic_sbp; /* We are allowed to create the directory */ /* First try to get the devfs entry for this directory */ - pi = get_devfs_inode_from_vfs_inode (dir); - if (pi == NULL) return -EINVAL; - parent = pi->de; + parent = get_devfs_entry_from_vfs_inode (dir); + if (parent == NULL) return -EINVAL; if (!parent->registered) return -ENOENT; /* Try to reclaim an existing devfs entry, create if there isn't one */ de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, @@ -3110,28 +2917,21 @@ static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode) } de->mode = mode; de->u.dir.num_removable = 0; - /* Search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - { - if (di->fs_info == fs_info) break; - } - if (di == NULL) di = create_devfs_inode (de, fs_info); - if (di == NULL) return -ENOMEM; - di->mode = mode; - di->uid = current->euid; - di->gid = current->egid; - di->atime = CURRENT_TIME; - di->mtime = CURRENT_TIME; - di->ctime = CURRENT_TIME; - if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + de->inode.mode = mode; + de->inode.uid = current->euid; + de->inode.gid = current->egid; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) return -ENOMEM; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_DISABLED) printk ("%s: mkdir(): new VFS inode(%u): %p dentry: %p\n", - DEVFS_NAME, di->ino, inode, dentry); + DEVFS_NAME, de->inode.ino, inode, dentry); #endif d_instantiate (dentry, inode); - devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info); return 0; } /* End Function devfs_mkdir */ @@ -3140,7 +2940,6 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry) { int has_children = FALSE; struct fs_info *fs_info; - struct devfs_inode *di = NULL; struct devfs_entry *de, *child; struct inode *inode = dentry->d_inode; @@ -3148,9 +2947,8 @@ static int devfs_rmdir (struct inode *dir, struct dentry *dentry) if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL; if (inode == dir) return -EPERM; fs_info = dir->i_sb->u.generic_sbp; - di = get_devfs_inode_from_vfs_inode (inode); - if (di == NULL) return -ENOENT; - de = di->de; + de = get_devfs_entry_from_vfs_inode (inode); + if (de == NULL) return -ENOENT; if (!de->registered) return -ENOENT; if ( !S_ISDIR (de->mode) ) return -ENOTDIR; for (child = de->u.dir.first; child != NULL; child = child->next) @@ -3173,8 +2971,6 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, { int is_new; struct fs_info *fs_info; - struct devfs_inode *di = NULL; - struct devfs_inode *pi; struct devfs_entry *parent, *de; struct inode *inode; @@ -3197,9 +2993,8 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, !S_ISSOCK (mode) ) return -EPERM; /* We are allowed to create the node */ /* First try to get the devfs entry for this directory */ - pi = get_devfs_inode_from_vfs_inode (dir); - if (pi == NULL) return -EINVAL; - parent = pi->de; + parent = get_devfs_entry_from_vfs_inode (dir); + if (parent == NULL) return -EINVAL; if (!parent->registered) return -ENOENT; /* Try to reclaim an existing devfs entry, create if there isn't one */ de = search_for_entry (parent, dentry->d_name.name, dentry->d_name.len, @@ -3230,44 +3025,37 @@ static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode, de->registered = TRUE; de->show_unreg = FALSE; de->hide = FALSE; - /* Search for an inode for this FS */ - for (di = de->first_inode; di != NULL; di = di->next) - { - if (di->fs_info == fs_info) break; - } - if (di == NULL) di = create_devfs_inode (de, fs_info); - if (di == NULL) return -ENOMEM; - di->mode = mode; - di->uid = current->euid; - di->gid = current->egid; - di->atime = CURRENT_TIME; - di->mtime = CURRENT_TIME; - di->ctime = CURRENT_TIME; - if ( ( inode = get_vfs_inode (dir->i_sb, di, dentry) ) == NULL ) + de->inode.mode = mode; + de->inode.uid = current->euid; + de->inode.gid = current->egid; + de->inode.atime = CURRENT_TIME; + de->inode.mtime = CURRENT_TIME; + de->inode.ctime = CURRENT_TIME; + if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL ) return -ENOMEM; #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_I_MKNOD) printk ("%s: new VFS inode(%u): %p dentry: %p\n", - DEVFS_NAME, di->ino, inode, dentry); + DEVFS_NAME, de->inode.ino, inode, dentry); #endif d_instantiate (dentry, inode); - devfsd_notify_one (di->de, DEVFSD_NOTIFY_CREATE, inode->i_mode, + devfsd_notify_one (de, DEVFSD_NOTIFY_CREATE, inode->i_mode, inode->i_uid, inode->i_gid, fs_info); return 0; } /* End Function devfs_mknod */ static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen) { - struct devfs_inode *di = get_devfs_inode_from_vfs_inode (dentry->d_inode); + struct devfs_entry *de = get_devfs_entry_from_vfs_inode (dentry->d_inode); - return vfs_readlink (dentry, buffer, buflen, di->de->u.symlink.linkname); + return vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname); } /* End Function devfs_readlink */ static int devfs_follow_link (struct dentry *dentry, struct nameidata *nd) { - struct devfs_inode *di = get_devfs_inode_from_vfs_inode (dentry->d_inode); + struct devfs_entry *de = get_devfs_entry_from_vfs_inode (dentry->d_inode); - return vfs_follow_link (nd, di->de->u.symlink.linkname); + return vfs_follow_link (nd, de->u.symlink.linkname); } /* End Function devfs_follow_link */ static struct inode_operations devfs_iops = @@ -3303,47 +3091,22 @@ static struct inode_operations devfs_symlink_iops = static struct super_block *devfs_read_super (struct super_block *sb, void *data, int silent) { - char *aopt = data; - struct fs_info *fs_info = NULL; - struct devfs_inode *di; struct inode *root_inode = NULL; if (get_root_entry () == NULL) goto out_no_root; - if ( ( fs_info = kmalloc (sizeof *fs_info, GFP_KERNEL) ) == NULL ) - return NULL; - memset (fs_info, 0, sizeof *fs_info); - atomic_set (&fs_info->devfsd_overrun_count, 0); - init_waitqueue_head (&fs_info->devfsd_wait_queue); - init_waitqueue_head (&fs_info->revalidate_wait_queue); - fs_info->prev = last_fs; - if (first_fs == NULL) first_fs = fs_info; - else last_fs->next = fs_info; - last_fs = fs_info; - fs_info->sb = sb; - if (aopt) - { - if (strcmp (aopt, "explicit") == 0) fs_info->require_explicit = TRUE; - } - sb->u.generic_sbp = fs_info; + atomic_set (&fs_info.devfsd_overrun_count, 0); + init_waitqueue_head (&fs_info.devfsd_wait_queue); + init_waitqueue_head (&fs_info.revalidate_wait_queue); + fs_info.sb = sb; + sb->u.generic_sbp = &fs_info; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = DEVFS_SUPER_MAGIC; sb->s_op = &devfs_sops; - di = create_devfs_inode (root_entry, fs_info); - if (di == NULL) goto out_no_root; - if (di->ino != 1) - { - printk ("%s: read_super: root inode number is: %d!\n", - DEVFS_NAME, di->ino); - goto out_no_root; - } - if ( ( root_inode = get_vfs_inode (sb, di, NULL) ) == NULL ) + if ( ( root_inode = get_vfs_inode (sb, root_entry, NULL) ) == NULL ) goto out_no_root; - sb->s_root = D_ALLOC_ROOT (root_inode); + sb->s_root = d_alloc_root (root_inode); if (!sb->s_root) goto out_no_root; -#ifdef CONFIG_DEVFS_TUNNEL - di->covered = dget (sb->s_root->d_covered); -#endif #ifdef CONFIG_DEVFS_DEBUG if (devfs_debug & DEBUG_DISABLED) printk ("%s: read super, made devfs ptr: %p\n", @@ -3353,13 +3116,12 @@ static struct super_block *devfs_read_super (struct super_block *sb, out_no_root: printk ("devfs_read_super: get root inode failed\n"); - delete_fs (fs_info); if (root_inode) iput (root_inode); return NULL; } /* End Function devfs_read_super */ -static DECLARE_FSTYPE (devfs_fs_type, DEVFS_NAME, devfs_read_super, 0); +static DECLARE_FSTYPE (devfs_fs_type, DEVFS_NAME, devfs_read_super, FS_SINGLE); /* File operations for devfsd follow */ @@ -3549,25 +3311,27 @@ static int devfsd_close (struct inode *inode, struct file *file) } /* End Function devfsd_close */ -#ifdef MODULE -int init_module (void) -#else int __init init_devfs_fs (void) -#endif { + int err; + printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", DEVFS_NAME, DEVFS_VERSION); -#if defined(CONFIG_DEVFS_DEBUG) && !defined(MODULE) +#ifdef CONFIG_DEVFS_DEBUG devfs_debug = devfs_debug_init; printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug); #endif -#if !defined(MODULE) printk ("%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options); -#endif - return register_filesystem (&devfs_fs_type); -} + err = register_filesystem (&devfs_fs_type); + if (!err) + { + struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type); + err = PTR_ERR (devfs_mnt); + if ( !IS_ERR (devfs_mnt) ) err = 0; + } + return err; +} /* End Function init_devfs_fs */ -#ifndef MODULE void __init mount_devfs_fs (void) { int err; @@ -3577,39 +3341,3 @@ void __init mount_devfs_fs (void) if (err == 0) printk ("Mounted devfs on /dev\n"); else printk ("Warning: unable to mount devfs, err: %d\n", err); } /* End Function mount_devfs_fs */ -#endif - -#ifdef MODULE -static void free_entry (struct devfs_entry *parent) -{ - struct devfs_entry *de, *next; - - if (parent == NULL) return; - for (de = parent->u.dir.first; de != NULL; de = next) - { - next = de->next; - if (de->first_inode != NULL) - { - printk ("%s: free_entry(): unfreed inodes!\n", DEVFS_NAME); - } - if ( S_ISDIR (de->mode) ) - { - /* Recursively free the subdirectories: this is a stack chomper */ - free_entry (de); - } - else kfree (de); - } - kfree (parent); -} /* End Function free_entry */ - -void cleanup_module (void) -{ - unregister_filesystem (&devfs_fs_type); - if (first_fs != NULL) - { - printk ("%s: cleanup_module(): still mounted mounted filesystems!\n", - DEVFS_NAME); - } - free_entry (root_entry); -} -#endif /* MODULE */ diff --git a/fs/devfs/util.c b/fs/devfs/util.c index fe4746448..6e3f2f782 100644 --- a/fs/devfs/util.c +++ b/fs/devfs/util.c @@ -28,6 +28,8 @@ Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs 20000203 Richard Gooch <rgooch@atnf.csiro.au> Changed operations pointer type to void *. + 20000621 Richard Gooch <rgooch@atnf.csiro.au> + Changed interface to <devfs_register_series>. */ #include <linux/module.h> #include <linux/init.h> @@ -134,8 +136,6 @@ EXPORT_SYMBOL(devfs_register_tape); * @major: The major number. Not needed for regular files. * @minor_start: The starting minor number. Not needed for regular files. * @mode: The default file mode. - * @uid: The default UID of the file. - * @guid: The default GID of the file. * @ops: The &file_operations or &block_device_operations structure. * This must not be externally deallocated. * @info: An arbitrary pointer which will be written to the private_data @@ -147,8 +147,7 @@ EXPORT_SYMBOL(devfs_register_tape); void devfs_register_series (devfs_handle_t dir, const char *format, unsigned int num_entries, unsigned int flags, unsigned int major, unsigned int minor_start, - umode_t mode, uid_t uid, gid_t gid, - void *ops, void *info) + umode_t mode, void *ops, void *info) { unsigned int count; char devname[128]; @@ -156,8 +155,8 @@ void devfs_register_series (devfs_handle_t dir, const char *format, for (count = 0; count < num_entries; ++count) { sprintf (devname, format, count); - devfs_register (dir, devname, 0, flags, major, minor_start + count, - mode, uid, gid, ops, info); + devfs_register (dir, devname, flags, major, minor_start + count, + mode, ops, info); } } /* End Function devfs_register_series */ EXPORT_SYMBOL(devfs_register_series); @@ -101,54 +101,37 @@ static inline void put_binfmt(struct linux_binfmt * fmt) */ asmlinkage long sys_uselib(const char * library) { + int fd, retval; struct file * file; - struct nameidata nd; - int error; - - error = user_path_walk(library, &nd); - if (error) - goto out; - - error = -EINVAL; - if (!S_ISREG(nd.dentry->d_inode->i_mode)) - goto exit; - - error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC); - if (error) - goto exit; - - lock_kernel(); - file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); - unlock_kernel(); - error = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - error = -ENOEXEC; - if(file->f_op && file->f_op->read) { - struct linux_binfmt * fmt; + fd = sys_open(library, 0, 0); + if (fd < 0) + return fd; + file = fget(fd); + retval = -ENOEXEC; + if (file) { + if(file->f_op && file->f_op->read) { + struct linux_binfmt * fmt; - read_lock(&binfmt_lock); - for (fmt = formats ; fmt ; fmt = fmt->next) { - if (!fmt->load_shlib) - continue; - if (!try_inc_mod_count(fmt->module)) - continue; - read_unlock(&binfmt_lock); - error = fmt->load_shlib(file); read_lock(&binfmt_lock); - put_binfmt(fmt); - if (error != -ENOEXEC) - break; + for (fmt = formats ; fmt ; fmt = fmt->next) { + if (!fmt->load_shlib) + continue; + if (!try_inc_mod_count(fmt->module)) + continue; + read_unlock(&binfmt_lock); + retval = fmt->load_shlib(file); + read_lock(&binfmt_lock); + put_binfmt(fmt); + if (retval != -ENOEXEC) + break; + } + read_unlock(&binfmt_lock); } - read_unlock(&binfmt_lock); + fput(file); } - fput(file); -out: - return error; -exit: - path_release(&nd); - goto out; + sys_close(fd); + return retval; } /* @@ -336,7 +319,6 @@ int setup_arg_pages(struct linux_binprm *bprm) struct file *open_exec(const char *name) { struct nameidata nd; - struct inode *inode; struct file *file; int err = 0; @@ -346,22 +328,14 @@ struct file *open_exec(const char *name) unlock_kernel(); file = ERR_PTR(err); if (!err) { - inode = nd.dentry->d_inode; file = ERR_PTR(-EACCES); - if (!IS_NOEXEC(inode) && S_ISREG(inode->i_mode)) { - int err = permission(inode, MAY_EXEC); + if (S_ISREG(nd.dentry->d_inode->i_mode)) { + int err = permission(nd.dentry->d_inode, MAY_EXEC); file = ERR_PTR(err); if (!err) { lock_kernel(); file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); unlock_kernel(); - if (!IS_ERR(file)) { - err = deny_write_access(file); - if (err) { - fput(file); - file = ERR_PTR(err); - } - } out: return file; } @@ -556,7 +530,7 @@ flush_failed: */ static inline int must_not_trace_exec(struct task_struct * p) { - return (p->ptrace&PT_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE); + return (p->ptrace & PT_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE); } /* @@ -566,13 +540,23 @@ static inline int must_not_trace_exec(struct task_struct * p) int prepare_binprm(struct linux_binprm *bprm) { int mode; - int id_change,cap_raised; + int retval,id_change,cap_raised; struct inode * inode = bprm->file->f_dentry->d_inode; mode = inode->i_mode; - /* Huh? We had already checked for MAY_EXEC, WTF do we check this? */ - if (!(mode & 0111)) /* with at least _one_ execute bit set */ + if (!S_ISREG(mode)) /* must be regular file */ + return -EACCES; + if (!(mode & 0111)) /* with at least _one_ execute bit set */ return -EACCES; + if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */ + return -EACCES; + if (!inode->i_sb) + return -EACCES; + if ((retval = permission(inode, MAY_EXEC)) != 0) + return retval; + /* better not execute files which are being written to */ + if (atomic_read(&inode->i_writecount) > 0) + return -ETXTBSY; bprm->e_uid = current->euid; bprm->e_gid = current->egid; @@ -744,7 +728,6 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) char * dynloader[] = { "/sbin/loader" }; struct file * file; - allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; @@ -778,7 +761,6 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) retval = fn(bprm, regs); if (retval >= 0) { put_binfmt(fmt); - allow_write_access(bprm->file); if (bprm->file) fput(bprm->file); bprm->file = NULL; @@ -840,13 +822,11 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) { - allow_write_access(file); fput(file); return bprm.argc; } if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) { - allow_write_access(file); fput(file); return bprm.envc; } @@ -875,7 +855,6 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs out: /* Something went wrong, return the inode and free the argument pages*/ - allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 97fb703e1..a3f8ae4ce 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -473,8 +473,11 @@ repeat: if (i >= sb->u.ext2_sb.s_groups_count) i = 0; gdp = ext2_get_group_desc (sb, i, &bh2); - if (!gdp) - goto io_error; + if (!gdp) { + *err = -EIO; + unlock_super (sb); + return 0; + } if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) break; } diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index cd62f058d..3a18b375c 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -26,7 +26,7 @@ struct file_operations ext2_dir_operations = { read: generic_read_dir, readdir: ext2_readdir, ioctl: ext2_ioctl, - fsync: ext2_fsync_file, + fsync: ext2_sync_file, }; int ext2_check_dir_entry (const char * function, struct inode * dir, diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 130013e50..d2c137e2c 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -91,7 +91,6 @@ static int ext2_open_file (struct inode * inode, struct file * filp) return 0; } - /* * We have mostly NULL's here: the current defaults are ok for * the ext2 filesystem. @@ -104,7 +103,7 @@ struct file_operations ext2_file_operations = { mmap: generic_file_mmap, open: ext2_open_file, release: ext2_release_file, - fsync: ext2_fsync_file, + fsync: ext2_sync_file, }; struct inode_operations ext2_file_inode_operations = { diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 5b58f6cad..52ffd6138 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -27,28 +27,131 @@ #include <linux/smp_lock.h> +#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) +#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) + +static int sync_indirect(struct inode * inode, u32 * block, int wait) +{ + struct buffer_head * bh; + + if (!*block) + return 0; + bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize); + if (!bh) + return 0; + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + /* There can be a parallell read(2) that started read-I/O + on the buffer so we can't assume that there's been + an I/O error without first waiting I/O completation. */ + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + { + brelse (bh); + return -1; + } + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + if (wait) + /* when we return from fsync all the blocks + must be _just_ stored on disk */ + wait_on_buffer(bh); + brelse(bh); + return 0; + } + ll_rw_block(WRITE, 1, &bh); + atomic_dec(&bh->b_count); + return 0; +} + +static int sync_iblock(struct inode * inode, u32 * iblock, + struct buffer_head ** bh, int wait) +{ + int rc, tmp; + + *bh = NULL; + tmp = le32_to_cpu(*iblock); + if (!tmp) + return 0; + rc = sync_indirect(inode, iblock, wait); + if (rc) + return rc; + *bh = bread(inode->i_dev, tmp, blocksize); + if (!*bh) + return -1; + return 0; +} + +static int sync_dindirect(struct inode * inode, u32 * diblock, int wait) +{ + int i; + struct buffer_head * dind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, diblock, &dind_bh, wait); + if (rc || !dind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait); + if (rc) + err = rc; + } + brelse(dind_bh); + return err; +} + +static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait) +{ + int i; + struct buffer_head * tind_bh; + int rc, err = 0; + + rc = sync_iblock(inode, tiblock, &tind_bh, wait); + if (rc || !tind_bh) + return rc; + + for (i = 0; i < addr_per_block; i++) { + rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait); + if (rc) + err = rc; + } + brelse(tind_bh); + return err; +} + /* * File may be NULL when we are called. Perhaps we shouldn't * even pass file to fsync ? */ -int ext2_fsync_file(struct file * file, struct dentry *dentry, int datasync) +int ext2_sync_file(struct file * file, struct dentry *dentry) { + int wait, err = 0; struct inode *inode = dentry->d_inode; - return ext2_fsync_inode(inode, datasync); -} -int ext2_fsync_inode(struct inode *inode, int datasync) -{ - int err; - - err = fsync_inode_buffers(inode); - if (!(inode->i_state & I_DIRTY)) - return err; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - return err; - - err |= ext2_sync_inode(inode); + lock_kernel(); + if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) + /* + * Don't sync fast links! + */ + goto skip; + + err = generic_buffer_fdatasync(inode, 0, ~0UL); + + for (wait=0; wait<=1; wait++) + { + err |= sync_indirect(inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + wait); + err |= sync_dindirect(inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect(inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, + wait); + } +skip: + err |= ext2_sync_inode (inode); + unlock_kernel(); return err ? -EIO : 0; } - diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index cbc806cda..3c95ccd70 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -287,6 +287,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err) repeat: gdp = NULL; i=0; + *err = -ENOSPC; if (S_ISDIR(mode)) { avefreei = le32_to_cpu(es->s_free_inodes_count) / sb->u.ext2_sb.s_groups_count; @@ -368,7 +369,6 @@ repeat: if (!gdp) { unlock_super (sb); iput(inode); - *err = -ENOSPC; return NULL; } bitmap_nr = load_inode_bitmap (sb, i); @@ -398,8 +398,9 @@ repeat: ext2_error (sb, "ext2_new_inode", "Free inodes count corrupted in group %d", i); - /* If we continue recover from this case */ - gdp->bg_free_inodes_count = 0; + unlock_super (sb); + iput (inode); + return NULL; } goto repeat; } @@ -410,7 +411,6 @@ repeat: "block_group = %d,inode=%d", i, j); unlock_super (sb); iput (inode); - *err = EIO; /* Should never happen */ return NULL; } gdp->bg_free_inodes_count = diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index d3abb7cb2..7e5263fb1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -117,7 +117,7 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) inode->u.ext2_i.i_prealloc_count--; ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); - *err = 0; + } else { ext2_discard_prealloc (inode); ext2_debug ("preallocation miss (%lu/%lu).\n", @@ -200,7 +200,6 @@ out: return ret; } -/* returns NULL and sets *err on error */ static struct buffer_head * inode_getblk (struct inode * inode, int nr, int new_block, int * err, int metadata, long *phys, int *new) { @@ -224,6 +223,7 @@ repeat: return NULL; } } + *err = -EFBIG; /* Check file limits.. */ { @@ -311,7 +311,7 @@ repeat: * can fail due to: - not present * - out of space * - * NULL return in the data case, or an error, is mandatory. + * NULL return in the data case is mandatory. */ static struct buffer_head * block_getblk (struct inode * inode, struct buffer_head * bh, int nr, @@ -341,7 +341,6 @@ repeat: if (tmp == le32_to_cpu(*p)) goto out; brelse (result); - result = NULL; goto repeat; } else { *phys = tmp; @@ -403,9 +402,11 @@ repeat: *new = 1; } *p = le32_to_cpu(tmp); - mark_buffer_dirty_inode(bh, 1, inode); - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) + mark_buffer_dirty(bh, 1); + if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); + } inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocksize/512; mark_inode_dirty(inode); @@ -486,9 +487,9 @@ static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head * #define GET_INODE_PTR(x) \ inode_getblk(inode, x, iblock, &err, 1, NULL, NULL) #define GET_INDIRECT_DATABLOCK(x) \ - block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new) + block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new); #define GET_INDIRECT_PTR(x) \ - block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL) + block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL); if (ptr < direct_blocks) { bh = GET_INODE_DATABLOCK(ptr); @@ -546,11 +547,13 @@ abort_too_big: struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err) { struct buffer_head dummy; + int error; dummy.b_state = 0; dummy.b_blocknr = -1000; - *err = ext2_get_block(inode, block, &dummy, create); - if (!*err && buffer_mapped(&dummy)) { + error = ext2_get_block(inode, block, &dummy, create); + *err = error; + if (!error && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); if (buffer_new(&dummy)) { @@ -878,23 +881,8 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl); if (S_ISDIR(inode->i_mode)) raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); - else { + else raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); - if (inode->i_size >> 31) { - struct super_block *sb = inode->i_sb; - struct ext2_super_block *es = sb->u.ext2_sb.s_es; - if (!(es->s_feature_ro_compat & cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { - /* If this is the first large file - * created, add a flag to the superblock - * SMP Note: we're currently protected by the - * big kernel lock here, so this will need - * to be changed if that's no longer true. - */ - es->s_feature_ro_compat |= cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); - ext2_write_super(sb); - } - } - } raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) @@ -916,10 +904,10 @@ static int ext2_update_inode(struct inode * inode, int do_sync) return err; } -void ext2_write_inode (struct inode * inode, int wait) +void ext2_write_inode (struct inode * inode) { lock_kernel(); - ext2_update_inode (inode, wait); + ext2_update_inode (inode, 0); unlock_kernel(); } diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 116b4852f..46e273935 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -366,9 +366,12 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode) struct inode * inode; int err; + /* + * N.B. Several error exits in ext2_new_inode don't set err. + */ inode = ext2_new_inode (dir, mode, &err); if (!inode) - return err; + return -EIO; inode->i_op = &ext2_file_inode_operations; inode->i_fop = &ext2_file_operations; @@ -394,7 +397,7 @@ static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int inode = ext2_new_inode (dir, mode, &err); if (!inode) - return err; + return -EIO; inode->i_uid = current->fsuid; init_special_inode(inode, mode, rdev); @@ -425,7 +428,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode = ext2_new_inode (dir, S_IFDIR, &err); if (!inode) - return err; + return -EIO; inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; @@ -451,7 +454,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) strcpy (de->name, ".."); ext2_set_de_type(dir->i_sb, de, S_IFDIR); inode->i_nlink = 2; - mark_buffer_dirty_inode(dir_block, 1, dir); + mark_buffer_dirty(dir_block, 1); brelse (dir_block); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) @@ -631,7 +634,7 @@ static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * return -ENAMETOOLONG; if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) - return err; + return -EIO; inode->i_mode = S_IFLNK | S_IRWXUGO; @@ -788,7 +791,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); - mark_buffer_dirty_inode(dir_bh, 1, old_inode); + mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d3af3b992..aa6a599fc 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -593,6 +593,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, /* * set up enough so that it can read an inode */ + sb->s_dev = dev; sb->s_op = &ext2_sops; sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); if (!sb->s_root) { diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index 1c05cc09f..ba8397196 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -211,7 +211,7 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, struct buf inode->i_ino, tmp); *p = 0; if (dind_bh) - mark_buffer_dirty_inode(dind_bh, 1, inode); + mark_buffer_dirty(dind_bh, 1); else mark_inode_dirty(inode); return 0; @@ -279,7 +279,7 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p, inode->i_ino, tmp); *p = 0; if (tind_bh) - mark_buffer_dirty_inode(tind_bh, 1, inode); + mark_buffer_dirty(tind_bh, 1); else mark_inode_dirty(inode); return 0; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 8b29c563e..85cc4e1a6 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -837,7 +837,7 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) MSDOS_I(inode)->i_ctime_ms = de->ctime_ms; } -void fat_write_inode(struct inode *inode, int unused) +void fat_write_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; diff --git a/fs/fcntl.c b/fs/fcntl.c index 37e32a012..f6e4e1651 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -252,8 +252,8 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = sock_fcntl (filp, cmd, arg); break; } - unlock_kernel(); fput(filp); + unlock_kernel(); out: return err; } diff --git a/fs/file_table.c b/fs/file_table.c index ceb3b7069..ecaa46896 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -16,7 +16,9 @@ static kmem_cache_t *filp_cache; /* sysctl tunables... */ -struct files_stat_struct files_stat = {0, 0, NR_FILE}; +int nr_files; /* read only */ +int nr_free_files; /* read only */ +int max_files = NR_FILE;/* tunable */ /* Here the new files go */ static LIST_HEAD(anon_list); @@ -51,11 +53,11 @@ struct file * get_empty_filp(void) struct file * f; file_list_lock(); - if (files_stat.nr_free_files > NR_RESERVED_FILES) { + if (nr_free_files > NR_RESERVED_FILES) { used_one: f = list_entry(free_list.next, struct file, f_list); list_del(&f->f_list); - files_stat.nr_free_files--; + nr_free_files--; new_one: file_list_unlock(); memset(f, 0, sizeof(*f)); @@ -71,25 +73,25 @@ struct file * get_empty_filp(void) /* * Use a reserved one if we're the superuser */ - if (files_stat.nr_free_files && !current->euid) + if (nr_free_files && !current->euid) goto used_one; /* * Allocate a new one if we're below the limit. */ - if (files_stat.nr_files < files_stat.max_files) { + if (nr_files < max_files) { file_list_unlock(); f = kmem_cache_alloc(filp_cache, SLAB_KERNEL); file_list_lock(); if (f) { - files_stat.nr_files++; + nr_files++; goto new_one; } /* Big problems... */ printk("VFS: filp allocation failed\n"); - } else if (files_stat.max_files > old_max) { - printk("VFS: file-max limit %d reached\n", files_stat.max_files); - old_max = files_stat.max_files; + } else if (max_files > old_max) { + printk("VFS: file-max limit %d reached\n", max_files); + old_max = max_files; } file_list_unlock(); return NULL; @@ -98,8 +100,7 @@ struct file * get_empty_filp(void) /* * Clear and initialize a (private) struct file for the given dentry, * and call the open function (if any). The caller must verify that - * inode->i_fop is not NULL. The only user is nfsfh.c and this function - * will eventually go away. + * inode->i_fop is not NULL. */ int init_private_file(struct file *filp, struct dentry *dentry, int mode) { @@ -147,7 +148,7 @@ void _fput(struct file *file) file_list_lock(); list_del(&file->f_list); list_add(&file->f_list, &free_list); - files_stat.nr_free_files++; + nr_free_files++; file_list_unlock(); } @@ -159,7 +160,7 @@ void put_filp(struct file *file) file_list_lock(); list_del(&file->f_list); list_add(&file->f_list, &free_list); - files_stat.nr_free_files++; + nr_free_files++; file_list_unlock(); } } diff --git a/fs/filesystems.c b/fs/filesystems.c index 22f6aa6db..ce64f4c8e 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -52,7 +52,7 @@ void __init filesystem_setup(void) #ifdef CONFIG_NFSD_MODULE int (*do_nfsservctl)(int, void *, void *); #endif -int +long asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp) { #ifndef CONFIG_NFSD_MODULE diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index ebe12eb40..c0707b52c 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -56,7 +56,7 @@ void hpfs_truncate(struct inode *i) i->i_blocks = 1 + ((i->i_size + 511) >> 9); i->u.hpfs_i.mmu_private = i->i_size; hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); - hpfs_write_inode(i, 0); + hpfs_write_inode(i); } int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index a76efa035..a01140f1f 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -266,7 +266,7 @@ ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t void hpfs_read_inode(struct inode *); void hpfs_write_inode_ea(struct inode *, struct fnode *); -void hpfs_write_inode(struct inode *, int); +void hpfs_write_inode(struct inode *); void hpfs_write_inode_nolock(struct inode *); int hpfs_notify_change(struct dentry *, struct iattr *); void hpfs_write_if_changed(struct inode *); diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 2f3f3f32e..7938970c8 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -228,7 +228,7 @@ void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode) } } -void hpfs_write_inode(struct inode *i, int unused) +void hpfs_write_inode(struct inode *i) { struct inode *parent; if (!i->i_nlink) return; @@ -300,14 +300,14 @@ int hpfs_notify_change(struct dentry *dentry, struct iattr *attr) if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL; if ((error = inode_change_ok(inode, attr))) return error; inode_setattr(inode, attr); - hpfs_write_inode(inode, 0); + hpfs_write_inode(inode); return 0; } void hpfs_write_if_changed(struct inode *inode) { if (inode->i_hpfs_dirty) { - hpfs_write_inode(inode, 0); + hpfs_write_inode(inode); } } diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 5684801df..b09ad98ea 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -330,15 +330,7 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry) struct iattr newattrs; int err; hpfs_unlock_2inodes(dir, inode); - if (rep) - goto ret; - d_drop(dentry); - if (dentry->d_count > 1 || - permission(inode, MAY_WRITE) || - get_write_access(inode)) { - d_rehash(dentry); - goto ret; - } + if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret; /*printk("HPFS: truncating file before delete.\n");*/ down(&inode->i_sem); newattrs.ia_size = 0; diff --git a/fs/inode.c b/fs/inode.c index 27159b951..e46359b03 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -96,7 +96,6 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_data.pages); INIT_LIST_HEAD(&inode->i_dentry); - INIT_LIST_HEAD(&inode->i_dirty_buffers); sema_init(&inode->i_sem, 1); sema_init(&inode->i_zombie, 1); spin_lock_init(&inode->i_data.i_shared_lock); @@ -123,14 +122,14 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) * Mark an inode as dirty. Callers should use mark_inode_dirty. */ -void __mark_inode_dirty(struct inode *inode, int flags) +void __mark_inode_dirty(struct inode *inode) { struct super_block * sb = inode->i_sb; if (sb) { spin_lock(&inode_lock); - if ((inode->i_state & flags) != flags) { - inode->i_state |= flags; + if (!(inode->i_state & I_DIRTY)) { + inode->i_state |= I_DIRTY; /* Only add valid (ie hashed) inodes to the dirty list */ if (!list_empty(&inode->i_hash)) { list_del(&inode->i_list); @@ -163,10 +162,10 @@ static inline void wait_on_inode(struct inode *inode) } -static inline void write_inode(struct inode *inode, int wait) +static inline void write_inode(struct inode *inode) { if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) - inode->i_sb->s_op->write_inode(inode, wait); + inode->i_sb->s_op->write_inode(inode); } static inline void __iget(struct inode * inode) @@ -183,7 +182,7 @@ static inline void __iget(struct inode * inode) inodes_stat.nr_unused--; } -static inline void sync_one(struct inode *inode, int wait) +static inline void sync_one(struct inode *inode) { if (inode->i_state & I_LOCK) { __iget(inode); @@ -197,11 +196,10 @@ static inline void sync_one(struct inode *inode, int wait) ? &inode_in_use : &inode_unused); /* Set I_LOCK, reset I_DIRTY */ - inode->i_state |= I_LOCK; - inode->i_state &= ~I_DIRTY; + inode->i_state ^= I_DIRTY | I_LOCK; spin_unlock(&inode_lock); - write_inode(inode, wait); + write_inode(inode); spin_lock(&inode_lock); inode->i_state &= ~I_LOCK; @@ -214,7 +212,7 @@ static inline void sync_list(struct list_head *head) struct list_head * tmp; while ((tmp = head->prev) != head) - sync_one(list_entry(tmp, struct inode, i_list), 0); + sync_one(list_entry(tmp, struct inode, i_list)); } /** @@ -247,7 +245,6 @@ void sync_inodes(kdev_t dev) spin_unlock(&inode_lock); } - /* * Called with the spinlock already held.. */ @@ -264,20 +261,19 @@ static void sync_all_inodes(void) /** * write_inode_now - write an inode to disk * @inode: inode to write to disk - * @wait: if set, we wait for the write to complete on disk * * This function commits an inode to disk immediately if it is * dirty. This is primarily needed by knfsd. */ -void write_inode_now(struct inode *inode, int wait) +void write_inode_now(struct inode *inode) { struct super_block * sb = inode->i_sb; if (sb) { spin_lock(&inode_lock); while (inode->i_state & I_DIRTY) - sync_one(inode, wait); + sync_one(inode); spin_unlock(&inode_lock); } else @@ -285,60 +281,6 @@ void write_inode_now(struct inode *inode, int wait) } /** - * generic_osync_inode - flush all dirty data for a given inode to disk - * @inode: inode to write - * @datasync: if set, don't bother flushing timestamps - * - * This is called by generic_file_write for files which have the O_SYNC - * flag set, to flush dirty writes to disk. - */ - -int generic_osync_inode(struct inode *inode, int datasync) -{ - int err; - - /* - * WARNING - * - * Currently, the filesystem write path does not pass the - * filp down to the low-level write functions. Therefore it - * is impossible for (say) __block_commit_write to know if - * the operation is O_SYNC or not. - * - * Ideally, O_SYNC writes would have the filesystem call - * ll_rw_block as it went to kick-start the writes, and we - * could call osync_inode_buffers() here to wait only for - * those IOs which have already been submitted to the device - * driver layer. As it stands, if we did this we'd not write - * anything to disk since our writes have not been queued by - * this point: they are still on the dirty LRU. - * - * So, currently we will call fsync_inode_buffers() instead, - * to flush _all_ dirty buffers for this inode to disk on - * every O_SYNC write, not just the synchronous I/Os. --sct - */ - -#ifdef WRITERS_QUEUE_IO - err = osync_inode_buffers(inode); -#else - err = fsync_inode_buffers(inode); -#endif - - spin_lock(&inode_lock); - if (!(inode->i_state & I_DIRTY)) - goto out; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - goto out; - spin_unlock(&inode_lock); - write_inode_now(inode, 1); - return err; - - out: - spin_unlock(&inode_lock); - return err; -} - -/** * clear_inode - clear an inode * @inode: inode to clear * @@ -382,7 +324,7 @@ static void dispose_list(struct list_head * head) inode = list_entry(inode_entry, struct inode, i_list); if (inode->i_data.nrpages) - truncate_all_inode_pages(&inode->i_data); + truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); destroy_inode(inode); } @@ -407,7 +349,6 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru inode = list_entry(tmp, struct inode, i_list); if (inode->i_sb != sb) continue; - invalidate_inode_buffers(inode); if (!atomic_read(&inode->i_count)) { list_del(&inode->i_hash); INIT_LIST_HEAD(&inode->i_hash); @@ -469,8 +410,7 @@ int invalidate_inodes(struct super_block * sb) * dispose_list. */ #define CAN_UNUSE(inode) \ - ((((inode)->i_state | (inode)->i_data.nrpages) == 0) && \ - !inode_has_buffers(inode)) + (((inode)->i_state | (inode)->i_data.nrpages) == 0) #define INODE(entry) (list_entry(entry, struct inode, i_list)) void prune_icache(int goal) @@ -830,7 +770,7 @@ void iput(struct inode *inode) spin_unlock(&inode_lock); if (inode->i_data.nrpages) - truncate_all_inode_pages(&inode->i_data); + truncate_inode_pages(&inode->i_data, 0); destroy = 1; if (op && op->delete_inode) { @@ -985,7 +925,7 @@ void update_atime (struct inode *inode) if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return; if ( IS_RDONLY (inode) ) return; inode->i_atime = CURRENT_TIME; - mark_inode_dirty_sync (inode); + mark_inode_dirty (inode); } /* End Function update_atime */ diff --git a/fs/ioctl.c b/fs/ioctl.c index f02d766bd..16ad5ec26 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -107,8 +107,8 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) else if (filp->f_op && filp->f_op->ioctl) error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); } - unlock_kernel(); fput(filp); + unlock_kernel(); out: return error; diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index a3a4f072f..f89188d12 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -162,7 +162,8 @@ reclaimer(void *ptr) { struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; - struct list_head *tmp; + struct file_lock *fl; + struct inode *inode; /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ @@ -170,21 +171,19 @@ reclaimer(void *ptr) lockd_up(); /* First, reclaim all locks that have been granted previously. */ -restart: - tmp = file_lock_list.next; - while (tmp != &file_lock_list) { - struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - struct inode *inode = fl->fl_file->f_dentry->d_inode; - if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && - nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) && - fl->fl_u.nfs_fl.state != host->h_state && - (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { - fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; - nlmclnt_reclaim(host, fl); - goto restart; + do { + for (fl = file_lock_table; fl; fl = fl->fl_nextlink) { + inode = fl->fl_file->f_dentry->d_inode; + if (inode->i_sb->s_magic == NFS_SUPER_MAGIC + && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) + && fl->fl_u.nfs_fl.state != host->h_state + && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { + fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; + nlmclnt_reclaim(host, fl); + break; + } } - tmp = tmp->next; - } + } while (fl); host->h_reclaiming = 0; wake_up(&host->h_gracewait); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 56c8d8173..279fcc3c1 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -347,7 +347,7 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); - if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) { + if (!block->b_call.a_args.lock.fl.fl_prevblock) { /* Now add block to block list of the conflicting lock if we haven't done so. */ dprintk("lockd: blocking on this lock.\n"); diff --git a/fs/locks.c b/fs/locks.c index c8710dcc4..015b8e87a 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -108,150 +108,57 @@ #include <linux/malloc.h> #include <linux/file.h> #include <linux/smp_lock.h> -#include <linux/init.h> #include <asm/uaccess.h> -LIST_HEAD(file_lock_list); -static LIST_HEAD(blocked_list); - -static kmem_cache_t *filelock_cache; - -/* Allocate an empty lock structure. */ -static struct file_lock *locks_alloc_lock(void) +static int flock_make_lock(struct file *filp, struct file_lock *fl, + unsigned int cmd); +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l); +static int flock_locks_conflict(struct file_lock *caller_fl, + struct file_lock *sys_fl); +static int posix_locks_conflict(struct file_lock *caller_fl, + struct file_lock *sys_fl); +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); +static int flock_lock_file(struct file *filp, struct file_lock *caller, + unsigned int wait); +static int posix_locks_deadlock(struct file_lock *caller, + struct file_lock *blocker); + +static struct file_lock *locks_empty_lock(void); +static struct file_lock *locks_init_lock(struct file_lock *, + struct file_lock *); +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl); +static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait); +static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx); + +static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter); +static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter); +static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait); + +struct file_lock *file_lock_table = NULL; + +/* Allocate a new lock, and initialize its fields from fl. + * The lock is not inserted into any lists until locks_insert_lock() or + * locks_insert_block() are called. + */ +static inline struct file_lock *locks_alloc_lock(struct file_lock *fl) { - struct file_lock *fl; - fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL); - return fl; + return locks_init_lock(locks_empty_lock(), fl); } -/* Free a lock which is not in use. */ +/* Free lock not inserted in any queue. + */ static inline void locks_free_lock(struct file_lock *fl) { - if (fl == NULL) { - BUG(); - return; - } - if (waitqueue_active(&fl->fl_wait)) panic("Attempting to free lock with active wait queue"); - if (!list_empty(&fl->fl_block)) + if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL) panic("Attempting to free lock with active block list"); - - if (!list_empty(&fl->fl_link)) - panic("Attempting to free lock on active lock list"); - - kmem_cache_free(filelock_cache, fl); -} - -/* - * Initialises the fields of the file lock which are invariant for - * free file_locks. - */ -static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags) -{ - struct file_lock *lock = (struct file_lock *) foo; - - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) != - SLAB_CTOR_CONSTRUCTOR) - return; - - lock->fl_next = NULL; - INIT_LIST_HEAD(&lock->fl_link); - INIT_LIST_HEAD(&lock->fl_block); - init_waitqueue_head(&lock->fl_wait); -} - -/* - * Initialize a new lock from an existing file_lock structure. - */ -static void locks_copy_lock(struct file_lock *new, struct file_lock *fl) -{ - new->fl_owner = fl->fl_owner; - new->fl_pid = fl->fl_pid; - new->fl_file = fl->fl_file; - new->fl_flags = fl->fl_flags; - new->fl_type = fl->fl_type; - new->fl_start = fl->fl_start; - new->fl_end = fl->fl_end; - new->fl_notify = fl->fl_notify; - new->fl_insert = fl->fl_insert; - new->fl_remove = fl->fl_remove; - new->fl_u = fl->fl_u; -} - -/* Fill in a file_lock structure with an appropriate FLOCK lock. */ -static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) -{ - struct file_lock *fl = locks_alloc_lock(); - if (fl == NULL) - return NULL; - - fl->fl_owner = NULL; - fl->fl_file = filp; - fl->fl_pid = current->pid; - fl->fl_flags = FL_FLOCK; - fl->fl_type = type; - fl->fl_start = 0; - fl->fl_end = OFFSET_MAX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; - - return fl; -} - -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX - * style lock. - */ -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l) -{ - loff_t start; - - switch (l->l_whence) { - case 0: /*SEEK_SET*/ - start = 0; - break; - case 1: /*SEEK_CUR*/ - start = filp->f_pos; - break; - case 2: /*SEEK_END*/ - start = filp->f_dentry->d_inode->i_size; - break; - default: - return (0); - } - - if (((start += l->l_start) < 0) || (l->l_len < 0)) - return (0); - fl->fl_end = start + l->l_len - 1; - if (l->l_len > 0 && fl->fl_end < 0) - return (0); - fl->fl_start = start; /* we record the absolute position */ - if (l->l_len == 0) - fl->fl_end = OFFSET_MAX; - - fl->fl_owner = current->files; - fl->fl_pid = current->pid; - fl->fl_file = filp; - fl->fl_flags = FL_POSIX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; - - switch (l->l_type) { - case F_RDLCK: - case F_WRLCK: - case F_UNLCK: - fl->fl_type = l->l_type; - break; - default: - return (0); - } - - return (1); + + kfree(fl); + return; } /* Check if two locks overlap each other. @@ -274,17 +181,6 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) (fl1->fl_pid == fl2->fl_pid); } -/* Remove waiter from blocker's block list. - * When blocker ends up pointing to itself then the list is empty. - */ -static void locks_delete_block(struct file_lock *waiter) -{ - list_del(&waiter->fl_block); - INIT_LIST_HEAD(&waiter->fl_block); - list_del(&waiter->fl_link); - INIT_LIST_HEAD(&waiter->fl_link); -} - /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but @@ -293,15 +189,71 @@ static void locks_delete_block(struct file_lock *waiter) static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) { - if (!list_empty(&waiter->fl_block)) { - printk(KERN_ERR "locks_insert_block: removing duplicated lock " - "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid, - waiter->fl_start, waiter->fl_end, waiter->fl_type); - locks_delete_block(waiter); + struct file_lock *prevblock; + + if (waiter->fl_prevblock) { + printk(KERN_ERR "locks_insert_block: remove duplicated lock " + "(pid=%d %Ld-%Ld type=%d)\n", + waiter->fl_pid, (long long)waiter->fl_start, + (long long)waiter->fl_end, waiter->fl_type); + locks_delete_block(waiter->fl_prevblock, waiter); } - list_add_tail(&waiter->fl_block, &blocker->fl_block); -// list_add(&waiter->fl_link, &blocked_list); -// waiter->fl_next = blocker; + + if (blocker->fl_prevblock == NULL) + /* No previous waiters - list is empty */ + prevblock = blocker; + else + /* Previous waiters exist - add to end of list */ + prevblock = blocker->fl_prevblock; + + prevblock->fl_nextblock = waiter; + blocker->fl_prevblock = waiter; + waiter->fl_nextblock = blocker; + waiter->fl_prevblock = prevblock; + + return; +} + +/* Remove waiter from blocker's block list. + * When blocker ends up pointing to itself then the list is empty. + */ +static void locks_delete_block(struct file_lock *blocker, + struct file_lock *waiter) +{ + struct file_lock *nextblock; + struct file_lock *prevblock; + + nextblock = waiter->fl_nextblock; + prevblock = waiter->fl_prevblock; + + if (nextblock == NULL) + return; + + nextblock->fl_prevblock = prevblock; + prevblock->fl_nextblock = nextblock; + + waiter->fl_prevblock = waiter->fl_nextblock = NULL; + if (blocker->fl_nextblock == blocker) + /* No more locks on blocker's blocked list */ + blocker->fl_prevblock = blocker->fl_nextblock = NULL; + return; +} + +/* The following two are for the benefit of lockd. + */ +void +posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) +{ + locks_insert_block(blocker, waiter); + return; +} + +void +posix_unblock_lock(struct file_lock *waiter) +{ + if (waiter->fl_prevblock) + locks_delete_block(waiter->fl_prevblock, waiter); + return; } /* Wake up processes blocked waiting for blocker. @@ -310,8 +262,9 @@ static void locks_insert_block(struct file_lock *blocker, */ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) { - while (!list_empty(&blocker->fl_block)) { - struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block); + struct file_lock *waiter; + + while ((waiter = blocker->fl_nextblock) != NULL) { /* N.B. Is it possible for the notify function to block?? */ if (waiter->fl_notify) waiter->fl_notify(waiter); @@ -326,105 +279,262 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) /* Remove waiter from the block list, because by the * time it wakes up blocker won't exist any more. */ - locks_delete_block(waiter); + locks_delete_block(blocker, waiter); } } + return; } -/* Insert file lock fl into an inode's lock list at the position indicated - * by pos. At the same time add the lock to the global file lock list. +/* flock() system call entry point. Apply a FL_FLOCK style lock to + * an open file descriptor. */ -static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) +asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) { - list_add(&fl->fl_link, &file_lock_list); - - /* insert into file's list */ - fl->fl_next = *pos; - *pos = fl; + struct file_lock file_lock; + struct file *filp; + int error; - if (fl->fl_insert) - fl->fl_insert(fl); + lock_kernel(); + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + error = -EINVAL; + if (!flock_make_lock(filp, &file_lock, cmd)) + goto out_putf; + error = -EBADF; + if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3)) + goto out_putf; + error = flock_lock_file(filp, &file_lock, + (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); +out_putf: + fput(filp); +out: + unlock_kernel(); + return (error); } -/* Delete a lock and free it. - * First remove our lock from the active lock lists. Then call - * locks_wake_up_blocks() to wake up processes that are blocked - * waiting for this lock. Finally free the lock structure. +/* Report the first existing lock that would conflict with l. + * This implements the F_GETLK command of fcntl(). */ -static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) +int fcntl_getlk(unsigned int fd, struct flock *l) { - int (*lock)(struct file *, int, struct file_lock *); - struct file_lock *fl = *thisfl_p; + struct file *filp; + struct file_lock *fl,file_lock; + struct flock flock; + int error; - *thisfl_p = fl->fl_next; - fl->fl_next = NULL; + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; + error = -EINVAL; + if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) + goto out; - list_del(&fl->fl_link); - INIT_LIST_HEAD(&fl->fl_link); + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; - if (fl->fl_remove) - fl->fl_remove(fl); + if (!posix_make_lock(filp, &file_lock, &flock)) + goto out_putf; - locks_wake_up_blocks(fl, wait); - lock = fl->fl_file->f_op->lock; - if (lock) { - fl->fl_type = F_UNLCK; - lock(fl->fl_file, F_SETLK, fl); + if (filp->f_op->lock) { + error = filp->f_op->lock(filp, F_GETLK, &file_lock); + if (error < 0) + goto out_putf; + else if (error == LOCK_USE_CLNT) + /* Bypass for NFS with no locking - 2.0.36 compat */ + fl = posix_test_lock(filp, &file_lock); + else + fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); + } else { + fl = posix_test_lock(filp, &file_lock); + } + + flock.l_type = F_UNLCK; + if (fl != NULL) { + flock.l_pid = fl->fl_pid; + flock.l_start = fl->fl_start; + flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : + fl->fl_end - fl->fl_start + 1; + flock.l_whence = 0; + flock.l_type = fl->fl_type; } - locks_free_lock(fl); + error = -EFAULT; + if (!copy_to_user(l, &flock, sizeof(flock))) + error = 0; + +out_putf: + fput(filp); +out: + return error; } -/* Determine if lock sys_fl blocks lock caller_fl. Common functionality - * checks for overlapping locks and shared/exclusive status. +/* Apply the lock described by l to an open file descriptor. + * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ -static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { - if (!locks_overlap(caller_fl, sys_fl)) - return (0); + struct file *filp; + struct file_lock file_lock; + struct flock flock; + struct inode *inode; + int error; - switch (caller_fl->fl_type) { + /* + * This might block, so we do it before checking the inode. + */ + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; + + /* Get arguments and validate them ... + */ + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = -EINVAL; + inode = filp->f_dentry->d_inode; + + /* Don't allow mandatory locks on files that may be memory mapped + * and shared. + */ + if (IS_MANDLOCK(inode) && + (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { + struct vm_area_struct *vma; + struct address_space *mapping = inode->i_mapping; + spin_lock(&mapping->i_shared_lock); + for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { + if (!(vma->vm_flags & VM_MAYSHARE)) + continue; + spin_unlock(&mapping->i_shared_lock); + error = -EAGAIN; + goto out_putf; + } + spin_unlock(&mapping->i_shared_lock); + } + + error = -EINVAL; + if (!posix_make_lock(filp, &file_lock, &flock)) + goto out_putf; + + error = -EBADF; + switch (flock.l_type) { case F_RDLCK: - return (sys_fl->fl_type == F_WRLCK); - + if (!(filp->f_mode & FMODE_READ)) + goto out_putf; + break; case F_WRLCK: - return (1); - - default: - printk("locks_conflict(): impossible lock type - %d\n", - caller_fl->fl_type); + if (!(filp->f_mode & FMODE_WRITE)) + goto out_putf; + break; + case F_UNLCK: break; + case F_SHLCK: + case F_EXLCK: +#ifdef __sparc__ +/* warn a bit for now, but don't overdo it */ +{ + static int count = 0; + if (!count) { + count=1; + printk(KERN_WARNING + "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", + current->pid, current->comm); } - return (0); /* This should never happen */ } + if (!(filp->f_mode & 3)) + goto out_putf; + break; +#endif + default: + error = -EINVAL; + goto out_putf; + } -/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific - * checking before calling the locks_conflict(). + if (filp->f_op->lock != NULL) { + error = filp->f_op->lock(filp, cmd, &file_lock); + if (error < 0) + goto out_putf; + } + error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW); + +out_putf: + fput(filp); +out: + return error; +} + +/* + * This function is called when the file is being removed + * from the task's fd array. */ -static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +void locks_remove_posix(struct file *filp, fl_owner_t owner) { - /* POSIX locks owned by the same process do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_POSIX) || - locks_same_owner(caller_fl, sys_fl)) - return (0); + struct inode * inode = filp->f_dentry->d_inode; + struct file_lock file_lock, *fl; + struct file_lock **before; - return (locks_conflict(caller_fl, sys_fl)); + /* + * For POSIX locks we free all locks on this file for the given task. + */ +repeat: + before = &inode->i_flock; + while ((fl = *before) != NULL) { + if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { + int (*lock)(struct file *, int, struct file_lock *); + lock = filp->f_op->lock; + if (lock) { + file_lock = *fl; + file_lock.fl_type = F_UNLCK; + } + locks_delete_lock(before, 0); + if (lock) { + lock(filp, F_SETLK, &file_lock); + /* List may have changed: */ + goto repeat; + } + continue; + } + before = &fl->fl_next; + } } -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). +/* + * This function is called on the last close of an open file. */ -static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +void locks_remove_flock(struct file *filp) { - /* FLOCK locks referring to the same filp do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_FLOCK) || - (caller_fl->fl_file == sys_fl->fl_file)) - return (0); + struct inode * inode = filp->f_dentry->d_inode; + struct file_lock file_lock, *fl; + struct file_lock **before; - return (locks_conflict(caller_fl, sys_fl)); +repeat: + before = &inode->i_flock; + while ((fl = *before) != NULL) { + if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { + int (*lock)(struct file *, int, struct file_lock *); + lock = NULL; + if (filp->f_op) + lock = filp->f_op->lock; + if (lock) { + file_lock = *fl; + file_lock.fl_type = F_UNLCK; + } + locks_delete_lock(before, 0); + if (lock) { + lock(filp, F_SETLK, &file_lock); + /* List may have changed: */ + goto repeat; + } + continue; + } + before = &fl->fl_next; + } } struct file_lock * @@ -442,57 +552,6 @@ posix_test_lock(struct file *filp, struct file_lock *fl) return (cfl); } -/* This function tests for deadlock condition before putting a process to - * sleep. The detection scheme is no longer recursive. Recursive was neat, - * but dangerous - we risked stack corruption if the lock data was bad, or - * if the recursion was too deep for any other reason. - * - * We rely on the fact that a task can only be on one lock's wait queue - * at a time. When we find blocked_task on a wait queue we can re-search - * with blocked_task equal to that queue's owner, until either blocked_task - * isn't found, or blocked_task is found on a queue owned by my_task. - * - * Note: the above assumption may not be true when handling lock requests - * from a broken NFS client. But broken NFS clients have a lot more to - * worry about than proper deadlock detection anyway... --okir - */ -static int posix_locks_deadlock(struct file_lock *caller_fl, - struct file_lock *block_fl) -{ - struct list_head *tmp; - void *caller_owner, *blocked_owner; - unsigned int caller_pid, blocked_pid; - - caller_owner = caller_fl->fl_owner; - caller_pid = caller_fl->fl_pid; - blocked_owner = block_fl->fl_owner; - blocked_pid = block_fl->fl_pid; - -next_task: - if (caller_owner == blocked_owner && caller_pid == blocked_pid) - return 1; - list_for_each(tmp, &file_lock_list) { - struct list_head *btmp; - struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - if (fl->fl_owner == NULL || list_empty(&fl->fl_block)) - continue; - list_for_each(btmp, &fl->fl_block) { - struct file_lock *bfl = list_entry(tmp, struct file_lock, fl_block); - if (bfl->fl_owner == blocked_owner && - bfl->fl_pid == blocked_pid) { - if (fl->fl_owner == caller_owner && - fl->fl_pid == caller_pid) { - return (1); - } - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; - goto next_task; - } - } - } - return 0; -} - int locks_mandatory_locked(struct inode *inode) { fl_owner_t owner = current->files; @@ -517,16 +576,19 @@ int locks_mandatory_area(int read_write, struct inode *inode, size_t count) { struct file_lock *fl; - struct file_lock *new_fl = locks_alloc_lock(); + struct file_lock tfl; int error; - new_fl->fl_owner = current->files; - new_fl->fl_pid = current->pid; - new_fl->fl_file = filp; - new_fl->fl_flags = FL_POSIX | FL_ACCESS; - new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; - new_fl->fl_start = offset; - new_fl->fl_end = offset + count - 1; + memset(&tfl, 0, sizeof(tfl)); + + tfl.fl_file = filp; + tfl.fl_flags = FL_POSIX | FL_ACCESS; + tfl.fl_owner = current->files; + tfl.fl_pid = current->pid; + init_waitqueue_head(&tfl.fl_wait); + tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; + tfl.fl_start = offset; + tfl.fl_end = offset + count - 1; error = 0; lock_kernel(); @@ -544,7 +606,7 @@ repeat: /* Block for writes against a "read" lock, * and both reads and writes against a "write" lock. */ - if (posix_locks_conflict(new_fl, fl)) { + if (posix_locks_conflict(&tfl, fl)) { error = -EAGAIN; if (filp && (filp->f_flags & O_NONBLOCK)) break; @@ -552,12 +614,12 @@ repeat: if (signal_pending(current)) break; error = -EDEADLK; - if (posix_locks_deadlock(new_fl, fl)) + if (posix_locks_deadlock(&tfl, fl)) break; - locks_insert_block(fl, new_fl); - interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(new_fl); + locks_insert_block(fl, &tfl); + interruptible_sleep_on(&tfl.fl_wait); + locks_delete_block(fl, &tfl); /* * If we've been sleeping someone might have @@ -569,15 +631,202 @@ repeat: } } unlock_kernel(); - locks_free_lock(new_fl); return error; } +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX + * style lock. + */ +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l) +{ + loff_t start; + + memset(fl, 0, sizeof(*fl)); + + init_waitqueue_head(&fl->fl_wait); + fl->fl_flags = FL_POSIX; + + switch (l->l_type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = l->l_type; + break; + default: + return (0); + } + + switch (l->l_whence) { + case 0: /*SEEK_SET*/ + start = 0; + break; + case 1: /*SEEK_CUR*/ + start = filp->f_pos; + break; + case 2: /*SEEK_END*/ + start = filp->f_dentry->d_inode->i_size; + break; + default: + return (0); + } + + if (((start += l->l_start) < 0) || (l->l_len < 0)) + return (0); + fl->fl_end = start + l->l_len - 1; + if (l->l_len > 0 && fl->fl_end < 0) + return (0); + fl->fl_start = start; /* we record the absolute position */ + if (l->l_len == 0) + fl->fl_end = OFFSET_MAX; + + fl->fl_file = filp; + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + + return (1); +} + +/* Verify a call to flock() and fill in a file_lock structure with + * an appropriate FLOCK lock. + */ +static int flock_make_lock(struct file *filp, struct file_lock *fl, + unsigned int cmd) +{ + memset(fl, 0, sizeof(*fl)); + + init_waitqueue_head(&fl->fl_wait); + + switch (cmd & ~LOCK_NB) { + case LOCK_SH: + fl->fl_type = F_RDLCK; + break; + case LOCK_EX: + fl->fl_type = F_WRLCK; + break; + case LOCK_UN: + fl->fl_type = F_UNLCK; + break; + default: + return (0); + } + + fl->fl_flags = FL_FLOCK; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_file = filp; + fl->fl_owner = NULL; + + return (1); +} + +/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific + * checking before calling the locks_conflict(). + */ +static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + /* POSIX locks owned by the same process do not conflict with + * each other. + */ + if (!(sys_fl->fl_flags & FL_POSIX) || + locks_same_owner(caller_fl, sys_fl)) + return (0); + + return (locks_conflict(caller_fl, sys_fl)); +} + +/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific + * checking before calling the locks_conflict(). + */ +static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + /* FLOCK locks referring to the same filp do not conflict with + * each other. + */ + if (!(sys_fl->fl_flags & FL_FLOCK) || + (caller_fl->fl_file == sys_fl->fl_file)) + return (0); + + return (locks_conflict(caller_fl, sys_fl)); +} + +/* Determine if lock sys_fl blocks lock caller_fl. Common functionality + * checks for overlapping locks and shared/exclusive status. + */ +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + if (!locks_overlap(caller_fl, sys_fl)) + return (0); + + switch (caller_fl->fl_type) { + case F_RDLCK: + return (sys_fl->fl_type == F_WRLCK); + + case F_WRLCK: + return (1); + + default: + printk("locks_conflict(): impossible lock type - %d\n", + caller_fl->fl_type); + break; + } + return (0); /* This should never happen */ +} + +/* This function tests for deadlock condition before putting a process to + * sleep. The detection scheme is no longer recursive. Recursive was neat, + * but dangerous - we risked stack corruption if the lock data was bad, or + * if the recursion was too deep for any other reason. + * + * We rely on the fact that a task can only be on one lock's wait queue + * at a time. When we find blocked_task on a wait queue we can re-search + * with blocked_task equal to that queue's owner, until either blocked_task + * isn't found, or blocked_task is found on a queue owned by my_task. + * + * Note: the above assumption may not be true when handling lock requests + * from a broken NFS client. But broken NFS clients have a lot more to + * worry about than proper deadlock detection anyway... --okir + */ +static int posix_locks_deadlock(struct file_lock *caller_fl, + struct file_lock *block_fl) +{ + struct file_lock *fl; + struct file_lock *bfl; + void *caller_owner, *blocked_owner; + unsigned int caller_pid, blocked_pid; + + caller_owner = caller_fl->fl_owner; + caller_pid = caller_fl->fl_pid; + blocked_owner = block_fl->fl_owner; + blocked_pid = block_fl->fl_pid; + +next_task: + if (caller_owner == blocked_owner && caller_pid == blocked_pid) + return (1); + for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { + if (fl->fl_owner == NULL || fl->fl_nextblock == NULL) + continue; + for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) { + if (bfl->fl_owner == blocked_owner && + bfl->fl_pid == blocked_pid) { + if (fl->fl_owner == caller_owner && + fl->fl_pid == caller_pid) { + return (1); + } + blocked_owner = fl->fl_owner; + blocked_pid = fl->fl_pid; + goto next_task; + } + } + } + return (0); +} + /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at * the head of the list, but that's secret knowledge known only to the next * two functions. */ -static int flock_lock_file(struct file *filp, unsigned int lock_type, +static int flock_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; @@ -585,14 +834,14 @@ static int flock_lock_file(struct file *filp, unsigned int lock_type, struct file_lock **before; struct inode * inode = filp->f_dentry->d_inode; int error, change; - int unlock = (lock_type == F_UNLCK); + int unlock = (caller->fl_type == F_UNLCK); /* * If we need a new lock, get it in advance to avoid races. */ if (!unlock) { error = -ENOLCK; - new_fl = flock_make_lock(filp, lock_type); + new_fl = locks_alloc_lock(caller); if (!new_fl) goto out; } @@ -602,8 +851,8 @@ search: change = 0; before = &inode->i_flock; while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { - if (filp == fl->fl_file) { - if (lock_type == fl->fl_type) + if (caller->fl_file == fl->fl_file) { + if (caller->fl_type == fl->fl_type) goto out; change = 1; break; @@ -639,7 +888,7 @@ repeat: goto out; locks_insert_block(fl, new_fl); interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(new_fl); + locks_delete_block(fl, new_fl); goto repeat; } locks_insert_lock(&inode->i_flock, new_fl); @@ -679,8 +928,8 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ - new_fl = locks_alloc_lock(); - new_fl2 = locks_alloc_lock(); + new_fl = locks_empty_lock(); + new_fl2 = locks_empty_lock(); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; @@ -703,7 +952,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); - locks_delete_block(caller); + locks_delete_block(fl, caller); goto repeat; } } @@ -809,7 +1058,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, if (!added) { if (caller->fl_type == F_UNLCK) goto out; - locks_copy_lock(new_fl, caller); + locks_init_lock(new_fl, caller); locks_insert_lock(before, new_fl); new_fl = NULL; } @@ -819,9 +1068,8 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, * so we have to use the second new lock (in this * case, even F_UNLCK may fail!). */ - locks_copy_lock(new_fl2, right); + left = locks_init_lock(new_fl2, right); locks_insert_lock(before, left); - left = new_fl2; new_fl2 = NULL; } right->fl_start = caller->fl_end + 1; @@ -833,288 +1081,101 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, } out: /* - * Free any unused locks. + * Free any unused locks. (They haven't + * ever been used, so we use kfree().) */ if (new_fl) - locks_free_lock(new_fl); + kfree(new_fl); if (new_fl2) - locks_free_lock(new_fl2); + kfree(new_fl2); return error; } -static inline int flock_translate_cmd(int cmd) { - switch (cmd &~ LOCK_NB) { - case LOCK_SH: - return F_RDLCK; - case LOCK_EX: - return F_WRLCK; - case LOCK_UN: - return F_UNLCK; - } - return -EINVAL; -} - -/* flock() system call entry point. Apply a FL_FLOCK style lock to - * an open file descriptor. +/* + * Allocate an empty lock structure. We can use GFP_KERNEL now that + * all allocations are done in advance. */ -asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) +static struct file_lock *locks_empty_lock(void) { - struct file *filp; - int error, type; - - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - - error = flock_translate_cmd(cmd); - if (error < 0) - goto out_putf; - type = error; - - error = -EBADF; - if ((type != F_UNLCK) && !(filp->f_mode & 3)) - goto out_putf; - - lock_kernel(); - error = flock_lock_file(filp, type, - (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); - unlock_kernel(); - -out_putf: - fput(filp); -out: - return error; + /* Okay, let's make a new file_lock structure... */ + return ((struct file_lock *) kmalloc(sizeof(struct file_lock), + GFP_KERNEL)); } -/* Report the first existing lock that would conflict with l. - * This implements the F_GETLK command of fcntl(). +/* + * Initialize a new lock from an existing file_lock structure. */ -int fcntl_getlk(unsigned int fd, struct flock *l) +static struct file_lock *locks_init_lock(struct file_lock *new, + struct file_lock *fl) { - struct file *filp; - struct file_lock *fl, *file_lock = locks_alloc_lock(); - struct flock flock; - int error; - - error = -EFAULT; - if (copy_from_user(&flock, l, sizeof(flock))) - goto out; - error = -EINVAL; - if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) - goto out; - - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - - if (!posix_make_lock(filp, file_lock, &flock)) - goto out_putf; - - if (filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, file_lock); - if (error < 0) - goto out_putf; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, file_lock); - else - fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock); - } else { - fl = posix_test_lock(filp, file_lock); + if (new) { + memset(new, 0, sizeof(*new)); + new->fl_owner = fl->fl_owner; + new->fl_pid = fl->fl_pid; + init_waitqueue_head(&new->fl_wait); + new->fl_file = fl->fl_file; + new->fl_flags = fl->fl_flags; + new->fl_type = fl->fl_type; + new->fl_start = fl->fl_start; + new->fl_end = fl->fl_end; + new->fl_notify = fl->fl_notify; + new->fl_insert = fl->fl_insert; + new->fl_remove = fl->fl_remove; + new->fl_u = fl->fl_u; } - - flock.l_type = F_UNLCK; - if (fl != NULL) { - flock.l_pid = fl->fl_pid; - flock.l_start = fl->fl_start; - flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : - fl->fl_end - fl->fl_start + 1; - flock.l_whence = 0; - flock.l_type = fl->fl_type; - } - error = -EFAULT; - if (!copy_to_user(l, &flock, sizeof(flock))) - error = 0; - -out_putf: - fput(filp); -out: - locks_free_lock(file_lock); - return error; + return new; } -/* Apply the lock described by l to an open file descriptor. - * This implements both the F_SETLK and F_SETLKW commands of fcntl(). +/* Insert file lock fl into an inode's lock list at the position indicated + * by pos. At the same time add the lock to the global file lock list. */ -int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) -{ - struct file *filp; - struct file_lock *file_lock = locks_alloc_lock(); - struct flock flock; - struct inode *inode; - int error; - - /* - * This might block, so we do it before checking the inode. - */ - error = -EFAULT; - if (copy_from_user(&flock, l, sizeof(flock))) - goto out; - - /* Get arguments and validate them ... - */ - - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - - error = -EINVAL; - inode = filp->f_dentry->d_inode; - - /* Don't allow mandatory locks on files that may be memory mapped - * and shared. - */ - if (IS_MANDLOCK(inode) && - (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { - struct vm_area_struct *vma; - struct address_space *mapping = inode->i_mapping; - spin_lock(&mapping->i_shared_lock); - for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { - if (!(vma->vm_flags & VM_MAYSHARE)) - continue; - spin_unlock(&mapping->i_shared_lock); - error = -EAGAIN; - goto out_putf; - } - spin_unlock(&mapping->i_shared_lock); - } - - error = -EINVAL; - if (!posix_make_lock(filp, file_lock, &flock)) - goto out_putf; - - error = -EBADF; - switch (flock.l_type) { - case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - goto out_putf; - break; - case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - goto out_putf; - break; - case F_UNLCK: - break; - case F_SHLCK: - case F_EXLCK: -#ifdef __sparc__ -/* warn a bit for now, but don't overdo it */ +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { - static int count = 0; - if (!count) { - count=1; - printk(KERN_WARNING - "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", - current->pid, current->comm); - } -} - if (!(filp->f_mode & 3)) - goto out_putf; - break; -#endif - default: - error = -EINVAL; - goto out_putf; - } + fl->fl_nextlink = file_lock_table; + fl->fl_prevlink = NULL; + if (file_lock_table != NULL) + file_lock_table->fl_prevlink = fl; + file_lock_table = fl; + fl->fl_next = *pos; /* insert into file's list */ + *pos = fl; - if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp, cmd, file_lock); - if (error < 0) - goto out_putf; - } - error = posix_lock_file(filp, file_lock, cmd == F_SETLKW); + if (fl->fl_insert) + fl->fl_insert(fl); -out_putf: - fput(filp); -out: - locks_free_lock(file_lock); - return error; + return; } -/* - * This function is called when the file is being removed - * from the task's fd array. +/* Delete a lock and free it. + * First remove our lock from the active lock lists. Then call + * locks_wake_up_blocks() to wake up processes that are blocked + * waiting for this lock. Finally free the lock structure. */ -void locks_remove_posix(struct file *filp, fl_owner_t owner) +static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) { - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock *fl; - struct file_lock **before; + struct file_lock *thisfl; + struct file_lock *prevfl; + struct file_lock *nextfl; + + thisfl = *thisfl_p; + *thisfl_p = thisfl->fl_next; - /* - * For POSIX locks we free all locks on this file for the given task. - */ -repeat: - before = &inode->i_flock; - while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { - locks_delete_lock(before, 0); - goto repeat; - } - before = &fl->fl_next; - } -} + prevfl = thisfl->fl_prevlink; + nextfl = thisfl->fl_nextlink; -/* - * This function is called on the last close of an open file. - */ -void locks_remove_flock(struct file *filp) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; - struct file_lock **before; + if (nextfl != NULL) + nextfl->fl_prevlink = prevfl; -repeat: - before = &inode->i_flock; - while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { - int (*lock)(struct file *, int, struct file_lock *); - lock = NULL; - if (filp->f_op) - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } - locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } - continue; - } - before = &fl->fl_next; - } -} + if (prevfl != NULL) + prevfl->fl_nextlink = nextfl; + else + file_lock_table = nextfl; -/* The following two are for the benefit of lockd. - */ -void -posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) -{ - lock_kernel(); - locks_insert_block(blocker, waiter); - unlock_kernel(); -} + if (thisfl->fl_remove) + thisfl->fl_remove(thisfl); + + locks_wake_up_blocks(thisfl, wait); + locks_free_lock(thisfl); -void -posix_unblock_lock(struct file_lock *waiter) -{ - locks_delete_block(waiter); return; } @@ -1141,8 +1202,8 @@ static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) kdevname(inode->i_dev), inode->i_ino, (long long)fl->fl_start, (long long)fl->fl_end); sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n", - (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next, - (long)fl->fl_next, (long)fl->fl_block.next); + (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink, + (long)fl->fl_next, (long)fl->fl_nextblock); } static void move_lock_status(char **p, off_t* pos, off_t offset) @@ -1169,43 +1230,35 @@ static void move_lock_status(char **p, off_t* pos, off_t offset) int get_locks_status(char *buffer, char **start, off_t offset, int length) { - struct list_head *tmp; + struct file_lock *fl; + struct file_lock *bfl; char *q = buffer; off_t pos = 0; - int i = 0; + int i; - lock_kernel(); - list_for_each(tmp, &file_lock_list) { - struct list_head *btmp; - struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - lock_get_status(q, fl, ++i, ""); + for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) { + lock_get_status(q, fl, i, ""); move_lock_status(&q, &pos, offset); if(pos >= offset+length) goto done; - list_for_each(btmp, &fl->fl_block) { - struct file_lock *bfl = list_entry(btmp, - struct file_lock, fl_block); + if ((bfl = fl->fl_nextblock) == NULL) + continue; + do { lock_get_status(q, bfl, i, " ->"); move_lock_status(&q, &pos, offset); if(pos >= offset+length) goto done; - } + } while ((bfl = bfl->fl_nextblock) != fl); } done: - unlock_kernel(); *start = buffer; if(q-buffer < length) return (q-buffer); return length; } -void __init filelock_init(void) -{ - filelock_cache = kmem_cache_create("file lock cache", - sizeof(struct file_lock), 0, 0, init_once, NULL); - if (!filelock_cache) - panic("cannot create file lock slab cache"); -} + + diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index ca30b7753..075574876 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -276,13 +276,16 @@ struct inode * minix_new_inode(const struct inode * dir, int * error) mark_inode_dirty(inode); unlock_super(sb); +printk("m_n_i: allocated inode "); if(DQUOT_ALLOC_INODE(sb, inode)) { +printk("fails quota test\n"); sb->dq_op->drop(inode); inode->i_nlink = 0; iput(inode); *error = -EDQUOT; return NULL; } +printk("is within quota\n"); *error = 0; return inode; diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 96e1ffa86..30794d27a 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -329,7 +329,7 @@ static int V2_minix_sync_file(struct inode * inode, struct file * file) * NULL */ -int minix_sync_file(struct file * file, struct dentry *dentry, int datasync) +int minix_sync_file(struct file * file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 7b6850e6f..6ddc278aa 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -28,7 +28,7 @@ #include <linux/minix_fs.h> static void minix_read_inode(struct inode * inode); -static void minix_write_inode(struct inode * inode, int); +static void minix_write_inode(struct inode * inode); static int minix_statfs(struct super_block *sb, struct statfs *buf); static int minix_remount (struct super_block * sb, int * flags, char * data); @@ -1232,7 +1232,7 @@ static struct buffer_head *minix_update_inode(struct inode *inode) return V2_minix_update_inode(inode); } -static void minix_write_inode(struct inode * inode, int unused) +static void minix_write_inode(struct inode * inode) { struct buffer_head *bh; diff --git a/fs/namei.c b/fs/namei.c index 96ae55768..501000381 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -191,35 +191,21 @@ int permission(struct inode * inode,int mask) * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist * > 0: (i_writecount) users are writing to the file. * - * Normally we operate on that counter with atomic_{inc,dec} and it's safe - * except for the cases where we don't hold i_writecount yet. Then we need to - * use {get,deny}_write_access() - these functions check the sign and refuse - * to do the change if sign is wrong. Exclusion between them is provided by - * spinlock (arbitration_lock) and I'll rip the second arsehole to the first - * who will try to move it in struct inode - just leave it here. + * WARNING: as soon as we will move get_write_access(), do_mmap() or + * prepare_binfmt() out of the big lock we will need a spinlock protecting + * the checks in all 3. For the time being it is not needed. */ -static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED; int get_write_access(struct inode * inode) { - spin_lock(&arbitration_lock); - if (atomic_read(&inode->i_writecount) < 0) { - spin_unlock(&arbitration_lock); + if (atomic_read(&inode->i_writecount) < 0) return -ETXTBSY; - } atomic_inc(&inode->i_writecount); - spin_unlock(&arbitration_lock); return 0; } -int deny_write_access(struct file * file) + +void put_write_access(struct inode * inode) { - spin_lock(&arbitration_lock); - if (atomic_read(&file->f_dentry->d_inode->i_writecount) > 0) { - spin_unlock(&arbitration_lock); - return -ETXTBSY; - } - atomic_dec(&file->f_dentry->d_inode->i_writecount); - spin_unlock(&arbitration_lock); - return 0; + atomic_dec(&inode->i_writecount); } void path_release(struct nameidata *nd) @@ -351,34 +337,7 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) { return __follow_down(mnt,dentry); } - -static inline void follow_dotdot(struct nameidata *nd) -{ - while(1) { - struct vfsmount *parent; - struct dentry *dentry; - if (nd->dentry == current->fs->root && - nd->mnt == current->fs->rootmnt) { - break; - } - if (nd->dentry != nd->mnt->mnt_root) { - dentry = dget(nd->dentry->d_parent); - dput(nd->dentry); - nd->dentry = dentry; - break; - } - parent=nd->mnt->mnt_parent; - if (parent == nd->mnt) { - break; - } - mntget(parent); - dentry=dget(nd->mnt->mnt_mountpoint); - dput(nd->dentry); - nd->dentry = dentry; - mntput(nd->mnt); - nd->mnt = parent; - } -} + /* * Name resolution. * @@ -444,7 +403,19 @@ int path_walk(const char * name, struct nameidata *nd) case 2: if (this.name[1] != '.') break; - follow_dotdot(nd); + while (1) { + if (nd->dentry == current->fs->root && + nd->mnt == current->fs->rootmnt) + break; + if (nd->dentry != nd->mnt->mnt_root) { + dentry = dget(nd->dentry->d_parent); + dput(nd->dentry); + nd->dentry = dentry; + break; + } + if (!__follow_up(&nd->mnt, &nd->dentry)) + break; + } inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -512,7 +483,19 @@ last_component: case 2: if (this.name[1] != '.') break; - follow_dotdot(nd); + while (1) { + if (nd->dentry == current->fs->root && + nd->mnt == current->fs->rootmnt) + break; + if (nd->dentry != nd->mnt->mnt_root) { + dentry = dget(nd->dentry->d_parent); + dput(nd->dentry); + nd->dentry = dentry; + break; + } + if (!__follow_up(&nd->mnt, &nd->dentry)) + break; + } inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -788,6 +771,8 @@ static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir) int error; if (!victim->d_inode || victim->d_parent->d_inode != dir) return -ENOENT; + if (IS_DEADDIR(dir)) + return -ENOENT; error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) return error; @@ -801,6 +786,8 @@ static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir) return -ENOTDIR; if (IS_ROOT(victim)) return -EBUSY; + if (d_mountpoint(victim)) + return -EBUSY; } else if (S_ISDIR(victim->d_inode->i_mode)) return -EISDIR; return 0; @@ -930,22 +917,6 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) error = -EEXIST; if (flag & O_EXCL) goto exit_dput; - if (flag & O_NOFOLLOW) { - error = -ELOOP; - if (dentry->d_inode->i_op && - dentry->d_inode->i_op->follow_link) - goto exit_dput; - if (d_mountpoint(dentry)) - goto exit_dput; - goto got_it; - } - /* Check mountpoints - it may be a binding on file. */ - while (d_mountpoint(dentry) && - __follow_down(&nd->mnt, &dentry)) - ; - error = -ENOENT; - if (!dentry->d_inode) - goto exit_dput; if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) { /* @@ -959,7 +930,6 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) return error; dentry = nd->dentry; } else { - got_it: dput(nd->dentry); nd->dentry = dentry; } @@ -992,10 +962,6 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) goto exit; - error = -EOPNOTSUPP; - if (S_ISSOCK(inode->i_mode)) - goto exit; - error = permission(inode,acc_mode); if (error) goto exit; @@ -1247,15 +1213,9 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) double_down(&dir->i_zombie, &dentry->d_inode->i_zombie); d_unhash(dentry); - if (IS_DEADDIR(dir)) - error = -ENOENT; - else if (d_mountpoint(dentry)) - error = -EBUSY; - else { - error = dir->i_op->rmdir(dir, dentry); - if (!error) - dentry->d_inode->i_flags |= S_DEAD; - } + error = dir->i_op->rmdir(dir, dentry); + if (!error) + dentry->d_inode->i_flags |= S_DEAD; double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); if (!error) d_delete(dentry); @@ -1315,13 +1275,9 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) error = -EPERM; if (dir->i_op && dir->i_op->unlink) { DQUOT_INIT(dir); - if (d_mountpoint(dentry)) - error = -EBUSY; - else { - error = dir->i_op->unlink(dir, dentry); - if (!error) - d_delete(dentry); - } + error = dir->i_op->unlink(dir, dentry); + if (!error) + d_delete(dentry); } } up(&dir->i_zombie); @@ -1599,12 +1555,7 @@ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, } else double_down(&old_dir->i_zombie, &new_dir->i_zombie); - if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir)) - error = -ENOENT; - else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) - error = -EBUSY; - else - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (target) { if (!error) target->i_flags |= S_DEAD; @@ -1652,10 +1603,7 @@ int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); double_down(&old_dir->i_zombie, &new_dir->i_zombie); - if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) - error = -EBUSY; - else - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); double_up(&old_dir->i_zombie, &new_dir->i_zombie); if (error) return error; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 11694e79b..55daea198 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -973,7 +973,7 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) /* * Check whether to close the file ... */ - if (inode) { + if (inode && NCP_FINFO(inode)->opened) { PPRINTK("ncp_unlink: closing file\n"); ncp_make_closed(inode); } @@ -982,7 +982,7 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) #ifdef CONFIG_NCPFS_STRONG /* 9C is Invalid path.. It should be 8F, 90 - read only, but it is not :-( */ - if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */ + if (error == 0x9C && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */ error = ncp_force_unlink(dir, dentry); } #endif @@ -1051,7 +1051,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name, new_dir, __new_name); #ifdef CONFIG_NCPFS_STRONG - if ((error == 0x90 || error == 0x8B || error == -EACCES) && + if ((error == 0x90 || error == -EACCES) && server->m.flags & NCP_MOUNT_STRONG) { /* RO */ error = ncp_force_rename(old_dir, old_dentry, __old_name, new_dir, new_dentry, __new_name); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 3442c3f9f..6f8fd2d63 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -26,7 +26,7 @@ static inline unsigned int min(unsigned int a, unsigned int b) return a < b ? a : b; } -static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync) +static int ncp_fsync(struct file *file, struct dentry *dentry) { return 0; } @@ -46,12 +46,12 @@ int ncp_make_open(struct inode *inode, int right) } DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n", - atomic_read(&NCP_FINFO(inode)->opened), + NCP_FINFO(inode)->opened, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); error = -EACCES; - down(&NCP_FINFO(inode)->open_sem); - if (!atomic_read(&NCP_FINFO(inode)->opened)) { + lock_super(inode->i_sb); + if (!NCP_FINFO(inode)->opened) { struct ncp_entry_info finfo; int result; @@ -88,18 +88,15 @@ int ncp_make_open(struct inode *inode, int right) */ update: ncp_update_inode(inode, &finfo); - atomic_set(&NCP_FINFO(inode)->opened, 1); } access = NCP_FINFO(inode)->access; PPRINTK("ncp_make_open: file open, access=%x\n", access); - if (access == right || access == O_RDWR) { - atomic_inc(&NCP_FINFO(inode)->opened); + if (access == right || access == O_RDWR) error = 0; - } out_unlock: - up(&NCP_FINFO(inode)->open_sem); + unlock_super(inode->i_sb); out: return error; } @@ -156,7 +153,7 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) freelen = ncp_read_bounce_size(bufsize); freepage = kmalloc(freelen, GFP_NFS); if (!freepage) - goto outrel; + goto out; error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { @@ -169,8 +166,9 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) pos, to_read, buf, &read_this_time, freepage, freelen); if (error) { - error = -EIO; /* NW errno -> Linux errno */ - break; + kfree(freepage); + error = -EIO; /* This is not exact, i know.. */ + goto out; } pos += read_this_time; buf += read_this_time; @@ -190,8 +188,6 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) DPRINTK("ncp_file_read: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); -outrel: - ncp_inode_close(inode); out: return already_read ? already_read : error; } @@ -240,10 +236,8 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) already_written = 0; bouncebuffer = kmalloc(bufsize, GFP_NFS); - if (!bouncebuffer) { - errno = -EIO; /* -ENOMEM */ - goto outrel; - } + if (!bouncebuffer) + return -EIO; /* -ENOMEM */ while (already_written < count) { int written_this_time; size_t to_write = min(bufsize - (pos % bufsize), @@ -277,15 +271,15 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); -outrel: - ncp_inode_close(inode); out: return already_written ? already_written : errno; } static int ncp_release(struct inode *inode, struct file *file) { - if (ncp_make_closed(inode)) { - DPRINTK("ncp_release: failed to close\n"); + if (NCP_FINFO(inode)->opened) { + if (ncp_make_closed(inode)) { + DPRINTK("ncp_release: failed to close\n"); + } } return 0; } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index cff9649f5..e885aed47 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -61,6 +61,7 @@ void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo) #ifdef CONFIG_NCPFS_STRONG NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; #endif + NCP_FINFO(inode)->opened = nwinfo->opened; NCP_FINFO(inode)->access = nwinfo->access; NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle; memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, @@ -75,7 +76,7 @@ void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo) struct nw_info_struct *nwi = &nwinfo->i; struct ncp_server *server = NCP_SERVER(inode); - if (!atomic_read(&NCP_FINFO(inode)->opened)) { + if (!NCP_FINFO(inode)->opened) { #ifdef CONFIG_NCPFS_STRONG NCP_FINFO(inode)->nwattr = nwi->attributes; #endif @@ -215,9 +216,6 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) inode = get_empty_inode(); if (inode) { - init_MUTEX(&NCP_FINFO(inode)->open_sem); - atomic_set(&NCP_FINFO(inode)->opened, info->opened); - inode->i_sb = sb; inode->i_dev = sb->s_dev; inode->i_ino = info->ino; @@ -247,7 +245,7 @@ ncp_delete_inode(struct inode *inode) DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); } - if (ncp_make_closed(inode) != 0) { + if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) { /* We can't do anything but complain. */ printk(KERN_ERR "ncp_delete_inode: could not close\n"); } @@ -320,6 +318,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = NCP_SUPER_MAGIC; + sb->s_dev = dev; sb->s_op = &ncp_sops; server = NCP_SBP(sb); @@ -677,7 +676,6 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) /* According to ndir, the changes only take effect after closing the file */ - ncp_inode_close(inode); result = ncp_make_closed(inode); if (!result) vmtruncate(inode, attr->ia_size); diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 24e616396..26c95fc8f 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -335,12 +335,18 @@ int ncp_ioctl(struct inode *inode, struct file *filp, { return result; } - result = -EIO; if (!ncp_conn_valid(server)) - goto outrel; - result = -EISDIR; + { + return -EIO; + } if (!S_ISREG(inode->i_mode)) - goto outrel; + { + return -EISDIR; + } + if (!NCP_FINFO(inode)->opened) + { + return -EBADFD; + } if (rqdata.cmd == NCP_LOCK_CLEAR) { result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), @@ -367,8 +373,6 @@ int ncp_ioctl(struct inode *inode, struct file *filp, rqdata.timeout); if (result > 0) result = -EAGAIN; } -outrel: - ncp_inode_close(inode); return result; } #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 08d28d895..752ae1e1e 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -82,7 +82,6 @@ static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, break; } } - ncp_inode_close(inode); } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 0353882b9..73afd107a 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -221,23 +221,20 @@ ncp_close_file(struct ncp_server *server, const char *file_id) return result; } +/* + * Called with the superblock locked. + */ int ncp_make_closed(struct inode *inode) { int err; + NCP_FINFO(inode)->opened = 0; + err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); - err = 0; - down(&NCP_FINFO(inode)->open_sem); - if (atomic_read(&NCP_FINFO(inode)->opened) == 1) { - atomic_set(&NCP_FINFO(inode)->opened, 0); - err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); - - if (!err) - PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", - NCP_FINFO(inode)->volNumber, - NCP_FINFO(inode)->dirEntNum, err); - } - up(&NCP_FINFO(inode)->open_sem); + if (!err) + PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", + NCP_FINFO(inode)->volNumber, + NCP_FINFO(inode)->dirEntNum, err); return err; } @@ -616,8 +613,7 @@ int ncp_open_create_file_or_subdir(struct ncp_server *server, if ((result = ncp_request(server, 87)) != 0) goto out; - if (!(create_attributes & aDIR)) - target->opened = 1; + target->opened = 1; target->server_file_handle = ncp_reply_dword(server, 0); target->open_create_action = ncp_reply_byte(server, 4); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 31797a3c3..8b33a5c2e 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -57,10 +57,6 @@ int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, const char *, int *); -static inline void ncp_inode_close(struct inode *inode) { - atomic_dec(&NCP_FINFO(inode)->opened); -} - int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *); diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c index 0962593da..46925eb6d 100644 --- a/fs/ncpfs/symlink.c +++ b/fs/ncpfs/symlink.c @@ -50,6 +50,10 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) char *link; char *buf = (char*)kmap(page); + error = -EIO; + if (ncp_make_open(inode,O_RDONLY)) + goto fail; + error = -ENOMEM; for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_NFS))==NULL; cnt++) { if (cnt > 10) @@ -57,22 +61,20 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) schedule(); } - if (ncp_make_open(inode,O_RDONLY)) - goto failEIO; - error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, 0,NCP_MAX_SYMLINK_SIZE,link,&length); - ncp_inode_close(inode); - /* Close file handle if no other users... */ - ncp_make_closed(inode); - if (error) - goto failEIO; - + if (error) { + kfree(link); + goto fail; + } if (length<NCP_MIN_SYMLINK_SIZE || ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 || - ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) - goto failEIO; + ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { + error = -EIO; + kfree(link); + goto fail; + } len = NCP_MAX_SYMLINK_SIZE; error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link+8, length-8, 0); @@ -84,9 +86,6 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) UnlockPage(page); return 0; -failEIO: - error = -EIO; - kfree(link); fail: SetPageError(page); kunmap(page); @@ -121,15 +120,13 @@ int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL) return -ENOMEM; - err = -EIO; - if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) - goto failfree; + if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) { + kfree(link); + return -EIO; + } inode=dentry->d_inode; - if (ncp_make_open(inode, O_WRONLY)) - goto failfree; - ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0; ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1; @@ -137,26 +134,19 @@ int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { symlink can point out of ncp filesystem */ length += 1; err = ncp_io2vol(NCP_SERVER(inode),link+8,&length,symname,length-1,0); - if (err) - goto fail; + if (err) { + kfree(link); + return err; + } if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, 0, length+8, link, &i) || i!=length+8) { - err = -EIO; - goto fail; + kfree(link); + return -EIO; } - ncp_inode_close(inode); - ncp_make_closed(inode); kfree(link); return 0; - -fail: - ncp_inode_close(inode); - ncp_make_closed(inode); -failfree: - kfree(link); - return err; } #endif diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 4225bfc86..62b37c8cf 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -38,7 +38,7 @@ static int nfs_file_mmap(struct file *, struct vm_area_struct *); static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *); static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *); static int nfs_file_flush(struct file *); -static int nfs_fsync(struct file *, struct dentry *dentry, int); +static int nfs_fsync(struct file *, struct dentry *dentry); struct file_operations nfs_file_operations = { read: nfs_file_read, @@ -123,7 +123,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) * whether any write errors occurred for this process. */ static int -nfs_fsync(struct file *file, struct dentry *dentry, int datasync) +nfs_fsync(struct file *file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; int status; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c2607ff2e..c4e456185 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -218,7 +218,7 @@ static struct { }; #define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1) -int +long asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) { struct nfsctl_arg * argp = opaque_argp; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index a2b2b4971..fb3b32f8d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -83,10 +83,7 @@ nfsd_svc(unsigned short port, int nrservs) if (error < 0) goto failure; -#if CONFIG_NFSD_TCP - /* This is developer-only at the moment, - * there are untracked bugs as of 2.4.0-test1-ac11 - */ +#if 0 /* Don't even pretend that TCP works. It doesn't. */ error = svc_makesock(nfsd_serv, IPPROTO_TCP, port); if (error < 0) goto failure; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 601549ccc..40f1ab85a 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -314,7 +314,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(inode, 0); + write_inode_now(inode); err = 0; /* Don't unlock inode; the nfssvc_release functions are supposed @@ -512,7 +512,7 @@ nfsd_sync(struct file *filp) { dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); down(&filp->f_dentry->d_inode->i_sem); - filp->f_op->fsync(filp, filp->f_dentry,0); + filp->f_op->fsync(filp, filp->f_dentry); up(&filp->f_dentry->d_inode->i_sem); } @@ -520,10 +520,10 @@ void nfsd_sync_dir(struct dentry *dp) { struct inode *inode = dp->d_inode; - int (*fsync) (struct file *, struct dentry *, int); + int (*fsync) (struct file *, struct dentry *); if (inode->i_fop && (fsync = inode->i_fop->fsync)) { - fsync(NULL, dp, 0); + fsync(NULL, dp); } } @@ -891,7 +891,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (EX_ISSYNC(fhp->fh_export)) { nfsd_sync_dir(dentry); - write_inode_now(dchild->d_inode, 0); + write_inode_now(dchild->d_inode); } @@ -1118,7 +1118,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, | S_IFLNK; err = notify_change(dnew, iap); if (!err && EX_ISSYNC(fhp->fh_export)) - write_inode_now(dentry->d_inode, 0); + write_inode_now(dentry->d_inode); } } } else @@ -1178,7 +1178,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, if (!err) { if (EX_ISSYNC(ffhp->fh_export)) { nfsd_sync_dir(ddir); - write_inode_now(dest, 0); + write_inode_now(dest); } } else { if (err == -EXDEV && rqstp->rq_vers == 2) diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 9e7ab2eaf..6f0e188d1 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -3,7 +3,7 @@ O_TARGET := ntfs.o O_OBJS := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o M_OBJS := $(O_TARGET) -EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"000607\" +EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"000502\" include $(TOPDIR)/Rules.make diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index 8ec377e0a..e0649ec7b 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -80,7 +80,7 @@ ntfs_read(struct file * filp, char *buf, size_t count, loff_t *off) io.param=buf; io.size=count; error=ntfs_read_attr(ino,ino->vol->at_data,NULL,*off,&io); - if(error && !io.size)return -error; + if(error)return -error; *off+=io.size; return io.size; @@ -707,7 +707,7 @@ static void ntfs_read_inode(struct inode* inode) #ifdef CONFIG_NTFS_RW static void -ntfs_write_inode (struct inode *ino, int unused) +ntfs_write_inode (struct inode *ino) { ntfs_debug (DEBUG_LINUX, "ntfs:write inode %x\n", ino->i_ino); ntfs_update_inode (NTFS_LINO2NINO (ino)); diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 631323769..b0941569b 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -312,9 +312,9 @@ static void devfs_register_partition (struct gendisk *dev, int minor, int part) devfs_flags |= DEVFS_FL_REMOVABLE; sprintf (devname, "part%d", part); dev->part[minor + part].de = - devfs_register (dir, devname, 0, devfs_flags, + devfs_register (dir, devname, devfs_flags, dev->major, minor + part, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL); } @@ -350,8 +350,8 @@ static void devfs_register_disc (struct gendisk *dev, int minor) devfs_mk_symlink (devfs_handle, symlink, 0, DEVFS_FL_DEFAULT, dirname + pos, 0, &slave, NULL); dev->part[minor].de = - devfs_register (dir, "disc", 4, devfs_flags, dev->major, minor, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, dev->fops,NULL); + devfs_register (dir, "disc", devfs_flags, dev->major, minor, + S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL); devfs_auto_unregister (dev->part[minor].de, slave); if (!dev->de_arr) devfs_auto_unregister (slave, dir); @@ -607,8 +607,6 @@ static struct super_block * pipefs_read_super(struct super_block *sb, void *data root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; root->i_uid = root->i_gid = 0; root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; - root->i_sb = sb; - root->i_dev = sb->s_dev; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = PIPEFS_MAGIC; diff --git a/fs/proc/base.c b/fs/proc/base.c index fa7ff052d..fb63722d5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -303,7 +303,7 @@ static struct file_operations proc_info_file_operations = { }; #define MAY_PTRACE(p) \ -(p==current||(p->p_pptr==current&&(p->ptrace&PT_PTRACED)&&p->state==TASK_STOPPED)) +(p==current||(p->p_pptr==current&&(p->ptrace & PT_PTRACED)&&p->state==TASK_STOPPED)) static ssize_t mem_read(struct file * file, char * buf, size_t count, loff_t *ppos) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index dc6f96b17..9c7270070 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -44,7 +44,7 @@ static struct file_operations proc_file_operations = { #endif /* 4K page size but our output routines use some slack for overruns */ -#define PROC_BLOCK_SIZE (3*1024) +#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024) static ssize_t proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index f2503b765..9afe2d67c 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -325,14 +325,14 @@ static int kstat_read_proc(char *page, char **start, off_t off, for (major = 0; major < DK_MAX_MAJOR; major++) { for (disk = 0; disk < DK_MAX_DISK; disk++) { - int active = kstat.dk_drive[major][disk] + + int active = kstat.dk_drive_rio[major][disk] + kstat.dk_drive_rblk[major][disk] + + kstat.dk_drive_wio[major][disk] + kstat.dk_drive_wblk[major][disk]; if (active) len += sprintf(page + len, - "(%u,%u):(%u,%u,%u,%u,%u) ", + "(%u,%u):(%u,%u,%u,%u) ", major, disk, - kstat.dk_drive[major][disk], kstat.dk_drive_rio[major][disk], kstat.dk_drive_rblk[major][disk], kstat.dk_drive_wio[major][disk], diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c index e8f079c24..e90291f03 100644 --- a/fs/qnx4/fsync.c +++ b/fs/qnx4/fsync.c @@ -147,7 +147,7 @@ static int sync_dindirect(struct inode *inode, unsigned short *diblock, return err; } -int qnx4_sync_file(struct file *file, struct dentry *dentry, int datasync) +int qnx4_sync_file(struct file *file, struct dentry *dentry) { struct inode *inode = dentry->d_inode; int wait, err = 0; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 0785ee368..60393eb91 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -73,7 +73,7 @@ static void qnx4_write_super(struct super_block *sb) sb->s_dirt = 0; } -static void qnx4_write_inode(struct inode *inode, int unused) +static void qnx4_write_inode(struct inode *inode) { struct qnx4_inode_entry *raw_inode; int block, ino; @@ -340,6 +340,7 @@ static struct super_block *qnx4_read_super(struct super_block *s, set_blocksize(dev, QNX4_BLOCK_SIZE); s->s_blocksize = QNX4_BLOCK_SIZE; s->s_blocksize_bits = QNX4_BLOCK_SIZE_BITS; + s->s_dev = dev; /* Check the boot signature. Since the qnx4 code is dangerous, we should leave as quickly as possible diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 2a1a7423d..f87d30e0b 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -22,14 +22,13 @@ * caches is sufficient. */ - #include <linux/module.h> #include <linux/fs.h> #include <linux/pagemap.h> #include <linux/init.h> #include <linux/string.h> #include <linux/locks.h> -#include <linux/highmem.h> + #include <asm/uaccess.h> /* some random number */ @@ -66,8 +65,7 @@ static struct dentry * ramfs_lookup(struct inode *dir, struct dentry *dentry) static int ramfs_readpage(struct file *file, struct page * page) { if (!Page_Uptodate(page)) { - memset((void *) kmap(page), 0, PAGE_CACHE_SIZE); - kunmap(page); + memset((void *) page_address(page), 0, PAGE_CACHE_SIZE); SetPageUptodate(page); } UnlockPage(page); @@ -88,6 +86,7 @@ static int ramfs_prepare_write(struct file *file, struct page *page, unsigned of { void *addr; + addr = (void *) kmap(page); if (!Page_Uptodate(page)) { memset(addr, 0, PAGE_CACHE_SIZE); SetPageUptodate(page); diff --git a/fs/readdir.c b/fs/readdir.c index 059ab391d..8f40d846a 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -90,6 +90,8 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) #define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) +#ifndef __ia64__ + struct old_linux_dirent { unsigned long d_ino; unsigned long d_offset; @@ -144,6 +146,8 @@ out: return error; } +#endif /* !__ia64__ */ + /* * New, all-improved, singing, dancing, iBCS2-compliant getdents() * interface. diff --git a/fs/select.c b/fs/select.c index 3ee120746..86c2793d7 100644 --- a/fs/select.c +++ b/fs/select.c @@ -18,7 +18,6 @@ #include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/file.h> -#include <linux/vmalloc.h> #include <asm/uaccess.h> @@ -53,7 +52,6 @@ static poll_table* alloc_wait(int nfds) if(out==NULL) return NULL; out->nr = 0; - out->err = 0; out->entry = (struct poll_table_entry *)(out + 1); out->next = NULL; nfds -=__MAX_POLL_TABLE_ENTRIES; @@ -99,36 +97,19 @@ static void free_wait(poll_table * p) void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { - poll_table* walk = p; for (;;) { - if (walk->nr < __MAX_POLL_TABLE_ENTRIES) { + if (p->nr < __MAX_POLL_TABLE_ENTRIES) { struct poll_table_entry * entry; -ok_table: - entry = walk->entry + walk->nr; + entry = p->entry + p->nr; get_file(filp); entry->filp = filp; entry->wait_address = wait_address; init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait); - walk->nr++; + p->nr++; return; } - if (walk->next == NULL) { - poll_table *tmp; - current->state=TASK_RUNNING; - tmp = (poll_table *) __get_free_page(GFP_KERNEL); - if (!tmp) { - p->err=-ENOMEM; - return; - } - tmp->nr = 0; - tmp->entry = (struct poll_table_entry *)(tmp + 1); - tmp->next = NULL; - walk->next = tmp; - walk = tmp; - goto ok_table; - } - walk = walk->next; + p = p->next; } } @@ -245,16 +226,11 @@ int do_select(int n, fd_set_bits *fds, long *timeout) wait = NULL; } } + wait = NULL; if (retval || !__timeout || signal_pending(current)) break; - if(orig_wait->err) { - retval=orig_wait->err; - goto out; - } - wait = NULL; __timeout = schedule_timeout(__timeout); } -out: current->state = TASK_RUNNING; free_wait(orig_wait); @@ -318,10 +294,7 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) */ ret = -ENOMEM; size = FDS_BYTES(n); - if(size>8000) - bits = vmalloc(6 * size); - else - bits = kmalloc(6 * size, GFP_KERNEL); + bits = kmalloc(6 * size, GFP_KERNEL); if (!bits) goto out_nofds; fds.in = (unsigned long *) bits; @@ -366,10 +339,7 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) set_fd_set(n, exp, fds.res_ex); out: - if(size>8000) - vfree(bits); - else - kfree(bits); + kfree(bits); out_nofds: return ret; } @@ -412,7 +382,6 @@ static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft, struct pollfd *fds[], poll_table *wait, long timeout) { int count = 0; - poll_table* orig_wait = wait; for (;;) { unsigned int i; @@ -422,16 +391,11 @@ static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft, do_pollfd(POLLFD_PER_PAGE, fds[i], &wait, &count); if (nleft) do_pollfd(nleft, fds[nchunks], &wait, &count); + wait = NULL; if (count || !timeout || signal_pending(current)) break; - if(orig_wait->err) { - count=orig_wait->err; - goto out; - } - wait=NULL; timeout = schedule_timeout(timeout); } -out: current->state = TASK_RUNNING; return count; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 49d47afa7..b47e236b0 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -27,7 +27,7 @@ /* #define pr_debug printk */ static int -smb_fsync(struct file *file, struct dentry * dentry, int datasync) +smb_fsync(struct file *file, struct dentry * dentry) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_fsync: sync file %s/%s\n", diff --git a/fs/super.c b/fs/super.c index 57d3698d3..5b8974e5b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -281,28 +281,14 @@ static struct file_system_type *get_fs_type(const char *name) static LIST_HEAD(vfsmntlist); -/** - * add_vfsmnt - add a new mount node - * @nd: location of mountpoint or %NULL if we want a root node - * @root: root of (sub)tree to be mounted - * @dev_name: device name to show in /proc/mounts - * - * This is VFS idea of mount. New node is allocated, bound to a tree - * we are mounting and optionally (OK, usually) registered as mounted - * on a given mountpoint. Returns a pointer to new node or %NULL in - * case of failure. - * - * Potential reason for failure (aside of trivial lack of memory) is a - * deleted mountpoint. Caller must hold ->i_zombie on mountpoint - * dentry (if any). - */ - -static struct vfsmount *add_vfsmnt(struct nameidata *nd, +static struct vfsmount *add_vfsmnt(struct super_block *sb, + struct dentry *mountpoint, struct dentry *root, - const char *dev_name) + struct vfsmount *parent, + const char *dev_name, + const char *dir_name) { struct vfsmount *mnt; - struct super_block *sb = root->d_inode->i_sb; char *name; mnt = kmalloc(sizeof(struct vfsmount), GFP_KERNEL); @@ -310,7 +296,13 @@ static struct vfsmount *add_vfsmnt(struct nameidata *nd, goto out; memset(mnt, 0, sizeof(struct vfsmount)); - /* It may be NULL, but who cares? */ + atomic_set(&mnt->mnt_count,1); + mnt->mnt_sb = sb; + mnt->mnt_mountpoint = dget(mountpoint); + mnt->mnt_root = dget(root); + mnt->mnt_parent = parent ? mntget(parent) : mnt; + + /* N.B. Is it really OK to have a vfsmount without names? */ if (dev_name) { name = kmalloc(strlen(dev_name)+1, GFP_KERNEL); if (name) { @@ -318,53 +310,51 @@ static struct vfsmount *add_vfsmnt(struct nameidata *nd, mnt->mnt_devname = name; } } + name = kmalloc(strlen(dir_name)+1, GFP_KERNEL); + if (name) { + strcpy(name, dir_name); + mnt->mnt_dirname = name; + } mnt->mnt_owner = current->uid; - atomic_set(&mnt->mnt_count,1); - mnt->mnt_sb = sb; - - if (nd && !IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) - goto fail; - mnt->mnt_root = dget(root); - mnt->mnt_mountpoint = nd ? dget(nd->dentry) : dget(root); - mnt->mnt_parent = nd ? mntget(nd->mnt) : mnt; - if (nd) { - list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); - list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt); - } else { + if (parent) + list_add(&mnt->mnt_child, &parent->mnt_mounts); + else INIT_LIST_HEAD(&mnt->mnt_child); - INIT_LIST_HEAD(&mnt->mnt_clash); - } INIT_LIST_HEAD(&mnt->mnt_mounts); list_add(&mnt->mnt_instances, &sb->s_mounts); + list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); list_add(&mnt->mnt_list, vfsmntlist.prev); out: return mnt; -fail: - kfree(mnt->mnt_devname); - kfree(mnt); - return NULL; } static void move_vfsmnt(struct vfsmount *mnt, struct dentry *mountpoint, struct vfsmount *parent, - const char *dev_name) + const char *dev_name, + const char *dir_name) { - struct dentry *old_mountpoint; - struct vfsmount *old_parent; - char *new_devname = NULL; + struct dentry *old_mountpoint = mnt->mnt_mountpoint; + struct vfsmount *old_parent = mnt->mnt_parent; + char *new_devname = NULL, *new_dirname = NULL; if (dev_name) { new_devname = kmalloc(strlen(dev_name)+1, GFP_KERNEL); if (new_devname) strcpy(new_devname, dev_name); } - - old_mountpoint = mnt->mnt_mountpoint; - old_parent = mnt->mnt_parent; + if (dir_name) { + new_dirname = kmalloc(strlen(dir_name)+1, GFP_KERNEL); + if (new_dirname) + strcpy(new_dirname, dir_name); + } /* flip names */ + if (new_dirname) { + kfree(mnt->mnt_dirname); + mnt->mnt_dirname = new_dirname; + } if (new_devname) { kfree(mnt->mnt_devname); mnt->mnt_devname = new_devname; @@ -375,13 +365,11 @@ static void move_vfsmnt(struct vfsmount *mnt, mnt->mnt_parent = parent ? mntget(parent) : mnt; list_del(&mnt->mnt_clash); list_del(&mnt->mnt_child); - if (parent) { + list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); + if (parent) list_add(&mnt->mnt_child, &parent->mnt_mounts); - list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); - } else { + else INIT_LIST_HEAD(&mnt->mnt_child); - INIT_LIST_HEAD(&mnt->mnt_clash); - } /* put the old stuff */ dput(old_mountpoint); @@ -403,6 +391,7 @@ static void remove_vfsmnt(struct vfsmount *mnt) dput(mnt->mnt_mountpoint); dput(mnt->mnt_root); kfree(mnt->mnt_devname); + kfree(mnt->mnt_dirname); kfree(mnt); } @@ -749,6 +738,10 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, /* Done with lookups, semaphore down */ down(&mount_sem); dev = to_kdev_t(bdev->bd_dev); + check_disk_change(dev); + error = -EACCES; + if (!(flags & MS_RDONLY) && is_read_only(dev)) + goto out; sb = get_super(dev); if (sb) { if (fs_type == sb->s_type) { @@ -762,10 +755,6 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, error = blkdev_get(bdev, mode, 0, BDEV_FS); if (error) goto out; - check_disk_change(dev); - error = -EACCES; - if (!(flags & MS_RDONLY) && is_read_only(dev)) - goto out1; error = -EINVAL; sb = read_super(dev, bdev, fs_type, flags, data, 0); if (sb) { @@ -773,7 +762,6 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, path_release(&nd); return sb; } -out1: blkdev_put(bdev, BDEV_FS); } out: @@ -907,7 +895,7 @@ struct vfsmount *kern_mount(struct file_system_type *type) put_unnamed_dev(dev); return ERR_PTR(-EINVAL); } - mnt = add_vfsmnt(NULL, sb->s_root, "none"); + mnt = add_vfsmnt(sb, sb->s_root, sb->s_root, NULL, "none", type->name); if (!mnt) { kill_super(sb, 0); return ERR_PTR(-ENOMEM); @@ -921,7 +909,10 @@ struct vfsmount *kern_mount(struct file_system_type *type) void kern_umount(struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; + struct dentry *root = sb->s_root; remove_vfsmnt(mnt); + dput(root); + sb->s_root = NULL; kill_super(sb, 0); } @@ -941,16 +932,6 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) { struct super_block * sb = mnt->mnt_sb; - /* - * No sense to grab the lock for this test, but test itself looks - * somewhat bogus. Suggestions for better replacement? - * Ho-hum... In principle, we might treat that as umount + switch - * to rootfs. GC would eventually take care of the old vfsmount. - * The problem being: we have to implement rootfs and GC for that ;-) - * Actually it makes sense, especially if rootfs would contain a - * /reboot - static binary that would close all descriptors and - * call reboot(9). Then init(8) could umount root and exec /reboot. - */ if (mnt == current->fs->rootmnt && !umount_root) { int retval = 0; /* @@ -971,7 +952,6 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) if (mnt->mnt_instances.next != mnt->mnt_instances.prev) { if (sb->s_type->fs_flags & FS_SINGLE) put_filesystem(sb->s_type); - /* We hold two references, so mntput() is safe */ mntput(mnt); remove_vfsmnt(mnt); return 0; @@ -1008,14 +988,14 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) shrink_dcache_sb(sb); fsync_dev(sb->s_dev); - if (sb->s_root->d_inode->i_state) { + /* Something might grab it again - redo checks */ + + if (atomic_read(&mnt->mnt_count) > 2) { mntput(mnt); return -EBUSY; } - /* Something might grab it again - redo checks */ - - if (atomic_read(&mnt->mnt_count) > 2) { + if (sb->s_root->d_inode->i_state) { mntput(mnt); return -EBUSY; } @@ -1087,8 +1067,6 @@ static int mount_is_safe(struct nameidata *nd) { if (capable(CAP_SYS_ADMIN)) return 0; - return -EPERM; -#ifdef notyet if (S_ISLNK(nd->dentry->d_inode->i_mode)) return -EPERM; if (nd->dentry->d_inode->i_mode & S_ISVTX) { @@ -1098,7 +1076,6 @@ static int mount_is_safe(struct nameidata *nd) if (permission(nd->dentry->d_inode, MAY_WRITE)) return -EPERM; return 0; -#endif } /* @@ -1125,22 +1102,22 @@ static int do_loopback(char *old_name, char *new_name) if (S_ISDIR(new_nd.dentry->d_inode->i_mode) != S_ISDIR(old_nd.dentry->d_inode->i_mode)) goto out2; - - err = -ENOMEM; - if (old_nd.mnt->mnt_sb->s_type->fs_flags & FS_SINGLE) - get_filesystem(old_nd.mnt->mnt_sb->s_type); down(&mount_sem); + err = -ENOENT; + if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) + goto out3; + if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) + goto out3; /* there we go */ - down(&new_nd.dentry->d_inode->i_zombie); - if (IS_DEADDIR(new_nd.dentry->d_inode)) - err = -ENOENT; - else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname)) + err = -ENOMEM; + if (old_nd.mnt->mnt_sb->s_type->fs_flags & FS_SINGLE) + get_filesystem(old_nd.mnt->mnt_sb->s_type); + if (add_vfsmnt(old_nd.mnt->mnt_sb, new_nd.dentry, old_nd.dentry, + new_nd.mnt, old_nd.mnt->mnt_devname, new_name)) err = 0; - up(&new_nd.dentry->d_inode->i_zombie); +out3: up(&mount_sem); - if (err && old_nd.mnt->mnt_sb->s_type->fs_flags & FS_SINGLE) - put_filesystem(old_nd.mnt->mnt_sb->s_type); out2: path_release(&new_nd); out1: @@ -1238,7 +1215,7 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, { struct file_system_type * fstype; struct nameidata nd; - struct vfsmount *mnt = NULL; + struct vfsmount *mnt; struct super_block *sb; int retval = 0; unsigned long flags = 0; @@ -1247,6 +1224,8 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) return -EINVAL; + if (!type_page || !memchr(type_page, 0, PAGE_SIZE)) + return -EINVAL; if (dev_name && !memchr(dev_name, 0, PAGE_SIZE)) return -EINVAL; @@ -1260,11 +1239,6 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) flags = new_flags & ~MS_MGC_MSK; - /* For the rest we need the type */ - - if (!type_page || !memchr(type_page, 0, PAGE_SIZE)) - return -EINVAL; - /* loopback mount? This is special - requires fewer capabilities */ if (strcmp(type_page, "bind")==0) return do_loopback(dev_name, dir_name); @@ -1298,18 +1272,16 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if (IS_ERR(sb)) goto dput_out; + retval = -ENOENT; + if (d_unhashed(nd.dentry) && !IS_ROOT(nd.dentry)) + goto fail; + /* Something was mounted here while we slept */ while(d_mountpoint(nd.dentry) && follow_down(&nd.mnt, &nd.dentry)) ; - retval = -ENOENT; - if (!nd.dentry->d_inode) - goto fail; - down(&nd.dentry->d_inode->i_zombie); - if (!IS_DEADDIR(nd.dentry->d_inode)) { - retval = -ENOMEM; - mnt = add_vfsmnt(&nd, sb->s_root, dev_name); - } - up(&nd.dentry->d_inode->i_zombie); + + retval = -ENOMEM; + mnt = add_vfsmnt(sb, nd.dentry, sb->s_root, nd.mnt, dev_name, dir_name); if (!mnt) goto fail; retval = 0; @@ -1340,6 +1312,15 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, if (retval < 0) return retval; + /* copy_mount_options allows a NULL user pointer, + * and just returns zero in that case. But if we + * allow the type to be NULL we will crash. + * Previously we did not check this case. + */ + if (type_page == 0) + return -EINVAL; + + lock_kernel(); dir_page = getname(dir_name); retval = PTR_ERR(dir_page); if (IS_ERR(dir_page)) @@ -1350,10 +1331,8 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out2; retval = copy_mount_options (data, &data_page); if (retval >= 0) { - lock_kernel(); retval = do_mount((char*)dev_page,dir_page,(char*)type_page, new_flags, (void*)data_page); - unlock_kernel(); free_page(data_page); } free_page(dev_page); @@ -1361,6 +1340,7 @@ out2: putname(dir_page); out1: free_page(type_page); + unlock_kernel(); return retval; } @@ -1510,11 +1490,12 @@ mount_it: path + 5 + path_start, 0, NULL, NULL); memcpy (path + path_start, "/dev/", 5); - vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start); + vfsmnt = add_vfsmnt (sb, sb->s_root, sb->s_root, NULL, + path + path_start, "/"); } else - vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root"); - /* FIXME: if something will try to umount us right now... */ + vfsmnt = add_vfsmnt (sb, sb->s_root, sb->s_root, NULL, + "/dev/root", "/"); if (vfsmnt) { set_fs_root(current->fs, vfsmnt, sb->s_root); set_fs_pwd(current->fs, vfsmnt, sb->s_root); @@ -1535,7 +1516,6 @@ static void chroot_fs_refs(struct dentry *old_root, read_lock(&tasklist_lock); for_each_task(p) { - /* FIXME - unprotected usage of ->fs + (harmless) race */ if (!p->fs) continue; if (p->fs->root == old_root && p->fs->rootmnt == old_rootmnt) set_fs_root(p->fs, new_rootmnt, new_root); @@ -1596,10 +1576,7 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) root_mnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); down(&mount_sem); - down(&old_nd.dentry->d_inode->i_zombie); error = -ENOENT; - if (IS_DEADDIR(new_nd.dentry->d_inode)) - goto out2; if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) goto out2; if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) @@ -1622,12 +1599,19 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) } else if (!is_subdir(old_nd.dentry, new_nd.dentry)) goto out2; - move_vfsmnt(new_nd.mnt, new_nd.dentry, NULL, NULL); - move_vfsmnt(root_mnt, old_nd.dentry, old_nd.mnt, NULL); + error = -ENOMEM; + name = __getname(); + if (!name) + goto out2; + + move_vfsmnt(new_nd.mnt, new_nd.dentry, NULL, NULL, "/"); + move_vfsmnt(root_mnt, old_nd.dentry, old_nd.mnt, NULL, + __d_path(old_nd.dentry, old_nd.mnt, new_nd.dentry, + new_nd.mnt, name, PAGE_SIZE)); + putname(name); chroot_fs_refs(root,root_mnt,new_nd.dentry,new_nd.mnt); error = 0; out2: - up(&old_nd.dentry->d_inode->i_zombie); up(&mount_sem); dput(root); mntput(root_mnt); @@ -1645,11 +1629,10 @@ out0: int __init change_root(kdev_t new_root_dev,const char *put_old) { kdev_t old_root_dev = ROOT_DEV; - struct vfsmount *old_rootmnt; + struct vfsmount *old_rootmnt = mntget(current->fs->rootmnt); struct nameidata devfs_nd, nd; int error = 0; - old_rootmnt = mntget(current->fs->rootmnt); /* First unmount devfs if mounted */ if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd)) error = path_walk("/dev", &devfs_nd); @@ -1692,8 +1675,7 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) printk(KERN_ERR "error %ld\n",blivet); return error; } - /* FIXME: we should hold i_zombie on nd.dentry */ - move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old"); + move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old", put_old); mntput(old_rootmnt); path_release(&nd); return 0; diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index 091605cd1..3c9871be6 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -178,7 +178,7 @@ static int sync_tindirect(struct inode *inode, u32 *tiblockp, int convert, return err; } -int sysv_sync_file(struct file * file, struct dentry *dentry, int datasync) +int sysv_sync_file(struct file * file, struct dentry *dentry) { int wait, err = 0; struct inode *inode = dentry->d_inode; diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index bbd88336c..d7cc12187 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -142,7 +142,7 @@ struct inode * sysv_new_inode(const struct inode * dir) /* Change directory entry: */ inode->i_mode = 0; /* for sysv_write_inode() */ inode->i_size = 0; /* ditto */ - sysv_write_inode(inode, 0); /* ensure inode not allocated again */ + sysv_write_inode(inode); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 9f8df88e4..455818959 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -496,6 +496,7 @@ static struct super_block *sysv_read_super(struct super_block *sb, sb->s_blocksize = sb->sv_block_size; sb->s_blocksize_bits = sb->sv_block_size_bits; /* set up enough so that it can read an inode */ + sb->s_dev = dev; sb->s_op = &sysv_sops; root_inode = iget(sb,SYSV_ROOT_INO); sb->s_root = d_alloc_root(root_inode); @@ -1153,7 +1154,7 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) return bh; } -void sysv_write_inode(struct inode * inode, int unused) +void sysv_write_inode(struct inode * inode) { struct buffer_head *bh; bh = sysv_update_inode(inode); diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c index 64a5e41a1..e7d067e62 100644 --- a/fs/udf/fsync.c +++ b/fs/udf/fsync.c @@ -96,7 +96,7 @@ static int sync_all_extents(struct inode * inode, int wait) * even pass file to fsync ? */ -int udf_sync_file(struct file * file, struct dentry *dentry, int dsync) +int udf_sync_file(struct file * file, struct dentry *dentry) { int wait, err = 0; struct inode *inode = dentry->d_inode; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 3c2d50340..8c38883c0 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1213,10 +1213,10 @@ udf_convert_permissions(struct FileEntry *fe) * Written, tested, and released. */ -void udf_write_inode(struct inode * inode, int wait) +void udf_write_inode(struct inode * inode) { lock_kernel(); - udf_update_inode(inode, wait); + udf_update_inode(inode, 0); unlock_kernel(); } diff --git a/fs/udf/super.c b/fs/udf/super.c index f3f575d7e..5f76abbb0 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1419,6 +1419,7 @@ udf_read_super(struct super_block *sb, void *options, int silent) return sb; error_out: + sb->s_dev = NODEV; if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (!(sb->s_flags & MS_RDONLY)) diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 115db1bef..7dd00bc19 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -134,7 +134,7 @@ extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_read_inode(struct inode *); extern void udf_put_inode(struct inode *); extern void udf_delete_inode(struct inode *); -extern void udf_write_inode(struct inode *, int); +extern void udf_write_inode(struct inode *); extern long udf_locked_block_map(struct inode *, long); extern long udf_block_map(struct inode *, long); extern int inode_bmap(struct inode *, int, lb_addr *, Uint32 *, lb_addr *, Uint32 *, Uint32 *, struct buffer_head **); @@ -184,7 +184,7 @@ 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 *, int data); +extern int udf_sync_file(struct file *, struct dentry *); /* directory.c */ extern Uint8 * udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, struct buffer_head **, int *); diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 2a1d0f6ae..7801add9a 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -744,7 +744,7 @@ static int ufs_update_inode(struct inode * inode, int do_sync) return 0; } -void ufs_write_inode (struct inode * inode, int unused) +void ufs_write_inode (struct inode * inode) { ufs_update_inode (inode, 0); } diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index e4c991dc0..14b23467d 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -29,7 +29,7 @@ static struct dentry *check_pseudo_root(struct super_block *); /* - * Initialize a private filp. dentry is always a regular file one. + * Initialize a private filp */ void fill_new_filp (struct file *filp, struct dentry *dentry) { @@ -293,11 +293,11 @@ out: /* * Update the disk with the inode content */ -void UMSDOS_write_inode (struct inode *inode, int unused) +void UMSDOS_write_inode (struct inode *inode) { struct iattr newattrs; - fat_write_inode (inode, 0); + fat_write_inode (inode); newattrs.ia_mtime = inode->i_mtime; newattrs.ia_atime = inode->i_atime; newattrs.ia_ctime = inode->i_ctime; diff --git a/include/asm-alpha/cache.h b/include/asm-alpha/cache.h index 5e46073a8..d9483efb1 100644 --- a/include/asm-alpha/cache.h +++ b/include/asm-alpha/cache.h @@ -4,9 +4,18 @@ #ifndef __ARCH_ALPHA_CACHE_H #define __ARCH_ALPHA_CACHE_H -/* Bytes per L1 (data) cache line. Both EV4 and EV5 are write-through, - read-allocate, direct-mapped, physical. */ -#define L1_CACHE_BYTES 32 +#include <linux/config.h> + +/* Bytes per L1 (data) cache line. */ +#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EV6) +# define L1_CACHE_BYTES 32 /* should be 64, but networking dies */ +#else +/* Both EV4 and EV5 are write-through, read-allocate, + direct-mapped, physical. +*/ +# define L1_CACHE_BYTES 32 +#endif + #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) #define SMP_CACHE_BYTES L1_CACHE_BYTES diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index 27e06783a..a74290f55 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -56,6 +56,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* IOMMU controls. */ /* Allocate and map kernel buffer using consistant mode DMA for PCI diff --git a/include/asm-alpha/string.h b/include/asm-alpha/string.h index a7cf9557d..2b9cf9438 100644 --- a/include/asm-alpha/string.h +++ b/include/asm-alpha/string.h @@ -18,7 +18,7 @@ extern void * memmove(void *, const void *, size_t); /* For backward compatibility with modules. Unused otherwise. */ extern void * __memcpy(void *, const void *, size_t); -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 #define memcpy __builtin_memcpy #endif @@ -27,7 +27,7 @@ extern void * __constant_c_memset(void *, unsigned long, size_t); extern void * __memset(void *, int, size_t); extern void * memset(void *, int, size_t); -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 #define memset(s, c, n) \ (__builtin_constant_p(c) \ ? (__builtin_constant_p(n) && (c) == 0 \ diff --git a/include/asm-alpha/unaligned.h b/include/asm-alpha/unaligned.h index cd268017e..8017f6bfa 100644 --- a/include/asm-alpha/unaligned.h +++ b/include/asm-alpha/unaligned.h @@ -31,7 +31,7 @@ struct __una_u16 { __u16 x __attribute__((packed)); }; extern inline unsigned long __uldq(const unsigned long * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 const struct __una_u64 *ptr = (const struct __una_u64 *) r11; return ptr->x; #else @@ -50,7 +50,7 @@ extern inline unsigned long __uldq(const unsigned long * r11) extern inline unsigned long __uldl(const unsigned int * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 const struct __una_u32 *ptr = (const struct __una_u32 *) r11; return ptr->x; #else @@ -69,7 +69,7 @@ extern inline unsigned long __uldl(const unsigned int * r11) extern inline unsigned long __uldw(const unsigned short * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 const struct __una_u16 *ptr = (const struct __una_u16 *) r11; return ptr->x; #else @@ -92,7 +92,7 @@ extern inline unsigned long __uldw(const unsigned short * r11) extern inline void __ustq(unsigned long r5, unsigned long * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 struct __una_u64 *ptr = (struct __una_u64 *) r11; ptr->x = r5; #else @@ -117,7 +117,7 @@ extern inline void __ustq(unsigned long r5, unsigned long * r11) extern inline void __ustl(unsigned long r5, unsigned int * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 struct __una_u32 *ptr = (struct __una_u32 *) r11; ptr->x = r5; #else @@ -142,7 +142,7 @@ extern inline void __ustl(unsigned long r5, unsigned int * r11) extern inline void __ustw(unsigned long r5, unsigned short * r11) { -#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 struct __una_u16 *ptr = (struct __una_u16 *) r11; ptr->x = r5; #else diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h index 8ed8ebf3c..2a2fcc947 100644 --- a/include/asm-arm/pci.h +++ b/include/asm-arm/pci.h @@ -13,6 +13,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + #include <asm/scatterlist.h> #include <asm/io.h> diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index 80ba0d2e4..23460fcb6 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -22,6 +22,7 @@ #include <linux/config.h> #include <asm/processor.h> +#include <asm/i387.h> #include <asm/msr.h> static int __init no_halt(char *s) @@ -49,7 +50,7 @@ static int __init no_387(char *s) __setup("no387", no_387); -static char fpu_error __initdata = 0; +static char __initdata fpu_error = 0; static void __init copro_timeout(void) { @@ -62,12 +63,12 @@ static void __init copro_timeout(void) outb_p(0,0xf0); } -static double x __initdata = 4195835.0; -static double y __initdata = 3145727.0; +static double __initdata x = 4195835.0; +static double __initdata y = 3145727.0; #ifdef CONFIG_X86_XMM -static float zero[4] __initdata = { 0.0, 0.0, 0.0, 0.0 }; -static float one[4] __initdata = { 1.0, 1.0, 1.0, 1.0 }; +static float __initdata zero[4] = { 0.0, 0.0, 0.0, 0.0 }; +static float __initdata one[4] = { 1.0, 1.0, 1.0, 1.0 }; #endif static void __init check_fpu(void) @@ -148,11 +149,11 @@ static void __init check_fpu(void) else printk("Hmm, FPU using exception 16 error reporting with FDIV bug.\n"); -#ifdef CONFIG_X86_FXSR +#if defined(CONFIG_X86_FXSR) || defined(CONFIG_X86_RUNTIME_FXSR) /* * Verify that the FXSAVE/FXRSTOR data will be 16-byte aligned. */ - if (offsetof(struct task_struct, thread.i387.hard.fxsr_space[0]) & 15) + if (offsetof(struct task_struct, thread.i387.fxsave) & 15) panic("Kernel compiled for PII/PIII+ with FXSR, data not 16-byte aligned!"); if (cpu_has_fxsr) { @@ -168,14 +169,14 @@ static void __init check_fpu(void) printk("done.\n"); /* Check if exception 19 works okay. */ - set_fpu_mxcsr(XMM_UNMASKED_MXCSR); + load_mxcsr(0x0000); printk(KERN_INFO "Checking SIMD FPU exceptions... "); __asm__("movups %0,%%xmm0\n\t" "movups %1,%%xmm1\n\t" "divps %%xmm0,%%xmm1\n\t" : : "m" (*&zero), "m" (*&one)); printk("OK, SIMD FPU using exception 19 error reporting.\n"); - set_fpu_mxcsr(XMM_DEFAULT_MXCSR); + load_mxcsr(0x1f80); } #endif } diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h index 0083b3f20..5bf5ec1c8 100644 --- a/include/asm-i386/elf.h +++ b/include/asm-i386/elf.h @@ -14,6 +14,7 @@ typedef unsigned long elf_greg_t; typedef elf_greg_t elf_gregset_t[ELF_NGREG]; typedef struct user_i387_struct elf_fpregset_t; +typedef struct user_fxsr_struct elf_fpxregset_t; /* * This is used to ensure we don't load something for the wrong architecture. diff --git a/include/asm-i386/i387.h b/include/asm-i386/i387.h new file mode 100644 index 000000000..ec7650841 --- /dev/null +++ b/include/asm-i386/i387.h @@ -0,0 +1,85 @@ +/* + * include/asm-i386/i387.h + * + * Copyright (C) 1994 Linus Torvalds + * + * Pentium III FXSR, SSE support + * General FPU state handling cleanups + * Gareth Hughes <gareth@valinux.com>, May 2000 + */ + +#ifndef __ASM_I386_I387_H +#define __ASM_I386_I387_H + +#include <linux/sched.h> +#include <asm/processor.h> +#include <asm/sigcontext.h> +#include <asm/user.h> + +/* + * FPU lazy state save handling... + */ +extern void save_fpu( struct task_struct *tsk ); +extern void save_init_fpu( struct task_struct *tsk ); +extern void restore_fpu( struct task_struct *tsk ); + +#define unlazy_fpu( tsk ) do { \ + if ( tsk->flags & PF_USEDFPU ) \ + save_fpu( tsk ); \ +} while (0) + +#define clear_fpu( tsk ) do { \ + if ( tsk->flags & PF_USEDFPU ) { \ + tsk->flags &= ~PF_USEDFPU; \ + stts(); \ + } \ +} while (0) + +/* + * FPU state interaction... + */ +extern unsigned short get_fpu_cwd( struct task_struct *tsk ); +extern unsigned short get_fpu_swd( struct task_struct *tsk ); +extern unsigned short get_fpu_twd( struct task_struct *tsk ); +extern unsigned short get_fpu_mxcsr( struct task_struct *tsk ); + +extern void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd ); +extern void set_fpu_swd( struct task_struct *tsk, unsigned short swd ); +extern void set_fpu_twd( struct task_struct *tsk, unsigned short twd ); +extern void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ); + +#define load_mxcsr( val ) do { \ + if ( cpu_has_xmm ) { \ + unsigned long __mxcsr = ((unsigned long)(val) & 0xffff); \ + asm volatile( "ldmxcsr %0" : : "m" (__mxcsr) ); \ + } \ +} while (0) + +/* + * Signal frame handlers... + */ +extern int save_i387( struct _fpstate *buf ); +extern int restore_i387( struct _fpstate *buf ); + +/* + * ptrace request handers... + */ +extern int get_fpregs( struct user_i387_struct *buf, + struct task_struct *tsk ); +extern int set_fpregs( struct task_struct *tsk, + struct user_i387_struct *buf ); + +extern int get_fpxregs( struct user_fxsr_struct *buf, + struct task_struct *tsk ); +extern int set_fpxregs( struct task_struct *tsk, + struct user_fxsr_struct *buf ); + +/* + * FPU state for core dumps... + */ +extern int dump_fpu( struct pt_regs *regs, + struct user_i387_struct *fpu ); +extern int dump_extended_fpu( struct pt_regs *regs, + struct user_fxsr_struct *fpu ); + +#endif /* __ASM_I386_I387_H */ diff --git a/include/asm-i386/io_apic.h b/include/asm-i386/io_apic.h index 2068753df..9ac8df6ec 100644 --- a/include/asm-i386/io_apic.h +++ b/include/asm-i386/io_apic.h @@ -1,6 +1,7 @@ #ifndef __ASM_IO_APIC_H #define __ASM_IO_APIC_H +#include <linux/config.h> #include <asm/types.h> /* @@ -9,6 +10,8 @@ * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar */ +#ifdef CONFIG_X86_IO_APIC + #define IO_APIC_BASE(idx) \ ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) @@ -130,5 +133,14 @@ extern int nmi_watchdog; extern int skip_ioapic_setup; extern void IO_APIC_init_uniprocessor (void); +/* + * If we use the IO-APIC for IRQ routing, disable automatic + * assignment of PCI IRQ's. + */ +#define io_apic_assign_pci_irqs (mp_irq_entries != 0) + +#else /* !CONFIG_X86_IO_APIC */ +#define io_apic_assign_pci_irqs 0 #endif +#endif diff --git a/include/asm-i386/pci.h b/include/asm-i386/pci.h index d6d0311b7..074957886 100644 --- a/include/asm-i386/pci.h +++ b/include/asm-i386/pci.h @@ -13,6 +13,7 @@ #define PCIBIOS_MIN_MEM 0x10000000 void pcibios_set_master(struct pci_dev *dev); +void pcibios_penalize_isa_irq(int irq); /* Dynamic DMA mapping stuff. * i386 has everything mapped statically. diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 76fd66fd8..80aeb3201 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -2,9 +2,6 @@ * include/asm-i386/processor.h * * Copyright (C) 1994 Linus Torvalds - * - * Pentium III FXSR, SSE support - * Gareth Hughes <gareth@valinux.com>, May 2000 */ #ifndef __ASM_I386_PROCESSOR_H @@ -86,7 +83,7 @@ struct cpuinfo_x86 { #define X86_FEATURE_PGE 0x00002000 /* Page Global Enable */ #define X86_FEATURE_MCA 0x00004000 /* Machine Check Architecture */ #define X86_FEATURE_CMOV 0x00008000 /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ -#define X86_FEATURE_PAT 0x00010000 /* Page Attribute Table */ +#define X86_FEATURE_PAT 0x00010000 /* Page Attribute Table */ #define X86_FEATURE_PSE36 0x00020000 /* 36-bit PSEs */ #define X86_FEATURE_18 0x00040000 #define X86_FEATURE_19 0x00080000 @@ -95,7 +92,7 @@ struct cpuinfo_x86 { #define X86_FEATURE_22 0x00400000 #define X86_FEATURE_MMX 0x00800000 /* Multimedia Extensions */ #define X86_FEATURE_FXSR 0x01000000 /* FXSAVE and FXRSTOR instructions (fast save and restore of FPU context), and CR4.OSFXSR (OS uses these instructions) available */ -#define X86_FEATURE_XMM 0x02000000 /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM 0x02000000 /* Streaming SIMD Extensions */ #define X86_FEATURE_26 0x04000000 #define X86_FEATURE_27 0x08000000 #define X86_FEATURE_28 0x10000000 @@ -147,7 +144,8 @@ extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) "=b" (*ebx), "=c" (*ecx), "=d" (*edx) - : "a" (op)); + : "a" (op) + : "cc"); } @@ -250,9 +248,7 @@ extern unsigned int mca_pentium_flag; #define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap) #define INVALID_IO_BITMAP_OFFSET 0x8000 -#ifndef CONFIG_X86_FXSR - -struct i387_hard_struct { +struct i387_fsave_struct { long cwd; long swd; long twd; @@ -264,22 +260,15 @@ struct i387_hard_struct { long status; /* software status information */ }; -#else - -/* It doesn't matter if the CPU writes only part of this struct; it gets - * copied by do_fork, so the unimplemented area never changes from what - * init_task.i387 is initialized to (all zeroes). */ - -struct i387_hard_struct { - long cwd; - long swd; - long twd; +struct i387_fxsave_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; long fip; long fcs; long foo; long fos; - long status; /* software status information */ - long fxsr_space[6]; /* FXSR FPU environment must not be used */ long mxcsr; long reserved; long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ @@ -287,10 +276,6 @@ struct i387_hard_struct { long padding[56]; } __attribute__ ((aligned (16))); -#define X86_FXSR_MAGIC 0x0000 -#define X86_FXSR_SIZE 512 -#endif - struct i387_soft_struct { long cwd; long swd; @@ -306,7 +291,8 @@ struct i387_soft_struct { }; union i387_union { - struct i387_hard_struct hard; + struct i387_fsave_struct fsave; + struct i387_fxsave_struct fxsave; struct i387_soft_struct soft; }; @@ -423,75 +409,6 @@ extern void release_segments(struct mm_struct * mm); extern void forget_segments(void); /* - * FPU lazy state save handling.. - */ -#ifndef CONFIG_X86_FXSR - -#define save_fpu(tsk) do { \ - asm volatile("fnsave %0 ; fwait" \ - : "=m" (tsk->thread.i387.hard)); \ - tsk->flags &= ~PF_USEDFPU; \ - stts(); \ -} while (0) - -#define save_init_fpu(tsk) save_fpu(tsk) - -#define restore_fpu(tsk) do { \ - asm volatile("frstor %0" \ - : : "m" (tsk->thread.i387.hard)); \ -} while (0) - -#else /* CONFIG_X86_FXSR */ - -#define save_fpu(tsk) do { \ - asm volatile("fnstenv %0 ; fxsave %1 ; fwait" \ - : "=m" (tsk->thread.i387.hard), \ - "=m" (tsk->thread.i387.hard.fxsr_space[0])); \ - tsk->flags &= ~PF_USEDFPU; \ - stts(); \ -} while (0) - -#define save_init_fpu(tsk) do { \ - asm volatile("fnstenv %0 ; fxsave %1 ; fnclex" \ - : "=m" (tsk->thread.i387.hard), \ - "=m" (tsk->thread.i387.hard.fxsr_space[0])); \ - tsk->flags &= ~PF_USEDFPU; \ - stts(); \ -} while (0) - -#define restore_fpu(tsk) do { \ - asm volatile("fxrstor %0 ; fldenv %1" \ - : : "m" (tsk->thread.i387.hard.fxsr_space[0]), \ - "m" (tsk->thread.i387.hard)); \ -} while (0) - -#endif /* CONFIG_X86_FXSR */ - -#define unlazy_fpu(tsk) do { \ - if (tsk->flags & PF_USEDFPU) \ - save_fpu(tsk); \ -} while (0) - -#define clear_fpu(tsk) do { \ - if (tsk->flags & PF_USEDFPU) { \ - tsk->flags &= ~PF_USEDFPU; \ - stts(); \ - } \ -} while (0) - -#ifdef CONFIG_X86_XMM -#define XMM_DEFAULT_MXCSR 0x1f80 -#define XMM_UNMASKED_MXCSR 0x0000 - -#define set_fpu_mxcsr(val) do { \ - if (cpu_has_xmm) { \ - unsigned long __mxcsr = ((unsigned long)(val) & 0xffff); \ - asm volatile("ldmxcsr %0" : : "m" (__mxcsr)); \ - } \ -} while (0) -#endif - -/* * Return saved PC of a blocked thread. */ extern inline unsigned long thread_saved_pc(struct thread_struct *t) diff --git a/include/asm-i386/ptrace.h b/include/asm-i386/ptrace.h index 05189f6f3..6a52f4e76 100644 --- a/include/asm-i386/ptrace.h +++ b/include/asm-i386/ptrace.h @@ -46,8 +46,8 @@ struct pt_regs { #define PTRACE_SETREGS 13 #define PTRACE_GETFPREGS 14 #define PTRACE_SETFPREGS 15 -#define PTRACE_GETXFPREGS 25 -#define PTRACE_SETXFPREGS 26 +#define PTRACE_GETFPXREGS 18 +#define PTRACE_SETFPXREGS 19 #ifdef __KERNEL__ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs)) diff --git a/include/asm-i386/sigcontext.h b/include/asm-i386/sigcontext.h index 77ef66680..b51145936 100644 --- a/include/asm-i386/sigcontext.h +++ b/include/asm-i386/sigcontext.h @@ -52,6 +52,8 @@ struct _fpstate { unsigned long padding[56]; }; +#define X86_FXSR_MAGIC 0x0000 + struct sigcontext { unsigned short gs, __gsh; unsigned short fs, __fsh; diff --git a/include/asm-i386/user.h b/include/asm-i386/user.h index e034118f3..dcd555159 100644 --- a/include/asm-i386/user.h +++ b/include/asm-i386/user.h @@ -34,12 +34,12 @@ * Pentium III FXSR, SSE support * Gareth Hughes <gareth@valinux.com>, May 2000 * - * Provide support for the GDB 5.0 PTRACE_*XFPREGS requests for interacting - * with the FXSR-format floating point environment. Floating point data - * can be accessed in the regular FSAVE format in the usual manner, and the - * XMM register/control data can be accessed via the new ptrace requests. - * Note that the floating point environment contained in the FXSR format - * is ignored and any changes to these fields will be lost. + * Provide support for the GDB 5.0+ PTRACE_{GET|SET}FPXREGS requests for + * interacting with the FXSR-format floating point environment. Floating + * point data can be accessed in the regular format in the usual manner, + * and both the standard and SIMD floating point data can be accessed via + * the new ptrace requests. In either case, changes to the FPU environment + * will be reflected in the task's state as expected. */ struct user_i387_struct { @@ -48,26 +48,25 @@ struct user_i387_struct { long twd; long fip; long fcs; - long foo; - long fos; + long fdp; + long fds; long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ }; -struct user_xfpregs_struct { - long cwd; - long swd; - long twd; +struct user_fxsr_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; long fip; long fcs; long foo; long fos; - long __reserved_00; - long fxsr_space[6]; /* FXSR FPU environment must not be used */ long mxcsr; - long __reserved_01; + long reserved; long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ - long __reserved_02[56]; + long padding[56]; }; /* diff --git a/include/asm-ia64/asmmacro.h b/include/asm-ia64/asmmacro.h new file mode 100644 index 000000000..4991bb26e --- /dev/null +++ b/include/asm-ia64/asmmacro.h @@ -0,0 +1,48 @@ +#ifndef _ASM_IA64_ASMMACRO_H +#define _ASM_IA64_ASMMACRO_H + +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + */ + +#if 1 + +/* + * This is a hack that's necessary as long as we support old versions + * of gas, that have no unwind support. + */ +#include <linux/config.h> + +#ifdef CONFIG_IA64_NEW_UNWIND +# define UNW(args...) args +#else +# define UNW(args...) +#endif + +#endif + +#define ENTRY(name) \ + .align 16; \ + .proc name; \ +name: + +#define GLOBAL_ENTRY(name) \ + .global name; \ + ENTRY(name) + +#define END(name) \ + .endp name + +/* + * Helper macros to make unwind directives more readable: + */ + +/* prologue_gr: */ +#define ASM_UNW_PRLG_RP 0x8 +#define ASM_UNW_PRLG_PFS 0x4 +#define ASM_UNW_PRLG_PSP 0x2 +#define ASM_UNW_PRLG_PR 0x1 +#define ASM_UNW_PRLG_GRSAVE(ninputs) (32+(ninputs)) + +#endif /* _ASM_IA64_ASMMACRO_H */ diff --git a/include/asm-ia64/dma.h b/include/asm-ia64/dma.h index 4e9b30a91..1d9eb2a41 100644 --- a/include/asm-ia64/dma.h +++ b/include/asm-ia64/dma.h @@ -21,7 +21,7 @@ #define dma_inb inb #define MAX_DMA_CHANNELS 8 -#define MAX_DMA_ADDRESS (~0UL) /* no limits on DMAing, for now */ +#define MAX_DMA_ADDRESS 0xffffffffUL extern spinlock_t dma_spin_lock; diff --git a/include/asm-ia64/ia32.h b/include/asm-ia64/ia32.h index 00219b00d..884f1314a 100644 --- a/include/asm-ia64/ia32.h +++ b/include/asm-ia64/ia32.h @@ -112,10 +112,16 @@ struct sigaction32 { sigset32_t sa_mask; /* A 32 bit mask */ }; +typedef struct sigaltstack_ia32 { + unsigned int ss_sp; + int ss_flags; + unsigned int ss_size; +} stack_ia32_t; + struct ucontext_ia32 { - unsigned long uc_flags; - struct ucontext_ia32 *uc_link; - stack_t uc_stack; + unsigned int uc_flags; + unsigned int uc_link; + stack_ia32_t uc_stack; struct sigcontext_ia32 uc_mcontext; sigset_t uc_sigmask; /* mask last for extensibility */ }; @@ -276,7 +282,8 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; #define ELF_PLATFORM 0 #ifdef __KERNEL__ -#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) +# define SET_PERSONALITY(EX,IBCS2) \ + (current->personality = (IBCS2) ? PER_SVR4 : PER_LINUX) #endif #define IA32_EFLAG 0x200 @@ -342,8 +349,8 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; * IA32 floating point control registers starting values */ -#define IA32_FSR_DEFAULT 0x55550000 /* set all tag bits */ -#define IA32_FCR_DEFAULT 0x33f /* single precision, all masks */ +#define IA32_FSR_DEFAULT 0x55550000 /* set all tag bits */ +#define IA32_FCR_DEFAULT 0x17800000037fULL /* extended precision, all masks */ #define IA32_PTRACE_GETREGS 12 #define IA32_PTRACE_SETREGS 13 diff --git a/include/asm-ia64/iosapic.h b/include/asm-ia64/iosapic.h index 95934da1e..995c948ba 100644 --- a/include/asm-ia64/iosapic.h +++ b/include/asm-ia64/iosapic.h @@ -92,7 +92,7 @@ extern struct intr_routing_entry intr_routing[]; * } */ extern unsigned int iosapic_version(unsigned long); -extern void iosapic_init(unsigned long); +extern void iosapic_init(unsigned long, int); struct iosapic_vector { unsigned long iosapic_base; /* IOSAPIC Base address */ diff --git a/include/asm-ia64/offsets.h b/include/asm-ia64/offsets.h index de309ee56..25cf32c44 100644 --- a/include/asm-ia64/offsets.h +++ b/include/asm-ia64/offsets.h @@ -25,11 +25,95 @@ #define IA64_TASK_PID_OFFSET 188 /* 0xbc */ #define IA64_TASK_MM_OFFSET 88 /* 0x58 */ #define IA64_PT_REGS_CR_IPSR_OFFSET 0 /* 0x0 */ +#define IA64_PT_REGS_CR_IIP_OFFSET 8 /* 0x8 */ +#define IA64_PT_REGS_CR_IFS_OFFSET 16 /* 0x10 */ +#define IA64_PT_REGS_AR_UNAT_OFFSET 24 /* 0x18 */ +#define IA64_PT_REGS_AR_PFS_OFFSET 32 /* 0x20 */ +#define IA64_PT_REGS_AR_RSC_OFFSET 40 /* 0x28 */ +#define IA64_PT_REGS_AR_RNAT_OFFSET 48 /* 0x30 */ +#define IA64_PT_REGS_AR_BSPSTORE_OFFSET 56 /* 0x38 */ +#define IA64_PT_REGS_PR_OFFSET 64 /* 0x40 */ +#define IA64_PT_REGS_B6_OFFSET 72 /* 0x48 */ +#define IA64_PT_REGS_LOADRS_OFFSET 80 /* 0x50 */ +#define IA64_PT_REGS_R1_OFFSET 88 /* 0x58 */ +#define IA64_PT_REGS_R2_OFFSET 96 /* 0x60 */ +#define IA64_PT_REGS_R3_OFFSET 104 /* 0x68 */ #define IA64_PT_REGS_R12_OFFSET 112 /* 0x70 */ +#define IA64_PT_REGS_R13_OFFSET 120 /* 0x78 */ +#define IA64_PT_REGS_R14_OFFSET 128 /* 0x80 */ +#define IA64_PT_REGS_R15_OFFSET 136 /* 0x88 */ #define IA64_PT_REGS_R8_OFFSET 144 /* 0x90 */ +#define IA64_PT_REGS_R9_OFFSET 152 /* 0x98 */ +#define IA64_PT_REGS_R10_OFFSET 160 /* 0xa0 */ +#define IA64_PT_REGS_R11_OFFSET 168 /* 0xa8 */ #define IA64_PT_REGS_R16_OFFSET 176 /* 0xb0 */ -#define IA64_SWITCH_STACK_B0_OFFSET 464 /* 0x1d0 */ +#define IA64_PT_REGS_R17_OFFSET 184 /* 0xb8 */ +#define IA64_PT_REGS_R18_OFFSET 192 /* 0xc0 */ +#define IA64_PT_REGS_R19_OFFSET 200 /* 0xc8 */ +#define IA64_PT_REGS_R20_OFFSET 208 /* 0xd0 */ +#define IA64_PT_REGS_R21_OFFSET 216 /* 0xd8 */ +#define IA64_PT_REGS_R22_OFFSET 224 /* 0xe0 */ +#define IA64_PT_REGS_R23_OFFSET 232 /* 0xe8 */ +#define IA64_PT_REGS_R24_OFFSET 240 /* 0xf0 */ +#define IA64_PT_REGS_R25_OFFSET 248 /* 0xf8 */ +#define IA64_PT_REGS_R26_OFFSET 256 /* 0x100 */ +#define IA64_PT_REGS_R27_OFFSET 264 /* 0x108 */ +#define IA64_PT_REGS_R28_OFFSET 272 /* 0x110 */ +#define IA64_PT_REGS_R29_OFFSET 280 /* 0x118 */ +#define IA64_PT_REGS_R30_OFFSET 288 /* 0x120 */ +#define IA64_PT_REGS_R31_OFFSET 296 /* 0x128 */ +#define IA64_PT_REGS_AR_CCV_OFFSET 304 /* 0x130 */ +#define IA64_PT_REGS_AR_FPSR_OFFSET 312 /* 0x138 */ +#define IA64_PT_REGS_B0_OFFSET 320 /* 0x140 */ +#define IA64_PT_REGS_B7_OFFSET 328 /* 0x148 */ +#define IA64_PT_REGS_F6_OFFSET 336 /* 0x150 */ +#define IA64_PT_REGS_F7_OFFSET 352 /* 0x160 */ +#define IA64_PT_REGS_F8_OFFSET 368 /* 0x170 */ +#define IA64_PT_REGS_F9_OFFSET 384 /* 0x180 */ #define IA64_SWITCH_STACK_CALLER_UNAT_OFFSET 0 /* 0x0 */ +#define IA64_SWITCH_STACK_AR_FPSR_OFFSET 8 /* 0x8 */ +#define IA64_SWITCH_STACK_F2_OFFSET 16 /* 0x10 */ +#define IA64_SWITCH_STACK_F3_OFFSET 32 /* 0x20 */ +#define IA64_SWITCH_STACK_F4_OFFSET 48 /* 0x30 */ +#define IA64_SWITCH_STACK_F5_OFFSET 64 /* 0x40 */ +#define IA64_SWITCH_STACK_F10_OFFSET 80 /* 0x50 */ +#define IA64_SWITCH_STACK_F11_OFFSET 96 /* 0x60 */ +#define IA64_SWITCH_STACK_F12_OFFSET 112 /* 0x70 */ +#define IA64_SWITCH_STACK_F13_OFFSET 128 /* 0x80 */ +#define IA64_SWITCH_STACK_F14_OFFSET 144 /* 0x90 */ +#define IA64_SWITCH_STACK_F15_OFFSET 160 /* 0xa0 */ +#define IA64_SWITCH_STACK_F16_OFFSET 176 /* 0xb0 */ +#define IA64_SWITCH_STACK_F17_OFFSET 192 /* 0xc0 */ +#define IA64_SWITCH_STACK_F18_OFFSET 208 /* 0xd0 */ +#define IA64_SWITCH_STACK_F19_OFFSET 224 /* 0xe0 */ +#define IA64_SWITCH_STACK_F20_OFFSET 240 /* 0xf0 */ +#define IA64_SWITCH_STACK_F21_OFFSET 256 /* 0x100 */ +#define IA64_SWITCH_STACK_F22_OFFSET 272 /* 0x110 */ +#define IA64_SWITCH_STACK_F23_OFFSET 288 /* 0x120 */ +#define IA64_SWITCH_STACK_F24_OFFSET 304 /* 0x130 */ +#define IA64_SWITCH_STACK_F25_OFFSET 320 /* 0x140 */ +#define IA64_SWITCH_STACK_F26_OFFSET 336 /* 0x150 */ +#define IA64_SWITCH_STACK_F27_OFFSET 352 /* 0x160 */ +#define IA64_SWITCH_STACK_F28_OFFSET 368 /* 0x170 */ +#define IA64_SWITCH_STACK_F29_OFFSET 384 /* 0x180 */ +#define IA64_SWITCH_STACK_F30_OFFSET 400 /* 0x190 */ +#define IA64_SWITCH_STACK_F31_OFFSET 416 /* 0x1a0 */ +#define IA64_SWITCH_STACK_R4_OFFSET 432 /* 0x1b0 */ +#define IA64_SWITCH_STACK_R5_OFFSET 440 /* 0x1b8 */ +#define IA64_SWITCH_STACK_R6_OFFSET 448 /* 0x1c0 */ +#define IA64_SWITCH_STACK_R7_OFFSET 456 /* 0x1c8 */ +#define IA64_SWITCH_STACK_B0_OFFSET 464 /* 0x1d0 */ +#define IA64_SWITCH_STACK_B1_OFFSET 472 /* 0x1d8 */ +#define IA64_SWITCH_STACK_B2_OFFSET 480 /* 0x1e0 */ +#define IA64_SWITCH_STACK_B3_OFFSET 488 /* 0x1e8 */ +#define IA64_SWITCH_STACK_B4_OFFSET 496 /* 0x1f0 */ +#define IA64_SWITCH_STACK_B5_OFFSET 504 /* 0x1f8 */ +#define IA64_SWITCH_STACK_AR_PFS_OFFSET 512 /* 0x200 */ +#define IA64_SWITCH_STACK_AR_LC_OFFSET 520 /* 0x208 */ +#define IA64_SWITCH_STACK_AR_UNAT_OFFSET 528 /* 0x210 */ +#define IA64_SWITCH_STACK_AR_RNAT_OFFSET 536 /* 0x218 */ +#define IA64_SWITCH_STACK_AR_BSPSTORE_OFFSET 544 /* 0x220 */ +#define IA64_SWITCH_STACK_PR_OFFSET 464 /* 0x1d0 */ #define IA64_SIGCONTEXT_AR_BSP_OFFSET 72 /* 0x48 */ #define IA64_SIGCONTEXT_AR_RNAT_OFFSET 80 /* 0x50 */ #define IA64_SIGCONTEXT_FLAGS_OFFSET 0 /* 0x0 */ diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 648ff8a12..445e42376 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -127,6 +127,12 @@ typedef union ia64_va { #define __pa(x) ({ia64_va _v; _v.l = (long) (x); _v.f.reg = 0; _v.l;}) #define __va(x) ({ia64_va _v; _v.l = (long) (x); _v.f.reg = -1; _v.p;}) +#define REGION_NUMBER(x) ({ia64_va _v; _v.l = (long) (x); _v.f.reg;}) +#define REGION_OFFSET(x) ({ia64_va _v; _v.l = (long) (x); _v.f.off;}) + +#define REGION_SIZE REGION_NUMBER(1) +#define REGION_KERNEL 7 + #define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) #define PAGE_BUG(page) do { BUG(); } while (0) diff --git a/include/asm-ia64/pal.h b/include/asm-ia64/pal.h index afae33050..5169b3f82 100644 --- a/include/asm-ia64/pal.h +++ b/include/asm-ia64/pal.h @@ -4,11 +4,12 @@ /* * Processor Abstraction Layer definitions. * - * This is based on version 2.4 of the manual "Enhanced Mode Processor - * Abstraction Layer". + * This is based on Intel IA-64 Architecture Software Developer's Manual rev 1.0 + * chapter 11 IA-64 Processor Abstraction Layer * * Copyright (C) 1998-2000 Hewlett-Packard Co * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com> * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> * Copyright (C) 1999 Srinivasa Prasad Thirumalachar <sprasad@sprasad.engr.sgi.com> @@ -16,6 +17,8 @@ * 99/10/01 davidm Make sure we pass zero for reserved parameters. * 00/03/07 davidm Updated pal_cache_flush() to be in sync with PAL v2.6. * 00/03/23 cfleck Modified processor min-state save area to match updated PAL & SAL info + * 00/05/24 eranian Updated to latest PAL spec, fix structures bugs, added + * 00/05/25 eranian Support for stack calls, and statis physical calls */ /* @@ -127,8 +130,8 @@ typedef struct pal_freq_ratio { typedef union pal_cache_config_info_1_s { struct { u64 u : 1, /* 0 Unified cache ? */ - reserved : 5, /* 7-3 Reserved */ at : 2, /* 2-1 Cache mem attr*/ + reserved : 5, /* 7-3 Reserved */ associativity : 8, /* 16-8 Associativity*/ line_size : 8, /* 23-17 Line size */ stride : 8, /* 31-24 Stride */ @@ -164,8 +167,8 @@ typedef struct pal_cache_config_info_s { u64 pcci_reserved; } pal_cache_config_info_t; -#define pcci_ld_hint pcci_info_1.pcci1.load_hints -#define pcci_st_hint pcci_info_1.pcci1_bits.store_hints +#define pcci_ld_hints pcci_info_1.pcci1_bits.load_hints +#define pcci_st_hints pcci_info_1.pcci1_bits.store_hints #define pcci_ld_latency pcci_info_1.pcci1_bits.load_latency #define pcci_st_latency pcci_info_1.pcci1_bits.store_latency #define pcci_stride pcci_info_1.pcci1_bits.stride @@ -640,23 +643,13 @@ struct ia64_pal_retval { * (generally 0) MUST be passed. Reserved parameters are not optional * parameters. */ -#ifdef __GCC_MULTIREG_RETVALS__ - extern struct ia64_pal_retval ia64_pal_call_static (u64, u64, u64, u64); - /* - * If multi-register return values are returned according to the - * ia-64 calling convention, we can call ia64_pal_call_static - * directly. - */ -# define PAL_CALL(iprv,a0,a1,a2,a3) iprv = ia64_pal_call_static(a0,a1, a2, a3) -#else - extern void ia64_pal_call_static (struct ia64_pal_retval *, u64, u64, u64, u64); - /* - * If multi-register return values are returned through an aggregate - * allocated in the caller, we need to use the stub implemented in - * sal-stub.S. - */ -# define PAL_CALL(iprv,a0,a1,a2,a3) ia64_pal_call_static(&iprv, a0, a1, a2, a3) -#endif +extern struct ia64_pal_retval ia64_pal_call_static (u64, u64, u64, u64); +extern struct ia64_pal_retval ia64_pal_call_stacked (u64, u64, u64, u64); +extern struct ia64_pal_retval ia64_pal_call_phys_static (u64, u64, u64, u64); + +#define PAL_CALL(iprv,a0,a1,a2,a3) iprv = ia64_pal_call_static(a0, a1, a2, a3) +#define PAL_CALL_STK(iprv,a0,a1,a2,a3) iprv = ia64_pal_call_stacked(a0, a1, a2, a3) +#define PAL_CALL_PHYS(iprv,a0,a1,a2,a3) iprv = ia64_pal_call_phys_static(a0, a1, a2, a3) typedef int (*ia64_pal_handler) (u64, ...); extern ia64_pal_handler ia64_pal; @@ -716,7 +709,7 @@ ia64_pal_bus_get_features (pal_bus_features_u_t *features_avail, pal_bus_features_u_t *features_control) { struct ia64_pal_retval iprv; - PAL_CALL(iprv, PAL_BUS_GET_FEATURES, 0, 0, 0); + PAL_CALL_PHYS(iprv, PAL_BUS_GET_FEATURES, 0, 0, 0); if (features_avail) features_avail->pal_bus_features_val = iprv.v0; if (features_status) @@ -725,15 +718,54 @@ ia64_pal_bus_get_features (pal_bus_features_u_t *features_avail, features_control->pal_bus_features_val = iprv.v2; return iprv.status; } + /* Enables/disables specific processor bus features */ extern inline s64 ia64_pal_bus_set_features (pal_bus_features_u_t feature_select) { struct ia64_pal_retval iprv; - PAL_CALL(iprv, PAL_BUS_SET_FEATURES, feature_select.pal_bus_features_val, 0, 0); + PAL_CALL_PHYS(iprv, PAL_BUS_SET_FEATURES, feature_select.pal_bus_features_val, 0, 0); return iprv.status; } +/* Get detailed cache information */ +extern inline s64 +ia64_pal_cache_config_info (u64 cache_level, u64 cache_type, pal_cache_config_info_t *conf) +{ + struct ia64_pal_retval iprv; + + PAL_CALL(iprv, PAL_CACHE_INFO, cache_level, cache_type, 0); + + if (iprv.status == 0) { + conf->pcci_status = iprv.status; + conf->pcci_info_1.pcci1_data = iprv.v0; + conf->pcci_info_2.pcci2_data = iprv.v1; + conf->pcci_reserved = iprv.v2; + } + return iprv.status; + +} + +/* Get detailed cche protection information */ +extern inline s64 +ia64_pal_cache_prot_info (u64 cache_level, u64 cache_type, pal_cache_protection_info_t *prot) +{ + struct ia64_pal_retval iprv; + + PAL_CALL(iprv, PAL_CACHE_PROT_INFO, cache_level, cache_type, 0); + + if (iprv.status == 0) { + prot->pcpi_status = iprv.status; + prot->pcp_info[0].pcpi_data = iprv.v0 & 0xffffffff; + prot->pcp_info[1].pcpi_data = iprv.v0 >> 32; + prot->pcp_info[2].pcpi_data = iprv.v1 & 0xffffffff; + prot->pcp_info[3].pcpi_data = iprv.v1 >> 32; + prot->pcp_info[4].pcpi_data = iprv.v2 & 0xffffffff; + prot->pcp_info[5].pcpi_data = iprv.v2 >> 32; + } + return iprv.status; +} + /* * Flush the processor instruction or data caches. *PROGRESS must be * initialized to zero before calling this for the first time.. @@ -909,16 +941,19 @@ typedef union pal_power_mgmt_info_u { struct { u64 exit_latency : 16, entry_latency : 16, - power_consumption : 32; + power_consumption : 28, + im : 1, + co : 1, + reserved : 2; } pal_power_mgmt_info_s; } pal_power_mgmt_info_u_t; /* Return information about processor's optional power management capabilities. */ extern inline s64 ia64_pal_halt_info (pal_power_mgmt_info_u_t *power_buf) -{ +{ struct ia64_pal_retval iprv; - PAL_CALL(iprv, PAL_HALT_INFO, (unsigned long) power_buf, 0, 0); + PAL_CALL_STK(iprv, PAL_HALT_INFO, (unsigned long) power_buf, 0, 0); return iprv.status; } @@ -1027,7 +1062,7 @@ ia64_pal_mem_attrib (u64 *mem_attrib) struct ia64_pal_retval iprv; PAL_CALL(iprv, PAL_MEM_ATTRIB, 0, 0, 0); if (mem_attrib) - *mem_attrib = iprv.v0; + *mem_attrib = iprv.v0 & 0xff; return iprv.status; } @@ -1090,28 +1125,32 @@ ia64_pal_pmi_entrypoint (u64 sal_pmi_entry_addr) return iprv.status; } -#ifdef TBD struct pal_features_s; /* Provide information about configurable processor features */ extern inline s64 -ia64_pal_proc_get_features (struct pal_features_s *features_avail, - struct pal_features_s *features_status, - struct pal_features_s *features_control) +ia64_pal_proc_get_features (u64 *features_avail, + u64 *features_status, + u64 *features_control) { struct ia64_pal_retval iprv; - PAL_CALL(iprv, PAL_PROC_GET_FEATURES, 0, 0, 0); + PAL_CALL_PHYS(iprv, PAL_PROC_GET_FEATURES, 0, 0, 0); + if (iprv.status == 0) { + *features_avail = iprv.v0; + *features_status = iprv.v1; + *features_control = iprv.v2; + } return iprv.status; } + /* Enable/disable processor dependent features */ extern inline s64 -ia64_pal_proc_set_features (feature_select) +ia64_pal_proc_set_features (u64 feature_select) { struct ia64_pal_retval iprv; - PAL_CALL(iprv, PAL_PROC_SET_FEATURES, feature_select, 0, 0); + PAL_CALL_PHYS(iprv, PAL_PROC_SET_FEATURES, feature_select, 0, 0); return iprv.status; } -#endif /* * Put everything in a struct so we avoid the global offset table whenever * possible. @@ -1220,12 +1259,16 @@ typedef union pal_version_u { /* Return PAL version information */ extern inline s64 -ia64_pal_version (pal_version_u_t *pal_version) +ia64_pal_version (pal_version_u_t *pal_min_version, pal_version_u_t *pal_cur_version) { struct ia64_pal_retval iprv; PAL_CALL(iprv, PAL_VERSION, 0, 0, 0); - if (pal_version) - pal_version->pal_version_val = iprv.v0; + if (pal_min_version) + pal_min_version->pal_version_val = iprv.v0; + + if (pal_cur_version) + pal_cur_version->pal_version_val = iprv.v1; + return iprv.status; } @@ -1242,7 +1285,14 @@ typedef union pal_tc_info_u { } pal_tc_info_s; } pal_tc_info_u_t; - +#define tc_reduce_tr pal_tc_info_s.reduce_tr +#define tc_unified pal_tc_info_s.unified +#define tc_pf pal_tc_info_s.pf +#define tc_num_entries pal_tc_info_s.num_entries +#define tc_associativity pal_tc_info_s.associativity +#define tc_num_sets pal_tc_info_s.num_sets + + /* Return information about the virtual memory characteristics of the processor * implementation. */ @@ -1278,7 +1328,7 @@ typedef union pal_vm_info_1_u { struct { u64 vw : 1, phys_add_size : 7, - key_size : 16, + key_size : 8, max_pkr : 8, hash_tag_id : 8, max_dtr_entry : 8, diff --git a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h index bbabe63e8..0c40b0e6b 100644 --- a/include/asm-ia64/pci.h +++ b/include/asm-ia64/pci.h @@ -16,6 +16,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* * Dynamic DMA mapping API. * IA-64 has everything mapped statically. diff --git a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h index a7f5ceb56..ce0dea3fe 100644 --- a/include/asm-ia64/pgtable.h +++ b/include/asm-ia64/pgtable.h @@ -12,27 +12,18 @@ * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com> */ -#include <linux/config.h> - #include <asm/mman.h> #include <asm/page.h> +#include <asm/processor.h> #include <asm/types.h> -/* Size of virtuaql and physical address spaces: */ -#ifdef CONFIG_ITANIUM -# define IA64_IMPL_VA_MSB 50 -# define IA64_PHYS_BITS 44 /* Itanium PRM defines 44 bits of ppn */ -#else -# define IA64_IMPL_VA_MSB 60 /* maximum value (bits 61-63 are region bits) */ -# define IA64_PHYS_BITS 50 /* EAS2.6 allows up to 50 bits of ppn */ -#endif -#define IA64_PHYS_SIZE (__IA64_UL(1) << IA64_PHYS_BITS) +#define IA64_MAX_PHYS_BITS 50 /* max. number of physical address bits (architected) */ /* Is ADDR a valid kernel address? */ #define kern_addr_valid(addr) ((addr) >= TASK_SIZE) /* Is ADDR a valid physical address? */ -#define phys_addr_valid(addr) ((addr) < IA64_PHYS_SIZE) +#define phys_addr_valid(addr) (((addr) & my_cpu_data.unimpl_pa_mask) == 0) /* * First, define the various bits in a PTE. Note that the PTE format @@ -63,7 +54,7 @@ #define _PAGE_AR_SHIFT 9 #define _PAGE_A (1 << 5) /* page accessed bit */ #define _PAGE_D (1 << 6) /* page dirty bit */ -#define _PAGE_PPN_MASK ((IA64_PHYS_SIZE - 1) & ~0xfffUL) +#define _PAGE_PPN_MASK (((__IA64_UL(1) << IA64_MAX_PHYS_BITS) - 1) & ~0xfffUL) #define _PAGE_ED (__IA64_UL(1) << 52) /* exception deferral */ #define _PAGE_PROTNONE (__IA64_UL(1) << 63) @@ -120,7 +111,6 @@ #include <asm/bitops.h> #include <asm/mmu_context.h> -#include <asm/processor.h> #include <asm/system.h> /* @@ -133,7 +123,7 @@ #define PAGE_READONLY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_R) #define PAGE_COPY __pgprot(__ACCESS_BITS | _PAGE_PL_3 | _PAGE_AR_RX) #define PAGE_GATE __pgprot(__ACCESS_BITS | _PAGE_PL_0 | _PAGE_AR_X_RX) -#define PAGE_KERNEL __pgprot(__DIRTY_BITS | _PAGE_PL_0 | _PAGE_AR_RW) +#define PAGE_KERNEL __pgprot(__DIRTY_BITS | _PAGE_PL_0 | _PAGE_AR_RWX) /* * Next come the mappings that determine how mmap() protection bits diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index d702b7f0c..fa3721bde 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -10,6 +10,7 @@ * * 11/24/98 S.Eranian added ia64_set_iva() * 12/03/99 D. Mosberger implement thread_saved_pc() via kernel unwind API + * 06/16/00 A. Mallick added csd/ssd/tssd for ia32 support */ #include <linux/config.h> @@ -237,6 +238,8 @@ struct cpuinfo_ia64 { __u64 proc_freq; /* frequency of processor */ __u64 cyc_per_usec; /* itc_freq/1000000 */ __u64 usec_per_cyc; /* 2^IA64_USEC_PER_CYC_SHIFT*1000000/itc_freq */ + __u64 unimpl_va_mask; /* mask of unimplemented virtual address bits (from PAL) */ + __u64 unimpl_pa_mask; /* mask of unimplemented physical address bits (from PAL) */ #ifdef CONFIG_SMP __u64 loops_per_sec; __u64 ipi_count; @@ -264,7 +267,8 @@ typedef struct { #define SET_UNALIGN_CTL(task,value) \ ({ \ - (task)->thread.flags |= ((value) << IA64_THREAD_UAC_SHIFT) & IA64_THREAD_UAC_MASK; \ + (task)->thread.flags = (((task)->thread.flags & ~IA64_THREAD_UAC_MASK) \ + | (((value) << IA64_THREAD_UAC_SHIFT) & IA64_THREAD_UAC_MASK)); \ 0; \ }) #define GET_UNALIGN_CTL(task,addr) \ @@ -288,10 +292,13 @@ struct thread_struct { __u64 fcr; /* IA32 floating pt control reg */ __u64 fir; /* IA32 fp except. instr. reg */ __u64 fdr; /* IA32 fp except. data reg */ + __u64 csd; /* IA32 code selector descriptor */ + __u64 ssd; /* IA32 stack selector descriptor */ + __u64 tssd; /* IA32 TSS descriptor */ union { __u64 sigmask; /* aligned mask for sigsuspend scall */ } un; -# define INIT_THREAD_IA32 , 0, 0, 0, 0, 0, {0} +# define INIT_THREAD_IA32 , 0, 0, 0x17800000037fULL, 0, 0, 0, 0, 0, {0} #else # define INIT_THREAD_IA32 #endif /* CONFIG_IA32_SUPPORT */ @@ -318,6 +325,7 @@ struct thread_struct { set_fs(USER_DS); \ ia64_psr(regs)->cpl = 3; /* set user mode */ \ ia64_psr(regs)->ri = 0; /* clear return slot number */ \ + ia64_psr(regs)->is = 0; /* IA-64 instruction set */ \ regs->cr_iip = new_ip; \ regs->ar_rsc = 0xf; /* eager mode, privilege level 3 */ \ regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \ @@ -436,6 +444,14 @@ ia64_srlz_d (void) __asm__ __volatile__ (";; srlz.d" ::: "memory"); } +extern inline __u64 +ia64_get_rr (__u64 reg_bits) +{ + __u64 r; + __asm__ __volatile__ ("mov %0=rr[%1]" : "=r"(r) : "r"(reg_bits) : "memory"); + return r; +} + extern inline void ia64_set_rr (__u64 reg_bits, __u64 rr_val) { @@ -645,14 +661,17 @@ ia64_set_unat (__u64 *unat, void *spill_addr, unsigned long nat) extern inline unsigned long thread_saved_pc (struct thread_struct *t) { - struct ia64_frame_info info; + struct unw_frame_info info; + unsigned long ip; + /* XXX ouch: Linus, please pass the task pointer to thread_saved_pc() instead! */ struct task_struct *p = (void *) ((unsigned long) t - IA64_TASK_THREAD_OFFSET); - ia64_unwind_init_from_blocked_task(&info, p); - if (ia64_unwind_to_previous_frame(&info) < 0) + unw_init_from_blocked_task(&info, p); + if (unw_unwind(&info) < 0) return 0; - return ia64_unwind_get_ip(&info); + unw_get_ip(&info, &ip); + return ip; } /* diff --git a/include/asm-ia64/ptrace.h b/include/asm-ia64/ptrace.h index caae43a3d..b71acee5f 100644 --- a/include/asm-ia64/ptrace.h +++ b/include/asm-ia64/ptrace.h @@ -136,8 +136,8 @@ struct pt_regs { unsigned long r30; /* scratch */ unsigned long r31; /* scratch */ - unsigned long ar_ccv; /* compare/exchange value */ - unsigned long ar_fpsr; /* floating point status*/ + unsigned long ar_ccv; /* compare/exchange value (scratch) */ + unsigned long ar_fpsr; /* floating point status (preserved) */ unsigned long b0; /* return pointer (bp) */ unsigned long b7; /* scratch */ @@ -219,11 +219,19 @@ struct switch_stack { extern void show_regs (struct pt_regs *); extern long ia64_peek (struct pt_regs *, struct task_struct *, unsigned long addr, long *val); extern long ia64_poke (struct pt_regs *, struct task_struct *, unsigned long addr, long val); + extern void ia64_sync_fph (struct task_struct *t); +#ifdef CONFIG_IA64_NEW_UNWIND + /* get nat bits for scratch registers such that bit N==1 iff scratch register rN is a NaT */ + extern unsigned long ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat); + /* put nat bits for scratch registers such that scratch register rN is a NaT iff bit N==1 */ + extern unsigned long ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat); +#else /* get nat bits for r1-r31 such that bit N==1 iff rN is a NaT */ extern long ia64_get_nat_bits (struct pt_regs *pt, struct switch_stack *sw); /* put nat bits for r1-r31 such that rN is a NaT iff bit N==1 */ extern void ia64_put_nat_bits (struct pt_regs *pt, struct switch_stack *sw, unsigned long nat); +#endif extern void ia64_increment_ip (struct pt_regs *pt); extern void ia64_decrement_ip (struct pt_regs *pt); diff --git a/include/asm-ia64/ptrace_offsets.h b/include/asm-ia64/ptrace_offsets.h index fbbe9bff4..b32b6c89d 100644 --- a/include/asm-ia64/ptrace_offsets.h +++ b/include/asm-ia64/ptrace_offsets.h @@ -118,8 +118,8 @@ #define PT_F126 0x05e0 #define PT_F127 0x05f0 /* switch stack: */ -#define PT_CALLER_UNAT 0x0600 -#define PT_KERNEL_FPSR 0x0608 +#define PT_NAT_BITS 0x0600 + #define PT_F2 0x0610 #define PT_F3 0x0620 #define PT_F4 0x0630 @@ -150,23 +150,19 @@ #define PT_R5 0x07b8 #define PT_R6 0x07c0 #define PT_R7 0x07c8 -#define PT_K_B0 0x07d0 + #define PT_B1 0x07d8 #define PT_B2 0x07e0 #define PT_B3 0x07e8 #define PT_B4 0x07f0 #define PT_B5 0x07f8 -#define PT_K_AR_PFS 0x0800 + #define PT_AR_LC 0x0808 -#define PT_K_AR_UNAT 0x0810 -#define PT_K_AR_RNAT 0x0818 -#define PT_K_AR_BSPSTORE 0x0820 -#define PT_K_PR 0x0828 + /* pt_regs */ #define PT_CR_IPSR 0x0830 #define PT_CR_IIP 0x0838 #define PT_CFM 0x0840 -#define PT_CR_IFS PT_CFM /* Use of PT_CR_IFS is deprecated */ #define PT_AR_UNAT 0x0848 #define PT_AR_PFS 0x0850 #define PT_AR_RSC 0x0858 diff --git a/include/asm-ia64/sal.h b/include/asm-ia64/sal.h index 5fadec55f..06096644b 100644 --- a/include/asm-ia64/sal.h +++ b/include/asm-ia64/sal.h @@ -23,17 +23,7 @@ extern spinlock_t sal_lock; -#ifdef __GCC_MULTIREG_RETVALS__ - /* If multi-register return values are returned according to the - ia-64 calling convention, we can call ia64_sal directly. */ -# define __SAL_CALL(result,args...) result = (*ia64_sal)(args) -#else - /* If multi-register return values are returned through an aggregate - allocated in the caller, we need to use the stub implemented in - sal-stub.S. */ - extern struct ia64_sal_retval ia64_sal_stub (u64 index, ...); -# define __SAL_CALL(result,args...) result = ia64_sal_stub(args) -#endif +#define __SAL_CALL(result,args...) result = (*ia64_sal)(args) #ifdef CONFIG_SMP # define SAL_CALL(result,args...) do { \ @@ -494,7 +484,19 @@ extern inline s64 ia64_sal_pci_config_read (u64 pci_config_addr, u64 size, u64 *value) { struct ia64_sal_retval isrv; +#ifdef CONFIG_ITANIUM_A1_SPECIFIC + extern spinlock_t ivr_read_lock; + unsigned long flags; + + /* + * Avoid PCI configuration read/write overwrite -- A0 Interrupt loss workaround + */ + spin_lock_irqsave(&ivr_read_lock, flags); +#endif SAL_CALL(isrv, SAL_PCI_CONFIG_READ, pci_config_addr, size); +#ifdef CONFIG_ITANIUM_A1_SPECIFIC + spin_unlock_irqrestore(&ivr_read_lock, flags); +#endif if (value) *value = isrv.v0; return isrv.status; @@ -505,7 +507,7 @@ extern inline s64 ia64_sal_pci_config_write (u64 pci_config_addr, u64 size, u64 value) { struct ia64_sal_retval isrv; -#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) && !defined(SAPIC_FIXED) +#ifdef CONFIG_ITANIUM_A1_SPECIFIC extern spinlock_t ivr_read_lock; unsigned long flags; @@ -515,7 +517,7 @@ ia64_sal_pci_config_write (u64 pci_config_addr, u64 size, u64 value) spin_lock_irqsave(&ivr_read_lock, flags); #endif SAL_CALL(isrv, SAL_PCI_CONFIG_WRITE, pci_config_addr, size, value); -#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) && !defined(SAPIC_FIXED) +#ifdef CONFIG_ITANIUM_A1_SPECIFIC spin_unlock_irqrestore(&ivr_read_lock, flags); #endif return isrv.status; diff --git a/include/asm-ia64/siginfo.h b/include/asm-ia64/siginfo.h index f997b468d..7222fb285 100644 --- a/include/asm-ia64/siginfo.h +++ b/include/asm-ia64/siginfo.h @@ -56,6 +56,8 @@ typedef struct siginfo { struct { void *_addr; /* faulting insn/memory ref. */ int _imm; /* immediate value for "break" */ + int _pad0; + unsigned long _isr; /* isr */ } _sigfault; /* SIGPOLL */ @@ -79,6 +81,7 @@ typedef struct siginfo { #define si_ptr _sifields._rt._sigval.sival_ptr #define si_addr _sifields._sigfault._addr #define si_imm _sifields._sigfault._imm /* as per UNIX SysV ABI spec */ +#define si_isr _sifields._sigfault._isr /* valid if si_code==FPE_FLTxxx */ #define si_band _sifields._sigpoll._band #define si_fd _sifields._sigpoll._fd diff --git a/include/asm-ia64/spinlock.h b/include/asm-ia64/spinlock.h index 97a9511e8..fedd8f8c6 100644 --- a/include/asm-ia64/spinlock.h +++ b/include/asm-ia64/spinlock.h @@ -9,6 +9,8 @@ * This file is used for SMP configurations only. */ +#include <linux/kernel.h> + #include <asm/system.h> #include <asm/bitops.h> #include <asm/atomic.h> @@ -40,7 +42,7 @@ typedef struct { "cmp4.eq p0,p7 = r0, r2\n" \ "(p7) br.cond.spnt.few 1b\n" \ ";;\n" \ - :: "m" __atomic_fool_gcc((x)) : "r2", "r29") + :: "m" __atomic_fool_gcc((x)) : "r2", "r29", "memory") #else #define spin_lock(x) \ @@ -55,22 +57,12 @@ typedef struct { #define spin_is_locked(x) ((x)->lock != 0) -#define spin_unlock(x) (((spinlock_t *) x)->lock = 0) +#define spin_unlock(x) ({((spinlock_t *) x)->lock = 0; barrier();}) /* Streamlined !test_and_set_bit(0, (x)) */ -#define spin_trylock(x) \ -({ \ - spinlock_t *__x = (x); \ - __u32 old; \ - \ - do { \ - old = __x->lock; \ - } while (cmpxchg_acq(&__x->lock, old, 1) != old); \ - old == 0; \ -}) - -#define spin_unlock_wait(x) \ - ({ do { barrier(); } while(((volatile spinlock_t *)x)->lock); }) +#define spin_trylock(x) (cmpxchg_acq(&(x)->lock, 0, 1) == 0) + +#define spin_unlock_wait(x) ({ do { barrier(); } while ((x)->lock); }) typedef struct { volatile int read_counter:31; @@ -78,45 +70,49 @@ typedef struct { } rwlock_t; #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } -#define read_lock(rw) \ -do { \ - int tmp = 0; \ - __asm__ __volatile__ ("1:\tfetchadd4.acq %0 = %1, 1\n" \ - ";;\n" \ - "tbit.nz p6,p0 = %0, 31\n" \ - "(p6) br.cond.sptk.few 2f\n" \ - ".section .text.lock,\"ax\"\n" \ - "2:\tfetchadd4.rel %0 = %1, -1\n" \ - ";;\n" \ - "3:\tld4.acq %0 = %1\n" \ - ";;\n" \ - "tbit.nz p6,p0 = %0, 31\n" \ - "(p6) br.cond.sptk.few 3b\n" \ - "br.cond.sptk.few 1b\n" \ - ";;\n" \ - ".previous\n": "=r" (tmp), "=m" (__atomic_fool_gcc(rw))); \ +#define read_lock(rw) \ +do { \ + int tmp = 0; \ + __asm__ __volatile__ ("1:\tfetchadd4.acq %0 = %1, 1\n" \ + ";;\n" \ + "tbit.nz p6,p0 = %0, 31\n" \ + "(p6) br.cond.sptk.few 2f\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\tfetchadd4.rel %0 = %1, -1\n" \ + ";;\n" \ + "3:\tld4.acq %0 = %1\n" \ + ";;\n" \ + "tbit.nz p6,p0 = %0, 31\n" \ + "(p6) br.cond.sptk.few 3b\n" \ + "br.cond.sptk.few 1b\n" \ + ";;\n" \ + ".previous\n" \ + : "=r" (tmp), "=m" (__atomic_fool_gcc(rw)) \ + :: "memory"); \ } while(0) -#define read_unlock(rw) \ -do { \ - int tmp = 0; \ - __asm__ __volatile__ ("fetchadd4.rel %0 = %1, -1\n" \ - : "=r" (tmp) : "m" (__atomic_fool_gcc(rw))); \ +#define read_unlock(rw) \ +do { \ + int tmp = 0; \ + __asm__ __volatile__ ("fetchadd4.rel %0 = %1, -1\n" \ + : "=r" (tmp) \ + : "m" (__atomic_fool_gcc(rw)) \ + : "memory"); \ } while(0) #define write_lock(rw) \ -while(1) { \ +do { \ do { \ - } while (!test_and_set_bit(31, (rw))); \ - if ((rw)->read_counter) { \ - clear_bit(31, (rw)); \ - while ((rw)->read_counter) \ - ; \ - } else { \ - break; \ - } \ -} + while ((rw)->write_lock); \ + } while (test_and_set_bit(31, (rw))); \ + while ((rw)->read_counter); \ + barrier(); \ +} while (0) -#define write_unlock(x) (clear_bit(31, (x))) +/* + * clear_bit() has "acq" semantics; we're really need "rel" semantics, + * but for simplicity, we simply do a fence for now... + */ +#define write_unlock(x) ({clear_bit(31, (x)); mb();}) #endif /* _ASM_IA64_SPINLOCK_H */ diff --git a/include/asm-ia64/stat.h b/include/asm-ia64/stat.h index c261a337e..b9dd64bd5 100644 --- a/include/asm-ia64/stat.h +++ b/include/asm-ia64/stat.h @@ -7,6 +7,27 @@ */ struct stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad0; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long __reserved0; /* reserved for atime.nanoseconds */ + unsigned long st_mtime; + unsigned long __reserved1; /* reserved for mtime.nanoseconds */ + unsigned long st_ctime; + unsigned long __reserved2; /* reserved for ctime.nanoseconds */ + unsigned long st_blksize; + long st_blocks; + unsigned long __unused[3]; +}; + +struct ia64_oldstat { unsigned int st_dev; unsigned int st_ino; unsigned int st_mode; diff --git a/include/asm-ia64/string.h b/include/asm-ia64/string.h index 09a99daf4..2b7292067 100644 --- a/include/asm-ia64/string.h +++ b/include/asm-ia64/string.h @@ -12,4 +12,7 @@ #define __HAVE_ARCH_STRLEN 1 /* see arch/ia64/lib/strlen.S */ #define __HAVE_ARCH_MEMSET 1 /* see arch/ia64/lib/memset.S */ +extern __kernel_size_t strlen (const char *); +extern void *memset (void *,int,__kernel_size_t); + #endif /* _ASM_IA64_STRING_H */ diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h index a7ba6daf7..689be6df6 100644 --- a/include/asm-ia64/system.h +++ b/include/asm-ia64/system.h @@ -33,9 +33,9 @@ struct pci_vector_struct { __u16 bus; /* PCI Bus number */ - __u32 pci_id; /* ACPI split 16 bits device, 16 bits function (see section 6.1.1) */ - __u8 pin; /* PCI PIN (0 = A, 1 = B, 2 = C, 3 = D) */ - __u8 irq; /* IRQ assigned */ + __u32 pci_id; /* ACPI split 16 bits device, 16 bits function (see section 6.1.1) */ + __u8 pin; /* PCI PIN (0 = A, 1 = B, 2 = C, 3 = D) */ + __u8 irq; /* IRQ assigned */ }; extern struct ia64_boot_param { @@ -54,6 +54,8 @@ extern struct ia64_boot_param { __u16 num_pci_vectors; /* number of ACPI derived PCI IRQ's*/ __u64 pci_vectors; /* physical address of PCI data (pci_vector_struct)*/ __u64 fpswa; /* physical address of the the fpswa interface */ + __u64 initrd_start; + __u64 initrd_size; } ia64_boot_param; extern inline void @@ -135,7 +137,7 @@ do { \ do { \ unsigned long ip, old_psr, psr = (x); \ \ - __asm__ __volatile__ ("mov %0=psr; mov psr.l=%1;; srlz.d" \ + __asm__ __volatile__ (";;mov %0=psr; mov psr.l=%1;; srlz.d" \ : "=&r" (old_psr) : "r" (psr) : "memory"); \ if ((old_psr & (1UL << 14)) && !(psr & (1UL << 14))) { \ __asm__ ("mov %0=ip" : "=r"(ip)); \ @@ -149,7 +151,7 @@ do { \ : "=r" (x) :: "memory") # define local_irq_disable() __asm__ __volatile__ (";; rsm psr.i;;" ::: "memory") /* (potentially) setting psr.i requires data serialization: */ -# define local_irq_restore(x) __asm__ __volatile__ ("mov psr.l=%0;; srlz.d" \ +# define local_irq_restore(x) __asm__ __volatile__ (";; mov psr.l=%0;; srlz.d" \ :: "r" (x) : "memory") #endif /* !CONFIG_IA64_DEBUG_IRQ */ @@ -394,32 +396,13 @@ struct __xchg_dummy { unsigned long a[100]; }; #ifdef __KERNEL__ -extern void ia64_save_debug_regs (unsigned long *save_area); -extern void ia64_load_debug_regs (unsigned long *save_area); - #define prepare_to_switch() do { } while(0) #ifdef CONFIG_IA32_SUPPORT # define IS_IA32_PROCESS(regs) (ia64_psr(regs)->is != 0) -# define IA32_STATE(prev,next) \ - if (IS_IA32_PROCESS(ia64_task_regs(prev))) { \ - __asm__ __volatile__("mov %0=ar.eflag":"=r"((prev)->thread.eflag)); \ - __asm__ __volatile__("mov %0=ar.fsr":"=r"((prev)->thread.fsr)); \ - __asm__ __volatile__("mov %0=ar.fcr":"=r"((prev)->thread.fcr)); \ - __asm__ __volatile__("mov %0=ar.fir":"=r"((prev)->thread.fir)); \ - __asm__ __volatile__("mov %0=ar.fdr":"=r"((prev)->thread.fdr)); \ - } \ - if (IS_IA32_PROCESS(ia64_task_regs(next))) { \ - __asm__ __volatile__("mov ar.eflag=%0"::"r"((next)->thread.eflag)); \ - __asm__ __volatile__("mov ar.fsr=%0"::"r"((next)->thread.fsr)); \ - __asm__ __volatile__("mov ar.fcr=%0"::"r"((next)->thread.fcr)); \ - __asm__ __volatile__("mov ar.fir=%0"::"r"((next)->thread.fir)); \ - __asm__ __volatile__("mov ar.fdr=%0"::"r"((next)->thread.fdr)); \ - } -#else /* !CONFIG_IA32_SUPPORT */ -# define IA32_STATE(prev,next) +#else # define IS_IA32_PROCESS(regs) 0 -#endif /* CONFIG_IA32_SUPPORT */ +#endif /* * Context switch from one thread to another. If the two threads have @@ -432,15 +415,18 @@ extern void ia64_load_debug_regs (unsigned long *save_area); * ia64_ret_from_syscall_clear_r8. */ extern struct task_struct *ia64_switch_to (void *next_task); + +extern void ia64_save_extra (struct task_struct *task); +extern void ia64_load_extra (struct task_struct *task); + #define __switch_to(prev,next,last) do { \ + if (((prev)->thread.flags & IA64_THREAD_DBG_VALID) \ + || IS_IA32_PROCESS(ia64_task_regs(prev))) \ + ia64_save_extra(prev); \ + if (((next)->thread.flags & IA64_THREAD_DBG_VALID) \ + || IS_IA32_PROCESS(ia64_task_regs(next))) \ + ia64_load_extra(next); \ ia64_psr(ia64_task_regs(next))->dfh = (ia64_get_fpu_owner() != (next)); \ - if ((prev)->thread.flags & IA64_THREAD_DBG_VALID) { \ - ia64_save_debug_regs(&(prev)->thread.dbr[0]); \ - } \ - if ((next)->thread.flags & IA64_THREAD_DBG_VALID) { \ - ia64_load_debug_regs(&(next)->thread.dbr[0]); \ - } \ - IA32_STATE(prev,next); \ (last) = ia64_switch_to((next)); \ } while (0) diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 5be533112..41ffaaf2d 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -109,9 +109,9 @@ #define __NR_syslog 1117 #define __NR_setitimer 1118 #define __NR_getitimer 1119 -#define __NR_stat 1120 -#define __NR_lstat 1121 -#define __NR_fstat 1122 +#define __NR_old_stat 1120 +#define __NR_old_lstat 1121 +#define __NR_old_fstat 1122 #define __NR_vhangup 1123 #define __NR_lchown 1124 #define __NR_vm86 1125 @@ -199,6 +199,9 @@ #define __NR_sys_pivot_root 1207 #define __NR_mincore 1208 #define __NR_madvise 1209 +#define __NR_stat 1210 +#define __NR_lstat 1211 +#define __NR_fstat 1212 #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) @@ -269,7 +272,7 @@ type \ name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ { \ return __ia64_syscall((long) arg1, (long) arg2, (long) arg3, \ - (long) arg4, (long), __NR_##name); \ + (long) arg4, (long) arg5, __NR_##name); \ } #ifdef __KERNEL_SYSCALLS__ diff --git a/include/asm-ia64/unwind.h b/include/asm-ia64/unwind.h index 038edb798..60bb46cf9 100644 --- a/include/asm-ia64/unwind.h +++ b/include/asm-ia64/unwind.h @@ -2,8 +2,8 @@ #define _ASM_IA64_UNWIND_H /* - * Copyright (C) 1999 Hewlett-Packard Co - * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 1999-2000 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> * * A simple API for unwinding kernel stacks. This is used for * debugging and error reporting purposes. The kernel doesn't need @@ -16,27 +16,72 @@ struct task_struct; /* forward declaration */ struct switch_stack; /* forward declaration */ +enum unw_application_register { + UNW_AR_BSP, + UNW_AR_BSPSTORE, + UNW_AR_PFS, + UNW_AR_RNAT, + UNW_AR_UNAT, + UNW_AR_LC, + UNW_AR_EC, + UNW_AR_FPSR, + UNW_AR_RSC, + UNW_AR_CCV +}; + /* * The following declarations are private to the unwind * implementation: */ -struct ia64_stack { - unsigned long *limit; - unsigned long *top; +struct unw_stack { + unsigned long limit; + unsigned long top; }; +#define UNW_FLAG_INTERRUPT_FRAME (1UL << 0) + /* * No user of this module should every access this structure directly * as it is subject to change. It is declared here solely so we can * use automatic variables. */ -struct ia64_frame_info { - struct ia64_stack regstk; - unsigned long *bsp; - unsigned long top_rnat; /* RSE NaT collection at top of backing store */ - unsigned long cfm; +struct unw_frame_info { + struct unw_stack regstk; + struct unw_stack memstk; + unsigned int flags; + short hint; + short prev_script; + unsigned long bsp; + unsigned long sp; /* stack pointer */ + unsigned long psp; /* previous sp */ unsigned long ip; /* instruction pointer */ + unsigned long pr_val; /* current predicates */ + unsigned long *cfm; + + struct task_struct *task; + struct switch_stack *sw; + + /* preserved state: */ + unsigned long *pbsp; /* previous bsp */ + unsigned long *bspstore; + unsigned long *pfs; + unsigned long *rnat; + unsigned long *rp; + unsigned long *pri_unat; + unsigned long *unat; + unsigned long *pr; + unsigned long *lc; + unsigned long *fpsr; + struct unw_ireg { + unsigned long *loc; + struct unw_ireg_nat { + int type : 3; /* enum unw_nat_type */ + signed int off; /* NaT word is at loc+nat.off */ + } nat; + } r4, r5, r6, r7; + unsigned long *b1, *b2, *b3, *b4, *b5; + struct ia64_fpreg *f2, *f3, *f4, *f5, *fr[16]; }; /* @@ -44,10 +89,22 @@ struct ia64_frame_info { */ /* + * Initialize unwind support. + */ +extern void unw_init (void); + +extern void *unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp, + void *table_start, void *table_end); + +extern void unw_remove_unwind_table (void *handle); + +/* * Prepare to unwind blocked task t. */ -extern void ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, - struct task_struct *t); +extern void unw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t); + +extern void unw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, + struct switch_stack *sw); /* * Prepare to unwind the current task. For this to work, the kernel @@ -63,15 +120,82 @@ extern void ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, * | struct switch_stack | * +---------------------+ */ -extern void ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *regs); +extern void unw_init_from_current (struct unw_frame_info *info, struct pt_regs *regs); + +/* + * Prepare to unwind the currently running thread. + */ +extern void unw_init_running (void (*callback)(struct unw_frame_info *info, void *arg), void *arg); /* * Unwind to previous to frame. Returns 0 if successful, negative * number in case of an error. */ -extern int ia64_unwind_to_previous_frame (struct ia64_frame_info *info); +extern int unw_unwind (struct unw_frame_info *info); + +/* + * Unwind until the return pointer is in user-land (or until an error + * occurs). Returns 0 if successful, negative number in case of + * error. + */ +extern int unw_unwind_to_user (struct unw_frame_info *info); + +#define unw_get_ip(info,vp) ({*(vp) = (info)->ip; 0;}) +#define unw_get_sp(info,vp) ({*(vp) = (unsigned long) (info)->sp; 0;}) +#define unw_get_psp(info,vp) ({*(vp) = (unsigned long) (info)->psp; 0;}) +#define unw_get_bsp(info,vp) ({*(vp) = (unsigned long) (info)->bsp; 0;}) +#define unw_get_cfm(info,vp) ({*(vp) = *(info)->cfm; 0;}) +#define unw_set_cfm(info,val) ({*(info)->cfm = (val); 0;}) + +static inline int +unw_get_rp (struct unw_frame_info *info, unsigned long *val) +{ + if (!info->rp) + return -1; + *val = *info->rp; + return 0; +} + +extern int unw_access_gr (struct unw_frame_info *, int, unsigned long *, char *, int); +extern int unw_access_br (struct unw_frame_info *, int, unsigned long *, int); +extern int unw_access_fr (struct unw_frame_info *, int, struct ia64_fpreg *, int); +extern int unw_access_ar (struct unw_frame_info *, int, unsigned long *, int); +extern int unw_access_pr (struct unw_frame_info *, unsigned long *, int); + +static inline int +unw_set_gr (struct unw_frame_info *i, int n, unsigned long v, char nat) +{ + return unw_access_gr(i, n, &v, &nat, 1); +} + +static inline int +unw_set_br (struct unw_frame_info *i, int n, unsigned long v) +{ + return unw_access_br(i, n, &v, 1); +} + +static inline int +unw_set_fr (struct unw_frame_info *i, int n, struct ia64_fpreg v) +{ + return unw_access_fr(i, n, &v, 1); +} + +static inline int +unw_set_ar (struct unw_frame_info *i, int n, unsigned long v) +{ + return unw_access_ar(i, n, &v, 1); +} + +static inline int +unw_set_pr (struct unw_frame_info *i, unsigned long v) +{ + return unw_access_pr(i, &v, 1); +} -#define ia64_unwind_get_ip(info) ((info)->ip) -#define ia64_unwind_get_bsp(info) ((unsigned long) (info)->bsp) +#define unw_get_gr(i,n,v,nat) unw_access_gr(i,n,v,nat,0) +#define unw_get_br(i,n,v) unw_access_br(i,n,v,0) +#define unw_get_fr(i,n,v) unw_access_fr(i,n,v,0) +#define unw_get_ar(i,n,v) unw_access_ar(i,n,v,0) +#define unw_get_pr(i,v) unw_access_pr(i,v,0) -#endif /* _ASM_IA64_UNWIND_H */ +#endif /* _ASM_UNWIND_H */ diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h index efbaf41b5..2e767ed59 100644 --- a/include/asm-m68k/pci.h +++ b/include/asm-m68k/pci.h @@ -40,4 +40,9 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + #endif /* _ASM_M68K_PCI_H */ diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 14126235c..77c99d347 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -23,6 +23,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* * Dynamic DMA mapping stuff. * MIPS has everything mapped statically. diff --git a/include/asm-mips64/pci.h b/include/asm-mips64/pci.h index 7769fef70..b530f89a2 100644 --- a/include/asm-mips64/pci.h +++ b/include/asm-mips64/pci.h @@ -23,6 +23,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* * Dynamic DMA mapping stuff. * MIPS has everything mapped statically. diff --git a/include/asm-ppc/byteorder.h b/include/asm-ppc/byteorder.h index c45dba13b..0fdd67844 100644 --- a/include/asm-ppc/byteorder.h +++ b/include/asm-ppc/byteorder.h @@ -38,7 +38,7 @@ extern __inline__ void st_le32(volatile unsigned *addr, const unsigned val) /* alas, egcs sounds like it has a bug in this code that doesn't use the inline asm correctly, and can cause file corruption. Until I hear that it's fixed, I can live without the extra speed. I hope. */ -#if !((__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 90)) +#if !(__GNUC__ >= 2 && __GNUC_MINOR__ >= 90) #if 0 # define __arch_swab16(x) ld_le16(&x) # define __arch_swab32(x) ld_le32(&x) diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h index 479f8ba2c..26fe498dc 100644 --- a/include/asm-ppc/pci.h +++ b/include/asm-ppc/pci.h @@ -15,6 +15,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* Dynamic DMA Mapping stuff * ++ajoshi */ diff --git a/include/asm-sh/pci.h b/include/asm-sh/pci.h index bf8fe93f5..34398480a 100644 --- a/include/asm-sh/pci.h +++ b/include/asm-sh/pci.h @@ -14,6 +14,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* Dynamic DMA mapping stuff. * SuperH has everything mapped statically like x86. */ diff --git a/include/asm-sparc/pci.h b/include/asm-sparc/pci.h index f96225e2a..bbc55d013 100644 --- a/include/asm-sparc/pci.h +++ b/include/asm-sparc/pci.h @@ -17,6 +17,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* Dynamic DMA mapping stuff. */ diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h index 1fd24a47f..9f0c24175 100644 --- a/include/asm-sparc64/pci.h +++ b/include/asm-sparc64/pci.h @@ -17,6 +17,11 @@ extern inline void pcibios_set_master(struct pci_dev *dev) /* No special bus mastering setup handling */ } +extern inline void pcibios_penalize_isa_irq(int irq) +{ + /* We don't do dynamic PCI IRQ allocation */ +} + /* Dynamic DMA mapping stuff. */ diff --git a/include/linux/affs_fs.h b/include/linux/affs_fs.h index 8a09cd72d..60af0d40c 100644 --- a/include/linux/affs_fs.h +++ b/include/linux/affs_fs.h @@ -84,7 +84,7 @@ extern int affs_add_entry(struct inode *dir, struct inode *link, extern void affs_put_inode(struct inode *inode); extern void affs_delete_inode(struct inode *inode); extern void affs_read_inode(struct inode *inode); -extern void affs_write_inode(struct inode *inode, int); +extern void affs_write_inode(struct inode *inode); /* super.c */ diff --git a/include/linux/blk.h b/include/linux/blk.h index 777469870..e8b5e5a85 100644 --- a/include/linux/blk.h +++ b/include/linux/blk.h @@ -14,13 +14,6 @@ extern spinlock_t io_request_lock; /* - * NR_REQUEST is the number of entries in the request-queue. - * NOTE that writes may use only the low 2/3 of these: reads - * take precedence. - */ -#define NR_REQUEST 256 - -/* * Initialization functions. */ extern int isp16_init(void); @@ -94,12 +87,9 @@ void initrd_init(void); extern inline void blkdev_dequeue_request(struct request * req) { - if (req->q) - { - if (req->cmd == READ) - req->q->elevator.read_pendings--; - req->q->elevator.nr_segments -= req->nr_segments; - req->q = NULL; + if (req->e) { + req->e->dequeue_fn(req); + req->e = NULL; } list_del(&req->queue); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 371069369..5b62f88ad 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -9,6 +9,8 @@ struct request_queue; typedef struct request_queue request_queue_t; +struct elevator_s; +typedef struct elevator_s elevator_t; /* * Ok, this is an expanded form so that we can use the same @@ -19,7 +21,11 @@ typedef struct request_queue request_queue_t; struct request { struct list_head queue; int elevator_sequence; + struct list_head table; + /* + * queue free list belongs to + */ volatile int rq_status; /* should split this into a few status bits */ #define RQ_INACTIVE (-1) #define RQ_ACTIVE 1 @@ -41,7 +47,8 @@ struct request { struct semaphore * sem; struct buffer_head * bh; struct buffer_head * bhtail; - request_queue_t * q; + request_queue_t *q; + elevator_t *e; }; #include <linux/elevator.h> @@ -60,11 +67,25 @@ typedef int (make_request_fn) (request_queue_t *q, int rw, struct buffer_head *b typedef void (plug_device_fn) (request_queue_t *q, kdev_t device); typedef void (unplug_device_fn) (void *q); +/* + * Default nr free requests per queue + */ +#define QUEUE_NR_REQUESTS 512 +#define QUEUE_WRITES_MAX ((2 * QUEUE_NR_REQUESTS) / 3) + struct request_queue { - struct list_head queue_head; - /* together with queue_head for cacheline sharing */ - elevator_t elevator; + /* + * the queue request freelist, one for reads and one for writes + */ + struct list_head request_freelist; + int queue_requests; + + /* + * Together with queue_head for cacheline sharing + */ + struct list_head queue_head; + elevator_t elevator; request_fn_proc * request_fn; merge_request_fn * back_merge_fn; @@ -76,22 +97,34 @@ struct request_queue * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. */ - void * queuedata; + void * queuedata; /* * This is used to remove the plug when tq_disk runs. */ - struct tq_struct plug_tq; + struct tq_struct plug_tq; + /* * Boolean that indicates whether this queue is plugged or not. */ - char plugged; + char plugged; /* * Boolean that indicates whether current_request is active or * not. */ - char head_active; + char head_active; + + /* + * Is meant to protect the queue in the future instead of + * io_request_lock + */ + spinlock_t request_lock; + + /* + * Tasks wait here for free request + */ + wait_queue_head_t wait_for_request; }; struct blk_dev_struct { @@ -118,13 +151,13 @@ struct sec_size { extern struct sec_size * blk_sec[MAX_BLKDEV]; extern struct blk_dev_struct blk_dev[MAX_BLKDEV]; -extern wait_queue_head_t wait_for_request; extern void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size); extern void register_disk(struct gendisk *dev, kdev_t first, unsigned minors, struct block_device_operations *ops, long size); -extern void generic_unplug_device(void * data); +extern void generic_unplug_device(void *data); extern int generic_make_request(request_queue_t *q, int rw, struct buffer_head * bh); -extern request_queue_t * blk_get_queue(kdev_t dev); +extern request_queue_t *blk_get_queue(kdev_t dev); +extern void blkdev_release_request(struct request *); /* * Access functions for manipulating queue properties diff --git a/include/linux/devfs_fs_kernel.h b/include/linux/devfs_fs_kernel.h index b35fbe6e1..8173779f3 100644 --- a/include/linux/devfs_fs_kernel.h +++ b/include/linux/devfs_fs_kernel.h @@ -35,6 +35,7 @@ #define DEVFS_FL_REMOVABLE 0x020 /* This is a removable media device */ #define DEVFS_FL_WAIT 0x040 /* Wait for devfsd to finish */ #define DEVFS_FL_NO_PERSISTENCE 0x080 /* Forget changes after unregister */ +#define DEVFS_FL_CURRENT_OWNER 0x100 /* Set initial ownership to current */ #define DEVFS_FL_DEFAULT DEVFS_FL_NONE @@ -52,12 +53,10 @@ typedef struct devfs_entry * devfs_handle_t; #ifdef CONFIG_DEVFS_FS -extern devfs_handle_t devfs_register (devfs_handle_t dir, - const char *name, unsigned int namelen, +extern devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, unsigned int flags, unsigned int major, unsigned int minor, - umode_t mode, uid_t uid, gid_t gid, - void *ops, void *info); + umode_t mode, void *ops, void *info); extern void devfs_unregister (devfs_handle_t de); extern int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int namelen, @@ -98,8 +97,7 @@ extern void devfs_register_series (devfs_handle_t dir, const char *format, unsigned int num_entries, unsigned int flags, unsigned int major, unsigned int minor_start, - umode_t mode, uid_t uid, gid_t gid, - void *ops, void *info); + umode_t mode, void *ops, void *info); extern int init_devfs_fs (void); extern void mount_devfs_fs (void); @@ -107,12 +105,10 @@ extern void devfs_make_root (const char *name); #else /* CONFIG_DEVFS_FS */ static inline devfs_handle_t devfs_register (devfs_handle_t dir, const char *name, - unsigned int namelen, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, - uid_t uid, gid_t gid, void *ops, void *info) { return NULL; @@ -239,8 +235,7 @@ static inline void devfs_register_series (devfs_handle_t dir, unsigned int flags, unsigned int major, unsigned int minor_start, - umode_t mode, uid_t uid, gid_t gid, - void *ops, void *info) + umode_t mode, void *ops, void *info) { return; } diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 988862534..b1f56316b 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -3,13 +3,15 @@ #define ELEVATOR_DEBUG -struct elevator_s; -typedef struct elevator_s elevator_t; - typedef void (elevator_fn) (struct request *, elevator_t *, struct list_head *, struct list_head *, int); +typedef int (elevator_merge_fn) (request_queue_t *, struct request **, + struct buffer_head *, int, int *, int *); + +typedef void (elevator_dequeue_fn) (struct request *); + struct elevator_s { int sequence; @@ -22,24 +24,20 @@ struct elevator_s int read_pendings; elevator_fn * elevator_fn; + elevator_merge_fn *elevator_merge_fn; + elevator_dequeue_fn *dequeue_fn; unsigned int queue_ID; }; -#define ELEVATOR_DEFAULTS \ -((elevator_t) { \ - 0, /* sequence */ \ - \ - 128, /* read_latency */ \ - 8192, /* write_latency */ \ - 32, /* max_bomb_segments */ \ - \ - 0, /* nr_segments */ \ - 0, /* read_pendings */ \ - \ - elevator_default, /* elevator_fn */ \ - }) - +void elevator_default(struct request *, elevator_t *, struct list_head *, struct list_head *, int); +int elevator_default_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); +void elevator_default_dequeue(struct request *); +void elevator_noop(struct request *, elevator_t *, struct list_head *, struct list_head *, int); +int elevator_noop_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); +void elevator_noop_dequeue(struct request *); +void elevator_linus(struct request *, elevator_t *, struct list_head *, struct list_head *, int); +int elevator_linus_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); typedef struct blkelv_ioctl_arg_s { int queue_ID; @@ -54,13 +52,12 @@ typedef struct blkelv_ioctl_arg_s { extern int blkelvget_ioctl(elevator_t *, blkelv_ioctl_arg_t *); extern int blkelvset_ioctl(elevator_t *, const blkelv_ioctl_arg_t *); - -extern void elevator_init(elevator_t *); +extern void elevator_init(elevator_t *, elevator_t); #ifdef ELEVATOR_DEBUG -extern void elevator_debug(request_queue_t *, kdev_t); +extern void elevator_default_debug(request_queue_t *, kdev_t); #else -#define elevator_debug(a,b) do { } while(0) +#define elevator_default_debug(a,b) do { } while(0) #endif #define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0) @@ -69,6 +66,13 @@ extern void elevator_debug(request_queue_t *, kdev_t); #define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a) /* + * Return values from elevator merger + */ +#define ELEVATOR_NO_MERGE 0 +#define ELEVATOR_FRONT_MERGE 1 +#define ELEVATOR_BACK_MERGE 2 + +/* * This is used in the elevator algorithm. We don't prioritise reads * over writes any more --- although reads are more time-critical than * writes, by treating them equally we increase filesystem throughput. @@ -79,12 +83,12 @@ extern void elevator_debug(request_queue_t *, kdev_t); (s1)->sector < (s2)->sector)) || \ (s1)->rq_dev < (s2)->rq_dev) -static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next) +static inline void elevator_merge_requests(struct request * req, struct request * next) { if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence)) req->elevator_sequence = next->elevator_sequence; if (req->cmd == READ) - e->read_pendings--; + req->e->read_pendings--; } @@ -93,23 +97,23 @@ static inline int elevator_sequence(elevator_t * e, int latency) return latency + e->sequence; } -#define elevator_merge_before(q, req, lat) __elevator_merge((q), (req), (lat), 0) -#define elevator_merge_after(q, req, lat) __elevator_merge((q), (req), (lat), 1) -static inline void __elevator_merge(elevator_t * elevator, struct request * req, int latency, int after) +#define elevator_merge_before(req, lat) __elevator_merge((req), (lat), 0) +#define elevator_merge_after(req, lat) __elevator_merge((req), (lat), 1) +static inline void __elevator_merge(struct request * req, int latency, int after) { - int sequence = elevator_sequence(elevator, latency); + int sequence = elevator_sequence(req->e, latency); if (after) sequence -= req->nr_segments; if (elevator_sequence_before(sequence, req->elevator_sequence)) req->elevator_sequence = sequence; } -static inline void elevator_account_request(elevator_t * elevator, struct request * req) +static inline void elevator_account_request(struct request * req) { - elevator->sequence++; + req->e->sequence++; if (req->cmd == READ) - elevator->read_pendings++; - elevator->nr_segments++; + req->e->read_pendings++; + req->e->nr_segments++; } static inline int elevator_request_latency(elevator_t * elevator, int rw) @@ -123,4 +127,52 @@ static inline int elevator_request_latency(elevator_t * elevator, int rw) return latency; } +#define ELEVATOR_DEFAULT \ +((elevator_t) { \ + 0, /* sequence */ \ + \ + 100000, /* read_latency */ \ + 100000, /* write_latency */ \ + 128, /* max_bomb_segments */ \ + \ + 0, /* nr_segments */ \ + 0, /* read_pendings */ \ + \ + elevator_default, /* elevator_fn */ \ + elevator_default_merge, /* elevator_merge_fn */ \ + elevator_default_dequeue, /* dequeue_fn */ \ + }) + +#define ELEVATOR_NOOP \ +((elevator_t) { \ + 0, /* sequence */ \ + \ + 0, /* read_latency */ \ + 0, /* write_latency */ \ + 0, /* max_bomb_segments */ \ + \ + 0, /* nr_segments */ \ + 0, /* read_pendings */ \ + \ + elevator_noop, /* elevator_fn */ \ + elevator_noop_merge, /* elevator_merge_fn */ \ + elevator_noop_dequeue, /* dequeue_fn */ \ + }) + +#define ELEVATOR_LINUS \ +((elevator_t) { \ + 0, /* not used */ \ + \ + 1000000, /* read passovers */ \ + 2000000, /* write passovers */ \ + 0, /* max_bomb_segments */ \ + \ + 0, /* not used */ \ + 0, /* not used */ \ + \ + elevator_linus, /* elevator_fn */ \ + elevator_linus_merge, /* elevator_merge_fn */ \ + elevator_noop_dequeue, /* dequeue_fn */ \ + }) + #endif diff --git a/include/linux/elf.h b/include/linux/elf.h index 6f70727ab..f22e7387e 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -565,6 +565,7 @@ typedef struct elf64_shdr { #define NT_PRFPREG 2 #define NT_PRPSINFO 3 #define NT_TASKSTRUCT 4 +#define NT_PRFPXREG 20 /* Note header in a PT_NOTE section */ typedef struct elf32_note { diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index 6dd1a31b2..3129abe82 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -20,6 +20,7 @@ struct elf_siginfo typedef elf_greg_t greg_t; typedef elf_gregset_t gregset_t; typedef elf_fpregset_t fpregset_t; +typedef elf_fpxregset_t fpxregset_t; #define NGREG ELF_NGREG #endif diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 4bd6a5d72..25178b66b 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -548,9 +548,7 @@ extern int ext2_read (struct inode *, struct file *, char *, int); extern int ext2_write (struct inode *, struct file *, char *, int); /* fsync.c */ -extern int ext2_fsync_file (struct file *, struct dentry *, int); -extern int ext2_fsync_inode (struct inode *, int); -extern int ext2_osync_inode (struct inode *, int); +extern int ext2_sync_file (struct file *, struct dentry *); /* ialloc.c */ extern struct inode * ext2_new_inode (const struct inode *, int, int *); @@ -564,7 +562,7 @@ extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *); extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); extern void ext2_read_inode (struct inode *); -extern void ext2_write_inode (struct inode *, int); +extern void ext2_write_inode (struct inode *); extern void ext2_put_inode (struct inode *); extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); diff --git a/include/linux/fb.h b/include/linux/fb.h index 4147f05ad..896dadef5 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -27,6 +27,8 @@ #define FBIOGET_VBLANK _IOR('F', 0x12, struct fb_vblank) #define FBIO_ALLOC 0x4613 #define FBIO_FREE 0x4614 +#define FBIOGET_GLYPH 0x4615 +#define FBIOGET_HWCINFO 0x4616 #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ #define FB_TYPE_PLANES 1 /* Non interleaved planes */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 3eafb9a55..8a1f8e9b6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -47,12 +47,7 @@ struct poll_table_struct; #define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) /* And dynamically-tunable limits and defaults: */ -struct files_stat_struct { - int nr_files; /* read only */ - int nr_free_files; /* read only */ - int max_files; /* tunable */ -}; -extern struct files_stat_struct files_stat; +extern int max_files, nr_files, nr_free_files; extern int max_super_blocks, nr_super_blocks; #define NR_FILE 8192 /* this can well be larger on a larger system */ @@ -241,9 +236,6 @@ struct buffer_head { unsigned long b_rsector; /* Real buffer location on disk */ wait_queue_head_t b_wait; struct kiobuf * b_kiobuf; /* kiobuf which owns this IO */ - - struct inode * b_inode; - struct list_head b_inode_buffers; /* doubly linked list of inode dirty buffers */ }; typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate); @@ -383,8 +375,6 @@ struct inode { struct list_head i_list; struct list_head i_dentry; - struct list_head i_dirty_buffers; - unsigned long i_ino; atomic_t i_count; kdev_t i_dev; @@ -451,24 +441,16 @@ struct inode { }; /* Inode state bits.. */ -#define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */ -#define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */ -#define I_LOCK 4 -#define I_FREEING 8 -#define I_CLEAR 16 +#define I_DIRTY 1 +#define I_LOCK 2 +#define I_FREEING 4 +#define I_CLEAR 8 -#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC) - -extern void __mark_inode_dirty(struct inode *, int); +extern void __mark_inode_dirty(struct inode *); static inline void mark_inode_dirty(struct inode *inode) { - if ((inode->i_state & I_DIRTY) != I_DIRTY) - __mark_inode_dirty(inode, I_DIRTY); -} -static inline void mark_inode_dirty_sync(struct inode *inode) -{ - if (!(inode->i_state & I_DIRTY_SYNC)) - __mark_inode_dirty(inode, I_DIRTY_SYNC); + if (!(inode->i_state & I_DIRTY)) + __mark_inode_dirty(inode); } struct fown_struct { @@ -522,8 +504,10 @@ typedef struct files_struct *fl_owner_t; struct file_lock { struct file_lock *fl_next; /* singly linked list for this inode */ - struct list_head fl_link; /* doubly linked list of all locks */ - struct list_head fl_block; /* circular list of blocked processes */ + struct file_lock *fl_nextlink; /* doubly linked list of all locks */ + struct file_lock *fl_prevlink; /* used to simplify lock removal */ + struct file_lock *fl_nextblock; /* circular list of blocked processes */ + struct file_lock *fl_prevblock; fl_owner_t fl_owner; unsigned int fl_pid; wait_queue_head_t fl_wait; @@ -548,7 +532,7 @@ struct file_lock { #define OFFSET_MAX INT_LIMIT(loff_t) #endif -extern struct list_head file_lock_list; +extern struct file_lock *file_lock_table; #include <linux/fcntl.h> @@ -737,7 +721,7 @@ struct file_operations { int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); - int (*fsync) (struct file *, struct dentry *, int datasync); + int (*fsync) (struct file *, struct dentry *); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); @@ -770,7 +754,7 @@ struct inode_operations { */ struct super_operations { void (*read_inode) (struct inode *); - void (*write_inode) (struct inode *, int); + void (*write_inode) (struct inode *); void (*put_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); @@ -875,8 +859,8 @@ static inline int locks_verify_truncate(struct inode *inode, return locks_mandatory_area( FLOCK_VERIFY_WRITE, inode, filp, size < inode->i_size ? size : inode->i_size, - size < inode->i_size ? inode->i_size - size - : size - inode->i_size); + abs(inode->i_size - size) + ); return 0; } @@ -997,44 +981,23 @@ static inline void buffer_IO_error(struct buffer_head * bh) bh->b_end_io(bh, 0); } -extern void buffer_insert_inode_queue(struct buffer_head *, struct inode *); -static inline void mark_buffer_dirty_inode(struct buffer_head *bh, int flag, struct inode *inode) -{ - mark_buffer_dirty(bh, flag); - buffer_insert_inode_queue(bh, inode); -} - extern void balance_dirty(kdev_t); extern int check_disk_change(kdev_t); extern int invalidate_inodes(struct super_block *); extern void invalidate_inode_pages(struct inode *); -extern void invalidate_inode_buffers(struct inode *); #define invalidate_buffers(dev) __invalidate_buffers((dev), 0) #define destroy_buffers(dev) __invalidate_buffers((dev), 1) extern void __invalidate_buffers(kdev_t dev, int); extern void sync_inodes(kdev_t); -extern void write_inode_now(struct inode *, int); +extern void write_inode_now(struct inode *); extern void sync_dev(kdev_t); extern int fsync_dev(kdev_t); -extern int fsync_inode_buffers(struct inode *); -extern int osync_inode_buffers(struct inode *); -extern int generic_osync_inode(struct inode *, int); -extern int inode_has_buffers(struct inode *); extern void sync_supers(kdev_t); extern int bmap(struct inode *, int); extern int notify_change(struct dentry *, struct iattr *); extern int permission(struct inode *, int); extern int get_write_access(struct inode *); -extern int deny_write_access(struct file *); -static inline void put_write_access(struct inode * inode) -{ - atomic_dec(&inode->i_writecount); -} -static inline void allow_write_access(struct file *file) -{ - if (file) - atomic_inc(&file->f_dentry->d_inode->i_writecount); -} +extern void put_write_access(struct inode *); extern int do_pipe(int *); extern int open_namei(const char *, int, int, struct nameidata *); @@ -1156,7 +1119,6 @@ typedef int (get_block_t)(struct inode*,long,struct buffer_head*,int); /* Generic buffer handling for block filesystems.. */ extern int block_flushpage(struct page *, unsigned long); -extern void block_destroy_buffers(struct page *); extern int block_symlink(struct inode *, const char *, int); extern int block_write_full_page(struct page*, get_block_t*); extern int block_read_full_page(struct page*, get_block_t*); @@ -1210,7 +1172,7 @@ extern int read_ahead[]; extern ssize_t char_write(struct file *, const char *, size_t, loff_t *); extern ssize_t block_write(struct file *, const char *, size_t, loff_t *); -extern int file_fsync(struct file *, struct dentry *, int); +extern int file_fsync(struct file *, struct dentry *); extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start_idx, unsigned long end_idx); extern int inode_change_ok(struct inode *, struct iattr *); @@ -1224,6 +1186,20 @@ extern void inode_setattr(struct inode *, struct iattr *); */ /* + * We need to do a check-parent every time + * after we have locked the parent - to verify + * that the parent is still our parent and + * that we are still hashed onto it.. + * + * This is required in case two processes race + * on removing (or moving) the same entry: the + * parent lock will serialize them, but the + * other process will be too late.. + */ +#define check_parent(dir, dentry) \ + ((dir) == (dentry)->d_parent && !d_unhashed(dentry)) + +/* * Locking the parent is needed to: * - serialize directory operations * - make sure the parent doesn't change from diff --git a/include/linux/gameport.h b/include/linux/gameport.h new file mode 100644 index 000000000..972dd0bfb --- /dev/null +++ b/include/linux/gameport.h @@ -0,0 +1,142 @@ +#ifndef _GAMEPORT_H +#define _GAMEPORT_H + +/* + * $Id: gameport.h,v 1.8 2000/06/03 20:18:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> + +struct gameport; + +struct gameport { + + void *private; + void *driver; + + int number; + + int io; + int size; + int speed; + int fuzz; + int type; + struct pci_dev *pci; + + void (*trigger)(struct gameport *); + unsigned char (*read)(struct gameport *); + int (*cooked_read)(struct gameport *, int *, int *); + int (*calibrate)(struct gameport *, int *, int *); + int (*open)(struct gameport *, int); + void (*close)(struct gameport *); + + struct gameport_dev *dev; + + struct gameport *next; +}; + +struct gameport_dev { + + void *private; + + void (*connect)(struct gameport *, struct gameport_dev *dev); + void (*disconnect)(struct gameport *); + + struct gameport_dev *next; +}; + +int gameport_open(struct gameport *gameport, struct gameport_dev *dev, int mode); +void gameport_close(struct gameport *gameport); +void gameport_rescan(struct gameport *gameport); + +void gameport_register_port(struct gameport *gameport); +void gameport_unregister_port(struct gameport *gameport); +void gameport_register_device(struct gameport_dev *dev); +void gameport_unregister_device(struct gameport_dev *dev); + +#define GAMEPORT_MODE_DISABLED 0 +#define GAMEPORT_MODE_RAW 1 +#define GAMEPORT_MODE_COOKED 2 + +#define GAMEPORT_ISA 0 +#define GAMEPORT_PNP 1 +#define GAMEPORT_EXT 2 + +#define GAMEPORT_ID_VENDOR_ANALOG 0x0001 +#define GAMEPORT_ID_VENDOR_MADCATZ 0x0002 +#define GAMEPORT_ID_VENDOR_LOGITECH 0x0003 +#define GAMEPORT_ID_VENDOR_CREATIVE 0x0004 +#define GAMEPORT_ID_VENDOR_GENIUS 0x0005 +#define GAMEPORT_ID_VENDOR_INTERACT 0x0006 +#define GAMEPORT_ID_VENDOR_MICROSOFT 0x0007 +#define GAMEPORT_ID_VENDOR_THRUSTMASTER 0x0008 +#define GAMEPORT_ID_VENDOR_GRAVIS 0x0009 + +static __inline__ void gameport_trigger(struct gameport *gameport) +{ + if (gameport->trigger) + gameport->trigger(gameport); + else + outb(0xff, gameport->io); +} + +static __inline__ unsigned char gameport_read(struct gameport *gameport) +{ + if (gameport->read) + return gameport->read(gameport); + else + return inb(gameport->io); +} + +static __inline__ int gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + if (gameport->cooked_read) + return gameport->cooked_read(gameport, axes, buttons); + else + return -1; +} + +static __inline__ int gameport_calibrate(struct gameport *gameport, int *axes, int *max) +{ + if (gameport->calibrate) + return gameport->calibrate(gameport, axes, max); + else + return -1; +} + +static __inline__ int gameport_time(struct gameport *gameport, int time) +{ + return (time * gameport->speed) / 1000; +} + +static __inline__ void wait_ms(unsigned int ms) +{ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); +} + +#endif diff --git a/include/linux/input.h b/include/linux/input.h index 6d19fc2a9..a44e6f8e1 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -2,7 +2,7 @@ #define _INPUT_H /* - * $Id: input.h,v 1.13 2000/05/29 10:54:53 vojtech Exp $ + * $Id: input.h,v 1.14 2000/06/03 20:18:52 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -359,8 +359,9 @@ struct input_event { #define ABS_RZ 0x05 #define ABS_THROTTLE 0x06 #define ABS_RUDDER 0x07 -#define ABS_TL 0x08 -#define ABS_TR 0x09 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a #define ABS_HAT0X 0x10 #define ABS_HAT0Y 0x11 #define ABS_HAT1X 0x12 diff --git a/include/linux/irq.h b/include/linux/irq.h index 69040207e..de2476503 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -18,6 +18,7 @@ #define IRQ_WAITING 32 /* IRQ not yet seen - for autodetection */ #define IRQ_LEVEL 64 /* IRQ level triggered */ #define IRQ_MASKED 128 /* IRQ masked - shouldn't be seen again */ +#define IRQ_PER_CPU 256 /* IRQ is per CPU */ /* * Interrupt controller descriptor. This is all we need diff --git a/include/linux/joystick.h b/include/linux/joystick.h index c0e148171..1836aa95a 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -2,11 +2,11 @@ #define _LINUX_JOYSTICK_H /* - * /usr/include/linux/joystick.h Version 1.2 + * $Id: joystick.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $ * - * Copyright (C) 1996-1999 Vojtech Pavlik + * Copyright (C) 1996-2000 Vojtech Pavlik * - * Sponsored by SuSE + * Sponsored by SuSE */ /* @@ -30,13 +30,12 @@ */ #include <asm/types.h> -#include <linux/module.h> /* * Version */ -#define JS_VERSION 0x01020f +#define JS_VERSION 0x020000 /* * Types and constants for reading from /dev/js @@ -120,164 +119,4 @@ struct JS_DATA_SAVE_TYPE { struct JS_DATA_TYPE JS_CORR; }; -/* - * Internal definitions - */ - -#ifdef __KERNEL__ - -#define JS_BUFF_SIZE 64 /* output buffer size */ - -#include <linux/version.h> -#include <linux/devfs_fs_kernel.h> - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) -#error "You need to use at least v2.2 Linux kernel." -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#include <asm/spinlock.h> -typedef struct wait_queue *wait_queue_head_t; -#define __setup(a,b) -#define BASE_ADDRESS(x,i) ((x)->base_address[i]) -#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = { y, NULL } -#define init_waitqueue_head(x) do { *(x) = NULL; } while (0) -#define __set_current_state(x) current->state = x -#define SETUP_PARAM char *str, int *ints -#define SETUP_PARSE(x) do {} while (0) -#else -#include <linux/spinlock.h> -#define BASE_ADDRESS(x,i) ((x)->resource[i].start) -#define SETUP_PARAM char *str -#define SETUP_PARSE(x) int ints[x]; get_options(str, x, ints) -#endif - -#define PCI_VENDOR_ID_AUREAL 0x12eb - -/* - * Parport stuff - */ - -#include <linux/parport.h> - -#define JS_PAR_STATUS_INVERT (0x80) -#define JS_PAR_CTRL_INVERT (0x04) -#define JS_PAR_DATA_IN(y) parport_read_data(y->port) -#define JS_PAR_DATA_OUT(x,y) parport_write_data(y->port, x) -#define JS_PAR_STATUS(y) parport_read_status(y->port) - -#ifndef PARPORT_NEED_GENERIC_OPS -#define JS_PAR_CTRL_IN(y) parport_read_control(y->port) -#else -#define JS_PAR_CTRL_IN(y) inb(y->port->base+2) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#define JS_PAR_CTRL_OUT(x,y) parport_write_control(y->port, x) -#define JS_PAR_ECTRL_OUT(x,y) parport_write_econtrol(y->port, x) -#else -#define JS_PAR_CTRL_OUT(x,y) \ - do { \ - if ((x) & 0x20) parport_data_reverse(y->port); \ - else parport_data_forward(y->port); \ - parport_write_control(y->port, (x) & ~0x20); \ - } while (0) -#define JS_PAR_ECTRL_OUT(x,y) /*parport sets PS/2 mode on ECR chips */ -#define PARPORT_MODE_PCPS2 PARPORT_MODE_TRISTATE -#define PARPORT_MODE_PCECPPS2 PARPORT_MODE_TRISTATE -#endif - -/* - * Internal types - */ - -struct js_dev; - -typedef int (*js_read_func)(void *info, int **axes, int **buttons); -typedef int (*js_ops_func)(struct js_dev *dev); - -struct js_data { - int *axes; - int *buttons; -}; - -struct js_dev { - struct js_dev *next; - struct js_list *list; - struct js_port *port; - wait_queue_head_t wait; - struct js_data cur; - struct js_data new; - struct js_corr *corr; - struct js_event buff[JS_BUFF_SIZE]; - js_ops_func open; - js_ops_func close; - int ahead; - int bhead; - int tail; - int num_axes; - int num_buttons; - char *name; - devfs_handle_t devfs_handle; - struct module *owner; -}; - -struct js_list { - struct js_list *next; - struct js_dev *dev; - int tail; - int startup; -}; - -struct js_port { - struct js_port *next; - struct js_port *prev; - js_read_func read; - struct js_dev **devs; - int **axes; - int **buttons; - struct js_corr **corr; - void *info; - int ndevs; - int fail; - int total; -}; - -/* - * Sub-module interface - */ - -extern struct js_port *js_register_port(struct js_port *port, void *info, - int devs, int infos, js_read_func read); -extern struct js_port *js_unregister_port(struct js_port *port); - -extern int js_register_device(struct js_port *port, int number, int axes, - int buttons, char *name, struct module *owner, js_ops_func open, js_ops_func close); -extern void js_unregister_device(struct js_dev *dev); - -/* - * Kernel interface - */ - -extern int js_init(void); -extern int js_am_init(void); -extern int js_an_init(void); -extern int js_as_init(void); -extern int js_console_init(void); -extern int js_cr_init(void); -extern int js_db9_init(void); -extern int js_gr_init(void); -extern int js_l4_init(void); -extern int js_lt_init(void); -extern int js_mag_init(void); -extern int js_pci_init(void); -extern int js_sw_init(void); -extern int js_sball_init(void); -extern int js_orb_init(void); -extern int js_tm_init(void); -extern int js_tg_init(void); -extern int js_war_init(void); - -#endif /* __KERNEL__ */ - #endif /* _LINUX_JOYSTICK_H */ diff --git a/include/linux/linkage.h b/include/linux/linkage.h index d1f1c2861..aaf6edf02 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -9,7 +9,7 @@ #define CPP_ASMLINKAGE #endif -#if defined __i386__ && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ > 7) +#if defined __i386__ && (__GNUC__ > 2 || __GNUC_MINOR__ > 7) #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) #elif defined __ia64__ #define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage)) diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h index 99cd63717..1eca767b4 100644 --- a/include/linux/minix_fs.h +++ b/include/linux/minix_fs.h @@ -101,7 +101,7 @@ extern struct buffer_head * minix_bread(struct inode *, int, int); extern void minix_truncate(struct inode *); extern int minix_sync_inode(struct inode *); -extern int minix_sync_file(struct file *, struct dentry *, int); +extern int minix_sync_file(struct file *, struct dentry *); extern struct address_space_operations minix_aops; extern struct inode_operations minix_file_inode_operations; diff --git a/include/linux/mm.h b/include/linux/mm.h index 5875fa8bf..37cb9664e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -153,7 +153,6 @@ typedef struct page { struct buffer_head * buffers; unsigned long virtual; /* nonzero if kmapped */ struct zone_struct *zone; - unsigned int age; } mem_map_t; #define get_page(p) atomic_inc(&(p)->count) @@ -170,7 +169,7 @@ typedef struct page { #define PG_dirty 4 #define PG_decr_after 5 #define PG_unused_01 6 -#define PG_active 7 +#define PG__unused_02 7 #define PG_slab 8 #define PG_swap_cache 9 #define PG_skip 10 @@ -186,7 +185,6 @@ typedef struct page { #define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags) #define PageDirty(page) test_bit(PG_dirty, &(page)->flags) #define SetPageDirty(page) set_bit(PG_dirty, &(page)->flags) -#define ClearPageDirty(page) clear_bit(PG_dirty, &(page)->flags) #define PageLocked(page) test_bit(PG_locked, &(page)->flags) #define LockPage(page) set_bit(PG_locked, &(page)->flags) #define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags) @@ -194,15 +192,11 @@ typedef struct page { clear_bit(PG_locked, &(page)->flags); \ wake_up(&page->wait); \ } while (0) -#define PageActive(page) test_bit(PG_active, &(page)->flags) -#define SetPageActive(page) set_bit(PG_active, &(page)->flags) -#define ClearPageActive(page) clear_bit(PG_active, &(page)->flags) #define PageError(page) test_bit(PG_error, &(page)->flags) #define SetPageError(page) set_bit(PG_error, &(page)->flags) #define ClearPageError(page) clear_bit(PG_error, &(page)->flags) #define PageReferenced(page) test_bit(PG_referenced, &(page)->flags) #define SetPageReferenced(page) set_bit(PG_referenced, &(page)->flags) -#define ClearPageReferenced(page) clear_bit(PG_referenced, &(page)->flags) #define PageTestandClearReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags) #define PageDecrAfter(page) test_bit(PG_decr_after, &(page)->flags) #define SetPageDecrAfter(page) set_bit(PG_decr_after, &(page)->flags) @@ -463,7 +457,6 @@ extern void remove_inode_page(struct page *); extern unsigned long page_unuse(struct page *); extern int shrink_mmap(int, int); extern void truncate_inode_pages(struct address_space *, loff_t); -extern void truncate_all_inode_pages(struct address_space *); /* generic vm_area_ops exported for stackable file systems */ extern int filemap_swapout(struct page * page, struct file *file); diff --git a/include/linux/mount.h b/include/linux/mount.h index adb571de2..61ab19b1f 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -26,6 +26,7 @@ struct vfsmount atomic_t mnt_count; char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ + char *mnt_dirname; /* Name of directory mounted on */ struct list_head mnt_list; uid_t mnt_owner; }; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index cbbf78528..4897f2ec9 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -248,7 +248,7 @@ extern struct inode *fat_build_inode(struct super_block*,struct msdos_dir_entry* extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent, struct inode_operations *dir_ops); extern void msdos_put_super(struct super_block *sb); extern int fat_statfs(struct super_block *sb,struct statfs *buf); -extern void fat_write_inode(struct inode *inode, int); +extern void fat_write_inode(struct inode *inode); /* dir.c */ extern struct file_operations fat_dir_operations; diff --git a/include/linux/ncp_fs_i.h b/include/linux/ncp_fs_i.h index ffdf49f4c..96728bcdd 100644 --- a/include/linux/ncp_fs_i.h +++ b/include/linux/ncp_fs_i.h @@ -19,8 +19,7 @@ struct ncp_inode_info { __u32 DosDirNum __attribute__((packed)); __u32 volNumber __attribute__((packed)); __u32 nwattr; - struct semaphore open_sem; - atomic_t opened; + int opened; int access; __u32 server_file_handle __attribute__((packed)); __u8 open_create_action __attribute__((packed)); diff --git a/include/linux/nfsd/syscall.h b/include/linux/nfsd/syscall.h index 4f20ad01f..e98c9fa8a 100644 --- a/include/linux/nfsd/syscall.h +++ b/include/linux/nfsd/syscall.h @@ -133,7 +133,7 @@ union nfsctl_res { * Kernel syscall implementation. */ #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) -extern asmlinkage int sys_nfsservctl(int, void *, void *); +extern asmlinkage long sys_nfsservctl(int, void *, void *); #else #define sys_nfsservctl sys_ni_syscall #endif diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 42a85261f..5512d2a51 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -330,11 +330,12 @@ #define PCI_DEVICE_ID_SI_6205 0x0205 #define PCI_DEVICE_ID_SI_501 0x0406 #define PCI_DEVICE_ID_SI_496 0x0496 +#define PCI_DEVICE_ID_SI_300 0x0300 #define PCI_DEVICE_ID_SI_530 0x0530 -#define PCI_DEVICE_ID_SI_540 0x0540 +#define PCI_DEVICE_ID_SI_540 0x5300 #define PCI_DEVICE_ID_SI_601 0x0601 #define PCI_DEVICE_ID_SI_620 0x0620 -#define PCI_DEVICE_ID_SI_630 0x0630 +#define PCI_DEVICE_ID_SI_630 0x6300 #define PCI_DEVICE_ID_SI_5107 0x5107 #define PCI_DEVICE_ID_SI_5511 0x5511 #define PCI_DEVICE_ID_SI_5513 0x5513 @@ -1201,7 +1202,6 @@ #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 #define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 #define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 -#define PCI_DEVICE_ID_INTEL_82440MX_1 0x7194 #define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 #define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 #define PCI_DEVICE_ID_INTEL_82443MX_2 0x719a @@ -1286,8 +1286,3 @@ #define PCI_DEVICE_ID_ARK_STING 0xa091 #define PCI_DEVICE_ID_ARK_STINGARK 0xa099 #define PCI_DEVICE_ID_ARK_2000MT 0xa0a1 - -#define PCI_VENDOR_ID_SIS 0x1039 -#define PCI_DEVICE_ID_SIS_300 0x0300 -#define PCI_DEVICE_ID_SIS_540 0x5300 -#define PCI_DEVICE_ID_SIS_630 0x6300 diff --git a/include/linux/poll.h b/include/linux/poll.h index b83e62d74..b56cdcf4c 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -20,7 +20,6 @@ struct poll_table_entry { typedef struct poll_table_struct { struct poll_table_struct * next; unsigned int nr; - int err; struct poll_table_entry * entry; } poll_table; diff --git a/include/linux/qnx4_fs.h b/include/linux/qnx4_fs.h index dd9b7cb6e..ad5ca5543 100644 --- a/include/linux/qnx4_fs.h +++ b/include/linux/qnx4_fs.h @@ -116,7 +116,7 @@ extern void qnx4_truncate(struct inode *inode); extern void qnx4_free_inode(struct inode *inode); extern int qnx4_unlink(struct inode *dir, struct dentry *dentry); extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry); -extern int qnx4_sync_file(struct file *file, struct dentry *dentry, int); +extern int qnx4_sync_file(struct file *file, struct dentry *dentry); extern int qnx4_sync_inode(struct inode *inode); extern int qnx4_get_block(struct inode *inode, long iblock, struct buffer_head *bh, int create); diff --git a/include/linux/serio.h b/include/linux/serio.h new file mode 100644 index 000000000..9cb8bb525 --- /dev/null +++ b/include/linux/serio.h @@ -0,0 +1,110 @@ +#ifndef _SERIO_H +#define _SERIO_H + +/* + * $Id: serio.h,v 1.7 2000/06/01 11:39:46 vojtech Exp $ + * + * Copyright (C) 1999 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +/* + * The serial port set type ioctl. + */ + +#include <linux/ioctl.h> +#define SPIOCSTYPE _IOW('q', 0x01, unsigned long) + +struct serio; + +struct serio { + + void *private; + void *driver; + + unsigned long type; + int number; + + int (*write)(struct serio *, unsigned char); + int (*open)(struct serio *); + void (*close)(struct serio *); + + struct serio_dev *dev; + + struct serio *next; +}; + +struct serio_dev { + + void *private; + + void (*interrupt)(struct serio *, unsigned char, unsigned int); + void (*connect)(struct serio *, struct serio_dev *dev); + void (*disconnect)(struct serio *); + + struct serio_dev *next; +}; + +int serio_open(struct serio *serio, struct serio_dev *dev); +void serio_close(struct serio *serio); +void serio_rescan(struct serio *serio); + +void serio_register_port(struct serio *serio); +void serio_unregister_port(struct serio *serio); +void serio_register_device(struct serio_dev *dev); +void serio_unregister_device(struct serio_dev *dev); + +static __inline__ int serio_write(struct serio *serio, unsigned char data) +{ + return serio->write(serio, data); +} + +#define SERIO_TIMEOUT 1 +#define SERIO_PARITY 2 + +#define SERIO_TYPE 0xff000000UL +#define SERIO_XT 0x00000000UL +#define SERIO_8042 0x01000000UL +#define SERIO_RS232 0x02000000UL + +#define SERIO_PROTO 0xFFUL +#define SERIO_MSC 0x01 +#define SERIO_SUN 0x02 +#define SERIO_MS 0x03 +#define SERIO_MP 0x04 +#define SERIO_MZ 0x05 +#define SERIO_MZP 0x06 +#define SERIO_MZPP 0x07 +#define SERIO_SUNKBD 0x10 +#define SERIO_WARRIOR 0x18 +#define SERIO_SPACEORB 0x19 +#define SERIO_MAGELLAN 0x1a +#define SERIO_SPACEBALL 0x1b +#define SERIO_GUNZE 0x1c +#define SERIO_IFORCE 0x1d + +#define SERIO_ID 0xff00UL +#define SERIO_EXTRA 0xff0000UL + +#endif diff --git a/include/linux/swap.h b/include/linux/swap.h index 5ee3b7b77..9226ce0a5 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -161,16 +161,6 @@ static inline int is_page_shared(struct page *page) extern spinlock_t pagemap_lru_lock; /* - * Magic constants for page aging. If the system is programmed - * right, tweaking these should have almost no effect... - * The 2.4 code, however, is mostly simple and stable ;) - */ -#define PG_AGE_MAX 64 -#define PG_AGE_START 2 -#define PG_AGE_ADV 3 -#define PG_AGE_DECL 1 - -/* * Helper macros for lru_pages handling. */ #define lru_cache_add(page) \ @@ -178,16 +168,12 @@ do { \ spin_lock(&pagemap_lru_lock); \ list_add(&(page)->lru, &lru_cache); \ nr_lru_pages++; \ - page->age = PG_AGE_START; \ - ClearPageReferenced(page); \ - SetPageActive(page); \ spin_unlock(&pagemap_lru_lock); \ } while (0) #define __lru_cache_del(page) \ do { \ list_del(&(page)->lru); \ - ClearPageActive(page); \ nr_lru_pages--; \ } while (0) diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index 4cff4260c..d9c2557e5 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -377,9 +377,9 @@ extern unsigned long sysv_count_free_blocks(struct super_block *sb); extern struct buffer_head * sysv_file_bread(struct inode *, int, int); extern void sysv_truncate(struct inode *); -extern void sysv_write_inode(struct inode *, int); +extern void sysv_write_inode(struct inode *); extern int sysv_sync_inode(struct inode *); -extern int sysv_sync_file(struct file *, struct dentry *, int); +extern int sysv_sync_file(struct file *, struct dentry *); extern int sysv_notify_change(struct dentry *, struct iattr *); extern struct inode_operations sysv_file_inode_operations; diff --git a/include/linux/timer.h b/include/linux/timer.h index 45887372a..2de8050ea 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -1,8 +1,6 @@ #ifndef _LINUX_TIMER_H #define _LINUX_TIMER_H -#ifdef __KERNEL__ - #include <linux/config.h> #include <linux/list.h> @@ -93,11 +91,9 @@ extern int del_timer_sync(struct timer_list * timer); #define timer_set_running(t) (void)(t) #define timer_is_running(t) (0) #define timer_synchronize(t) do { (void)(t); barrier(); } while(0) -#define del_timer_sync del_timer +#define del_timer_sync(t) del_timer(t) #endif -#define del_timer_async del_timer - /* * These inlines deal with timer wrapping correctly. You are * strongly encouraged to use them @@ -116,4 +112,3 @@ extern int del_timer_sync(struct timer_list * timer); #define time_before_eq(a,b) time_after_eq(b,a) #endif -#endif diff --git a/include/linux/types.h b/include/linux/types.h index df4808fcd..196c5f4e0 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -1,10 +1,7 @@ #ifndef _LINUX_TYPES_H #define _LINUX_TYPES_H -#ifdef __KERNEL__ #include <linux/config.h> -#endif - #include <linux/posix_types.h> #include <asm/types.h> diff --git a/include/linux/ufs_fs.h b/include/linux/ufs_fs.h index 3c8f1d415..96cb38e67 100644 --- a/include/linux/ufs_fs.h +++ b/include/linux/ufs_fs.h @@ -560,8 +560,9 @@ extern struct inode * ufs_new_inode (const struct inode *, int, int *); extern int ufs_frag_map (struct inode *, int); extern void ufs_read_inode (struct inode *); extern void ufs_put_inode (struct inode *); -extern void ufs_write_inode (struct inode *, int); +extern void ufs_write_inode (struct inode *); extern int ufs_sync_inode (struct inode *); +extern void ufs_write_inode (struct inode *); extern void ufs_delete_inode (struct inode *); extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index 677bee22e..76436a479 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -48,7 +48,7 @@ int umsdos_isempty (struct dentry *); /* inode.c 12/06/95 09.49.40 */ void fill_new_filp (struct file *filp, struct dentry *dentry); void UMSDOS_read_inode (struct inode *); -void UMSDOS_write_inode (struct inode *, int); +void UMSDOS_write_inode (struct inode *); int UMSDOS_notify_change (struct dentry *, struct iattr *attr); int umsdos_notify_change_locked(struct dentry *, struct iattr *attr); void UMSDOS_put_inode (struct inode *); diff --git a/include/net/dn.h b/include/net/dn.h index c1d4854de..52c6ba44d 100644 --- a/include/net/dn.h +++ b/include/net/dn.h @@ -6,8 +6,8 @@ typedef unsigned short dn_address; -#define dn_ntohs(x) le16_to_cpu(x) -#define dn_htons(x) cpu_to_le16(x) +#define dn_ntohs(x) le16_to_cpu((unsigned short)(x)) +#define dn_htons(x) cpu_to_le16((unsigned short)(x)) struct dn_scp /* Session Control Port */ { diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index d816d4fac..2ea636834 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -64,7 +64,9 @@ struct ifmcaddr6 struct ifmcaddr6 *next; struct timer_list mca_timer; unsigned mca_flags; - atomic_t mca_users; + int mca_users; + atomic_t mca_refcnt; + spinlock_t mca_lock; }; #define IFA_HOST IPV6_ADDR_LOOPBACK diff --git a/init/main.c b/init/main.c index c9fb91dd5..e8d8f0d99 100644 --- a/init/main.c +++ b/init/main.c @@ -98,7 +98,6 @@ extern void signals_init(void); extern void bdev_init(void); extern int init_pcmcia_ds(void); extern int usb_init(void); -extern void filelock_init(void); extern void free_initmem(void); extern void filesystem_setup(void); @@ -243,6 +242,25 @@ static struct dev_name_struct { { "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) }, { "dasdh", (DASD_MAJOR << MINORBITS) + (7 << 2) }, #endif +#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE) + { "ida/c0d0p",0x4800 }, + { "ida/c0d1p",0x4810 }, + { "ida/c0d2p",0x4820 }, + { "ida/c0d3p",0x4830 }, + { "ida/c0d4p",0x4840 }, + { "ida/c0d5p",0x4850 }, + { "ida/c0d6p",0x4860 }, + { "ida/c0d7p",0x4870 }, + { "ida/c0d8p",0x4880 }, + { "ida/c0d9p",0x4890 }, + { "ida/c0d10p",0x48A0 }, + { "ida/c0d11p",0x48B0 }, + { "ida/c0d12p",0x48C0 }, + { "ida/c0d13p",0x48D0 }, + { "ida/c0d14p",0x48E0 }, + { "ida/c0d15p",0x48F0 }, +#endif + { NULL, 0 } }; @@ -563,7 +581,6 @@ asmlinkage void __init start_kernel(void) bdev_init(); inode_init(mempages); file_table_init(); - filelock_init(); #if defined(CONFIG_SYSVIPC) ipc_init(); #endif @@ -52,7 +52,7 @@ static struct super_block *shm_read_super(struct super_block *,void *, int); static void shm_put_super (struct super_block *); static int shm_remount_fs (struct super_block *, int *, char *); static void shm_read_inode (struct inode *); -static void shm_write_inode(struct inode *, int); +static void shm_write_inode(struct inode *); static int shm_statfs (struct super_block *, struct statfs *); static int shm_create (struct inode *,struct dentry *,int); static struct dentry *shm_lookup (struct inode *,struct dentry *); @@ -371,7 +371,7 @@ static int shm_statfs(struct super_block *sb, struct statfs *buf) return 0; } -static void shm_write_inode(struct inode * inode, int unused) +static void shm_write_inode(struct inode * inode) { } diff --git a/ipc/util.c b/ipc/util.c index a72dc14e6..66eae7215 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -312,6 +312,8 @@ void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out) out->seq = in->seq; } +#ifndef __ia64__ + /** * ipc_parse_version - IPC call version * @cmd: pointer to command @@ -331,6 +333,8 @@ int ipc_parse_version (int *cmd) } } +#endif /* __ia64__ */ + #else /* * Dummy functions when SYSV IPC isn't configured @@ -408,7 +412,7 @@ void shm_unuse(swp_entry_t entry, struct page *page) int map_zero_setup(struct vm_area_struct *vma) { - return -ENOSYS; + return -EINVAL; } #endif /* CONFIG_SYSVIPC */ diff --git a/ipc/util.h b/ipc/util.h index cde68bcad..ff4bfb0fc 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -99,4 +99,9 @@ extern inline int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, i void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); +#ifdef __ia64__ + /* On IA-64, we always use the "64-bit version" of the IPC structures. */ +# define ipc_parse_version(cmd) IPC_64 +#else int ipc_parse_version (int *cmd); +#endif diff --git a/kernel/dma.c b/kernel/dma.c index 73b51a72b..983dedb60 100644 --- a/kernel/dma.c +++ b/kernel/dma.c @@ -115,8 +115,9 @@ int request_dma(unsigned int dmanr, const char *device_id) return -EINVAL; } -void free_dma(unsigned int dmanr) +int free_dma(unsigned int dmanr) { + return -EINVAL; } int get_dma_list(char *buf) diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index 1daf64cc1..3f3b5fc16 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -104,37 +104,32 @@ int unregister_exec_domain(struct exec_domain *it) void __set_personality(unsigned long personality) { - struct exec_domain *it, *prev; + struct exec_domain *it; it = lookup_exec_domain(personality); - if (it == current->exec_domain) { - current->personality = personality; - return; - } - if (!it) - return; - if (atomic_read(¤t->fs->count) != 1) { - struct fs_struct *new = copy_fs_struct(current->fs); - struct fs_struct *old; - if (!new) { - put_exec_domain(it); - return; + if (it) { + if (atomic_read(¤t->fs->count) != 1) { + struct fs_struct *new = copy_fs_struct(current->fs); + struct fs_struct *old; + if (!new) { + put_exec_domain(it); + return; + } + task_lock(current); + old = current->fs; + current->fs = new; + task_unlock(current); + put_fs_struct(old); } - task_lock(current); - old = current->fs; - current->fs = new; - task_unlock(current); - put_fs_struct(old); + /* + * At that point we are guaranteed to be the sole owner of + * current->fs. + */ + current->personality = personality; + current->exec_domain = it; + set_fs_altroot(); + put_exec_domain(current->exec_domain); } - /* - * At that point we are guaranteed to be the sole owner of - * current->fs. - */ - current->personality = personality; - prev = current->exec_domain; - current->exec_domain = it; - set_fs_altroot(); - put_exec_domain(prev); } asmlinkage long sys_personality(unsigned long personality) diff --git a/kernel/exit.c b/kernel/exit.c index 3bd501cf1..e912b75bc 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -514,7 +514,7 @@ repeat: case TASK_STOPPED: if (!p->exit_code) continue; - if (!(options & WUNTRACED) && !(p->ptrace&PT_PTRACED)) + if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED)) continue; read_unlock(&tasklist_lock); retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; diff --git a/kernel/fork.c b/kernel/fork.c index 07bc503ac..b441988aa 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -601,9 +601,8 @@ static inline void copy_flags(unsigned long clone_flags, struct task_struct *p) new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU | PF_VFORK); new_flags |= PF_FORKNOEXEC; - if (!(clone_flags & CLONE_PTRACE)) { + if (!(clone_flags & CLONE_PTRACE)) p->ptrace = 0; - } if (clone_flags & CLONE_VFORK) new_flags |= PF_VFORK; p->flags = new_flags; @@ -675,7 +674,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) { p->p_opptr = current; - if (!(p->ptrace&PT_PTRACED)) + if (!(p->ptrace & PT_PTRACED)) p->p_pptr = current; } p->p_cptr = NULL; diff --git a/kernel/ksyms.c b/kernel/ksyms.c index a885a0c7f..f1032de30 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -172,8 +172,6 @@ EXPORT_SYMBOL(__invalidate_buffers); EXPORT_SYMBOL(invalidate_inodes); EXPORT_SYMBOL(invalidate_inode_pages); EXPORT_SYMBOL(truncate_inode_pages); -EXPORT_SYMBOL(fsync_inode_buffers); -EXPORT_SYMBOL(buffer_insert_inode_queue); EXPORT_SYMBOL(fsync_dev); EXPORT_SYMBOL(permission); EXPORT_SYMBOL(inode_setattr); @@ -207,7 +205,7 @@ EXPORT_SYMBOL(generic_ro_fops); EXPORT_SYMBOL(generic_buffer_fdatasync); EXPORT_SYMBOL(page_hash_bits); EXPORT_SYMBOL(page_hash_table); -EXPORT_SYMBOL(file_lock_list); +EXPORT_SYMBOL(file_lock_table); EXPORT_SYMBOL(posix_lock_file); EXPORT_SYMBOL(posix_test_lock); EXPORT_SYMBOL(posix_block_lock); @@ -273,7 +271,6 @@ EXPORT_SYMBOL(tty_std_termios); /* block device driver support */ EXPORT_SYMBOL(block_read); EXPORT_SYMBOL(block_write); -EXPORT_SYMBOL(wait_for_request); EXPORT_SYMBOL(blksize_size); EXPORT_SYMBOL(hardsect_size); EXPORT_SYMBOL(blk_size); @@ -296,8 +293,6 @@ EXPORT_SYMBOL(refile_buffer); EXPORT_SYMBOL(max_sectors); EXPORT_SYMBOL(max_readahead); EXPORT_SYMBOL(file_moveto); -EXPORT_SYMBOL(drive_stat_acct); -EXPORT_SYMBOL(set_bh_page); /* tty routines */ EXPORT_SYMBOL(tty_hangup); @@ -507,6 +502,7 @@ EXPORT_SYMBOL(disk_name); /* for md.c */ /* binfmt_aout */ EXPORT_SYMBOL(get_write_access); +EXPORT_SYMBOL(put_write_access); /* dynamic registering of consoles */ EXPORT_SYMBOL(register_console); diff --git a/kernel/sched.c b/kernel/sched.c index b8d389ef8..f85cc4213 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -60,8 +60,8 @@ struct task_struct * init_tasks[NR_CPUS] = {&init_task, }; * The run-queue lock locks the parts that actually access * and change the run-queues, and have to be interrupt-safe. */ -spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED; /* second */ -rwlock_t tasklist_lock = RW_LOCK_UNLOCKED; /* third */ +__cacheline_aligned spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED; /* second */ +__cacheline_aligned rwlock_t tasklist_lock = RW_LOCK_UNLOCKED; /* third */ static LIST_HEAD(runqueue_head); @@ -337,23 +337,16 @@ out: spin_unlock_irqrestore(&runqueue_lock, flags); } -struct foo_struct { - struct task_struct *process; - struct timer_list timer; -}; - static void process_timeout(unsigned long __data) { - struct foo_struct * foo = (struct foo_struct *) __data; - - wake_up_process(foo->process); + struct task_struct * p = (struct task_struct *) __data; - timer_exit(&foo->timer); + wake_up_process(p); } signed long schedule_timeout(signed long timeout) { - struct foo_struct foo; + struct timer_list timer; unsigned long expire; switch (timeout) @@ -388,16 +381,14 @@ signed long schedule_timeout(signed long timeout) expire = timeout + jiffies; - init_timer(&foo.timer); - foo.timer.expires = expire; - foo.timer.data = (unsigned long) &foo; - foo.timer.function = process_timeout; - - foo.process = current; + init_timer(&timer); + timer.expires = expire; + timer.data = (unsigned long) current; + timer.function = process_timeout; - add_timer(&foo.timer); + add_timer(&timer); schedule(); - del_timer_sync(&foo.timer); + del_timer(&timer); /* RED-PEN. Timer may be running now on another cpu. * Pray that process will not exit enough fastly. */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ab62787d1..5fc0418f9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -255,9 +255,9 @@ static ctl_table fs_table[] = { 0444, NULL, &proc_dointvec}, {FS_STATINODE, "inode-state", &inodes_stat, 7*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_NRFILE, "file-nr", &files_stat, 3*sizeof(int), + {FS_NRFILE, "file-nr", &nr_files, 3*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int), + {FS_MAXFILE, "file-max", &max_files, sizeof(int), 0644, NULL, &proc_dointvec}, {FS_NRSUPER, "super-nr", &nr_super_blocks, sizeof(int), 0444, NULL, &proc_dointvec}, diff --git a/kernel/timer.c b/kernel/timer.c index 5457c450b..9fa35a63b 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -238,17 +238,7 @@ int del_timer_sync(struct timer_list * timer) if (!running) return ret; - - { - int count = 50*1000*1000; - while (timer_is_running(timer) && --count) - ; - if (count == 0) { - printk( "del_timer_sync(%p): deadlock! Called from %p\n", - timer, __builtin_return_address(0)); - printk("See http://www.uow.edu.au/~andrewm/linux/deadlock.html\n"); - } - } + timer_synchronize(timer); } return ret; diff --git a/mm/filemap.c b/mm/filemap.c index 5e9e6e00b..b1e2b8547 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -56,8 +56,6 @@ spinlock_t pagemap_lru_lock = SPIN_LOCK_UNLOCKED; #define CLUSTER_PAGES (1 << page_cluster) #define CLUSTER_OFFSET(x) (((x) >> page_cluster) << page_cluster) -#define min(a,b) ((a < b) ? a : b) - void __add_page_to_hash_queue(struct page * page, struct page **p) { atomic_inc(&page_cache_size); @@ -92,16 +90,10 @@ static inline int sync_page(struct page *page) /* * Remove a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage - * is safe. We need that the page don't have any buffers. + * is safe. */ static inline void __remove_inode_page(struct page *page) { - if (!PageLocked(page)) - PAGE_BUG(page); - - if (page->buffers) - BUG(); - remove_page_from_inode_queue(page); remove_page_from_hash_queue(page); page->mapping = NULL; @@ -109,6 +101,9 @@ static inline void __remove_inode_page(struct page *page) void remove_inode_page(struct page *page) { + if (!PageLocked(page)) + PAGE_BUG(page); + spin_lock(&pagecache_lock); __remove_inode_page(page); spin_unlock(&pagecache_lock); @@ -119,16 +114,16 @@ void remove_inode_page(struct page *page) * @inode: the inode which pages we want to invalidate * * This function only removes the unlocked pages, if you want to - * remove all the pages of one inode, you must call - * truncate_inode_pages. This function is not supposed to be called - * by block based filesystems. + * remove all the pages of one inode, you must call truncate_inode_pages. */ + void invalidate_inode_pages(struct inode * inode) { struct list_head *head, *curr; struct page * page; head = &inode->i_mapping->pages; + spin_lock(&pagecache_lock); spin_lock(&pagemap_lru_lock); curr = head->next; @@ -140,53 +135,20 @@ void invalidate_inode_pages(struct inode * inode) /* We cannot invalidate a locked page */ if (TryLockPage(page)) continue; - /* We _should not be called_ by block based filesystems */ - if (page->buffers) - BUG(); - __remove_inode_page(page); __lru_cache_del(page); + __remove_inode_page(page); UnlockPage(page); page_cache_release(page); } + spin_unlock(&pagemap_lru_lock); spin_unlock(&pagecache_lock); } -static inline void truncate_partial_page(struct page *page, unsigned partial) -{ - memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial); - - if (page->buffers) - block_flushpage(page, partial); - -} - -static inline void truncate_complete_page(struct page *page) -{ - if (page->buffers) - block_destroy_buffers(page); - lru_cache_del(page); - - /* - * We remove the page from the page cache _after_ we have - * destroyed all buffer-cache references to it. Otherwise some - * other process might think this inode page is not in the - * page cache and creates a buffer-cache alias to it causing - * all sorts of fun problems ... - */ - remove_inode_page(page); - page_cache_release(page); -} - -/** - * truncate_inode_pages - truncate *all* the pages from an offset - * @mapping: mapping to truncate - * @lstart: offset from with to truncate - * +/* * Truncate the page cache at a set offset, removing the pages * that are beyond that offset (and zeroing out partial pages). - * If any page is locked we wait for it to become unlocked. */ void truncate_inode_pages(struct address_space * mapping, loff_t lstart) { @@ -206,10 +168,11 @@ repeat: page = list_entry(curr, struct page, list); curr = curr->next; + offset = page->index; - /* Is one of the pages to truncate? */ - if ((offset >= start) || (partial && (offset + 1) == start)) { + /* page wholly truncated - free it */ + if (offset >= start) { if (TryLockPage(page)) { page_cache_get(page); spin_unlock(&pagecache_lock); @@ -220,14 +183,22 @@ repeat: page_cache_get(page); spin_unlock(&pagecache_lock); - if (partial && (offset + 1) == start) { - truncate_partial_page(page, partial); - partial = 0; - } else - truncate_complete_page(page); + if (!page->buffers || block_flushpage(page, 0)) + lru_cache_del(page); + + /* + * We remove the page from the page cache + * _after_ we have destroyed all buffer-cache + * references to it. Otherwise some other process + * might think this inode page is not in the + * page cache and creates a buffer-cache alias + * to it causing all sorts of fun problems ... + */ + remove_inode_page(page); UnlockPage(page); page_cache_release(page); + page_cache_release(page); /* * We have done things without the pagecache lock, @@ -238,59 +209,38 @@ repeat: */ goto repeat; } - } - spin_unlock(&pagecache_lock); -} - -/** - * truncate_all_inode_pages - truncate *all* the pages - * @mapping: mapping to truncate - * - * Truncate all the inode pages. If any page is locked we wait for it - * to become unlocked. This function can block. - */ -void truncate_all_inode_pages(struct address_space * mapping) -{ - struct list_head *head, *curr; - struct page * page; - - head = &mapping->pages; -repeat: - spin_lock(&pagecache_lock); - spin_lock(&pagemap_lru_lock); - curr = head->next; + /* + * there is only one partial page possible. + */ + if (!partial) + continue; - while (curr != head) { - page = list_entry(curr, struct page, list); - curr = curr->next; + /* and it's the one preceeding the first wholly truncated page */ + if ((offset + 1) != start) + continue; + /* partial truncate, clear end of page */ if (TryLockPage(page)) { - page_cache_get(page); - spin_unlock(&pagemap_lru_lock); - spin_unlock(&pagecache_lock); - wait_on_page(page); - page_cache_release(page); - goto repeat; - } - if (page->buffers) { - page_cache_get(page); - spin_unlock(&pagemap_lru_lock); spin_unlock(&pagecache_lock); - block_destroy_buffers(page); - remove_inode_page(page); - lru_cache_del(page); - page_cache_release(page); - UnlockPage(page); - page_cache_release(page); goto repeat; } - __lru_cache_del(page); - __remove_inode_page(page); + page_cache_get(page); + spin_unlock(&pagecache_lock); + + memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial); + if (page->buffers) + block_flushpage(page, partial); + + partial = 0; + + /* + * we have dropped the spinlock so we have to + * restart. + */ UnlockPage(page); page_cache_release(page); + goto repeat; } - - spin_unlock(&pagemap_lru_lock); spin_unlock(&pagecache_lock); } @@ -314,15 +264,7 @@ int shrink_mmap(int priority, int gfp_mask) page = list_entry(page_lru, struct page, lru); list_del(page_lru); - if (PageTestandClearReferenced(page)) { - page->age += PG_AGE_ADV; - if (page->age > PG_AGE_MAX) - page->age = PG_AGE_MAX; - goto dispose_continue; - } - page->age -= min(PG_AGE_DECL, page->age); - - if (page->age) + if (PageTestandClearReferenced(page)) goto dispose_continue; count--; @@ -380,23 +322,17 @@ int shrink_mmap(int priority, int gfp_mask) * were to be marked referenced.. */ if (PageSwapCache(page)) { - if (!PageDirty(page)) { - spin_unlock(&pagecache_lock); - __delete_from_swap_cache(page); - goto made_inode_progress; - } - /* PageDeferswap -> we swap out the page now. */ - if (gfp_mask & __GFP_IO) { - spin_unlock(&pagecache_lock); - /* Do NOT unlock the page ... brw_page does. */ - ClearPageDirty(page); - rw_swap_page(WRITE, page, 0); - spin_lock(&pagemap_lru_lock); - page_cache_release(page); - goto dispose_continue; - } + spin_unlock(&pagecache_lock); + __delete_from_swap_cache(page); + goto made_inode_progress; + } + + /* + * Page is from a zone we don't care about. + * Don't drop page cache entries in vain. + */ + if (page->zone->free_pages > page->zone->pages_high) goto cache_unlock_continue; - } /* is it a page-cache page? */ if (page->mapping) { @@ -564,7 +500,7 @@ void add_to_page_cache_locked(struct page * page, struct address_space *mapping, /* * This adds a page to the page cache, starting out as locked, - * owned by us, but not uptodate and with no errors. + * owned by us, referenced, but not uptodate and with no errors. */ static inline void __add_to_page_cache(struct page * page, struct address_space *mapping, unsigned long offset, @@ -576,8 +512,8 @@ static inline void __add_to_page_cache(struct page * page, if (PageLocked(page)) BUG(); - flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error)); - page->flags = flags | (1 << PG_locked); + flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty)); + page->flags = flags | (1 << PG_locked) | (1 << PG_referenced); page_cache_get(page); page->index = offset; add_page_to_inode_queue(mapping, page); @@ -1808,7 +1744,7 @@ static int msync_interval(struct vm_area_struct * vma, if (!error && (flags & MS_SYNC)) { struct file * file = vma->vm_file; if (file && file->f_op && file->f_op->fsync) - error = file->f_op->fsync(file, file->f_dentry, 1); + error = file->f_op->fsync(file, file->f_dentry); } return error; } @@ -2547,7 +2483,7 @@ generic_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) if (count) { remove_suid(inode); inode->i_ctime = inode->i_mtime = CURRENT_TIME; - mark_inode_dirty_sync(inode); + mark_inode_dirty(inode); } while (count) { @@ -2604,13 +2540,7 @@ unlock: if (cached_page) page_cache_free(cached_page); - /* For now, when the user asks for O_SYNC, we'll actually - * provide O_DSYNC. */ - if ((status >= 0) && (file->f_flags & O_SYNC)) - status = generic_osync_inode(inode, 1); /* 1 means datasync */ - err = written ? written : status; - out: up(&inode->i_sem); return err; diff --git a/mm/memory.c b/mm/memory.c index e2609758e..de7dc07f8 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -847,7 +847,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct * vma, UnlockPage(old_page); break; } - SetPageDirty(old_page); + delete_from_swap_cache_nolock(old_page); UnlockPage(old_page); /* FallThrough */ case 1: @@ -1058,7 +1058,7 @@ static int do_swap_page(struct mm_struct * mm, */ lock_page(page); swap_free(entry); - if (write_access && !is_page_shared(page) && nr_free_highpages()) { + if (write_access && !is_page_shared(page)) { delete_from_swap_cache_nolock(page); UnlockPage(page); page = replace_with_highmem(page); @@ -166,7 +166,6 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon { struct mm_struct * mm = current->mm; struct vm_area_struct * vma; - int correct_wcount = 0; int error; if (file && (!file->f_op || !file->f_op->mmap)) @@ -297,19 +296,32 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon goto free_vma; if (file) { + int correct_wcount = 0; if (vma->vm_flags & VM_DENYWRITE) { - error = deny_write_access(file); - if (error) + if (atomic_read(&file->f_dentry->d_inode->i_writecount) > 0) { + error = -ETXTBSY; goto free_vma; + } + /* f_op->mmap might possibly sleep + * (generic_file_mmap doesn't, but other code + * might). In any case, this takes care of any + * race that this might cause. + */ + atomic_dec(&file->f_dentry->d_inode->i_writecount); correct_wcount = 1; } vma->vm_file = file; get_file(file); error = file->f_op->mmap(file, vma); + /* Fix up the count if necessary, then check for an error */ + if (correct_wcount) + atomic_inc(&file->f_dentry->d_inode->i_writecount); if (error) goto unmap_and_free_vma; } else if (flags & MAP_SHARED) { error = map_zero_setup(vma); + if (error) + goto free_vma; } /* @@ -320,8 +332,6 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon addr = vma->vm_start; /* can addr have changed?? */ vmlist_modify_lock(mm); insert_vm_struct(mm, vma); - if (correct_wcount) - atomic_inc(&file->f_dentry->d_inode->i_writecount); merge_segments(mm, vma->vm_start, vma->vm_end); vmlist_modify_unlock(mm); @@ -333,8 +343,6 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon return addr; unmap_and_free_vma: - if (correct_wcount) - atomic_inc(&file->f_dentry->d_inode->i_writecount); vma->vm_file = NULL; fput(file); /* Undo any partial mapping done by a device driver. */ @@ -686,11 +694,9 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) * so release them, and unmap the page range.. * If the one of the segments is only being partially unmapped, * it will put new vm_area_struct(s) into the address space. - * In that case we have to be careful with VM_DENYWRITE. */ while ((mpnt = free) != NULL) { unsigned long st, end, size; - struct file *file = NULL; free = free->vm_next; @@ -702,11 +708,6 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) if (mpnt->vm_ops && mpnt->vm_ops->unmap) mpnt->vm_ops->unmap(mpnt, st, size); - if (mpnt->vm_flags & VM_DENYWRITE && - (st != mpnt->vm_start || end != mpnt->vm_end) && - (file = mpnt->vm_file) != NULL) { - atomic_dec(&file->f_dentry->d_inode->i_writecount); - } remove_shared_vm_struct(mpnt); mm->map_count--; @@ -718,8 +719,6 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) * Fix the mapping, and free the old area if it wasn't reused. */ extra = unmap_fixup(mm, mpnt, st, size, extra); - if (file) - atomic_inc(&file->f_dentry->d_inode->i_writecount); } /* Release the extra vma struct if it wasn't used */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 4766127b2..926364499 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -29,7 +29,7 @@ int nr_lru_pages; pg_data_t *pgdat_list; static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; -static int zone_balance_ratio[MAX_NR_ZONES] = { 128, 128, 512, }; +static int zone_balance_ratio[MAX_NR_ZONES] = { 128, 128, 128, }; static int zone_balance_min[MAX_NR_ZONES] = { 10 , 10, 10, }; static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, }; @@ -93,8 +93,6 @@ void __free_pages_ok (struct page *page, unsigned long order) BUG(); if (PageDecrAfter(page)) BUG(); - if (PageDirty(page)) - BUG(); zone = page->zone; @@ -141,12 +139,9 @@ void __free_pages_ok (struct page *page, unsigned long order) spin_unlock_irqrestore(&zone->lock, flags); - if (zone->free_pages >= zone->pages_low) { - zone->low_on_memory = 0; - } - - if (zone->free_pages >= zone->pages_high) { + if (zone->free_pages > zone->pages_high) { zone->zone_wake_kswapd = 0; + zone->low_on_memory = 0; } } @@ -222,9 +217,6 @@ struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) { zone_t **zone = zonelist->zones; extern wait_queue_head_t kswapd_wait; - static int last_woke_kswapd; - static int kswapd_pause = HZ; - int gfp_mask = zonelist->gfp_mask; /* * (If anyone calls gfp from interrupts nonatomically then it @@ -245,6 +237,8 @@ struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) struct page *page = rmqueue(z, order); if (z->free_pages < z->pages_low) { z->zone_wake_kswapd = 1; + if (waitqueue_active(&kswapd_wait)) + wake_up_interruptible(&kswapd_wait); } if (page) return page; @@ -252,27 +246,9 @@ struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) } /* - * Kswapd should be freeing enough memory to satisfy all allocations - * immediately. Calling try_to_free_pages from processes will slow - * down the system a lot. On the other hand, waking up kswapd too - * often means wasted memory and cpu time. - * - * We tune the kswapd pause interval in such a way that kswapd is - * always just agressive enough to free the amount of memory we - * want freed. - */ - if (waitqueue_active(&kswapd_wait) && - time_after(jiffies, last_woke_kswapd + kswapd_pause)) { - kswapd_pause++; - last_woke_kswapd = jiffies; - wake_up_interruptible(&kswapd_wait); - } - - /* * Ok, we don't have any zones that don't need some * balancing.. See if we have any that aren't critical.. */ -again: zone = zonelist->zones; for (;;) { zone_t *z = *(zone++); @@ -280,33 +256,20 @@ again: break; if (!z->low_on_memory) { struct page *page = rmqueue(z, order); - if (z->free_pages < (z->pages_min + z->pages_low) / 2) + if (z->free_pages < z->pages_min) z->low_on_memory = 1; if (page) return page; - } else { - if (kswapd_pause > 0) - kswapd_pause--; } } - /* We didn't kick kswapd often enough... */ - kswapd_pause /= 2; - if (waitqueue_active(&kswapd_wait)) - wake_up_interruptible(&kswapd_wait); - /* If we're low priority, we just wait a bit and try again later. */ - if ((gfp_mask & __GFP_WAIT) && current->need_resched && - current->state == TASK_RUNNING) { - schedule(); - goto again; - } - /* * Uhhuh. All the zones have been critical, which means that * we'd better do some synchronous swap-out. kswapd has not * been able to cope.. */ if (!(current->flags & PF_MEMALLOC)) { + int gfp_mask = zonelist->gfp_mask; if (!try_to_free_pages(gfp_mask)) { if (!(gfp_mask & __GFP_HIGH)) goto fail; @@ -314,7 +277,7 @@ again: } /* - * We freed something, so we're allowed to allocate anything we can! + * Final phase: allocate anything we can! */ zone = zonelist->zones; for (;;) { @@ -329,18 +292,6 @@ again: } fail: - /* Last try, zone->low_on_memory isn't reset until we hit pages_low */ - zone = zonelist->zones; - for (;;) { - zone_t *z = *(zone++); - if (!z) - break; - if (z->free_pages > z->pages_min) { - struct page *page = rmqueue(z, order); - if (page) - return page; - } - } /* No luck.. */ return NULL; } diff --git a/mm/swap_state.c b/mm/swap_state.c index 00000843a..2405aba2f 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -58,8 +58,8 @@ void add_to_swap_cache(struct page *page, swp_entry_t entry) BUG(); if (page->mapping) BUG(); - flags = page->flags & ~(1 << PG_error); - page->flags = flags | (1 << PG_uptodate); + flags = page->flags & ~((1 << PG_error) | (1 << PG_dirty)); + page->flags = flags | (1 << PG_referenced) | (1 << PG_uptodate); add_to_page_cache_locked(page, &swapper_space, entry.val); } @@ -73,7 +73,6 @@ static inline void remove_from_swap_cache(struct page *page) PAGE_BUG(page); PageClearSwapCache(page); - ClearPageDirty(page); remove_inode_page(page); } @@ -103,10 +102,9 @@ void delete_from_swap_cache_nolock(struct page *page) if (!PageLocked(page)) BUG(); - if (page->buffers) - block_destroy_buffers(page); + if (block_flushpage(page, 0)) + lru_cache_del(page); - lru_cache_del(page); __delete_from_swap_cache(page); page_cache_release(page); } diff --git a/mm/swapfile.c b/mm/swapfile.c index 5d3a7f23e..55ef476a3 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -407,11 +407,11 @@ asmlinkage long sys_swapoff(const char * specialfile) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + lock_kernel(); err = user_path_walk(specialfile, &nd); if (err) goto out; - lock_kernel(); prev = -1; swap_list_lock(); for (type = swap_list.head; type >= 0; type = swap_info[type].next) { @@ -478,9 +478,9 @@ asmlinkage long sys_swapoff(const char * specialfile) err = 0; out_dput: - unlock_kernel(); path_release(&nd); out: + unlock_kernel(); return err; } @@ -555,6 +555,7 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) unsigned long maxpages; int swapfilesize; struct block_device *bdev = NULL; + char *name; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -585,7 +586,14 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) } else { p->prio = --least_priority; } - error = user_path_walk(specialfile, &nd); + name = getname(specialfile); + error = PTR_ERR(name); + if (IS_ERR(name)) + goto bad_swap_2; + error = 0; + if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) + error = path_walk(name, &nd); + putname(name); if (error) goto bad_swap_2; diff --git a/mm/vmscan.c b/mm/vmscan.c index 4e7ad6ab7..1919c0961 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -62,10 +62,6 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un goto out_failed; } - /* Can only do this if we age all active pages. */ - if (PageActive(page) && page->age > 1) - goto out_failed; - if (TryLockPage(page)) goto out_failed; @@ -78,8 +74,6 @@ static int try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, un * memory, and we should just continue our scan. */ if (PageSwapCache(page)) { - if (pte_dirty(pte)) - SetPageDirty(page); entry.val = page->index; swap_duplicate(entry); set_pte(page_table, swp_entry_to_pte(entry)); @@ -187,10 +181,7 @@ drop_pte: vmlist_access_unlock(vma->vm_mm); /* OK, do a physical asynchronous write to swap. */ - // rw_swap_page(WRITE, page, 0); - /* Let shrink_mmap handle this swapout. */ - SetPageDirty(page); - UnlockPage(page); + rw_swap_page(WRITE, page, 0); out_free_success: page_cache_release(page); @@ -439,13 +430,12 @@ out: * latency. */ #define FREE_COUNT 8 +#define SWAP_COUNT 16 static int do_try_to_free_pages(unsigned int gfp_mask) { int priority; int count = FREE_COUNT; - int swap_count = 0; - int made_progress = 0; - int ret = 0; + int swap_count; /* Always trim SLAB caches when memory gets low. */ kmem_cache_reap(gfp_mask); @@ -453,7 +443,6 @@ static int do_try_to_free_pages(unsigned int gfp_mask) priority = 64; do { while (shrink_mmap(priority, gfp_mask)) { - made_progress = 1; if (!--count) goto done; } @@ -468,12 +457,9 @@ static int do_try_to_free_pages(unsigned int gfp_mask) */ count -= shrink_dcache_memory(priority, gfp_mask); count -= shrink_icache_memory(priority, gfp_mask); - if (count <= 0) { - made_progress = 1; + if (count <= 0) goto done; - } while (shm_swap(priority, gfp_mask)) { - made_progress = 1; if (!--count) goto done; } @@ -485,44 +471,24 @@ static int do_try_to_free_pages(unsigned int gfp_mask) * This will not actually free any pages (they get * put in the swap cache), so we must not count this * as a "count" success. - * - * The amount we page out is the amount of pages we're - * short freeing, amplified by the number of times we - * failed above. This generates a negative feedback loop: - * the more difficult it was to free pages, the easier we - * will make it. */ - swap_count += count; - while (swap_out(priority, gfp_mask)) { - made_progress = 1; + swap_count = SWAP_COUNT; + while (swap_out(priority, gfp_mask)) if (--swap_count < 0) break; - } - /* - * If we made progress at the current priority, the next - * loop will also be done at this priority level. There's - * absolutely no reason to drop to a lower priority and - * potentially upset the balance between shrink_mmap and - * swap_out. - */ - if (made_progress) { - made_progress = 0; - ret = 1; - } else { - priority--; - } - } while (priority >= 0); + } while (--priority >= 0); /* Always end on a shrink_mmap.. */ while (shrink_mmap(0, gfp_mask)) { - ret = 1; if (!--count) goto done; } + /* We return 1 if we are freed some page */ + return (count != FREE_COUNT); done: - return ret; + return 1; } DECLARE_WAIT_QUEUE_HEAD(kswapd_wait); diff --git a/net/core/profile.c b/net/core/profile.c index 0408447cb..a3ff36417 100644 --- a/net/core/profile.c +++ b/net/core/profile.c @@ -145,11 +145,12 @@ int whitehole_count; static int whitehole_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats; - dev_kfree_skb(skb); + stats = (struct net_device_stats *)dev->priv; stats->tx_packets++; stats->tx_bytes+=skb->len; + dev_kfree_skb(skb); return 0; } diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index ab244350e..5322b3a89 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -285,7 +285,7 @@ int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, switch(*fmt) { case 0: - sdn->sdn_objnum = dn_htons(type); + sdn->sdn_objnum = type; return 2; case 1: namel = 16; @@ -526,10 +526,6 @@ static void dn_destroy_sock(struct sock *sk) { struct dn_scp *scp = &sk->protinfo.dn; - if (sk->dead) - return; - - sk->dead = 1; scp->nsp_rxtshift = 0; /* reset back off */ if (sk->socket) { @@ -661,11 +657,12 @@ dn_release(struct socket *sock) struct sock *sk = sock->sk; if (sk) { + sock_orphan(sk); + sock_hold(sk); lock_sock(sk); - sock->sk = NULL; - sk->socket = NULL; dn_destroy_sock(sk); release_sock(sk); + sock_put(sk); } return 0; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index b2c6b2051..00c27cdec 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -441,7 +441,7 @@ int dn_neigh_router_hello(struct sk_buff *skb) struct dn_dev *dn_db; dn_address src; - src = dn_eth2dn(msg->id); + src = dn_htons(dn_eth2dn(msg->id)); neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); @@ -498,7 +498,7 @@ int dn_neigh_endnode_hello(struct sk_buff *skb) struct dn_neigh *dn; dn_address src; - src = dn_eth2dn(msg->id); + src = dn_htons(dn_eth2dn(msg->id)); neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 11c67f0c5..5ce55ebb2 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -131,7 +131,7 @@ static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst) return dn_rt_hash_mask & (unsigned)tmp; } -static void dn_dst_check_expire(unsigned long dummy) +static void SMP_TIMER_NAME(dn_dst_check_expire)(unsigned long dummy) { int i; struct dn_route *rt, **rtp; @@ -142,10 +142,12 @@ static void dn_dst_check_expire(unsigned long dummy) rtp = &dn_rt_hash_table[i].chain; write_lock(&dn_rt_hash_table[i].lock); - for(;(rt=*rtp); rtp = &rt->u.rt_next) { + while((rt=*rtp) != NULL) { if (atomic_read(&rt->u.dst.__refcnt) || - (now - rt->u.dst.lastuse) < expire) + (now - rt->u.dst.lastuse) < expire) { + rtp = &rt->u.rt_next; continue; + } *rtp = rt->u.rt_next; rt->u.rt_next = NULL; dst_free(&rt->u.dst); @@ -156,10 +158,11 @@ static void dn_dst_check_expire(unsigned long dummy) break; } - dn_route_timer.expires = now + decnet_dst_gc_interval * HZ; - add_timer(&dn_route_timer); + mod_timer(&dn_route_timer, now + decnet_dst_gc_interval * HZ); } +SMP_TIMER_DEFINE(dn_dst_check_expire, dn_dst_task); + static int dn_dst_gc(void) { struct dn_route *rt, **rtp; @@ -172,10 +175,12 @@ static int dn_dst_gc(void) write_lock_bh(&dn_rt_hash_table[i].lock); rtp = &dn_rt_hash_table[i].chain; - for(; (rt=*rtp); rtp = &rt->u.rt_next) { + while((rt=*rtp) != NULL) { if (atomic_read(&rt->u.dst.__refcnt) || - (now - rt->u.dst.lastuse) < expire) + (now - rt->u.dst.lastuse) < expire) { + rtp = &rt->u.rt_next; continue; + } *rtp = rt->u.rt_next; rt->u.rt_next = NULL; dst_free(&rt->u.dst); @@ -229,7 +234,7 @@ static void dn_insert_route(struct dn_route *rt, unsigned hash) write_unlock_bh(&dn_rt_hash_table[hash].lock); } -void dn_run_flush(unsigned long dummy) +void SMP_TIMER_NAME(dn_run_flush)(unsigned long dummy) { int i; struct dn_route *rt, *next; @@ -251,6 +256,8 @@ nothing_to_declare: } } +SMP_TIMER_DEFINE(dn_run_flush, dn_flush_task); + static spinlock_t dn_rt_flush_lock = SPIN_LOCK_UNLOCKED; void dn_rt_cache_flush(int delay) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index bb9f81658..2601d2412 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: semantics. * - * Version: $Id: fib_semantics.c,v 1.15 1999/08/20 11:05:07 davem Exp $ + * Version: $Id: fib_semantics.c,v 1.16 2000/06/21 17:14:50 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -925,7 +925,7 @@ int fib_sync_up(struct net_device *dev) nh->nh_flags &= ~RTNH_F_DEAD; } endfor_nexthops(fi) - if (alive == fi->fib_nhs) { + if (alive > 0) { fi->fib_flags &= ~RTNH_F_DEAD; ret++; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index f15d94388..6cac59c91 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,7 +3,7 @@ * * Alan Cox, <alan@redhat.com> * - * Version: $Id: icmp.c,v 1.69 2000/04/15 01:48:10 davem Exp $ + * Version: $Id: icmp.c,v 1.70 2000/06/21 17:16:21 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -669,7 +669,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info) room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen; room -= sizeof(struct icmphdr); - icmp_param.data_len=(iph->ihl<<2)+skb_in->len; + icmp_param.data_len=(iph->ihl<<2)+(skb_in->tail-(u8*)iph); if (icmp_param.data_len > room) icmp_param.data_len = room; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index c93da55f9..aed0945c5 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -8,7 +8,7 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.38 2000/02/27 01:20:02 davem Exp $ + * Version: $Id: igmp.c,v 1.39 2000/06/21 17:17:32 davem Exp $ * * Authors: * Alan Cox <Alan.Cox@linux.org> @@ -150,15 +150,14 @@ static __inline__ void igmp_stop_timer(struct ip_mc_list *im) spin_unlock_bh(&im->lock); } -static __inline__ void igmp_start_timer(struct ip_mc_list *im, int max_delay) +/* It must be called with locked im->lock */ +static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { int tv=net_random() % max_delay; - spin_lock_bh(&im->lock); im->tm_running=1; if (!mod_timer(&im->timer, jiffies+tv+2)) atomic_inc(&im->refcnt); - spin_unlock_bh(&im->lock); } static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) @@ -174,9 +173,8 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay) } atomic_dec(&im->refcnt); } - spin_unlock_bh(&im->lock); - igmp_start_timer(im, max_delay); + spin_unlock_bh(&im->lock); } @@ -259,6 +257,7 @@ static void igmp_timer_expire(unsigned long data) struct in_device *in_dev = im->interface; int err; + spin_lock(&im->lock); im->tm_running=0; if (IGMP_V1_SEEN(in_dev)) @@ -270,8 +269,7 @@ static void igmp_timer_expire(unsigned long data) if (err) { if (!in_dev->dead) igmp_start_timer(im, IGMP_Unsolicited_Report_Interval); - ip_ma_put(im); - return; + goto out; } if (im->unsolicit_count) { @@ -279,6 +277,8 @@ static void igmp_timer_expire(unsigned long data) igmp_start_timer(im, IGMP_Unsolicited_Report_Interval); } im->reporter = 1; +out: + spin_unlock(&im->lock); ip_ma_put(im); } @@ -455,7 +455,9 @@ static void igmp_group_added(struct ip_mc_list *im) if (im->multiaddr == IGMP_ALL_HOSTS) return; + spin_lock_bh(&im->lock); igmp_start_timer(im, IGMP_Initial_Report_Delay); + spin_unlock_bh(&im->lock); #endif } diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index cf82c1623..f34fa37d2 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1,5 +1,5 @@ /* - * $Id: ipconfig.c,v 1.32 2000/06/19 06:24:59 davem Exp $ + * $Id: ipconfig.c,v 1.33 2000/06/21 17:21:43 davem Exp $ * * Automatic Configuration of IP -- use BOOTP or RARP or user-supplied * information to configure own IP address and routes. diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4d124d558..b1e7e7dc5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.26 2000/03/01 02:58:12 davem Exp $ + * $Id: ip6_output.c,v 1.27 2000/06/21 17:18:40 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -200,7 +200,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, if (skb_headroom(skb) < head_room) { struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); - kfree(skb); + kfree_skb(skb); skb = skb2; if (skb == NULL) return -ENOBUFS; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 053db0c72..dd4b55b0f 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: mcast.c,v 1.30 2000/02/08 21:27:23 davem Exp $ + * $Id: mcast.c,v 1.31 2000/06/21 17:23:54 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -196,16 +196,26 @@ int inet6_mc_check(struct sock *sk, struct in6_addr *addr) return 0; } +static void ma_put(struct ifmcaddr6 *mc) +{ + if (atomic_dec_and_test(&mc->mca_refcnt)) { + in6_dev_put(mc->idev); + kfree_s(mc, sizeof(*mc)); + } +} + static int igmp6_group_added(struct ifmcaddr6 *mc) { struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + spin_lock_bh(&mc->mca_lock); if (!(mc->mca_flags&MAF_LOADED)) { mc->mca_flags |= MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_add(dev, buf, dev->addr_len, 0); } + spin_unlock_bh(&mc->mca_lock); if (dev->flags&IFF_UP) igmp6_join_group(mc); @@ -217,11 +227,13 @@ static int igmp6_group_dropped(struct ifmcaddr6 *mc) struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + spin_lock_bh(&mc->mca_lock); if (mc->mca_flags&MAF_LOADED) { mc->mca_flags &= ~MAF_LOADED; if (ndisc_mc_map(&mc->mca_addr, buf, dev, 0) == 0) dev_mc_delete(dev, buf, dev->addr_len, 0); } + spin_unlock_bh(&mc->mca_lock); if (dev->flags&IFF_UP) igmp6_leave_group(mc); @@ -251,7 +263,7 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr) for (mc = idev->mc_list; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { - atomic_inc(&mc->mca_users); + mc->mca_users++; write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; @@ -276,15 +288,16 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr) memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr)); mc->idev = idev; - atomic_set(&mc->mca_users, 1); + mc->mca_users = 1; + atomic_set(&mc->mca_refcnt, 2); + mc->mca_lock = SPIN_LOCK_UNLOCKED; mc->next = idev->mc_list; idev->mc_list = mc; - - igmp6_group_added(mc); - write_unlock_bh(&idev->lock); + igmp6_group_added(mc); + ma_put(mc); return 0; } @@ -303,16 +316,13 @@ int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr) write_lock_bh(&idev->lock); for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) { - if (atomic_dec_and_test(&ma->mca_users)) { + if (--ma->mca_users == 0) { *map = ma->next; write_unlock_bh(&idev->lock); igmp6_group_dropped(ma); - if (ma->idev) - __in6_dev_put(ma->idev); - - kfree(ma); + ma_put(ma); in6_dev_put(idev); return 0; } @@ -363,8 +373,11 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) if (ipv6_addr_type(&ma->mca_addr)&(IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)) return; - if (del_timer(&ma->mca_timer)) + spin_lock(&ma->mca_lock); + if (del_timer(&ma->mca_timer)) { + atomic_dec(&ma->mca_refcnt); delay = ma->mca_timer.expires - jiffies; + } if (delay >= resptime) { if (resptime) @@ -373,9 +386,10 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) delay = 1; } - ma->mca_flags |= MAF_TIMER_RUNNING; ma->mca_timer.expires = jiffies + delay; - add_timer(&ma->mca_timer); + if (!mod_timer(&ma->mca_timer, jiffies + delay)) + atomic_inc(&ma->mca_refcnt); + spin_unlock(&ma->mca_lock); } int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len) @@ -453,12 +467,11 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len) read_lock(&idev->lock); for (ma = idev->mc_list; ma; ma=ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) { - if (ma->mca_flags & MAF_TIMER_RUNNING) { - del_timer(&ma->mca_timer); - ma->mca_flags &= ~MAF_TIMER_RUNNING; - } - - ma->mca_flags &= ~MAF_LAST_REPORTER; + spin_lock(&ma->mca_lock); + if (del_timer(&ma->mca_timer)) + atomic_dec(&ma->mca_refcnt); + ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING); + spin_unlock(&ma->mca_lock); break; } } @@ -552,13 +565,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); delay = net_random() % IGMP6_UNSOLICITED_IVAL; - if (del_timer(&ma->mca_timer)) - delay = ma->mca_timer.expires - jiffies; - ma->mca_timer.expires = jiffies + delay; + spin_lock_bh(&ma->mca_lock); + if (del_timer(&ma->mca_timer)) { + atomic_dec(&ma->mca_refcnt); + delay = ma->mca_timer.expires - jiffies; + } - add_timer(&ma->mca_timer); + if (!mod_timer(&ma->mca_timer, jiffies + delay)) + atomic_inc(&ma->mca_refcnt); ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; + spin_unlock_bh(&ma->mca_lock); } static void igmp6_leave_group(struct ifmcaddr6 *ma) @@ -573,17 +590,23 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma) if (ma->mca_flags & MAF_LAST_REPORTER) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REDUCTION); - if (ma->mca_flags & MAF_TIMER_RUNNING) - del_timer(&ma->mca_timer); + spin_lock_bh(&ma->mca_lock); + if (del_timer(&ma->mca_timer)) + atomic_dec(&ma->mca_refcnt); + spin_unlock_bh(&ma->mca_lock); } void igmp6_timer_handler(unsigned long data) { struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data; - ma->mca_flags |= MAF_LAST_REPORTER; igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); + + spin_lock(&ma->mca_lock); + ma->mca_flags |= MAF_LAST_REPORTER; ma->mca_flags &= ~MAF_TIMER_RUNNING; + spin_unlock(&ma->mca_lock); + ma_put(ma); } /* Device going down */ @@ -640,10 +663,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev) write_unlock_bh(&idev->lock); igmp6_group_dropped(i); - - if (i->idev) - in6_dev_put(i->idev); - kfree(i); + ma_put(i); write_lock_bh(&idev->lock); } @@ -677,7 +697,7 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset, len+=sprintf(buffer+len, " %5d %08X %ld\n", - atomic_read(&im->mca_users), + im->mca_users, im->mca_flags, (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0); diff --git a/net/netlink/netlink_dev.c b/net/netlink/netlink_dev.c index b0eb9788e..4da257555 100644 --- a/net/netlink/netlink_dev.c +++ b/net/netlink/netlink_dev.c @@ -178,9 +178,9 @@ static devfs_handle_t devfs_handle = NULL; static void __init make_devfs_entries (const char *name, int minor) { - devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, NETLINK_MAJOR, minor, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &netlink_fops, NULL); } @@ -201,7 +201,7 @@ int __init init_netlink(void) make_devfs_entries ("IP6_FW", 13); devfs_register_series (devfs_handle, "tap%u", 16, DEVFS_FL_DEFAULT, NETLINK_MAJOR, 16, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &netlink_fops, NULL); return 0; } diff --git a/net/netsyms.c b/net/netsyms.c index cd4a2bdb9..c209ff991 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -196,7 +196,7 @@ EXPORT_SYMBOL(__scm_send); /* Needed by unix.o */ EXPORT_SYMBOL(scm_fp_dup); -EXPORT_SYMBOL(files_stat); +EXPORT_SYMBOL(max_files); EXPORT_SYMBOL(memcpy_toiovec); EXPORT_SYMBOL(csum_partial); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 9da8dcdcd..8a046514f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1111,7 +1111,7 @@ PSCHED_WATCHER psched_time_mark; static void psched_tick(unsigned long); static struct timer_list psched_timer = - { NULL, NULL, 0, 0L, psched_tick }; + { function: psched_tick }; static void psched_tick(unsigned long dummy) { diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 926f64f2b..d71a527fe 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.98 2000/06/19 06:24:59 davem Exp $ + * Version: $Id: af_unix.c,v 1.99 2000/06/22 11:42:32 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -445,7 +445,7 @@ static struct sock * unix_create1(struct socket *sock) { struct sock *sk; - if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files) + if (atomic_read(&unix_nr_socks) >= 2*max_files) return NULL; MOD_INC_USE_COUNT; |