From de0e0ad94aaeb9fd40de3957e6bb5ae2a0329538 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:35:38 -0500 Subject: [PATCH] Import 2.4.0-test2pre7 --- Documentation/Configure.help | 250 +++--- Documentation/filesystems/devfs/ChangeLog | 35 + Documentation/filesystems/devfs/README | 24 +- Documentation/filesystems/devfs/modules.conf | 2 +- Documentation/joystick-parport.txt | 138 ++- Documentation/joystick.txt | 673 +++++---------- drivers/block/Config.in | 16 +- drivers/block/linear.c | 18 +- drivers/block/loop.c | 34 +- drivers/block/md.c | 491 ++++++++--- drivers/block/raid0.c | 16 +- drivers/block/raid1.c | 776 ++++++++++++----- drivers/block/raid5.c | 120 ++- drivers/block/rd.c | 9 + drivers/block/xor.c | 5 +- drivers/char/busmouse.c | 3 +- drivers/char/drm/Makefile | 14 + drivers/char/drm/bufs.c | 4 + drivers/char/drm/drmP.h | 6 +- drivers/char/drm/ffb_context.c | 530 ++++++++++++ drivers/char/drm/ffb_drv.c | 842 ++++++++++++++++++ drivers/char/drm/ffb_drv.h | 276 ++++++ drivers/char/drm/fops.c | 5 +- drivers/char/drm/init.c | 4 + drivers/char/drm/proc.c | 6 +- drivers/char/joystick/Config.in | 64 +- drivers/char/joystick/Makefile | 217 ++--- drivers/char/joystick/a3d.c | 387 +++++++++ drivers/char/joystick/adi.c | 554 ++++++++++++ drivers/char/joystick/amijoy.c | 149 ++++ drivers/char/joystick/analog.c | 757 ++++++++++++++++ drivers/char/joystick/cobra.c | 250 ++++++ drivers/char/joystick/db9.c | 423 +++++++++ drivers/char/joystick/gamecon.c | 668 ++++++++++++++ drivers/char/joystick/gameport.c | 201 +++++ drivers/char/joystick/gf2k.c | 359 ++++++++ drivers/char/joystick/grip.c | 423 +++++++++ drivers/char/joystick/interact.c | 306 +++++++ drivers/char/joystick/joy-amiga.c | 143 --- drivers/char/joystick/joy-analog.c | 295 ------- drivers/char/joystick/joy-analog.h | 287 ------ drivers/char/joystick/joy-assassin.c | 396 --------- drivers/char/joystick/joy-console.c | 811 ----------------- drivers/char/joystick/joy-creative.c | 267 ------ drivers/char/joystick/joy-db9.c | 421 --------- drivers/char/joystick/joy-gravis.c | 383 -------- drivers/char/joystick/joy-lightning.c | 351 -------- drivers/char/joystick/joy-logitech.c | 534 ------------ drivers/char/joystick/joy-magellan.c | 395 --------- drivers/char/joystick/joy-pci.c | 254 ------ drivers/char/joystick/joy-sidewinder.c | 835 ------------------ drivers/char/joystick/joy-spaceball.c | 343 -------- drivers/char/joystick/joy-spaceorb.c | 301 ------- drivers/char/joystick/joy-thrustmaster.c | 280 ------ drivers/char/joystick/joy-turbografx.c | 267 ------ drivers/char/joystick/joy-warrior.c | 301 ------- drivers/char/joystick/joystick.c | 864 ------------------- drivers/char/joystick/lightning.c | 300 +++++++ drivers/char/joystick/magellan.c | 210 +++++ drivers/char/joystick/ns558.c | 366 ++++++++ drivers/char/joystick/pcigame.c | 198 +++++ drivers/char/joystick/serio.c | 146 ++++ drivers/char/joystick/serport.c | 220 +++++ drivers/char/joystick/sidewinder.c | 756 ++++++++++++++++ drivers/char/joystick/spaceball.c | 231 +++++ drivers/char/joystick/spaceorb.c | 225 +++++ drivers/char/joystick/tmdc.c | 348 ++++++++ drivers/char/joystick/turbografx.c | 258 ++++++ drivers/char/joystick/warrior.c | 212 +++++ drivers/char/mem.c | 8 - drivers/char/n_hdlc.c | 2 +- drivers/char/n_tty.c | 6 +- drivers/char/pc110pad.c | 3 +- drivers/char/pc_keyb.c | 3 +- drivers/char/qpmouse.c | 3 +- drivers/char/rtc.c | 6 +- drivers/char/serial.c | 833 +++++++++++++++--- drivers/char/tty_io.c | 43 - drivers/net/dmfe.c | 245 +++--- drivers/net/irda/girbil.c | 4 +- drivers/net/wan/comx.c | 4 + drivers/pcmcia/yenta.c | 29 +- drivers/telephony/ixj.c | 31 +- drivers/usb/Config.in | 13 +- drivers/usb/Makefile | 2 +- drivers/usb/iforce.c | 335 +++++++ drivers/usb/wmforce.c | 190 ---- fs/adfs/dir.c | 5 - fs/adfs/dir_f.c | 5 - fs/adfs/dir_fplus.c | 5 - fs/adfs/file.c | 11 - fs/adfs/inode.c | 11 - fs/adfs/map.c | 5 - fs/adfs/super.c | 12 +- fs/bfs/inode.c | 4 +- fs/coda/cache.c | 155 +--- fs/coda/cnode.c | 100 +-- fs/coda/dir.c | 339 ++++---- fs/coda/file.c | 151 +--- fs/coda/inode.c | 73 +- fs/coda/psdev.c | 160 ++-- fs/coda/stats.c | 416 --------- fs/coda/sysctl.c | 14 +- fs/coda/upcall.c | 188 ++-- fs/devfs/base.c | 855 +++++++----------- fs/devpts/devpts_i.h | 2 - fs/devpts/inode.c | 236 ++--- fs/efs/super.c | 6 - fs/fcntl.c | 55 +- fs/ioctl.c | 6 +- fs/lockd/clntproc.c | 2 +- fs/lockd/host.c | 2 +- fs/ntfs/inode.c | 5 +- fs/ntfs/super.c | 1 - fs/open.c | 16 - fs/partitions/acorn.c | 2 +- fs/partitions/check.c | 4 +- fs/partitions/msdos.c | 5 + include/linux/coda.h | 3 +- include/linux/coda_cache.h | 43 - include/linux/coda_fs_i.h | 14 +- include/linux/coda_linux.h | 31 +- include/linux/coda_opstats.h | 94 -- include/linux/coda_proc.h | 4 - include/linux/file.h | 18 + include/linux/fs.h | 17 +- include/linux/gameport.h | 142 +++ include/linux/genhd.h | 1 - include/linux/input.h | 7 +- include/linux/joystick.h | 169 +--- include/linux/mc146818rtc.h | 18 +- include/linux/raid/md.h | 1 - include/linux/raid/md_k.h | 12 +- include/linux/raid/raid1.h | 19 +- include/linux/raid1.h | 49 -- include/linux/raid5.h | 110 --- include/linux/serial.h | 20 +- include/linux/serialP.h | 10 +- include/linux/serial_reg.h | 49 ++ include/linux/serio.h | 110 +++ include/linux/shm.h | 2 +- include/linux/sunrpc/clnt.h | 2 +- include/linux/sunrpc/debug.h | 4 + init/main.c | 7 +- ipc/shm.c | 124 ++- kernel/exit.c | 2 + kernel/ksyms.c | 3 + kernel/sched.c | 15 +- net/decnet/dn_nsp_in.c | 2 +- net/ipv4/udp.c | 3 +- net/netsyms.c | 4 +- net/socket.c | 4 +- net/sunrpc/clnt.c | 36 +- net/sunrpc/pmap_clnt.c | 8 + net/sunrpc/sched.c | 8 +- net/sunrpc/svc.c | 4 +- net/sunrpc/svcsock.c | 49 +- net/sunrpc/xprt.c | 42 +- net/unix/af_unix.c | 51 +- 159 files changed, 14073 insertions(+), 12102 deletions(-) create mode 100644 drivers/char/drm/ffb_context.c create mode 100644 drivers/char/drm/ffb_drv.c create mode 100644 drivers/char/drm/ffb_drv.h create mode 100644 drivers/char/joystick/a3d.c create mode 100644 drivers/char/joystick/adi.c create mode 100644 drivers/char/joystick/amijoy.c create mode 100644 drivers/char/joystick/analog.c create mode 100644 drivers/char/joystick/cobra.c create mode 100644 drivers/char/joystick/db9.c create mode 100644 drivers/char/joystick/gamecon.c create mode 100644 drivers/char/joystick/gameport.c create mode 100644 drivers/char/joystick/gf2k.c create mode 100644 drivers/char/joystick/grip.c create mode 100644 drivers/char/joystick/interact.c delete mode 100644 drivers/char/joystick/joy-amiga.c delete mode 100644 drivers/char/joystick/joy-analog.c delete mode 100644 drivers/char/joystick/joy-analog.h delete mode 100644 drivers/char/joystick/joy-assassin.c delete mode 100644 drivers/char/joystick/joy-console.c delete mode 100644 drivers/char/joystick/joy-creative.c delete mode 100644 drivers/char/joystick/joy-db9.c delete mode 100644 drivers/char/joystick/joy-gravis.c delete mode 100644 drivers/char/joystick/joy-lightning.c delete mode 100644 drivers/char/joystick/joy-logitech.c delete mode 100644 drivers/char/joystick/joy-magellan.c delete mode 100644 drivers/char/joystick/joy-pci.c delete mode 100644 drivers/char/joystick/joy-sidewinder.c delete mode 100644 drivers/char/joystick/joy-spaceball.c delete mode 100644 drivers/char/joystick/joy-spaceorb.c delete mode 100644 drivers/char/joystick/joy-thrustmaster.c delete mode 100644 drivers/char/joystick/joy-turbografx.c delete mode 100644 drivers/char/joystick/joy-warrior.c delete mode 100644 drivers/char/joystick/joystick.c create mode 100644 drivers/char/joystick/lightning.c create mode 100644 drivers/char/joystick/magellan.c create mode 100644 drivers/char/joystick/ns558.c create mode 100644 drivers/char/joystick/pcigame.c create mode 100644 drivers/char/joystick/serio.c create mode 100644 drivers/char/joystick/serport.c create mode 100644 drivers/char/joystick/sidewinder.c create mode 100644 drivers/char/joystick/spaceball.c create mode 100644 drivers/char/joystick/spaceorb.c create mode 100644 drivers/char/joystick/tmdc.c create mode 100644 drivers/char/joystick/turbografx.c create mode 100644 drivers/char/joystick/warrior.c create mode 100644 drivers/usb/iforce.c delete mode 100644 drivers/usb/wmforce.c delete mode 100644 fs/coda/stats.c delete mode 100644 include/linux/coda_opstats.h create mode 100644 include/linux/gameport.h delete mode 100644 include/linux/raid1.h delete mode 100644 include/linux/raid5.h create mode 100644 include/linux/serio.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 35960812b496..5900eb09d415 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -12801,143 +12801,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 @@ -12945,37 +12985,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. @@ -12984,24 +13026,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 diff --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog index 5728384e600f..8285489a918a 100644 --- a/Documentation/filesystems/devfs/ChangeLog +++ b/Documentation/filesystems/devfs/ChangeLog @@ -1533,3 +1533,38 @@ 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 which used to call + + +- Ported to kernel 2.4.0-test2-pre3 diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README index c9b111b9b5b1..b09c8fd9ea97 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 d925cd28b55b..a91c99651170 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/joystick-parport.txt b/Documentation/joystick-parport.txt index 2217a0489bf2..0576fc07ebb7 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 + Linux Joystick parport drivers v2.0 + (c) 1998-2000 Vojtech Pavlik (c) 1998 Andree Borrmann 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 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 163fece8c930..4e53bd2812f0 100644 --- a/Documentation/joystick.txt +++ b/Documentation/joystick.txt @@ -1,5 +1,5 @@ - Linux Joystick driver v1.2.15 - (c) 1996-1999 Vojtech Pavlik + Linux Joystick driver v2.0.0 + (c) 1996-2000 Vojtech Pavlik 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 , -who wrote an attempt of a driver for them. - - Thanks to Creative Labs Europe, and Ifor Powell , -who provided me with docs for their first generation Blaster GamePad. - - Special thanks go to FP-Gaming, Inc. and James C Barnes , -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 and Brand Kvavle -, 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 , -Thomas Burgel , Avinash Shinde - 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/drivers/block/Config.in b/drivers/block/Config.in index 37f0669d6dbe..b04bd828e7f4 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -36,8 +36,6 @@ fi dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI -comment 'Additional Block Devices' - tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET @@ -49,14 +47,16 @@ fi bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' RAID-1/RAID-5 code (DANGEROUS)' CONFIG_RAID15_DANGEROUS - if [ "$CONFIG_RAID15_DANGEROUS" = "y" ]; then - dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD - dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD - fi +dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD +dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD +if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_RAID0" = "y" -o "$CONFIG_MD_RAID1" = "y" -o "$CONFIG_MD_RAID5" = "y" ]; then + bool ' Boot support' CONFIG_MD_BOOT + bool ' Auto Detect support' CONFIG_AUTODETECT_RAID 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 dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM endmenu diff --git a/drivers/block/linear.c b/drivers/block/linear.c index 926792a0fd7e..26d159473290 100644 --- a/drivers/block/linear.c +++ b/drivers/block/linear.c @@ -148,7 +148,7 @@ static int linear_make_request (request_queue_t *q, mddev_t *mddev, return -1; } bh->b_rdev = tmp_dev->dev; - bh->b_rsector = (block - tmp_dev->offset) << 1; + bh->b_rsector = ((block - tmp_dev->offset) << 1) + (bh->b_rsector & 1); return 1; } @@ -183,17 +183,11 @@ static int linear_status (char *page, mddev_t *mddev) static mdk_personality_t linear_personality= { - "linear", - linear_make_request, - NULL, - linear_run, - linear_stop, - linear_status, - 0, - NULL, - NULL, - NULL, - NULL + name: "linear", + make_request: linear_make_request, + run: linear_run, + stop: linear_stop, + status: linear_status, }; #ifndef MODULE diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 9d518eaf9c31..96dacdc225f7 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -238,7 +238,8 @@ static int lo_read_actor(read_descriptor_t * desc, struct page *page, unsigned l kaddr = (char*)kmap(page); if ((lo->transfer)(lo,READ,kaddr+offset,p->data,size,IV)) { size = 0; - printk(KERN_ERR "loop: transfer error block %ld\n",page->index); + printk(KERN_ERR "loop: transfer error block %ld\n", + page->index); desc->error = -EINVAL; } kunmap(page); @@ -345,9 +346,11 @@ repeat: } } - if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset, - dest_addr, size, block)) { - printk(KERN_ERR "loop: transfer error block %d\n", block); + if ((lo->transfer)(lo, current_request->cmd, + bh->b_data + offset, + dest_addr, size, block)) { + printk(KERN_ERR "loop: transfer error block %d\n", + block); brelse(bh); goto error_out_lock; } @@ -469,7 +472,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) lo->lo_backing_file->f_owner = file->f_owner; lo->lo_backing_file->f_dentry = file->f_dentry; lo->lo_backing_file->f_vfsmnt = mntget(file->f_vfsmnt); - lo->lo_backing_file->f_op = file->f_op; + lo->lo_backing_file->f_op = fops_get(file->f_op); lo->lo_backing_file->private_data = file->private_data; file_moveto(lo->lo_backing_file, file); @@ -539,8 +542,10 @@ static int loop_clr_fd(struct loop_device *lo, kdev_t dev) lo->lo_dentry = NULL; if (lo->lo_backing_file != NULL) { - put_write_access(lo->lo_backing_file->f_dentry->d_inode); - fput(lo->lo_backing_file); + struct file *filp = lo->lo_backing_file; + if ((filp->f_mode & FMODE_WRITE) == 0) + put_write_access(filp->f_dentry->d_inode); + fput(filp); lo->lo_backing_file = NULL; } else { dput(dentry); @@ -636,7 +641,8 @@ static int lo_ioctl(struct inode * inode, struct file * file, if (!inode) return -EINVAL; if (MAJOR(inode->i_rdev) != MAJOR_NR) { - printk(KERN_WARNING "lo_ioctl: pseudo-major != %d\n", MAJOR_NR); + printk(KERN_WARNING "lo_ioctl: pseudo-major != %d\n", + MAJOR_NR); return -ENODEV; } dev = MINOR(inode->i_rdev); @@ -698,7 +704,8 @@ static int lo_release(struct inode *inode, struct file *file) if (!inode) return 0; if (MAJOR(inode->i_rdev) != MAJOR_NR) { - printk(KERN_WARNING "lo_release: pseudo-major != %d\n", MAJOR_NR); + printk(KERN_WARNING "lo_release: pseudo-major != %d\n", + MAJOR_NR); return 0; } dev = MINOR(inode->i_rdev); @@ -706,7 +713,8 @@ static int lo_release(struct inode *inode, struct file *file) return 0; lo = &loop_dev[dev]; if (lo->lo_refcnt <= 0) - printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", lo->lo_refcnt); + printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", + lo->lo_refcnt); else { int type = lo->lo_encrypt_type; --lo->lo_refcnt; @@ -761,6 +769,10 @@ int loop_unregister_transfer(int number) EXPORT_SYMBOL(loop_register_transfer); EXPORT_SYMBOL(loop_unregister_transfer); +static void no_plug_device(request_queue_t *q, kdev_t device) +{ +} + int __init loop_init(void) { int i; @@ -806,6 +818,7 @@ int __init loop_init(void) } blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), no_plug_device); blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); for (i=0; i < max_loop; i++) { memset(&loop_dev[i], 0, sizeof(struct loop_device)); @@ -828,6 +841,7 @@ void cleanup_module(void) if (devfs_unregister_blkdev(MAJOR_NR, "loop") != 0) printk(KERN_WARNING "loop: cannot unregister blkdev\n"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); kfree (loop_dev); kfree (loop_sizes); kfree (loop_blksizes); diff --git a/drivers/block/md.c b/drivers/block/md.c index 883c2f7795e3..918dbdaf2636 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -22,10 +22,10 @@ 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. - + You should have received a copy of the GNU General Public License (for example /usr/src/linux/COPYING); if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include @@ -43,7 +43,7 @@ #include extern asmlinkage int sys_sched_yield(void); -extern asmlinkage int sys_setsid(void); +extern asmlinkage long sys_setsid(void); #define MAJOR_NR MD_MAJOR #define MD_DRIVER @@ -258,7 +258,7 @@ static mddev_t * alloc_mddev (kdev_t dev) /* * The 'base' mddev is the one with data NULL. - * personalities can create additional mddevs + * personalities can create additional mddevs * if necessary. */ add_mddev_mapping(mddev, dev, 0); @@ -327,11 +327,13 @@ char * partition_name (kdev_t dev) * ok, add this new device name to the list */ hd = find_gendisk (dev); - - if (!hd) - sprintf (dname->name, "[dev %s]", kdevname(dev)); - else - disk_name (hd, MINOR(dev), dname->name); + dname->name = NULL; + if (hd) + dname->name = disk_name (hd, MINOR(dev), dname->namebuf); + if (!dname->name) { + sprintf (dname->namebuf, "[dev %s]", kdevname(dev)); + dname->name = dname->namebuf; + } dname->dev = dev; MD_INIT_LIST_HEAD(&dname->list); @@ -435,7 +437,7 @@ int md_check_ordering (mddev_t *mddev) if (rdev->desc_nr == i) c++; } - if (c == 0) { + if (!c) { printk("md: md%d, missing disk #%d, aborting.\n", mdidx(mddev), i); goto abort; @@ -1010,7 +1012,7 @@ static int write_disk_sb(mdk_rdev_t * rdev) skip: return 0; } -#undef GETBLK_FAILED KERN_ERR +#undef GETBLK_FAILED static void set_this_disk(mddev_t *mddev, mdk_rdev_t *rdev) { @@ -1425,7 +1427,7 @@ static int analyze_sbs (mddev_t * mddev) break; } } - if (found) + if (found) continue; printk("md%d: former device %s is unavailable, removing from array!\n", mdidx(mddev), partition_name(dev)); @@ -1525,7 +1527,7 @@ static int device_size_calculation (mddev_t * mddev) * Do device size calculation. Bail out if too small. * (we have to do this after having validated chunk_size, * because device size has to be modulo chunk_size) - */ + */ persistent = !mddev->sb->not_persistent; ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) @@ -1576,7 +1578,7 @@ static int device_size_calculation (mddev_t * mddev) readahead = MD_READAHEAD; if ((sb->level == 0) || (sb->level == 4) || (sb->level == 5)) readahead = mddev->sb->chunk_size * 4 * data_disks; - if (readahead < data_disks * MAX_SECTORS*512*2) + if (readahead < data_disks * MAX_SECTORS*512*2) readahead = data_disks * MAX_SECTORS*512*2; else { if (sb->level == -3) @@ -1617,7 +1619,7 @@ static int do_md_run (mddev_t * mddev) MD_BUG(); return -EINVAL; } - + if (mddev->pers) return -EBUSY; @@ -1629,7 +1631,7 @@ static int do_md_run (mddev_t * mddev) /* * Analyze all RAID superblock(s) - */ + */ if (analyze_sbs(mddev)) { MD_BUG(); return -EINVAL; @@ -1683,7 +1685,7 @@ static int do_md_run (mddev_t * mddev) #endif return -EINVAL; } - + if (device_size_calculation(mddev)) return -EINVAL; @@ -1698,9 +1700,9 @@ static int do_md_run (mddev_t * mddev) fsync_dev(rdev->dev); invalidate_buffers(rdev->dev); } - + mddev->pers = pers[pnum]; - + err = mddev->pers->run(mddev); if (err) { printk("pers->run() failed ...\n"); @@ -1717,7 +1719,7 @@ static int do_md_run (mddev_t * mddev) */ md_hd_struct[mdidx(mddev)].start_sect = 0; md_hd_struct[mdidx(mddev)].nr_sects = md_size[mdidx(mddev)] << 1; - + read_ahead[MD_MAJOR] = 1024; return (0); } @@ -1730,7 +1732,7 @@ static int do_md_run (mddev_t * mddev) static int restart_array (mddev_t *mddev) { int err = 0; - + /* * Complain if it has no devices */ @@ -1754,7 +1756,7 @@ static int restart_array (mddev_t *mddev) mddev->pers->restart_resync(mddev); } else err = -EINVAL; - + out: return err; } @@ -1766,12 +1768,12 @@ static int do_md_stop (mddev_t * mddev, int ro) { int err = 0, resync_interrupted = 0; kdev_t dev = mddev_to_kdev(mddev); - + if (!ro && get_super(dev)) { printk (STILL_MOUNTED, mdidx(mddev)); OUT(-EBUSY); } - + if (mddev->pers) { /* * It is safe to call stop here, it only frees private @@ -1833,14 +1835,14 @@ static int do_md_stop (mddev_t * mddev, int ro) if (ro) set_device_ro(dev, 1); } - + /* * Free resources if final stop */ if (!ro) { + printk (KERN_INFO "md%d stopped.\n", mdidx(mddev)); free_mddev(mddev); - printk (KERN_INFO "md%d stopped.\n", mdidx(mddev)); } else printk (KERN_INFO "md%d switched to read-only mode.\n", mdidx(mddev)); @@ -2069,14 +2071,25 @@ struct { } raid_setup_args md__initdata = { 0, 0 }; +void md_setup_drive(void) md__init; + /* * Searches all registered partitions for autorun RAID arrays * at boot time. */ -void md__init autodetect_raid(void) +#ifdef CONFIG_AUTODETECT_RAID +static int detected_devices[128] md__initdata; +static int dev_cnt md__initdata=0; +void md__init md_autodetect_dev(kdev_t dev) +{ + if (dev_cnt < 127) + detected_devices[dev_cnt++] = dev; +} +#endif + +void md__init md_run_setup(void) { #ifdef CONFIG_AUTODETECT_RAID - struct gendisk *disk; mdk_rdev_t *rdev; int i; @@ -2086,36 +2099,35 @@ void md__init autodetect_raid(void) } printk(KERN_INFO "autodetecting RAID arrays\n"); - for (disk = gendisk_head ; disk ; disk = disk->next) { - for (i = 0; i < disk->max_p*disk->nr_real; i++) { - kdev_t dev = MKDEV(disk->major,i); + for (i=0; ipart[i].type != LINUX_RAID_PARTITION) - continue; - - if (md_import_device(dev,1)) { - printk(KERN_ALERT "could not import %s!\n", - partition_name(dev)); - continue; - } - /* - * Sanity checks: - */ - rdev = find_rdev_all(dev); - if (!rdev) { - MD_BUG(); - continue; - } - if (rdev->faulty) { - MD_BUG(); - continue; - } - md_list_add(&rdev->pending, &pending_raid_disks); + if (md_import_device(dev,1)) { + printk(KERN_ALERT "could not import %s!\n", + partition_name(dev)); + continue; } + /* + * Sanity checks: + */ + rdev = find_rdev_all(dev); + if (!rdev) { + MD_BUG(); + continue; + } + if (rdev->faulty) { + MD_BUG(); + continue; + } + md_list_add(&rdev->pending, &pending_raid_disks); } autorun_devices(); #endif +#ifdef CONFIG_MD_BOOT + md_setup_drive(); +#endif + } static int get_version (void * arg) @@ -2196,41 +2208,62 @@ static int get_disk_info (mddev_t * mddev, void * arg) } #undef SET_FROM_SB -#define SET_SB(x) mddev->sb->disks[nr].x = info.x +#define SET_SB(x) mddev->sb->disks[nr].x = info->x -static int add_new_disk (mddev_t * mddev, void * arg) +static int add_new_disk (mddev_t * mddev, mdu_disk_info_t *info) { int err, size, persistent; - mdu_disk_info_t info; mdk_rdev_t *rdev; unsigned int nr; kdev_t dev; - - if (!mddev->sb) - return -EINVAL; - - if (md_copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; - - nr = info.number; - if (nr >= mddev->sb->nr_disks) - return -EINVAL; - - dev = MKDEV(info.major,info.minor); + dev = MKDEV(info->major,info->minor); if (find_rdev_all(dev)) { - printk("device %s already used in a RAID array!\n", + printk("device %s already used in a RAID array!\n", partition_name(dev)); return -EBUSY; } + if (!mddev->sb) { + /* expecting a device which has a superblock */ + err = md_import_device(dev, 1); + if (err) { + printk("md error, md_import_device returned %d\n", err); + return -EINVAL; + } + rdev = find_rdev_all(dev); + if (!rdev) { + MD_BUG(); + return -EINVAL; + } + if (mddev->nb_dev) { + mdk_rdev_t *rdev0 = md_list_entry(mddev->disks.next, + mdk_rdev_t, same_set); + if (!uuid_equal(rdev0, rdev)) { + printk("md: %s has different UUID to %s\n", partition_name(rdev->dev), partition_name(rdev0->dev)); + export_rdev(rdev); + return -EINVAL; + } + if (!sb_equal(rdev0->sb, rdev->sb)) { + printk("md: %s has same UUID but different superblock to %s\n", partition_name(rdev->dev), partition_name(rdev0->dev)); + export_rdev(rdev); + return -EINVAL; + } + } + bind_rdev_to_array(rdev, mddev); + return 0; + } + + nr = info->number; + if (nr >= mddev->sb->nr_disks) + return -EINVAL; SET_SB(number); SET_SB(major); SET_SB(minor); SET_SB(raid_disk); SET_SB(state); - - if ((info.state & (1<state & (1<old_dev = dev; - rdev->desc_nr = info.number; - + rdev->desc_nr = info->number; + bind_rdev_to_array(rdev, mddev); - + persistent = !mddev->sb->not_persistent; if (!persistent) printk("nonpersistent superblock ...\n"); if (!mddev->sb->chunk_size) printk("no chunksize?\n"); - + size = calc_dev_size(dev, mddev, persistent); rdev->sb_offset = calc_dev_sboffset(dev, mddev, persistent); - + if (!mddev->sb->size || (mddev->sb->size > size)) mddev->sb->size = size; } - + /* * sync all other superblocks with the main superblock */ @@ -2444,19 +2477,9 @@ abort_export: return err; } -#define SET_SB(x) mddev->sb->x = info.x -static int set_array_info (mddev_t * mddev, void * arg) +#define SET_SB(x) mddev->sb->x = info->x +static int set_array_info (mddev_t * mddev, mdu_array_info_t *info) { - mdu_array_info_t info; - - if (mddev->sb) { - printk("array md%d already has a superblock!\n", - mdidx(mddev)); - return -EBUSY; - } - - if (md_copy_from_user(&info, arg, sizeof(info))) - return -EFAULT; if (alloc_array_sb(mddev)) return -ENOMEM; @@ -2634,11 +2657,25 @@ static int md_ioctl (struct inode *inode, struct file *file, printk("ioctl, reason %d, cmd %d\n", err, cmd); goto abort; } - err = set_array_info(mddev, (void *)arg); - if (err) { - printk("couldnt set array info. %d\n", err); + + if (mddev->sb) { + printk("array md%d already has a superblock!\n", + mdidx(mddev)); + err = -EBUSY; goto abort_unlock; } + if (arg) { + mdu_array_info_t info; + if (md_copy_from_user(&info, (void*)arg, sizeof(info))) { + err = -EFAULT; + goto abort_unlock; + } + err = set_array_info(mddev, &info); + if (err) { + printk("couldnt set array info. %d\n", err); + goto abort_unlock; + } + } goto done_unlock; case START_ARRAY: @@ -2669,6 +2706,11 @@ static int md_ioctl (struct inode *inode, struct file *file, printk("ioctl lock interrupted, reason %d, cmd %d\n",err, cmd); goto abort; } + /* if we don't have a superblock yet, only ADD_NEW_DISK or STOP_ARRAY is allowed */ + if (!mddev->sb && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY && cmd != RUN_ARRAY) { + err = -ENODEV; + goto abort_unlock; + } /* * Commands even a read-only array can execute: @@ -2689,7 +2731,10 @@ static int md_ioctl (struct inode *inode, struct file *file, case STOP_ARRAY: err = do_md_stop (mddev, 0); - goto done; + if (err) + goto done_unlock; + else + goto done; case STOP_ARRAY_RO: err = do_md_stop (mddev, 1); @@ -2738,9 +2783,14 @@ static int md_ioctl (struct inode *inode, struct file *file, goto done_unlock; case ADD_NEW_DISK: - err = add_new_disk(mddev, (void *)arg); + { + mdu_disk_info_t info; + if (md_copy_from_user(&info, (void*)arg, sizeof(info))) + err = -EFAULT; + else + err = add_new_disk(mddev, (void *)arg); goto done_unlock; - + } case HOT_REMOVE_DISK: err = hot_remove_disk(mddev, (kdev_t)arg); goto done_unlock; @@ -2771,13 +2821,13 @@ static int md_ioctl (struct inode *inode, struct file *file, case RUN_ARRAY: { +/* The data is never used.... mdu_param_t param; - err = md_copy_from_user(¶m, (mdu_param_t *)arg, sizeof(param)); if (err) goto abort_unlock; - +*/ err = do_md_run (mddev); /* * we have to clean up the mess if @@ -2825,7 +2875,7 @@ static struct block_device_operations md_fops= open: md_open, ioctl: md_ioctl, }; - + int md_thread(void * arg) { @@ -3020,9 +3070,9 @@ static int status_unused (char * page) static int status_resync (char * page, mddev_t * mddev) { int sz = 0; - unsigned int max_blocks, resync, res, dt, tt, et; + unsigned long max_blocks, resync, res, dt, db, rt; - resync = mddev->curr_resync; + resync = mddev->curr_resync - atomic_read(&mddev->recovery_active); max_blocks = mddev->sb->size; /* @@ -3047,13 +3097,13 @@ static int status_resync (char * page, mddev_t * mddev) /* * true resync */ - sz += sprintf(page + sz, " resync =%3u.%u%% (%u/%u)", + sz += sprintf(page + sz, " resync =%3lu.%lu%% (%lu/%lu)", res/10, res % 10, resync, max_blocks); else /* * recovery ... */ - sz += sprintf(page + sz, " recovery =%3u.%u%% (%u/%u)", + sz += sprintf(page + sz, " recovery =%3lu.%lu%% (%lu/%lu)", res/10, res % 10, resync, max_blocks); /* @@ -3061,21 +3111,18 @@ static int status_resync (char * page, mddev_t * mddev) * the * 100 / 100 trick are important. We do a +1 to be * safe against division by zero. We only estimate anyway. * - * dt: time until now - * tt: total time - * et: estimated finish time + * dt: time from mark until now + * db: blocks written from mark until now + * rt: remaining time */ - dt = ((jiffies - mddev->resync_start) / HZ); - tt = (dt * (max_blocks / (resync/100+1)))/100; - if (tt > dt) - et = tt - dt; - else - /* - * ignore rounding effects near finish time - */ - et = 0; + dt = ((jiffies - mddev->resync_mark) / HZ); + if (!dt) dt++; + db = resync - mddev->resync_mark_cnt; + rt = (dt * ((max_blocks-resync) / (db/100+1)))/100; - sz += sprintf(page + sz, " finish=%u.%umin", et / 60, (et % 60)/6); + sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + + sz += sprintf(page + sz, " speed=%ldK/sec", db/dt); return sz; } @@ -3101,7 +3148,7 @@ static int md_status_read_proc(char *page, char **start, off_t off, sz += sprintf(page+sz, "not set\n"); else sz += sprintf(page+sz, "%d sectors\n", read_ahead[MD_MAJOR]); - + ITERATE_MDDEV(mddev,tmp) { sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), mddev->pers ? "" : "in"); @@ -3158,7 +3205,7 @@ int register_md_personality (int pnum, mdk_personality_t *p) if (pers[pnum]) return -EBUSY; - + pers[pnum] = p; printk(KERN_INFO "%s personality registered\n", p->name); return 0; @@ -3172,7 +3219,7 @@ int unregister_md_personality (int pnum) printk(KERN_INFO "%s personality unregistered\n", pers[pnum]->name); pers[pnum] = NULL; return 0; -} +} static mdp_disk_t *get_spare(mddev_t *mddev) { @@ -3236,13 +3283,17 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) } } +#define SYNC_MARKS 10 +#define SYNC_MARK_STEP (3*HZ) int md_do_sync(mddev_t *mddev, mdp_disk_t *spare) { mddev_t *mddev2; unsigned int max_blocks, currspeed, j, window, err, serialize; kdev_t read_disk = mddev_to_kdev(mddev); - unsigned long starttime; + unsigned long mark[SYNC_MARKS]; + unsigned long mark_cnt[SYNC_MARKS]; + int last_mark,m; struct md_list_head *tmp; unsigned long last_check; @@ -3287,8 +3338,13 @@ recheck: current->priority = 1; is_mddev_idle(mddev); /* this also initializes IO event counters */ - starttime = jiffies; - mddev->resync_start = starttime; + for (m = 0; m < SYNC_MARKS; m++) { + mark[m] = jiffies; + mark_cnt[m] = 0; + } + last_mark = 0; + mddev->resync_mark = mark[last_mark]; + mddev->resync_mark_cnt = mark_cnt[last_mark]; /* * Tune reconstruction: @@ -3301,12 +3357,7 @@ recheck: last_check = 0; for (j = 0; j < max_blocks;) { int blocks; - if (j) - mddev->curr_resync = j; -/* wait_event(mddev->recovery_wait, - atomic_read(&mddev->recovery_active) < window); -*/ blocks = mddev->pers->sync_request(mddev, j); if (blocks < 0) { @@ -3315,12 +3366,24 @@ recheck: } atomic_add(blocks, &mddev->recovery_active); j += blocks; + mddev->curr_resync = j; if (last_check + window > j) continue; run_task_queue(&tq_disk); //?? + if (jiffies >= mark[last_mark] + SYNC_MARK_STEP ) { + /* step marks */ + int next = (last_mark+1) % SYNC_MARKS; + + mddev->resync_mark = mark[next]; + mddev->resync_mark_cnt = mark_cnt[next]; + mark[next] = jiffies; + mark_cnt[next] = j - atomic_read(&mddev->recovery_active); + last_mark = next; + } + if (md_signal_pending(current)) { /* @@ -3345,7 +3408,8 @@ repeat: if (md_need_resched(current)) schedule(); - currspeed = j/((jiffies-starttime)/HZ + 1) + 1; + currspeed = (j-mddev->resync_mark_cnt)/((jiffies-mddev->resync_mark)/HZ +1) +1; + if (currspeed > sysctl_speed_limit_min) { current->priority = 1; @@ -3359,7 +3423,6 @@ repeat: } else current->priority = 40; } - wait_event(mddev->recovery_wait, atomic_read(&mddev->recovery_active)==0); fsync_dev(read_disk); printk(KERN_INFO "md: md%d: sync done.\n",mdidx(mddev)); err = 0; @@ -3367,6 +3430,7 @@ repeat: * this also signals 'finished resyncing' to md_stop */ out: + wait_event(mddev->recovery_wait, atomic_read(&mddev->recovery_active)==0); up(&mddev->resync_sem); out_nolock: mddev->curr_resync = 0; @@ -3433,9 +3497,9 @@ restart: if (disk_faulty(spare)) mddev->pers->diskop(mddev, &spare, DISKOP_SPARE_INACTIVE); - if (err == -EINTR) { + if (err == -EINTR || err == -ENOMEM) { /* - * Recovery got interrupted ... + * Recovery got interrupted, or ran out of mem ... * signal back that we have finished using the array. */ mddev->pers->diskop(mddev, &spare, @@ -3604,6 +3668,173 @@ int md__init md_init (void) return (0); } +#ifdef CONFIG_MD_BOOT +#define MAX_MD_BOOT_DEVS 8 +struct { + unsigned long set; + int pers[MAX_MD_BOOT_DEVS]; + int chunk[MAX_MD_BOOT_DEVS]; + kdev_t devices[MAX_MD_BOOT_DEVS][MAX_REAL]; +} md_setup_args md__initdata; + +/* + * Parse the command-line parameters given our kernel, but do not + * actually try to invoke the MD device now; that is handled by + * md_setup_drive after the low-level disk drivers have initialised. + * + * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which + * assigns the task of parsing integer arguments to the + * invoked program now). Added ability to initialise all + * the MD devices (by specifying multiple "md=" lines) + * instead of just one. -- KTK + * 18May2000: Added support for persistant-superblock arrays: + * md=n,0,factor,fault,device-list uses RAID0 for device n + * md=n,-1,factor,fault,device-list uses LINEAR for device n + * md=n,device-list reads a RAID superblock from the devices + * elements in device-list are read by name_to_kdev_t so can be + * a hex number or something like /dev/hda1 /dev/sdb + */ +extern kdev_t name_to_kdev_t(char *line) md__init; +static int md__init md_setup(char *str) +{ + int minor, level, factor, fault, i=0; + kdev_t device; + char *devnames, *pername = ""; + + if(get_option(&str, &minor) != 2) { /* MD Number */ + printk("md: Too few arguments supplied to md=.\n"); + return 0; + } + if (minor >= MAX_MD_BOOT_DEVS) { + printk ("md: Minor device number too high.\n"); + return 0; + } else if (md_setup_args.set & (1 << minor)) { + printk ("md: Warning - md=%d,... has been specified twice;\n" + " will discard the first definition.\n", minor); + } + switch(get_option(&str, &level)) { /* RAID Personality */ + case 2: /* could be 0 or -1.. */ + if (level == 0 || level == -1) { + if (get_option(&str, &factor) != 2 || /* Chunk Size */ + get_option(&str, &fault) != 2) { + printk("md: Too few arguments supplied to md=.\n"); + return 0; + } + md_setup_args.pers[minor] = level; + md_setup_args.chunk[minor] = 1 << (factor+12); + switch(level) { + case -1: + level = LINEAR; + pername = "linear"; + break; + case 0: + level = RAID0; + pername = "raid0"; + break; + default: + printk ("md: The kernel has not been configured for raid%d" + " support!\n", level); + return 0; + } + md_setup_args.pers[minor] = level; + break; + } + /* FALL THROUGH */ + case 1: /* the first device is numeric */ + md_setup_args.devices[minor][i++] = level; + /* FALL THROUGH */ + case 0: + md_setup_args.pers[minor] = 0; + pername="super-block"; + } + devnames = str; + for (; isb->nr_disks++; + mddev->sb->raid_disks++; + mddev->sb->active_disks++; + mddev->sb->working_disks++; + err = add_new_disk (mddev, &dinfo); + } + } else { + /* persistent */ + for (i = 0; (dev = md_setup_args.devices[minor][i]); i++) { + dinfo.major = MAJOR(dev); + dinfo.minor = MINOR(dev); + add_new_disk (mddev, &dinfo); + } + } + if (!err) + err = do_md_run(mddev); + if (err) { + mddev->sb_dirty = 0; + do_md_stop(mddev, 0); + printk("md: starting md%d failed\n", minor); + } + } +} + +__setup("md=", md_setup); +#endif + + MD_EXPORT_SYMBOL(md_size); MD_EXPORT_SYMBOL(register_md_personality); MD_EXPORT_SYMBOL(unregister_md_personality); diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c index 83700e235d7a..32821d93619b 100644 --- a/drivers/block/raid0.c +++ b/drivers/block/raid0.c @@ -325,17 +325,11 @@ static int raid0_status (char *page, mddev_t *mddev) static mdk_personality_t raid0_personality= { - "raid0", - raid0_make_request, - NULL, /* no special end_request */ - raid0_run, - raid0_stop, - raid0_status, - 0, - NULL, /* no error_handler */ - NULL, /* no diskop */ - NULL, /* no stop resync */ - NULL /* no restart resync */ + name: "raid0", + make_request: raid0_make_request, + run: raid0_run, + stop: raid0_stop, + status: raid0_status, }; #ifndef MODULE diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c index 89b3e7514e5c..69d03feca8ee 100644 --- a/drivers/block/raid1.c +++ b/drivers/block/raid1.c @@ -7,6 +7,11 @@ * * RAID-1 management functions. * + * Better read-balancing code written by Mika Kuoppala , 2000 + * + * Fixes to reconstruction by Jakob Østergaard" + * Various fixes by Neil Brown + * * 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) @@ -26,10 +31,7 @@ #define MD_DRIVER #define MD_PERSONALITY -#define MAX_LINEAR_SECTORS 128 - -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_WORK_PER_DISK 128 /* * The following can be used to debug the driver @@ -41,48 +43,256 @@ #define inline #define __inline__ #else -#define inline -#define __inline__ #define PRINTK(x...) do { } while (0) #endif static mdk_personality_t raid1_personality; static md_spinlock_t retry_list_lock = MD_SPIN_LOCK_UNLOCKED; -struct buffer_head *raid1_retry_list = NULL, **raid1_retry_tail; +struct raid1_bh *raid1_retry_list = NULL, **raid1_retry_tail; -static void * raid1_kmalloc (int size) +static struct buffer_head *raid1_alloc_bh(raid1_conf_t *conf, int cnt) { - void * ptr; - /* - * now we are rather fault tolerant than nice, but - * there are a couple of places in the RAID code where we - * simply can not afford to fail an allocation because - * there is no failure return path (eg. make_request()) + /* return a linked list of "cnt" struct buffer_heads. + * don't take any off the free list unless we know we can + * get all we need, otherwise we could deadlock */ - while (!(ptr = kmalloc (size, GFP_KERNEL))) - printk ("raid1: out of memory, retrying...\n"); + struct buffer_head *bh=NULL; + + while(cnt) { + struct buffer_head *t; + md_spin_lock_irq(&conf->device_lock); + if (conf->freebh_cnt >= cnt) + while (cnt) { + t = conf->freebh; + conf->freebh = t->b_next; + t->b_next = bh; + bh = t; + t->b_state = 0; + conf->freebh_cnt--; + cnt--; + } + md_spin_unlock_irq(&conf->device_lock); + if (cnt == 0) + break; + t = (struct buffer_head *)kmalloc(sizeof(struct buffer_head), GFP_KERNEL); + if (t) { + memset(t, 0, sizeof(*t)); + t->b_next = bh; + bh = t; + cnt--; + } else { + PRINTK("waiting for %d bh\n", cnt); + wait_event(conf->wait_buffer, conf->freebh_cnt >= cnt); + } + } + return bh; +} - memset(ptr, 0, size); - return ptr; +static inline void raid1_free_bh(raid1_conf_t *conf, struct buffer_head *bh) +{ + md_spin_lock_irq(&conf->device_lock); + while (bh) { + struct buffer_head *t = bh; + bh=bh->b_next; + if (t->b_pprev == NULL) + kfree(t); + else { + t->b_next= conf->freebh; + conf->freebh = t; + conf->freebh_cnt++; + } + } + md_spin_unlock_irq(&conf->device_lock); + wake_up(&conf->wait_buffer); } -static struct page * raid1_gfp (void) +static int raid1_grow_bh(raid1_conf_t *conf, int cnt) { - struct page *page; - /* - * now we are rather fault tolerant than nice, but - * there are a couple of places in the RAID code where we - * simply can not afford to fail an allocation because - * there is no failure return path (eg. make_request()) - * FIXME: be nicer here. - */ - while (!(page = (void*)alloc_page(GFP_KERNEL))) { - printk ("raid1: GFP out of memory, retrying...\n"); - schedule_timeout(2); + /* allocate cnt buffer_heads, possibly less if kalloc fails */ + int i = 0; + + while (i < cnt) { + struct buffer_head *bh; + bh = kmalloc(sizeof(*bh), GFP_KERNEL); + if (!bh) break; + memset(bh, 0, sizeof(*bh)); + + md_spin_lock_irq(&conf->device_lock); + bh->b_pprev = &conf->freebh; + bh->b_next = conf->freebh; + conf->freebh = bh; + conf->freebh_cnt++; + md_spin_unlock_irq(&conf->device_lock); + + i++; } + return i; +} + +static int raid1_shrink_bh(raid1_conf_t *conf, int cnt) +{ + /* discard cnt buffer_heads, if we can find them */ + int i = 0; - return page; + md_spin_lock_irq(&conf->device_lock); + while ((i < cnt) && conf->freebh) { + struct buffer_head *bh = conf->freebh; + conf->freebh = bh->b_next; + kfree(bh); + i++; + conf->freebh_cnt--; + } + md_spin_unlock_irq(&conf->device_lock); + return i; +} + + +static struct raid1_bh *raid1_alloc_r1bh(raid1_conf_t *conf) +{ + struct raid1_bh *r1_bh = NULL; + + do { + md_spin_lock_irq(&conf->device_lock); + if (conf->freer1) { + r1_bh = conf->freer1; + conf->freer1 = r1_bh->next_r1; + r1_bh->next_r1 = NULL; + r1_bh->state = 0; + r1_bh->bh_req.b_state = 0; + } + md_spin_unlock_irq(&conf->device_lock); + if (r1_bh) + return r1_bh; + r1_bh = (struct raid1_bh *) kmalloc(sizeof(struct raid1_bh), + GFP_KERNEL); + if (r1_bh) { + memset(r1_bh, 0, sizeof(*r1_bh)); + return r1_bh; + } + wait_event(conf->wait_buffer, conf->freer1); + } while (1); +} + +static inline void raid1_free_r1bh(struct raid1_bh *r1_bh) +{ + struct buffer_head *bh = r1_bh->mirror_bh_list; + raid1_conf_t *conf = mddev_to_conf(r1_bh->mddev); + + r1_bh->mirror_bh_list = NULL; + + if (test_bit(R1BH_PreAlloc, &r1_bh->state)) { + md_spin_lock_irq(&conf->device_lock); + r1_bh->next_r1 = conf->freer1; + conf->freer1 = r1_bh; + md_spin_unlock_irq(&conf->device_lock); + } else { + kfree(r1_bh); + } + raid1_free_bh(conf, bh); +} + +static int raid1_grow_r1bh (raid1_conf_t *conf, int cnt) +{ + int i = 0; + + while (i < cnt) { + struct raid1_bh *r1_bh; + r1_bh = (struct raid1_bh*)kmalloc(sizeof(*r1_bh), GFP_KERNEL); + if (!r1_bh) + break; + memset(r1_bh, 0, sizeof(*r1_bh)); + + md_spin_lock_irq(&conf->device_lock); + set_bit(R1BH_PreAlloc, &r1_bh->state); + r1_bh->next_r1 = conf->freer1; + conf->freer1 = r1_bh; + md_spin_unlock_irq(&conf->device_lock); + + i++; + } + return i; +} + +static void raid1_shrink_r1bh(raid1_conf_t *conf) +{ + md_spin_lock_irq(&conf->device_lock); + while (conf->freer1) { + struct raid1_bh *r1_bh = conf->freer1; + conf->freer1 = r1_bh->next_r1; + kfree(r1_bh); + } + md_spin_unlock_irq(&conf->device_lock); +} + + + +static inline void raid1_free_buf(struct raid1_bh *r1_bh) +{ + struct buffer_head *bh = r1_bh->mirror_bh_list; + raid1_conf_t *conf = mddev_to_conf(r1_bh->mddev); + r1_bh->mirror_bh_list = NULL; + + md_spin_lock_irq(&conf->device_lock); + r1_bh->next_r1 = conf->freebuf; + conf->freebuf = r1_bh; + md_spin_unlock_irq(&conf->device_lock); + raid1_free_bh(conf, bh); +} + +static struct raid1_bh *raid1_alloc_buf(raid1_conf_t *conf) +{ + struct raid1_bh *r1_bh; + + md_spin_lock_irq(&conf->device_lock); + wait_event_lock_irq(conf->wait_buffer, conf->freebuf, conf->device_lock); + r1_bh = conf->freebuf; + conf->freebuf = r1_bh->next_r1; + r1_bh->next_r1= NULL; + md_spin_unlock_irq(&conf->device_lock); + + return r1_bh; +} + +static int raid1_grow_buffers (raid1_conf_t *conf, int cnt) +{ + int i = 0; + + md_spin_lock_irq(&conf->device_lock); + while (i < cnt) { + struct raid1_bh *r1_bh; + struct page *page; + + page = alloc_page(GFP_KERNEL); + if (!page) + break; + + r1_bh = (struct raid1_bh *) kmalloc(sizeof(*r1_bh), GFP_KERNEL); + if (!r1_bh) { + __free_page(page); + break; + } + memset(r1_bh, 0, sizeof(*r1_bh)); + r1_bh->bh_req.b_page = page; + r1_bh->bh_req.b_data = (char *) page_address(page); + r1_bh->next_r1 = conf->freebuf; + conf->freebuf = r1_bh; + i++; + } + md_spin_unlock_irq(&conf->device_lock); + return i; +} + +static void raid1_shrink_buffers (raid1_conf_t *conf) +{ + md_spin_lock_irq(&conf->device_lock); + while (conf->freebuf) { + struct raid1_bh *r1_bh = conf->freebuf; + conf->freebuf = r1_bh->next_r1; + __free_page(r1_bh->bh_req.b_page); + kfree(r1_bh); + } + md_spin_unlock_irq(&conf->device_lock); } static int raid1_map (mddev_t *mddev, kdev_t *rdev, unsigned long size) @@ -106,19 +316,18 @@ static int raid1_map (mddev_t *mddev, kdev_t *rdev, unsigned long size) return (-1); } -static void raid1_reschedule_retry (struct buffer_head *bh) +static void raid1_reschedule_retry (struct raid1_bh *r1_bh) { unsigned long flags; - struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); mddev_t *mddev = r1_bh->mddev; raid1_conf_t *conf = mddev_to_conf(mddev); md_spin_lock_irqsave(&retry_list_lock, flags); if (raid1_retry_list == NULL) raid1_retry_tail = &raid1_retry_list; - *raid1_retry_tail = bh; - raid1_retry_tail = &r1_bh->next_retry; - r1_bh->next_retry = NULL; + *raid1_retry_tail = r1_bh; + raid1_retry_tail = &r1_bh->next_r1; + r1_bh->next_r1 = NULL; md_spin_unlock_irqrestore(&retry_list_lock, flags); md_wakeup_thread(conf->thread); } @@ -166,7 +375,7 @@ static void raid1_end_bh_io (struct raid1_bh *r1_bh, int uptodate) test_bit(R1BH_SyncPhase, &r1_bh->state)); bh->b_end_io(bh, uptodate); - kfree(r1_bh); + raid1_free_r1bh(r1_bh); } void raid1_end_request (struct buffer_head *bh, int uptodate) { @@ -176,7 +385,7 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) * this branch is our 'one mirror IO has finished' event handler: */ if (!uptodate) - md_error (bh->b_dev, bh->b_rdev); + md_error (mddev_to_kdev(r1_bh->mddev), bh->b_dev); else /* * Set R1BH_Uptodate in our master buffer_head, so that @@ -208,7 +417,7 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) */ printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", partition_name(bh->b_dev), bh->b_blocknr); - raid1_reschedule_retry(bh); + raid1_reschedule_retry(r1_bh); return; } @@ -219,29 +428,128 @@ void raid1_end_request (struct buffer_head *bh, int uptodate) * already. */ - if (atomic_dec_and_test(&r1_bh->remaining)) { - int i, disks = MD_SB_DISKS; + if (atomic_dec_and_test(&r1_bh->remaining)) + raid1_end_bh_io(r1_bh, test_bit(R1BH_Uptodate, &r1_bh->state)); +} - for ( i = 0; i < disks; i++) { - struct buffer_head *bh = r1_bh->mirror_bh[i]; - if (bh) { - // FIXME: make us a regular bcache member - kfree(bh); - } +/* + * This routine returns the disk from which the requested read should + * be done. It bookkeeps the last read position for every disk + * in array and when new read requests come, the disk which last + * position is nearest to the request, is chosen. + * + * TODO: now if there are 2 mirrors in the same 2 devices, performance + * degrades dramatically because position is mirror, not device based. + * This should be changed to be device based. Also atomic sequential + * reads should be somehow balanced. + */ + +static int raid1_read_balance (raid1_conf_t *conf, struct buffer_head *bh) +{ + int new_disk = conf->last_used; + const int sectors = bh->b_size >> 9; + const long this_sector = bh->b_blocknr * sectors; + int disk = new_disk; + unsigned long new_distance; + unsigned long current_distance; + + /* + * Check if it is sane at all to balance + */ + + if (conf->resync_mirrors) + goto rb_out; + + if (conf->working_disks < 2) { + int i = 0; + + while( !conf->mirrors[new_disk].operational && + (i < MD_SB_DISKS) ) { + new_disk = conf->mirrors[new_disk].next; + i++; } + + if (i >= MD_SB_DISKS) { + /* + * This means no working disk was found + * Nothing much to do, lets not change anything + * and hope for the best... + */ + + new_disk = conf->last_used; + } + + goto rb_out; + } - raid1_end_bh_io(r1_bh, test_bit(R1BH_Uptodate, &r1_bh->state)); + /* + * Don't touch anything for sequential reads. + */ + + if (this_sector == conf->mirrors[new_disk].head_position) + goto rb_out; + + /* + * If reads have been done only on a single disk + * for a time, lets give another disk a change. + * This is for kicking those idling disks so that + * they would find work near some hotspot. + */ + + if (conf->sect_count >= conf->mirrors[new_disk].sect_limit) { + conf->sect_count = 0; + + while( new_disk != conf->mirrors[new_disk].next ) { + if ((conf->mirrors[new_disk].write_only) || + (!conf->mirrors[new_disk].operational) ) + continue; + + new_disk = conf->mirrors[new_disk].next; + break; + } + + goto rb_out; } + + current_distance = abs(this_sector - + conf->mirrors[disk].head_position); + + /* Find the disk which is closest */ + + while( conf->mirrors[disk].next != conf->last_used ) { + disk = conf->mirrors[disk].next; + + if ((conf->mirrors[disk].write_only) || + (!conf->mirrors[disk].operational)) + continue; + + new_distance = abs(this_sector - + conf->mirrors[disk].head_position); + + if (new_distance < current_distance) { + conf->sect_count = 0; + current_distance = new_distance; + new_disk = disk; + } + } + +rb_out: + conf->mirrors[new_disk].head_position = this_sector + sectors; + + conf->last_used = new_disk; + conf->sect_count += sectors; + + return new_disk; } static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, struct buffer_head * bh) { raid1_conf_t *conf = mddev_to_conf(mddev); - struct buffer_head *mirror_bh[MD_SB_DISKS], *bh_req; + struct buffer_head *bh_req, *bhl; struct raid1_bh * r1_bh; int disks = MD_SB_DISKS; - int i, sum_bhs = 0, switch_disks = 0, sectors; + int i, sum_bhs = 0, sectors; struct mirror_info *mirror; DECLARE_WAITQUEUE(wait, current); @@ -279,8 +587,7 @@ static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, return 0; } } - r1_bh = raid1_kmalloc (sizeof (struct raid1_bh)); - + r1_bh = raid1_alloc_r1bh (conf); spin_lock_irq(&conf->segment_lock); wait_event_lock_irq(conf->wait_done, @@ -305,41 +612,20 @@ static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, r1_bh->master_bh = bh; r1_bh->mddev = mddev; r1_bh->cmd = rw; - bh->b_rsector = bh->b_blocknr * (bh->b_size>>9); + sectors = bh->b_size >> 9; if (rw == READ) { - int last_used = conf->last_used; - /* * read balancing logic: */ - mirror = conf->mirrors + last_used; - bh->b_rdev = mirror->dev; - sectors = bh->b_size >> 9; - - switch_disks = 0; - if (bh->b_blocknr * sectors == conf->next_sect) { - conf->sect_count += sectors; - if (conf->sect_count >= mirror->sect_limit) - switch_disks = 1; - } else - switch_disks = 1; - conf->next_sect = (bh->b_blocknr + 1) * sectors; - /* - * Do not switch disks if full resync is in progress ... - */ - if (switch_disks && !conf->resync_mirrors) { - conf->sect_count = 0; - last_used = conf->last_used = mirror->next; - /* - * Do not switch to write-only disks ... - * reconstruction is in progress - */ - while (conf->mirrors[last_used].write_only) - conf->last_used = conf->mirrors[last_used].next; - } + mirror = conf->mirrors + raid1_read_balance(conf, bh); + bh_req = &r1_bh->bh_req; memcpy(bh_req, bh, sizeof(*bh)); + bh_req->b_blocknr = bh->b_rsector * sectors; + bh_req->b_dev = mirror->dev; + bh_req->b_rdev = mirror->dev; + /* bh_req->b_rsector = bh->n_rsector; */ bh_req->b_end_io = raid1_end_request; bh_req->b_dev_id = r1_bh; q = blk_get_queue(bh_req->b_rdev); @@ -351,15 +637,11 @@ static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, * WRITE: */ + bhl = raid1_alloc_bh(conf, conf->raid_disks); for (i = 0; i < disks; i++) { - - if (!conf->mirrors[i].operational) { - /* - * the r1_bh->mirror_bh[i] pointer remains NULL - */ - mirror_bh[i] = NULL; + struct buffer_head *mbh; + if (!conf->mirrors[i].operational) continue; - } /* * We should use a private pool (size depending on NR_REQUEST), @@ -373,30 +655,38 @@ static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, * manner in the write branch. Look how we lock the buffer at the * beginning of this function to grok the difference ;) */ - mirror_bh[i] = raid1_kmalloc(sizeof(struct buffer_head)); - mirror_bh[i]->b_this_page = (struct buffer_head *)1; + mbh = bhl; + if (mbh == NULL) { + MD_BUG(); + break; + } + bhl = mbh->b_next; + mbh->b_next = NULL; + mbh->b_this_page = (struct buffer_head *)1; /* - * prepare mirrored bh (fields ordered for max mem throughput): + * prepare mirrored mbh (fields ordered for max mem throughput): */ - mirror_bh[i]->b_blocknr = bh->b_blocknr; - mirror_bh[i]->b_dev = bh->b_dev; - mirror_bh[i]->b_rdev = conf->mirrors[i].dev; - mirror_bh[i]->b_rsector = bh->b_rsector; - mirror_bh[i]->b_state = (1<b_blocknr = bh->b_rsector * sectors; + mbh->b_dev = conf->mirrors[i].dev; + mbh->b_rdev = conf->mirrors[i].dev; + mbh->b_rsector = bh->b_rsector; + mbh->b_state = (1<b_count, 1); - mirror_bh[i]->b_size = bh->b_size; - mirror_bh[i]->b_data = bh->b_data; - mirror_bh[i]->b_list = BUF_LOCKED; - mirror_bh[i]->b_end_io = raid1_end_request; - mirror_bh[i]->b_dev_id = r1_bh; - - r1_bh->mirror_bh[i] = mirror_bh[i]; + atomic_set(&mbh->b_count, 1); + mbh->b_size = bh->b_size; + mbh->b_page = bh->b_page; + mbh->b_data = bh->b_data; + mbh->b_list = BUF_LOCKED; + mbh->b_end_io = raid1_end_request; + mbh->b_dev_id = r1_bh; + + mbh->b_next = r1_bh->mirror_bh_list; + r1_bh->mirror_bh_list = mbh; sum_bhs++; } - + if (bhl) raid1_free_bh(conf,bhl); md_atomic_set(&r1_bh->remaining, sum_bhs); /* @@ -410,12 +700,12 @@ static int raid1_make_request (request_queue_t *q, mddev_t *mddev, int rw, * all requests finish until we had a chance to set up the * semaphore correctly ... lots of races). */ - for (i = 0; i < disks; i++) { - struct buffer_head *mbh = mirror_bh[i]; - if (mbh) { - q = blk_get_queue(mbh->b_rdev); - generic_make_request(q, rw, mbh); - } + bh = r1_bh->mirror_bh_list; + while(bh) { + struct buffer_head *bh2 = bh; + bh = bh->b_next; + q = blk_get_queue(bh2->b_rdev); + generic_make_request(q, rw, bh2); } return (0); } @@ -792,6 +1082,7 @@ static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) adisk->write_only = 0; adisk->spare = 1; adisk->used_slot = 1; + adisk->head_position = 0; conf->nr_disks++; break; @@ -803,6 +1094,10 @@ static int raid1_diskop(mddev_t *mddev, mdp_disk_t **d, int state) } abort: md_spin_unlock_irq(&conf->device_lock); + if (state == DISKOP_SPARE_ACTIVE || state == DISKOP_SPARE_INACTIVE) + /* should move to "END_REBUILD" when such exists */ + raid1_shrink_buffers(conf); + print_raid1_conf(conf); return err; } @@ -836,19 +1131,19 @@ static void raid1d (void *data) for (;;) { md_spin_lock_irqsave(&retry_list_lock, flags); - bh = raid1_retry_list; - if (!bh) + r1_bh = raid1_retry_list; + if (!r1_bh) break; - r1_bh = (struct raid1_bh *)(bh->b_dev_id); - raid1_retry_list = r1_bh->next_retry; + raid1_retry_list = r1_bh->next_r1; md_spin_unlock_irqrestore(&retry_list_lock, flags); - mddev = kdev_to_mddev(bh->b_dev); + mddev = r1_bh->mddev; if (mddev->sb_dirty) { printk(KERN_INFO "dirty sb detected, updating.\n"); mddev->sb_dirty = 0; md_update_sb(mddev); } + bh = &r1_bh->bh_req; switch(r1_bh->cmd) { case SPECIAL: /* have to allocate lots of bh structures and @@ -857,68 +1152,74 @@ static void raid1d (void *data) if (test_bit(R1BH_Uptodate, &r1_bh->state)) { int i, sum_bhs = 0; int disks = MD_SB_DISKS; - struct buffer_head *mirror_bh[MD_SB_DISKS]; + struct buffer_head *bhl, *mbh; raid1_conf_t *conf; + int sectors = bh->b_size >> 9; conf = mddev_to_conf(mddev); + bhl = raid1_alloc_bh(conf, conf->raid_disks); /* don't really need this many */ for (i = 0; i < disks ; i++) { - if (!conf->mirrors[i].operational) { - mirror_bh[i] = NULL; + if (!conf->mirrors[i].operational) continue; - } - if (i==conf->last_used) { + if (i==conf->last_used) /* we read from here, no need to write */ - mirror_bh[i] = NULL; continue; - } if (i < conf->raid_disks - && !conf->resync_mirrors) { + && !conf->resync_mirrors) /* don't need to write this, * we are just rebuilding */ - mirror_bh[i] = NULL; continue; + mbh = bhl; + if (!mbh) { + MD_BUG(); + break; } + bhl = mbh->b_next; + mbh->b_this_page = (struct buffer_head *)1; - mirror_bh[i] = raid1_kmalloc(sizeof(struct buffer_head)); - mirror_bh[i]->b_this_page = (struct buffer_head *)1; /* * prepare mirrored bh (fields ordered for max mem throughput): */ - mirror_bh[i]->b_blocknr = bh->b_blocknr; - mirror_bh[i]->b_dev = bh->b_dev; - mirror_bh[i]->b_rdev = conf->mirrors[i].dev; - mirror_bh[i]->b_rsector = bh->b_rsector; - mirror_bh[i]->b_state = (1<b_blocknr = bh->b_blocknr; + mbh->b_dev = conf->mirrors[i].dev; + mbh->b_rdev = conf->mirrors[i].dev; + mbh->b_rsector = bh->b_blocknr * sectors; + mbh->b_state = (1<b_count, 1); + mbh->b_size = bh->b_size; + mbh->b_page = bh->b_page; + mbh->b_data = bh->b_data; + mbh->b_list = BUF_LOCKED; + mbh->b_end_io = end_sync_write; + mbh->b_dev_id = r1_bh; + + mbh->b_next = r1_bh->mirror_bh_list; + r1_bh->mirror_bh_list = mbh; - atomic_set(&mirror_bh[i]->b_count, 1); - mirror_bh[i]->b_size = bh->b_size; - mirror_bh[i]->b_data = bh->b_data; - mirror_bh[i]->b_list = BUF_LOCKED; - mirror_bh[i]->b_end_io = end_sync_write; - mirror_bh[i]->b_dev_id = r1_bh; - - r1_bh->mirror_bh[i] = mirror_bh[i]; sum_bhs++; } md_atomic_set(&r1_bh->remaining, sum_bhs); - for ( i = 0; i < disks ; i++) { - struct buffer_head *mbh = mirror_bh[i]; - if (mbh) { - q = blk_get_queue(mbh->b_rdev); - generic_make_request(q, WRITE, mbh); - } + if (bhl) raid1_free_bh(conf, bhl); + mbh = r1_bh->mirror_bh_list; + while (mbh) { + struct buffer_head *bh1 = mbh; + mbh = mbh->b_next; + q = blk_get_queue(bh1->b_rdev); + generic_make_request(q, WRITE, bh1); + drive_stat_acct(bh1->b_rdev, WRITE, -bh1->b_size/512, 0); } } else { - dev = bh->b_rdev; - raid1_map (mddev, &bh->b_rdev, bh->b_size >> 9); - if (bh->b_rdev == dev) { + dev = bh->b_dev; + raid1_map (mddev, &bh->b_dev, bh->b_size >> 9); + if (bh->b_dev == dev) { printk (IO_ERROR, partition_name(bh->b_dev), bh->b_blocknr); md_done_sync(mddev, bh->b_size>>10, 0); } else { printk (REDIRECT_SECTOR, partition_name(bh->b_dev), bh->b_blocknr); + bh->b_rdev = bh->b_dev; q = blk_get_queue(bh->b_rdev); generic_make_request (q, READ, bh); } @@ -927,15 +1228,16 @@ static void raid1d (void *data) break; case READ: case READA: - dev = bh->b_rdev; + dev = bh->b_dev; - raid1_map (mddev, &bh->b_rdev, bh->b_size >> 9); - if (bh->b_rdev == dev) { + raid1_map (mddev, &bh->b_dev, bh->b_size >> 9); + if (bh->b_dev == dev) { printk (IO_ERROR, partition_name(bh->b_dev), bh->b_blocknr); raid1_end_bh_io(r1_bh, 0); } else { printk (REDIRECT_SECTOR, partition_name(bh->b_dev), bh->b_blocknr); + bh->b_rdev = bh->b_dev; q = blk_get_queue(bh->b_rdev); generic_make_request (q, r1_bh->cmd, bh); } @@ -961,15 +1263,37 @@ static void raid1syncd (void *data) if (conf->resync_mirrors == 2) return; down(&mddev->recovery_sem); - if (md_do_sync(mddev, NULL)) { - up(&mddev->recovery_sem); - return; + if (!md_do_sync(mddev, NULL)) { + /* + * Only if everything went Ok. + */ + conf->resync_mirrors = 0; } - /* - * Only if everything went Ok. + + /* If reconstruction was interrupted, we need to close the "active" and "pending" + * holes. + * we know that there are no active rebuild requests, os cnt_active == cnt_ready ==0 */ - conf->resync_mirrors = 0; + /* this is really needed when recovery stops too... */ + spin_lock_irq(&conf->segment_lock); + conf->start_active = conf->start_pending; + conf->start_ready = conf->start_pending; + wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); + conf->start_active =conf->start_ready = conf->start_pending = conf->start_future; + conf->start_future = mddev->sb->size+1; + conf->cnt_pending = conf->cnt_future; + conf->cnt_future = 0; + conf->phase = conf->phase ^1; + wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); + conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0; + conf->phase = 0; + conf->cnt_future = conf->cnt_done;; + conf->cnt_done = 0; + spin_unlock_irq(&conf->segment_lock); + wake_up(&conf->wait_done); + up(&mddev->recovery_sem); + raid1_shrink_buffers(conf); } /* @@ -1032,12 +1356,19 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) spin_lock_irq(&conf->segment_lock); if (!block_nr) { /* initialize ...*/ + int buffs; conf->start_active = 0; conf->start_ready = 0; conf->start_pending = 0; conf->start_future = 0; conf->phase = 0; - conf->window = 128; + /* we want enough buffers to hold twice the window of 128*/ + buffs = 128 *2 / (PAGE_SIZE>>9); + buffs = raid1_grow_buffers(conf, buffs); + if (buffs < 2) + goto nomem; + + conf->window = buffs*(PAGE_SIZE>>9)/2; conf->cnt_future += conf->cnt_done+conf->cnt_pending; conf->cnt_done = conf->cnt_pending = 0; if (conf->cnt_ready || conf->cnt_active) @@ -1057,7 +1388,7 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) conf->start_ready = conf->start_pending; conf->start_pending = conf->start_future; conf->start_future = conf->start_future+conf->window; - // Note: falling of the end is not a problem + // Note: falling off the end is not a problem conf->phase = conf->phase ^1; conf->cnt_active = conf->cnt_ready; conf->cnt_ready = 0; @@ -1075,12 +1406,11 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) */ mirror = conf->mirrors+conf->last_used; - r1_bh = raid1_kmalloc (sizeof (struct raid1_bh)); + r1_bh = raid1_alloc_buf (conf); r1_bh->master_bh = NULL; r1_bh->mddev = mddev; r1_bh->cmd = SPECIAL; bh = &r1_bh->bh_req; - memset(bh, 0, sizeof(*bh)); bh->b_blocknr = block_nr; bsize = 1024; @@ -1091,11 +1421,15 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) } bh->b_size = bsize; bh->b_list = BUF_LOCKED; - bh->b_dev = mddev_to_kdev(mddev); + bh->b_dev = mirror->dev; bh->b_rdev = mirror->dev; bh->b_state = (1<b_page = raid1_gfp(); - bh->b_data = (char *) page_address(bh->b_page); + if (!bh->b_page) + BUG(); + if (!bh->b_data) + BUG(); + if (bh->b_data != (char *) page_address(bh->b_page)) + BUG(); bh->b_end_io = end_sync_read; bh->b_dev_id = (void *) r1_bh; bh->b_rsector = block_nr<<1; @@ -1106,6 +1440,11 @@ static int raid1_sync_request (mddev_t *mddev, unsigned long block_nr) drive_stat_acct(bh->b_rdev, READ, -bh->b_size/512, 0); return (bsize >> 10); + +nomem: + raid1_shrink_buffers(conf); + spin_unlock_irq(&conf->segment_lock); + return -ENOMEM; } static void end_sync_read(struct buffer_head *bh, int uptodate) @@ -1117,10 +1456,10 @@ static void end_sync_read(struct buffer_head *bh, int uptodate) * We don't do much here, just schedule handling by raid1d */ if (!uptodate) - md_error (bh->b_dev, bh->b_rdev); + md_error (mddev_to_kdev(r1_bh->mddev), bh->b_dev); else set_bit(R1BH_Uptodate, &r1_bh->state); - raid1_reschedule_retry(bh); + raid1_reschedule_retry(r1_bh); } static void end_sync_write(struct buffer_head *bh, int uptodate) @@ -1128,22 +1467,12 @@ static void end_sync_write(struct buffer_head *bh, int uptodate) struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); if (!uptodate) - md_error (bh->b_dev, bh->b_rdev); + md_error (mddev_to_kdev(r1_bh->mddev), bh->b_dev); if (atomic_dec_and_test(&r1_bh->remaining)) { - int i, disks = MD_SB_DISKS; mddev_t *mddev = r1_bh->mddev; - unsigned long sect = bh->b_rsector; + unsigned long sect = bh->b_blocknr * (bh->b_size>>9); int size = bh->b_size; - - free_page((unsigned long)bh->b_data); - for ( i = 0; i < disks; i++) { - struct buffer_head *bh = r1_bh->mirror_bh[i]; - if (bh) { - // FIXME: make us a regular bcache member - kfree(bh); - } - } - kfree(r1_bh); + raid1_free_buf(r1_bh); sync_request_done(sect, mddev_to_conf(mddev)); md_done_sync(mddev,size>>10, uptodate); } @@ -1277,12 +1606,13 @@ static int raid1_run (mddev_t *mddev) * should be freed in raid1_stop()] */ - conf = raid1_kmalloc(sizeof(raid1_conf_t)); + conf = kmalloc(sizeof(raid1_conf_t), GFP_KERNEL); mddev->private = conf; if (!conf) { printk(MEM_ERROR, mdidx(mddev)); goto out; } + memset(conf, 0, sizeof(*conf)); ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) { @@ -1305,11 +1635,12 @@ static int raid1_run (mddev_t *mddev) disk->number = descriptor->number; disk->raid_disk = disk_idx; disk->dev = rdev->dev; - disk->sect_limit = MAX_LINEAR_SECTORS; + disk->sect_limit = MAX_WORK_PER_DISK; disk->operational = 0; disk->write_only = 0; disk->spare = 0; disk->used_slot = 1; + disk->head_position = 0; continue; } if (disk_active(descriptor)) { @@ -1336,11 +1667,12 @@ static int raid1_run (mddev_t *mddev) disk->number = descriptor->number; disk->raid_disk = disk_idx; disk->dev = rdev->dev; - disk->sect_limit = MAX_LINEAR_SECTORS; + disk->sect_limit = MAX_WORK_PER_DISK; disk->operational = 1; disk->write_only = 0; disk->spare = 0; disk->used_slot = 1; + disk->head_position = 0; conf->working_disks++; } else { /* @@ -1350,27 +1682,43 @@ static int raid1_run (mddev_t *mddev) disk->number = descriptor->number; disk->raid_disk = disk_idx; disk->dev = rdev->dev; - disk->sect_limit = MAX_LINEAR_SECTORS; + disk->sect_limit = MAX_WORK_PER_DISK; disk->operational = 0; disk->write_only = 0; disk->spare = 1; disk->used_slot = 1; + disk->head_position = 0; } } - if (!conf->working_disks) { - printk(NONE_OPERATIONAL, mdidx(mddev)); - goto out_free_conf; - } - conf->raid_disks = sb->raid_disks; conf->nr_disks = sb->nr_disks; conf->mddev = mddev; conf->device_lock = MD_SPIN_LOCK_UNLOCKED; conf->segment_lock = MD_SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&conf->wait_buffer); init_waitqueue_head(&conf->wait_done); init_waitqueue_head(&conf->wait_ready); + if (!conf->working_disks) { + printk(NONE_OPERATIONAL, mdidx(mddev)); + goto out_free_conf; + } + + + /* pre-allocate some buffer_head structures. + * As a minimum, 1 r1bh and raid_disks buffer_heads + * would probably get us by in tight memory situations, + * but a few more is probably a good idea. + * For now, try 16 r1bh and 16*raid_disks bufferheads + * This will allow at least 16 concurrent reads or writes + * even if kmalloc starts failing + */ + if (raid1_grow_r1bh(conf, 16) < 16 || + raid1_grow_bh(conf, 16*conf->raid_disks)< 16*conf->raid_disks) { + printk(MEM_ERROR, mdidx(mddev)); + goto out_free_conf; + } for (i = 0; i < MD_SB_DISKS; i++) { @@ -1389,6 +1737,7 @@ static int raid1_run (mddev_t *mddev) disk->write_only = 0; disk->spare = 0; disk->used_slot = 1; + disk->head_position = 0; } } @@ -1476,6 +1825,9 @@ static int raid1_run (mddev_t *mddev) return 0; out_free_conf: + raid1_shrink_r1bh(conf); + raid1_shrink_bh(conf, conf->freebh_cnt); + raid1_shrink_buffers(conf); kfree(conf); mddev->private = NULL; out: @@ -1504,29 +1856,6 @@ static int raid1_stop_resync (mddev_t *mddev) conf->resync_mirrors = 2; md_interrupt_thread(conf->resync_thread); - /* this is really needed when recovery stops too... */ - spin_lock_irq(&conf->segment_lock); - wait_event_lock_irq(conf->wait_done, !conf->cnt_active, conf->segment_lock); - conf->start_active = conf->start_ready; - conf->start_ready = conf->start_pending; - conf->cnt_active = conf->cnt_ready; - conf->cnt_ready = 0; - wait_event_lock_irq(conf->wait_done, !conf->cnt_active, conf->segment_lock); - conf->start_active = conf->start_ready; - conf->cnt_ready = 0; - wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); - conf->start_active =conf->start_ready = conf->start_pending = conf->start_future; - conf->start_future = mddev->sb->size+1; - conf->cnt_pending = conf->cnt_future; - conf->cnt_future = 0; - conf->phase = conf->phase ^1; - wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); - conf->start_active = conf->start_ready = conf->start_pending = conf->start_future = 0; - conf->phase = 0; - conf->cnt_done = conf->cnt_future; - conf->cnt_future = 0; - wake_up(&conf->wait_done); - printk(KERN_INFO "raid1: mirror resync was not fully finished, restarting next time.\n"); return 1; } @@ -1558,6 +1887,9 @@ static int raid1_stop (mddev_t *mddev) md_unregister_thread(conf->thread); if (conf->resync_thread) md_unregister_thread(conf->resync_thread); + raid1_shrink_r1bh(conf); + raid1_shrink_bh(conf, conf->freebh_cnt); + raid1_shrink_buffers(conf); kfree(conf); mddev->private = NULL; MOD_DEC_USE_COUNT; @@ -1566,18 +1898,16 @@ static int raid1_stop (mddev_t *mddev) static mdk_personality_t raid1_personality= { - "raid1", - raid1_make_request, - raid1_end_request, - raid1_run, - raid1_stop, - raid1_status, - 0, - raid1_error, - raid1_diskop, - raid1_stop_resync, - raid1_restart_resync, - raid1_sync_request + name: "raid1", + make_request: raid1_make_request, + run: raid1_run, + stop: raid1_stop, + status: raid1_status, + error_handler: raid1_error, + diskop: raid1_diskop, + stop_resync: raid1_stop_resync, + restart_resync: raid1_restart_resync, + sync_request: raid1_sync_request }; int raid1_init (void) diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c index 2379ac6a2ce8..e7bf85d08d64 100644 --- a/drivers/block/raid5.c +++ b/drivers/block/raid5.c @@ -16,6 +16,7 @@ */ +#include #include #include #include @@ -40,18 +41,21 @@ static mdk_personality_t raid5_personality; * The following can be used to debug the driver */ #define RAID5_DEBUG 0 -#define RAID5_PARANOIA 1 -#define CHECK_DEVLOCK() if (!spin_is_locked(&conf->device_lock)) BUG() -#define CHECK_SHLOCK(sh) if (!stripe_locked(sh)) BUG() +#define RAID5_PARANOIA 1 +#if RAID5_PARANOIA && CONFIG_SMP +# define CHECK_DEVLOCK() if (!spin_is_locked(&conf->device_lock)) BUG() +# define CHECK_SHLOCK(sh) if (!stripe_locked(sh)) BUG() +#else +# define CHECK_DEVLOCK() +# define CHECK_SHLOCK(unused) +#endif #if RAID5_DEBUG -#define PRINTK(x...) printk(x) +#define PRINTK(x...) printk(x) #define inline #define __inline__ #else -#define inline -#define __inline__ -#define PRINTK(x...) do { } while (0) +#define PRINTK(x...) do { } while (0) #endif static void print_raid5_conf (raid5_conf_t *conf); @@ -365,8 +369,8 @@ static int shrink_stripe_cache(raid5_conf_t *conf, int nr) out: md_spin_unlock_irq(&conf->device_lock); PRINTK("shrink completed, nr_hashed_stripes %d, nr_pending_strips %d\n", - atomic_read(&conf->nr_hashed_stripes), - atomic_read(&conf->nr_pending_stripes)); + atomic_read(&conf->nr_hashed_stripes), + atomic_read(&conf->nr_pending_stripes)); return count; } @@ -422,7 +426,7 @@ static inline struct stripe_head *alloc_stripe(raid5_conf_t *conf, unsigned long sh = get_free_stripe(conf); if (!sh && cnt < (conf->max_nr_stripes/8)) { md_wakeup_thread(conf->thread); - PRINTK("waiting for some stripes to complete - %d %d\n", cnt, conf->max_nr_stripes/8); + PRINTK("waiting for some stripes to complete - %d %d\n", cnt, conf->max_nr_stripes/8); schedule(); } remove_wait_queue(&conf->wait_for_stripe, &wait); @@ -570,7 +574,7 @@ static void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate) { struct buffer_head *bh = sh->bh_new[i]; - PRINTK("raid5_end_buffer_io %lu, uptodate: %d.\n", bh->b_rsector, uptodate); + PRINTK("raid5_end_buffer_io %lu, uptodate: %d.\n", bh->b_blocknr, uptodate); sh->bh_new[i] = NULL; raid5_free_bh(sh, sh->bh_req[i]); sh->bh_req[i] = NULL; @@ -578,7 +582,9 @@ static void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate) bh->b_end_io(bh, uptodate); if (!uptodate) printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for " - "block %lu\n", partition_name(bh->b_dev), bh->b_blocknr); + "block %lu\n", + partition_name(mddev_to_kdev(sh->raid_conf->mddev)), + bh->b_blocknr); } static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate) @@ -600,14 +606,14 @@ static void raid5_end_request (struct buffer_head * bh, int uptodate) md_spin_lock_irqsave(&sh->stripe_lock, flags); raid5_mark_buffer_uptodate(bh, uptodate); if (!uptodate) - md_error(bh->b_dev, bh->b_rdev); + md_error(mddev_to_kdev(conf->mddev), bh->b_dev); if (conf->failed_disks) { for (i = 0; i < disks; i++) { if (conf->disks[i].operational) continue; if (bh != sh->bh_old[i] && bh != sh->bh_req[i] && bh != sh->bh_copy[i]) continue; - if (bh->b_rdev != conf->disks[i].dev) + if (bh->b_dev != conf->disks[i].dev) continue; set_bit(STRIPE_ERROR, &sh->state); } @@ -623,10 +629,8 @@ static void raid5_end_request (struct buffer_head * bh, int uptodate) static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i) { raid5_conf_t *conf = sh->raid_conf; - mddev_t *mddev = conf->mddev; char *b_data; struct page *b_page; - kdev_t dev = mddev_to_kdev(mddev); int block = sh->sector / (sh->size >> 9); b_data = bh->b_data; @@ -634,14 +638,14 @@ static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, i memset (bh, 0, sizeof (struct buffer_head)); init_waitqueue_head(&bh->b_wait); init_buffer(bh, raid5_end_request, sh); - bh->b_dev = dev; + bh->b_dev = conf->disks[i].dev; bh->b_blocknr = block; bh->b_data = b_data; bh->b_page = b_page; bh->b_rdev = conf->disks[i].dev; - bh->b_rsector = sh->sector; + bh->b_rsector = sh->sector; bh->b_state = (1 << BH_Req) | (1 << BH_Mapped); bh->b_size = sh->size; @@ -708,14 +712,14 @@ static int raid5_error (mddev_t *mddev, kdev_t dev) } /* - * Input: a 'big' sector number, + * Input: a 'big' sector number, * Output: index of the data and parity disk, and the sector # in them. */ static unsigned long raid5_compute_sector(int r_sector, unsigned int raid_disks, unsigned int data_disks, unsigned int * dd_idx, unsigned int * pd_idx, raid5_conf_t *conf) { - unsigned int stripe; + unsigned int stripe; int chunk_number, chunk_offset; unsigned long new_sector; int sectors_per_chunk = conf->chunk_size >> 9; @@ -770,15 +774,6 @@ static unsigned long raid5_compute_sector(int r_sector, unsigned int raid_disks, * Finally, compute the new sector number */ new_sector = stripe * sectors_per_chunk + chunk_offset; - -#if 0 - if ( *dd_idx > data_disks || *pd_idx > data_disks || - chunk_offset + bh->b_size / 512 > sectors_per_chunk ) - - printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", - *dd_idx, *pd_idx, chunk_offset); -#endif - return new_sector; } @@ -849,9 +844,8 @@ static void compute_block(struct stripe_head *sh, int dd_idx) count = 1; } } - if(count != 1) { + if (count != 1) xor_block(count, &bh_ptr[0]); - } raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1); } @@ -1092,20 +1086,20 @@ static void handle_stripe_write (mddev_t *mddev , raid5_conf_t *conf, if (!operational[i] && !conf->resync_parity) { PRINTK("writing spare %d\n", i); atomic_inc(&sh->nr_pending); - bh->b_rdev = conf->spare->dev; + bh->b_dev = bh->b_rdev = conf->spare->dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, WRITERAW, bh); } else { #if 0 atomic_inc(&sh->nr_pending); - bh->b_rdev = conf->disks[i].dev; + bh->b_dev = bh->b_rdev = conf->disks[i].dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, WRITERAW, bh); #else if (!allclean || (i==sh->pd_idx)) { PRINTK("writing dirty %d\n", i); atomic_inc(&sh->nr_pending); - bh->b_rdev = conf->disks[i].dev; + bh->b_dev = bh->b_rdev = conf->disks[i].dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, WRITERAW, bh); } else { @@ -1151,7 +1145,7 @@ static void handle_stripe_write (mddev_t *mddev , raid5_conf_t *conf, continue; lock_get_bh(sh->bh_old[i]); atomic_inc(&sh->nr_pending); - sh->bh_old[i]->b_rdev = conf->disks[i].dev; + sh->bh_old[i]->b_dev = sh->bh_old[i]->b_rdev = conf->disks[i].dev; q = blk_get_queue(sh->bh_old[i]->b_rdev); generic_make_request(q, READ, sh->bh_old[i]); atomic_dec(&sh->bh_old[i]->b_count); @@ -1198,7 +1192,7 @@ static void handle_stripe_read (mddev_t *mddev , raid5_conf_t *conf, raid5_build_block(sh, sh->bh_old[i], i); lock_get_bh(sh->bh_old[i]); atomic_inc(&sh->nr_pending); - sh->bh_old[i]->b_rdev = conf->disks[i].dev; + sh->bh_old[i]->b_dev = sh->bh_old[i]->b_rdev = conf->disks[i].dev; q = blk_get_queue(sh->bh_old[i]->b_rdev); generic_make_request(q, READ, sh->bh_old[i]); atomic_dec(&sh->bh_old[i]->b_count); @@ -1228,7 +1222,7 @@ static void handle_stripe_read (mddev_t *mddev , raid5_conf_t *conf, #endif lock_get_bh(sh->bh_req[i]); atomic_inc(&sh->nr_pending); - sh->bh_req[i]->b_rdev = conf->disks[i].dev; + sh->bh_req[i]->b_dev = sh->bh_req[i]->b_rdev = conf->disks[i].dev; q = blk_get_queue(sh->bh_req[i]->b_rdev); generic_make_request(q, READ, sh->bh_req[i]); atomic_dec(&sh->bh_req[i]->b_count); @@ -1252,8 +1246,7 @@ static void handle_stripe_sync (mddev_t *mddev , raid5_conf_t *conf, * in bh_old */ PRINTK("handle_stripe_sync: sec=%lu disks=%d nr_cache=%d\n", sh->sector, disks, nr_cache); - if (nr_cache < disks-1 - || (nr_cache==disks-1 && !(parity_failed+nr_failed_other+nr_failed_overwrite)) + if ((nr_cache < disks-1) || ((nr_cache == disks-1) && !(parity_failed+nr_failed_other+nr_failed_overwrite)) ) { sh->phase = PHASE_READ_OLD; for (i = 0; i < disks; i++) { @@ -1267,7 +1260,7 @@ static void handle_stripe_sync (mddev_t *mddev , raid5_conf_t *conf, raid5_build_block(sh, bh, i); lock_get_bh(bh); atomic_inc(&sh->nr_pending); - bh->b_rdev = conf->disks[i].dev; + bh->b_dev = bh->b_rdev = conf->disks[i].dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, READ, bh); drive_stat_acct(bh->b_rdev, READ, -bh->b_size/512, 0); @@ -1297,7 +1290,7 @@ static void handle_stripe_sync (mddev_t *mddev , raid5_conf_t *conf, } atomic_inc(&sh->nr_pending); lock_get_bh(bh); - bh->b_rdev = conf->spare->dev; + bh->b_dev = bh->b_rdev = conf->spare->dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, WRITERAW, bh); drive_stat_acct(bh->b_rdev, WRITE, -bh->b_size/512, 0); @@ -1310,7 +1303,7 @@ static void handle_stripe_sync (mddev_t *mddev , raid5_conf_t *conf, } /* nr_cache == disks: - * check parity and compute/write if needed + * check parity and compute/write if needed */ compute_parity(sh, RECONSTRUCT_WRITE); @@ -1324,13 +1317,13 @@ static void handle_stripe_sync (mddev_t *mddev , raid5_conf_t *conf, atomic_set_buffer_dirty(bh); lock_get_bh(bh); atomic_inc(&sh->nr_pending); - bh->b_rdev = conf->disks[pd_idx].dev; + bh->b_dev = bh->b_rdev = conf->disks[pd_idx].dev; q = blk_get_queue(bh->b_rdev); generic_make_request(q, WRITERAW, bh); drive_stat_acct(bh->b_rdev, WRITE, -bh->b_size/512, 0); atomic_dec(&bh->b_count); PRINTK("handle_stripe_sync() %lu phase WRITE, pending %d buffers\n", - sh->sector, md_atomic_read(&sh->nr_pending)); + sh->sector, md_atomic_read(&sh->nr_pending)); } } @@ -1374,8 +1367,8 @@ static void handle_stripe(struct stripe_head *sh) } if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) || - (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ) || - (sh->cmd == STRIPE_SYNC && sh->phase == PHASE_WRITE) + (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ) || + (sh->cmd == STRIPE_SYNC && sh->phase == PHASE_WRITE) ) { /* * Completed @@ -1500,7 +1493,7 @@ static int raid5_make_request (request_queue_t *q, mddev_t *mddev, int rw, struc raid5_conf_t *conf = (raid5_conf_t *) mddev->private; const unsigned int raid_disks = conf->raid_disks; const unsigned int data_disks = raid_disks - 1; - unsigned int dd_idx, pd_idx; + unsigned int dd_idx, pd_idx; unsigned long new_sector; struct stripe_head *sh; @@ -1508,7 +1501,7 @@ static int raid5_make_request (request_queue_t *q, mddev_t *mddev, int rw, struc if (rw == READA) rw = READ; - new_sector = raid5_compute_sector(bh->b_blocknr*(bh->b_size>>9), + new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, &dd_idx, &pd_idx, conf); PRINTK("raid5_make_request, sector %lu\n", new_sector); @@ -1567,13 +1560,12 @@ static int raid5_sync_request (mddev_t *mddev, unsigned long block_nr) if (!conf->buffer_size) conf->buffer_size = /* device_bsize(mddev_to_kdev(mddev))*/ PAGE_SIZE; bufsize = conf->buffer_size; - /* Hmm... race on buffer_size ?? */ + /* Hmm... race on buffer_size ?? */ redone = block_nr% (bufsize>>10); block_nr -= redone; sh = get_lock_stripe(conf, block_nr<<1, bufsize); - first_sector = raid5_compute_sector(stripe*data_disks*sectors_per_chunk+chunk_offset, - raid_disks, data_disks, - &dd_idx, &pd_idx, conf); + first_sector = raid5_compute_sector(stripe*data_disks*sectors_per_chunk + + chunk_offset, raid_disks, data_disks, &dd_idx, &pd_idx, conf); sh->pd_idx = pd_idx; sh->cmd = STRIPE_SYNC; sh->phase = PHASE_BEGIN; @@ -2147,7 +2139,7 @@ static int raid5_diskop(mddev_t *mddev, mdp_disk_t **d, int state) } /* * When we activate a spare disk we _must_ have a disk in - * the lower (active) part of the array to replace. + * the lower (active) part of the array to replace. */ if ((failed_disk == -1) || (failed_disk >= conf->raid_disks)) { MD_BUG(); @@ -2380,18 +2372,16 @@ abort: static mdk_personality_t raid5_personality= { - "raid5", - raid5_make_request, - raid5_end_request, - raid5_run, - raid5_stop, - raid5_status, - 0, - raid5_error, - raid5_diskop, - raid5_stop_resync, - raid5_restart_resync, - raid5_sync_request + name: "raid5", + make_request: raid5_make_request, + run: raid5_run, + stop: raid5_stop, + status: raid5_status, + error_handler: raid5_error, + diskop: raid5_diskop, + stop_resync: raid5_stop_resync, + restart_resync: raid5_restart_resync, + sync_request: raid5_sync_request }; int raid5_init (void) diff --git a/drivers/block/rd.c b/drivers/block/rd.c index b78d88ef7c43..564f822eaa8e 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -693,6 +693,9 @@ free_inode: iput(inode); } +#ifdef CONFIG_MAC_FLOPPY +int swim3_fd_eject(int devnum); +#endif static void __init rd_load_disk(int n) { @@ -713,6 +716,12 @@ static void __init rd_load_disk(int n) if (rd_prompt) { #ifdef CONFIG_BLK_DEV_FD floppy_eject(); +#endif +#ifdef CONFIG_MAC_FLOPPY + if(MAJOR(ROOT_DEV) == FLOPPY_MAJOR) + swim3_fd_eject(MINOR(ROOT_DEV)); + else if(MAJOR(real_root_dev) == FLOPPY_MAJOR) + swim3_fd_eject(MINOR(real_root_dev)); #endif printk(KERN_NOTICE "VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER\n"); diff --git a/drivers/block/xor.c b/drivers/block/xor.c index 9f54be5a2877..18abb66213aa 100644 --- a/drivers/block/xor.c +++ b/drivers/block/xor.c @@ -1814,7 +1814,7 @@ static inline void pick_fastest_function(void) fastest = f; } #ifdef CONFIG_X86_XMM - if (boot_cpu_data.mmu_cr4_features & X86_CR4_OSXMMEXCPT) { + if (cpu_has_xmm) { fastest = &t_xor_block_pIII_kni; } #endif @@ -1849,7 +1849,7 @@ void calibrate_xor_block(void) #endif #ifdef CONFIG_X86_XMM - if (boot_cpu_data.mmu_cr4_features & X86_CR4_OSXMMEXCPT) { + if (cpu_has_xmm) { printk(KERN_INFO "raid5: KNI detected, trying cache-avoiding KNI checksum routine\n"); /* we force the use of the KNI xor block because it @@ -1892,4 +1892,3 @@ void calibrate_xor_block(void) #endif /* __sparc_v9__ */ MD_EXPORT_SYMBOL(xor_block); - diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index 5051e80baf17..bc8345cff0e9 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -110,8 +110,7 @@ void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons) if (changed) { wake_up(&mse->wait); - if (mse->fasyncptr) - kill_fasync(mse->fasyncptr, SIGIO, POLL_IN); + kill_fasync(&mse->fasyncptr, SIGIO, POLL_IN); } } diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index ff70792d728b..1c68f0cd2a95 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -39,6 +39,17 @@ else endif endif +ifeq ($(CONFIG_DRM_FFB),y) + OX_OBJS += ffb_drv.o + O_OBJS += ffb_context.o +else + ifeq ($(CONFIG_DRM_FFB),m) + MIX_OBJC += ffb_drv.o + MI_OBJS += ffb_context.o + M_OBJS += ffb.o + endif +endif + O_OBJS += $(L_OBJS) include $(TOPDIR)/Rules.make @@ -48,3 +59,6 @@ gamma.o : gamma_drv.o gamma_dma.o $(L_OBJS) tdfx.o: tdfx_drv.o tdfx_context.o $(L_OBJS) $(LD) $(LD_RFLAG) -r -o $@ tdfx_drv.o tdfx_context.o $(L_OBJS) + +ffb.o: ffb_drv.o ffb_context.o $(L_OBJS) + $(LD) $(LD_RFLAG) -r -o $@ ffb_drv.o ffb_context.o $(L_OBJS) diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c index 4e8c50c59a93..3d4c402224d6 100644 --- a/drivers/char/drm/bufs.c +++ b/drivers/char/drm/bufs.c @@ -72,11 +72,13 @@ int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd, switch (map->type) { case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: +#ifndef __sparc__ if (map->offset + map->size < map->offset || map->offset < virt_to_phys(high_memory)) { drm_free(map, sizeof(*map), DRM_MEM_MAPS); return -EINVAL; } +#endif #ifdef CONFIG_MTRR if (map->type == _DRM_FRAME_BUFFER || (map->flags & _DRM_WRITE_COMBINING)) { @@ -478,8 +480,10 @@ int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, -EFAULT); if (request.count >= dma->buf_count) { + down(¤t->mm->mmap_sem); virtual = do_mmap(filp, 0, dma->byte_count, PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->mm->mmap_sem); if (virtual > -1024UL) { /* Real error */ retcode = (signed long)virtual; diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 4d003ba2c6e2..b39fec3f704a 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -433,9 +433,9 @@ typedef struct drm_device { /* Context support */ int irq; /* Interrupt used by board */ - __volatile__ int context_flag; /* Context swapping flag */ - __volatile__ int interrupt_flag;/* Interruption handler flag */ - __volatile__ int dma_flag; /* DMA dispatch flag */ + __volatile__ long context_flag; /* Context swapping flag */ + __volatile__ long interrupt_flag;/* Interruption handler flag */ + __volatile__ long dma_flag; /* DMA dispatch flag */ struct timer_list timer; /* Timer for delaying ctx switch */ wait_queue_head_t context_wait; /* Processes waiting on ctx switch */ int last_checked; /* Last context checked for DMA */ diff --git a/drivers/char/drm/ffb_context.c b/drivers/char/drm/ffb_context.c new file mode 100644 index 000000000000..a10b598985b5 --- /dev/null +++ b/drivers/char/drm/ffb_context.c @@ -0,0 +1,530 @@ +/* $Id: ffb_context.c,v 1.3 2000/06/09 03:46:53 davem Exp $ + * ffb_context.c: Creator/Creator3D DRI/DRM context switching. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + * + * Almost entirely stolen from tdfx_context.c, see there + * for authors. + */ + +#include +#include + +#include "drmP.h" + +#include "ffb_drv.h" + +static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int i; + + for (i = 0; i < FFB_MAX_CTXS; i++) { + if (fpriv->hw_state[i] == NULL) + break; + } + if (i == FFB_MAX_CTXS) + return -1; + + fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL); + if (fpriv->hw_state[i] == NULL) + return -1; + + fpriv->hw_state[i]->is_2d_only = is_2d_only; + + /* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */ + return i + 1; +} + +static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + ctx->drawop = upa_readl(&ffb->drawop); + ctx->ppc = upa_readl(&ffb->ppc); + ctx->wid = upa_readl(&ffb->wid); + ctx->fg = upa_readl(&ffb->fg); + ctx->bg = upa_readl(&ffb->bg); + ctx->xclip = upa_readl(&ffb->xclip); + ctx->fbc = upa_readl(&ffb->fbc); + ctx->rop = upa_readl(&ffb->rop); + ctx->cmp = upa_readl(&ffb->cmp); + ctx->matchab = upa_readl(&ffb->matchab); + ctx->magnab = upa_readl(&ffb->magnab); + ctx->pmask = upa_readl(&ffb->pmask); + ctx->xpmask = upa_readl(&ffb->xpmask); + ctx->lpat = upa_readl(&ffb->lpat); + ctx->fontxy = upa_readl(&ffb->fontxy); + ctx->fontw = upa_readl(&ffb->fontw); + ctx->fontinc = upa_readl(&ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + ctx->ucsr = upa_readl(&ffb->ucsr); + return; + } + + /* Fetch drawop. */ + ctx->drawop = upa_readl(&ffb->drawop); + + /* If we were saving the vertex registers, this is where + * we would do it. We would save 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Capture rendering attributes. */ + + ctx->ppc = upa_readl(&ffb->ppc); /* Pixel Processor Control */ + ctx->wid = upa_readl(&ffb->wid); /* Current WID */ + ctx->fg = upa_readl(&ffb->fg); /* Constant FG color */ + ctx->bg = upa_readl(&ffb->bg); /* Constant BG color */ + ctx->consty = upa_readl(&ffb->consty); /* Constant Y */ + ctx->constz = upa_readl(&ffb->constz); /* Constant Z */ + ctx->xclip = upa_readl(&ffb->xclip); /* X plane clip */ + ctx->dcss = upa_readl(&ffb->dcss); /* Depth Cue Scale Slope */ + ctx->vclipmin = upa_readl(&ffb->vclipmin); /* Primary XY clip, minimum */ + ctx->vclipmax = upa_readl(&ffb->vclipmax); /* Primary XY clip, maximum */ + ctx->vclipzmin = upa_readl(&ffb->vclipzmin); /* Primary Z clip, minimum */ + ctx->vclipzmax = upa_readl(&ffb->vclipzmax); /* Primary Z clip, maximum */ + ctx->dcsf = upa_readl(&ffb->dcsf); /* Depth Cue Scale Front Bound */ + ctx->dcsb = upa_readl(&ffb->dcsb); /* Depth Cue Scale Back Bound */ + ctx->dczf = upa_readl(&ffb->dczf); /* Depth Cue Scale Z Front */ + ctx->dczb = upa_readl(&ffb->dczb); /* Depth Cue Scale Z Back */ + ctx->blendc = upa_readl(&ffb->blendc); /* Alpha Blend Control */ + ctx->blendc1 = upa_readl(&ffb->blendc1); /* Alpha Blend Color 1 */ + ctx->blendc2 = upa_readl(&ffb->blendc2); /* Alpha Blend Color 2 */ + ctx->fbc = upa_readl(&ffb->fbc); /* Frame Buffer Control */ + ctx->rop = upa_readl(&ffb->rop); /* Raster Operation */ + ctx->cmp = upa_readl(&ffb->cmp); /* Compare Controls */ + ctx->matchab = upa_readl(&ffb->matchab); /* Buffer A/B Match Ops */ + ctx->matchc = upa_readl(&ffb->matchc); /* Buffer C Match Ops */ + ctx->magnab = upa_readl(&ffb->magnab); /* Buffer A/B Magnitude Ops */ + ctx->magnc = upa_readl(&ffb->magnc); /* Buffer C Magnitude Ops */ + ctx->pmask = upa_readl(&ffb->pmask); /* RGB Plane Mask */ + ctx->xpmask = upa_readl(&ffb->xpmask); /* X Plane Mask */ + ctx->ypmask = upa_readl(&ffb->ypmask); /* Y Plane Mask */ + ctx->zpmask = upa_readl(&ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min); + ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max); + ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min); + ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max); + ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min); + ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max); + ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min); + ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max); + + ctx->lpat = upa_readl(&ffb->lpat); /* Line Pattern */ + ctx->fontxy = upa_readl(&ffb->fontxy); /* XY Font Coordinate */ + ctx->fontw = upa_readl(&ffb->fontw); /* Font Width */ + ctx->fontinc = upa_readl(&ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + ctx->dcss1 = upa_readl(&ffb->dcss1); /* Depth Cue Scale Slope 1 */ + ctx->dcss2 = upa_readl(&ffb->dcss2); /* Depth Cue Scale Slope 2 */ + ctx->dcss2 = upa_readl(&ffb->dcss3); /* Depth Cue Scale Slope 3 */ + ctx->dcs2 = upa_readl(&ffb->dcs2); /* Depth Cue Scale 2 */ + ctx->dcs3 = upa_readl(&ffb->dcs3); /* Depth Cue Scale 3 */ + ctx->dcs4 = upa_readl(&ffb->dcs4); /* Depth Cue Scale 4 */ + ctx->dcd2 = upa_readl(&ffb->dcd2); /* Depth Cue Depth 2 */ + ctx->dcd3 = upa_readl(&ffb->dcd3); /* Depth Cue Depth 3 */ + ctx->dcd4 = upa_readl(&ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + } + + /* Save the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. */ + ctx->ucsr = upa_readl(&ffb->ucsr); +} + +static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + upa_writel(ctx->ppc, &ffb->ppc); + upa_writel(ctx->wid, &ffb->wid); + upa_writel(ctx->fg, &ffb->fg); + upa_writel(ctx->bg, &ffb->bg); + upa_writel(ctx->xclip, &ffb->xclip); + upa_writel(ctx->fbc, &ffb->fbc); + upa_writel(ctx->rop, &ffb->rop); + upa_writel(ctx->cmp, &ffb->cmp); + upa_writel(ctx->matchab, &ffb->matchab); + upa_writel(ctx->magnab, &ffb->magnab); + upa_writel(ctx->pmask, &ffb->pmask); + upa_writel(ctx->xpmask, &ffb->xpmask); + upa_writel(ctx->lpat, &ffb->lpat); + upa_writel(ctx->fontxy, &ffb->fontxy); + upa_writel(ctx->fontw, &ffb->fontw); + upa_writel(ctx->fontinc, &ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + upa_writel(ctx->fbc, &ffb->fbc); + } + + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); + return; + } + + /* Restore drawop. */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Restore rendering attributes. */ + + upa_writel(ctx->ppc, &ffb->ppc); /* Pixel Processor Control */ + upa_writel(ctx->wid, &ffb->wid); /* Current WID */ + upa_writel(ctx->fg, &ffb->fg); /* Constant FG color */ + upa_writel(ctx->bg, &ffb->bg); /* Constant BG color */ + upa_writel(ctx->consty, &ffb->consty); /* Constant Y */ + upa_writel(ctx->constz, &ffb->constz); /* Constant Z */ + upa_writel(ctx->xclip, &ffb->xclip); /* X plane clip */ + upa_writel(ctx->dcss, &ffb->dcss); /* Depth Cue Scale Slope */ + upa_writel(ctx->vclipmin, &ffb->vclipmin); /* Primary XY clip, minimum */ + upa_writel(ctx->vclipmax, &ffb->vclipmax); /* Primary XY clip, maximum */ + upa_writel(ctx->vclipzmin, &ffb->vclipzmin); /* Primary Z clip, minimum */ + upa_writel(ctx->vclipzmax, &ffb->vclipzmax); /* Primary Z clip, maximum */ + upa_writel(ctx->dcsf, &ffb->dcsf); /* Depth Cue Scale Front Bound */ + upa_writel(ctx->dcsb, &ffb->dcsb); /* Depth Cue Scale Back Bound */ + upa_writel(ctx->dczf, &ffb->dczf); /* Depth Cue Scale Z Front */ + upa_writel(ctx->dczb, &ffb->dczb); /* Depth Cue Scale Z Back */ + upa_writel(ctx->blendc, &ffb->blendc); /* Alpha Blend Control */ + upa_writel(ctx->blendc1, &ffb->blendc1); /* Alpha Blend Color 1 */ + upa_writel(ctx->blendc2, &ffb->blendc2); /* Alpha Blend Color 2 */ + upa_writel(ctx->fbc, &ffb->fbc); /* Frame Buffer Control */ + upa_writel(ctx->rop, &ffb->rop); /* Raster Operation */ + upa_writel(ctx->cmp, &ffb->cmp); /* Compare Controls */ + upa_writel(ctx->matchab, &ffb->matchab); /* Buffer A/B Match Ops */ + upa_writel(ctx->matchc, &ffb->matchc); /* Buffer C Match Ops */ + upa_writel(ctx->magnab, &ffb->magnab); /* Buffer A/B Magnitude Ops */ + upa_writel(ctx->magnc, &ffb->magnc); /* Buffer C Magnitude Ops */ + upa_writel(ctx->pmask, &ffb->pmask); /* RGB Plane Mask */ + upa_writel(ctx->xpmask, &ffb->xpmask); /* X Plane Mask */ + upa_writel(ctx->ypmask, &ffb->ypmask); /* Y Plane Mask */ + upa_writel(ctx->zpmask, &ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min); + upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max); + upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min); + upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max); + upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min); + upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max); + upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min); + upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max); + + upa_writel(ctx->lpat, &ffb->lpat); /* Line Pattern */ + upa_writel(ctx->fontxy, &ffb->fontxy); /* XY Font Coordinate */ + upa_writel(ctx->fontw, &ffb->fontw); /* Font Width */ + upa_writel(ctx->fontinc, &ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + upa_writel(ctx->dcss1, &ffb->dcss1); /* Depth Cue Scale Slope 1 */ + upa_writel(ctx->dcss2, &ffb->dcss2); /* Depth Cue Scale Slope 2 */ + upa_writel(ctx->dcss3, &ffb->dcss2); /* Depth Cue Scale Slope 3 */ + upa_writel(ctx->dcs2, &ffb->dcs2); /* Depth Cue Scale 2 */ + upa_writel(ctx->dcs3, &ffb->dcs3); /* Depth Cue Scale 3 */ + upa_writel(ctx->dcs4, &ffb->dcs4); /* Depth Cue Scale 4 */ + upa_writel(ctx->dcd2, &ffb->dcd2); /* Depth Cue Depth 2 */ + upa_writel(ctx->dcd3, &ffb->dcd3); /* Depth Cue Depth 3 */ + upa_writel(ctx->dcd4, &ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + /* Unfortunately, there is a hardware bug on + * the FFB2+ chips which prevents a normal write + * to the stencil control register from working + * as it should. + * + * The state controlled by the FFB stencilctl register + * really gets transferred to the per-buffer instances + * of the stencilctl register in the 3DRAM chips. + * + * The bug is that FFB does not update buffer C correctly, + * so we have to do it by hand for them. + */ + + /* This will update buffers A and B. */ + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + + /* Force FFB to use buffer C 3dram regs. */ + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + + /* Now restore the correct FBC controls. */ + upa_writel(ctx->fbc, &ffb->fbc); + } + } + + /* Restore the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. + * The only state we really preserve here is the picking + * control. + */ + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); +} + +#define FFB_UCSR_FB_BUSY 0x01000000 +#define FFB_UCSR_RP_BUSY 0x02000000 +#define FFB_UCSR_ALL_BUSY (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY) + +static void FFBWait(ffb_fbcPtr ffb) +{ + int limit = 100000; + + do { + u32 regval = upa_readl(&ffb->ucsr); + + if ((regval & FFB_UCSR_ALL_BUSY) == 0) + break; + } while (--limit); +} + +int ffb_context_switch(drm_device_t *dev, int old, int new) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + + atomic_inc(&dev->total_ctx); + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context || + dev->last_context == 0) { + dev->last_context = new; + return 0; + } + + FFBWait(fpriv->regs); + ffb_save_context(fpriv, old); + ffb_restore_context(fpriv, old, new); + FFBWait(fpriv->regs); + + dev->last_context = new; + + return 0; +} + +int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY)); + if (idx < 0) + return -ENFILE; + + DRM_DEBUG("%d\n", ctx.handle); + ctx.handle = idx; + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0) + hwctx->is_2d_only = 0; + else + hwctx->is_2d_only = 1; + + return 0; +} + +int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if (hwctx->is_2d_only != 0) + ctx.flags = _DRM_CONTEXT_2DONLY; + else + ctx.flags = 0; + + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + + return 0; +} + +int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return ffb_context_switch(dev, dev->last_context, ctx.handle); +} + +int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + return 0; +} + +int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + idx = ctx.handle - 1; + if (idx < 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + return 0; +} diff --git a/drivers/char/drm/ffb_drv.c b/drivers/char/drm/ffb_drv.c new file mode 100644 index 000000000000..a1ab1f27953f --- /dev/null +++ b/drivers/char/drm/ffb_drv.c @@ -0,0 +1,842 @@ +/* $Id: ffb_drv.c,v 1.3 2000/06/01 04:24:39 davem Exp $ + * ffb_drv.c: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +#include "drmP.h" + +#include +#include + +#include "ffb_drv.h" + +#define FFB_NAME "ffb" +#define FFB_DESC "Creator/Creator3D" +#define FFB_DATE "20000517" +#define FFB_MAJOR 0 +#define FFB_MINOR 0 +#define FFB_PATCHLEVEL 1 + +/* Forward declarations. */ +int ffb_init(void); +void ffb_cleanup(void); +static int ffb_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_open(struct inode *inode, struct file *filp); +static int ffb_release(struct inode *inode, struct file *filp); +static int ffb_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma); + +/* From ffb_context.c */ +extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_context_switch(drm_device_t *, int, int); + +static struct file_operations ffb_fops = { + open: ffb_open, + flush: drm_flush, + release: ffb_release, + ioctl: ffb_ioctl, + mmap: ffb_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +/* This is just a template, we make a new copy for each FFB + * we discover at init time so that each one gets a unique + * misc device minor number. + */ +static struct miscdevice ffb_misc = { + minor: MISC_DYNAMIC_MINOR, + name: FFB_NAME, + fops: &ffb_fops, +}; + +static drm_ioctl_desc_t ffb_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { ffb_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, /* XXX */ + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + + /* The implementation is currently a nop just like on tdfx. + * Later we can do something more clever. -DaveM + */ + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { ffb_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { ffb_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { ffb_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { ffb_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { ffb_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { ffb_resctx, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { ffb_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { ffb_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls) + +#ifdef MODULE +static char *ffb = NULL; +#endif + +MODULE_AUTHOR("David S. Miller (davem@redhat.com)"); +MODULE_DESCRIPTION("Sun Creator/Creator3D DRI"); + +static int ffb_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: + drm_ioremapfree(map->handle, map->size); + break; + + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + }; + + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +drm_device_t **ffb_dev_table; +static int ffb_dev_table_size; + +static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance) +{ + volatile unsigned char *strap_bits; + unsigned char val; + + strap_bits = (volatile unsigned char *) + (ffb_priv->card_phys_base + 0x00200000UL); + + /* Don't ask, you have to read the value twice for whatever + * reason to get correct contents. + */ + val = upa_readb(strap_bits); + val = upa_readb(strap_bits); + switch (val & 0x78) { + case (0x0 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb1_prototype; + printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance); + break; + case (0x0 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb1_standard; + printk("ffb%d: Detected FFB1\n", instance); + break; + case (0x0 << 5) | (0x3 << 3): + ffb_priv->ffb_type = ffb1_speedsort; + printk("ffb%d: Detected FFB1-SpeedSort\n", instance); + break; + case (0x1 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_prototype; + printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance); + break; + case (0x1 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Detected FFB2/vertical\n", instance); + break; + case (0x1 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_vertical_plus; + printk("ffb%d: Detected FFB2+/vertical\n", instance); + break; + case (0x2 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2/horizontal\n", instance); + break; + case (0x2 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2+/horizontal\n", instance); + break; + default: + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val); + break; + }; +} + +static int ffb_init_one(int prom_node, int instance) +{ + struct linux_prom64_registers regs[2*PROMREG_MAX]; + drm_device_t *dev; + ffb_dev_priv_t *ffb_priv; + int ret, i; + + dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + spin_lock_init(&dev->count_lock); + sema_init(&dev->struct_sem, 1); + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + ffb_priv->prom_node = prom_node; + if (prom_getproperty(ffb_priv->prom_node, "reg", + (void *)regs, sizeof(regs)) <= 0) { + kfree(dev); + return -EINVAL; + } + ffb_priv->card_phys_base = regs[0].phys_addr; + ffb_priv->regs = (ffb_fbcPtr) + (regs[0].phys_addr + 0x00600000UL); + get_ffb_type(ffb_priv, instance); + for (i = 0; i < FFB_MAX_CTXS; i++) + ffb_priv->hw_state[i] = NULL; + + ffb_dev_table[instance] = dev; + +#ifdef MODULE + drm_parse_options(ffb); +#endif + + memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc)); + ret = misc_register(&ffb_priv->miscdev); + if (ret) { + ffb_dev_table[instance] = NULL; + kfree(dev); + return ret; + } + + dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor); + dev->name = FFB_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n", + FFB_NAME, + FFB_MAJOR, + FFB_MINOR, + FFB_PATCHLEVEL, + FFB_DATE, + ffb_priv->miscdev.minor, + ffb_priv->card_phys_base); + + return 0; +} + +static int ffb_init_dev_table(void) +{ + int root, node; + int total = 0; + + root = prom_getchild(prom_root_node); + for (node = prom_searchsiblings(root, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) + total++; + + ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL); + if (!ffb_dev_table) + return -ENOMEM; + + ffb_dev_table_size = total; + + return 0; +} + +int ffb_init(void) +{ + int root, node, instance, ret; + + ret = ffb_init_dev_table(); + if (ret) + return ret; + + instance = 0; + root = prom_getchild(prom_root_node); + for (node = prom_searchsiblings(root, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) { + ret = ffb_init_one(node, instance); + if (ret) + return ret; + instance++; + } + + return 0; +} + +void ffb_cleanup(void) +{ + int instance; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + for (instance = 0; instance < ffb_dev_table_size; instance++) { + drm_device_t *dev = ffb_dev_table[instance]; + ffb_dev_priv_t *ffb_priv; + + if (!dev) + continue; + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + if (misc_deregister(&ffb_priv->miscdev)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + ffb_takedown(dev); + kfree(dev); + ffb_dev_table[instance] = NULL; + } + kfree(ffb_dev_table); + ffb_dev_table = NULL; + ffb_dev_table_size = 0; +} + +static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_version_t version; + int len, ret; + + ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version)); + if (ret) + return -EFAULT; + + version.version_major = FFB_MAJOR; + version.version_minor = FFB_MINOR; + version.version_patchlevel = FFB_PATCHLEVEL; + + len = strlen(FFB_NAME); + if (len > version.name_len) + len = version.name_len; + version.name_len = len; + if (len && version.name) { + ret = copy_to_user(version.name, FFB_NAME, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DATE); + if (len > version.date_len) + len = version.date_len; + version.date_len = len; + if (len && version.date) { + ret = copy_to_user(version.date, FFB_DATE, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DESC); + if (len > version.desc_len) + len = version.desc_len; + version.desc_len = len; + if (len && version.desc) { + ret = copy_to_user(version.desc, FFB_DESC, len); + if (ret) + return -EFAULT; + } + + ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version)); + if (ret) + ret = -EFAULT; + + return ret; +} + +static int ffb_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + return 0; +} + +static int ffb_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev; + int minor, i; + int ret = 0; + + minor = MINOR(inode->i_rdev); + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_dev_priv_t *ffb_priv; + + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + + if (ffb_priv->miscdev.minor == minor) + break; + } + + if (i >= ffb_dev_table_size) + return -EINVAL; + + dev = ffb_dev_table[i]; + if (!dev) + return -EINVAL; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + ret = drm_open_helper(inode, filp, dev); + if (!ret) { + MOD_INC_USE_COUNT; + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return ffb_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return ret; +} + +static int ffb_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int ret = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (dev->lock.hw_lock != NULL + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock); + int idx; + + /* We have to free up the rogue hw context state + * holding error or else we will leak it. + */ + idx = context - 1; + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + } + + ret = drm_release(inode, filp); + + if (!ret) { + MOD_DEC_USE_COUNT; + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + return ffb_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + return ret; +} + +static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + int ret; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= FFB_IOCTL_COUNT) { + ret = -EINVAL; + } else { + ioctl = &ffb_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + ret = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + ret = -EACCES; + } else { + ret = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + + return ret; +} + +static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + + if (!ret && + (dev->last_context != lock.context)) + ffb_context_switch(dev, dev->last_context, lock.context); + + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + + return ret; +} + +int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + unsigned int old, new, prev, ctx; + int ret; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + + /* We no longer really hold it, but if we are the next + * agent to request it then we should just be able to + * take it immediately and not eat the ioctl. + */ + dev->lock.pid = 0; + { + __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; + + do { + old = *plock; + new = ctx; + prev = cmpxchg(plock, old, new); + } while (prev != old); + } + + wake_up_interruptible(&dev->lock.lock_queue); + + return 0; +} + +static void align_fb_mapping(struct vm_area_struct *vma) +{ + unsigned long j, alignment; + + j = vma->vm_end - vma->vm_start; + for (alignment = (4 * 1024 * 1024); alignment > PAGE_SIZE; alignment >>= 3) + if (j >= alignment) + break; + if (alignment > PAGE_SIZE) { + j = alignment; + alignment = j - (vma->vm_start & (j - 1)); + if (alignment != j) { + struct vm_area_struct *vmm = find_vma(current->mm,vma->vm_start); + + if (!vmm || vmm->vm_start >= vma->vm_end + alignment) { + vma->vm_start += alignment; + vma->vm_end += alignment; + } + } + } +} + +/* The problem here is, due to virtual cache aliasing, + * we must make sure the shared memory area lands in the + * same dcache line for both the kernel and all drm clients. + */ +static void align_shm_mapping(struct vm_area_struct *vma, unsigned long kvirt) +{ + kvirt &= PAGE_SIZE; + if ((vma->vm_start & PAGE_SIZE) != kvirt) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + + if (!vmm || vmm->vm_start >= vma->vm_end + PAGE_SIZE) { + vma->vm_start += PAGE_SIZE; + vma->vm_end += PAGE_SIZE; + } + } +} + +extern struct vm_operations_struct drm_vm_ops; +extern struct vm_operations_struct drm_vm_shm_ops; + +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + ffb_dev_priv_t *ffb_priv; + int i, minor; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + ffb_priv = NULL; + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + if (ffb_priv->miscdev.minor == minor) + break; + } + if (i >= ffb_dev_table_size) + return -EINVAL; + + /* We don't support/need dma mappings, so... */ + if (!VM_OFFSET(vma)) + return -EINVAL; + + for (i = 0; i < dev->map_count; i++) { + unsigned long off; + + map = dev->maplist[i]; + + /* Ok, a little hack to make 32-bit apps work. */ + off = (map->offset & 0xffffffff); + if (off == VM_OFFSET(vma)) + break; + } + + if (i >= dev->map_count) + return -EINVAL; + + if (!map || + ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + if (map->size != (vma->vm_end - vma->vm_start)) + return -EINVAL; + + /* Set read-only attribute before mappings are created + * so it works for fb/reg maps too. + */ + if (map->flags & _DRM_READ_ONLY) + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); + + switch (map->type) { + case _DRM_FRAME_BUFFER: + align_fb_mapping(vma); + /* FALLTHROUGH */ + + case _DRM_REGISTERS: + /* In order to handle 32-bit drm apps/xserver we + * play a trick. The mappings only really specify + * the 32-bit offset from the cards 64-bit base + * address, and we just add in the base here. + */ + vma->vm_flags |= VM_IO; + if (io_remap_page_range(vma->vm_start, + ffb_priv->card_phys_base + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot, 0)) + return -EAGAIN; + + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + align_shm_mapping(vma, (unsigned long)dev->lock.hw_lock); + vma->vm_ops = &drm_vm_shm_ops; + + /* Don't let this area swap. Change when + * DRM_KERNEL advisory is supported. + */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + }; + + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +module_init(ffb_init); +module_exit(ffb_cleanup); diff --git a/drivers/char/drm/ffb_drv.h b/drivers/char/drm/ffb_drv.h new file mode 100644 index 000000000000..094bbce09dc8 --- /dev/null +++ b/drivers/char/drm/ffb_drv.h @@ -0,0 +1,276 @@ +/* $Id: ffb_drv.h,v 1.1 2000/06/01 04:24:39 davem Exp $ + * ffb_drv.h: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +/* Auxilliary clips. */ +typedef struct { + volatile unsigned int min; + volatile unsigned int max; +} ffb_auxclip, *ffb_auxclipPtr; + +/* FFB register set. */ +typedef struct _ffb_fbc { + /* Next vertex registers, on the right we list which drawops + * use said register and the logical name the register has in + * that context. + */ /* DESCRIPTION DRAWOP(NAME) */ +/*0x00*/unsigned int pad1[3]; /* Reserved */ +/*0x0c*/volatile unsigned int alpha; /* ALPHA Transparency */ +/*0x10*/volatile unsigned int red; /* RED */ +/*0x14*/volatile unsigned int green; /* GREEN */ +/*0x18*/volatile unsigned int blue; /* BLUE */ +/*0x1c*/volatile unsigned int z; /* DEPTH */ +/*0x20*/volatile unsigned int y; /* Y triangle(DOYF) */ + /* aadot(DYF) */ + /* ddline(DYF) */ + /* aaline(DYF) */ +/*0x24*/volatile unsigned int x; /* X triangle(DOXF) */ + /* aadot(DXF) */ + /* ddline(DXF) */ + /* aaline(DXF) */ +/*0x28*/unsigned int pad2[2]; /* Reserved */ +/*0x30*/volatile unsigned int ryf; /* Y (alias to DOYF) ddline(RYF) */ + /* aaline(RYF) */ + /* triangle(RYF) */ +/*0x34*/volatile unsigned int rxf; /* X ddline(RXF) */ + /* aaline(RXF) */ + /* triangle(RXF) */ +/*0x38*/unsigned int pad3[2]; /* Reserved */ +/*0x40*/volatile unsigned int dmyf; /* Y (alias to DOYF) triangle(DMYF) */ +/*0x44*/volatile unsigned int dmxf; /* X triangle(DMXF) */ +/*0x48*/unsigned int pad4[2]; /* Reserved */ +/*0x50*/volatile unsigned int ebyi; /* Y (alias to RYI) polygon(EBYI) */ +/*0x54*/volatile unsigned int ebxi; /* X polygon(EBXI) */ +/*0x58*/unsigned int pad5[2]; /* Reserved */ +/*0x60*/volatile unsigned int by; /* Y brline(RYI) */ + /* fastfill(OP) */ + /* polygon(YI) */ + /* rectangle(YI) */ + /* bcopy(SRCY) */ + /* vscroll(SRCY) */ +/*0x64*/volatile unsigned int bx; /* X brline(RXI) */ + /* polygon(XI) */ + /* rectangle(XI) */ + /* bcopy(SRCX) */ + /* vscroll(SRCX) */ + /* fastfill(GO) */ +/*0x68*/volatile unsigned int dy; /* destination Y fastfill(DSTY) */ + /* bcopy(DSRY) */ + /* vscroll(DSRY) */ +/*0x6c*/volatile unsigned int dx; /* destination X fastfill(DSTX) */ + /* bcopy(DSTX) */ + /* vscroll(DSTX) */ +/*0x70*/volatile unsigned int bh; /* Y (alias to RYI) brline(DYI) */ + /* dot(DYI) */ + /* polygon(ETYI) */ + /* Height fastfill(H) */ + /* bcopy(H) */ + /* vscroll(H) */ + /* Y count fastfill(NY) */ +/*0x74*/volatile unsigned int bw; /* X dot(DXI) */ + /* brline(DXI) */ + /* polygon(ETXI) */ + /* fastfill(W) */ + /* bcopy(W) */ + /* vscroll(W) */ + /* fastfill(NX) */ +/*0x78*/unsigned int pad6[2]; /* Reserved */ +/*0x80*/unsigned int pad7[32]; /* Reserved */ + + /* Setup Unit's vertex state register */ +/*100*/ volatile unsigned int suvtx; +/*104*/ unsigned int pad8[63]; /* Reserved */ + + /* Frame Buffer Control Registers */ +/*200*/ volatile unsigned int ppc; /* Pixel Processor Control */ +/*204*/ volatile unsigned int wid; /* Current WID */ +/*208*/ volatile unsigned int fg; /* FG data */ +/*20c*/ volatile unsigned int bg; /* BG data */ +/*210*/ volatile unsigned int consty; /* Constant Y */ +/*214*/ volatile unsigned int constz; /* Constant Z */ +/*218*/ volatile unsigned int xclip; /* X Clip */ +/*21c*/ volatile unsigned int dcss; /* Depth Cue Scale Slope */ +/*220*/ volatile unsigned int vclipmin; /* Viewclip XY Min Bounds */ +/*224*/ volatile unsigned int vclipmax; /* Viewclip XY Max Bounds */ +/*228*/ volatile unsigned int vclipzmin; /* Viewclip Z Min Bounds */ +/*22c*/ volatile unsigned int vclipzmax; /* Viewclip Z Max Bounds */ +/*230*/ volatile unsigned int dcsf; /* Depth Cue Scale Front Bound */ +/*234*/ volatile unsigned int dcsb; /* Depth Cue Scale Back Bound */ +/*238*/ volatile unsigned int dczf; /* Depth Cue Z Front */ +/*23c*/ volatile unsigned int dczb; /* Depth Cue Z Back */ +/*240*/ unsigned int pad9; /* Reserved */ +/*244*/ volatile unsigned int blendc; /* Alpha Blend Control */ +/*248*/ volatile unsigned int blendc1; /* Alpha Blend Color 1 */ +/*24c*/ volatile unsigned int blendc2; /* Alpha Blend Color 2 */ +/*250*/ volatile unsigned int fbramitc; /* FB RAM Interleave Test Control */ +/*254*/ volatile unsigned int fbc; /* Frame Buffer Control */ +/*258*/ volatile unsigned int rop; /* Raster OPeration */ +/*25c*/ volatile unsigned int cmp; /* Frame Buffer Compare */ +/*260*/ volatile unsigned int matchab; /* Buffer AB Match Mask */ +/*264*/ volatile unsigned int matchc; /* Buffer C(YZ) Match Mask */ +/*268*/ volatile unsigned int magnab; /* Buffer AB Magnitude Mask */ +/*26c*/ volatile unsigned int magnc; /* Buffer C(YZ) Magnitude Mask */ +/*270*/ volatile unsigned int fbcfg0; /* Frame Buffer Config 0 */ +/*274*/ volatile unsigned int fbcfg1; /* Frame Buffer Config 1 */ +/*278*/ volatile unsigned int fbcfg2; /* Frame Buffer Config 2 */ +/*27c*/ volatile unsigned int fbcfg3; /* Frame Buffer Config 3 */ +/*280*/ volatile unsigned int ppcfg; /* Pixel Processor Config */ +/*284*/ volatile unsigned int pick; /* Picking Control */ +/*288*/ volatile unsigned int fillmode; /* FillMode */ +/*28c*/ volatile unsigned int fbramwac; /* FB RAM Write Address Control */ +/*290*/ volatile unsigned int pmask; /* RGB PlaneMask */ +/*294*/ volatile unsigned int xpmask; /* X PlaneMask */ +/*298*/ volatile unsigned int ypmask; /* Y PlaneMask */ +/*29c*/ volatile unsigned int zpmask; /* Z PlaneMask */ +/*2a0*/ ffb_auxclip auxclip[4]; /* Auxilliary Viewport Clip */ + + /* New 3dRAM III support regs */ +/*2c0*/ volatile unsigned int rawblend2; +/*2c4*/ volatile unsigned int rawpreblend; +/*2c8*/ volatile unsigned int rawstencil; +/*2cc*/ volatile unsigned int rawstencilctl; +/*2d0*/ volatile unsigned int threedram1; +/*2d4*/ volatile unsigned int threedram2; +/*2d8*/ volatile unsigned int passin; +/*2dc*/ volatile unsigned int rawclrdepth; +/*2e0*/ volatile unsigned int rawpmask; +/*2e4*/ volatile unsigned int rawcsrc; +/*2e8*/ volatile unsigned int rawmatch; +/*2ec*/ volatile unsigned int rawmagn; +/*2f0*/ volatile unsigned int rawropblend; +/*2f4*/ volatile unsigned int rawcmp; +/*2f8*/ volatile unsigned int rawwac; +/*2fc*/ volatile unsigned int fbramid; + +/*300*/ volatile unsigned int drawop; /* Draw OPeration */ +/*304*/ unsigned int pad10[2]; /* Reserved */ +/*30c*/ volatile unsigned int lpat; /* Line Pattern control */ +/*310*/ unsigned int pad11; /* Reserved */ +/*314*/ volatile unsigned int fontxy; /* XY Font coordinate */ +/*318*/ volatile unsigned int fontw; /* Font Width */ +/*31c*/ volatile unsigned int fontinc; /* Font Increment */ +/*320*/ volatile unsigned int font; /* Font bits */ +/*324*/ unsigned int pad12[3]; /* Reserved */ +/*330*/ volatile unsigned int blend2; +/*334*/ volatile unsigned int preblend; +/*338*/ volatile unsigned int stencil; +/*33c*/ volatile unsigned int stencilctl; + +/*340*/ unsigned int pad13[4]; /* Reserved */ +/*350*/ volatile unsigned int dcss1; /* Depth Cue Scale Slope 1 */ +/*354*/ volatile unsigned int dcss2; /* Depth Cue Scale Slope 2 */ +/*358*/ volatile unsigned int dcss3; /* Depth Cue Scale Slope 3 */ +/*35c*/ volatile unsigned int widpmask; +/*360*/ volatile unsigned int dcs2; +/*364*/ volatile unsigned int dcs3; +/*368*/ volatile unsigned int dcs4; +/*36c*/ unsigned int pad14; /* Reserved */ +/*370*/ volatile unsigned int dcd2; +/*374*/ volatile unsigned int dcd3; +/*378*/ volatile unsigned int dcd4; +/*37c*/ unsigned int pad15; /* Reserved */ +/*380*/ volatile unsigned int pattern[32]; /* area Pattern */ +/*400*/ unsigned int pad16[8]; /* Reserved */ +/*420*/ volatile unsigned int reset; /* chip RESET */ +/*424*/ unsigned int pad17[247]; /* Reserved */ +/*800*/ volatile unsigned int devid; /* Device ID */ +/*804*/ unsigned int pad18[63]; /* Reserved */ +/*900*/ volatile unsigned int ucsr; /* User Control & Status Register */ +/*904*/ unsigned int pad19[31]; /* Reserved */ +/*980*/ volatile unsigned int mer; /* Mode Enable Register */ +/*984*/ unsigned int pad20[1439]; /* Reserved */ +} ffb_fbc, *ffb_fbcPtr; + +struct ffb_hw_context { + int is_2d_only; + + unsigned int ppc; + unsigned int wid; + unsigned int fg; + unsigned int bg; + unsigned int consty; + unsigned int constz; + unsigned int xclip; + unsigned int dcss; + unsigned int vclipmin; + unsigned int vclipmax; + unsigned int vclipzmin; + unsigned int vclipzmax; + unsigned int dcsf; + unsigned int dcsb; + unsigned int dczf; + unsigned int dczb; + unsigned int blendc; + unsigned int blendc1; + unsigned int blendc2; + unsigned int fbc; + unsigned int rop; + unsigned int cmp; + unsigned int matchab; + unsigned int matchc; + unsigned int magnab; + unsigned int magnc; + unsigned int pmask; + unsigned int xpmask; + unsigned int ypmask; + unsigned int zpmask; + unsigned int auxclip0min; + unsigned int auxclip0max; + unsigned int auxclip1min; + unsigned int auxclip1max; + unsigned int auxclip2min; + unsigned int auxclip2max; + unsigned int auxclip3min; + unsigned int auxclip3max; + unsigned int drawop; + unsigned int lpat; + unsigned int fontxy; + unsigned int fontw; + unsigned int fontinc; + unsigned int area_pattern[32]; + unsigned int ucsr; + unsigned int stencil; + unsigned int stencilctl; + unsigned int dcss1; + unsigned int dcss2; + unsigned int dcss3; + unsigned int dcs2; + unsigned int dcs3; + unsigned int dcs4; + unsigned int dcd2; + unsigned int dcd3; + unsigned int dcd4; + unsigned int mer; +}; + +#define FFB_MAX_CTXS 32 + +enum ffb_chip_type { + ffb1_prototype = 0, /* Early pre-FCS FFB */ + ffb1_standard, /* First FCS FFB, 100Mhz UPA, 66MHz gclk */ + ffb1_speedsort, /* Second FCS FFB, 100Mhz UPA, 75MHz gclk */ + ffb2_prototype, /* Early pre-FCS vertical FFB2 */ + ffb2_vertical, /* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk, + 75(SingleBuffer)/83(DoubleBuffer) MHz fclk */ + ffb2_vertical_plus, /* Second FCS FFB2/vertical, same timings */ + ffb2_horizontal, /* First FCS FFB2/horizontal, same timings as FFB2/vert */ + ffb2_horizontal_plus, /* Second FCS FFB2/horizontal, same timings */ + afb_m3, /* FCS Elite3D, 3 float chips */ + afb_m6 /* FCS Elite3D, 6 float chips */ +}; + +typedef struct ffb_dev_priv { + /* Misc software state. */ + int prom_node; + enum ffb_chip_type ffb_type; + u64 card_phys_base; + struct miscdevice miscdev; + + /* Controller registers. */ + ffb_fbcPtr regs; + + /* Context table. */ + struct ffb_hw_context *hw_state[FFB_MAX_CTXS]; +} ffb_dev_priv_t; diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c index a823db35638e..76e0dc2c1950 100644 --- a/drivers/char/drm/fops.c +++ b/drivers/char/drm/fops.c @@ -92,7 +92,8 @@ int drm_release(struct inode *inode, struct file *filp) DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", current->pid, dev->device, dev->open_count); - if (_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + if (dev->lock.hw_lock != NULL + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && dev->lock.pid == current->pid) { DRM_ERROR("Process %d dead, freeing lock for context %d\n", current->pid, @@ -216,7 +217,7 @@ int drm_write_string(drm_device_t *dev, const char *s) if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); #else /* Parameter added in 2.3.21 */ - if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); + kill_fasync(&dev->buf_async, SIGIO, POLL_IN); #endif DRM_DEBUG("waking\n"); wake_up_interruptible(&dev->buf_readers); diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c index 7a0115e86519..ad887490bda7 100644 --- a/drivers/char/drm/init.c +++ b/drivers/char/drm/init.c @@ -103,6 +103,10 @@ int drm_cpu_valid(void) { #if defined(__i386__) if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + if (1) + return 0; /* No cmpxchg before v9 sparc. */ #endif return 1; } diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c index 4d5d1a964b4f..d44195539010 100644 --- a/drivers/char/drm/proc.c +++ b/drivers/char/drm/proc.c @@ -512,9 +512,9 @@ static int _drm_histo_info(char *buf, char **start, off_t offset, int len, } else { DRM_PROC_PRINT("lock none\n"); } - DRM_PROC_PRINT("context_flag 0x%08x\n", dev->context_flag); - DRM_PROC_PRINT("interrupt_flag 0x%08x\n", dev->interrupt_flag); - DRM_PROC_PRINT("dma_flag 0x%08x\n", dev->dma_flag); + DRM_PROC_PRINT("context_flag 0x%08lx\n", dev->context_flag); + DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag); + DRM_PROC_PRINT("dma_flag 0x%08lx\n", dev->dma_flag); DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count); DRM_PROC_PRINT("last_context %10d\n", dev->last_context); diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in index 3e371744e412..1547e5f38f88 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 2bb5870b0d93..2ad4526183dd 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 000000000000..edfbadb4b05e --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#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, 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] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + a3d->axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + a3d->axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + a3d->axes[3] = ((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, ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128); + input_report_abs(dev, ABS_Y, ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128); + input_report_abs(dev, ABS_RUDDER, ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128); + input_report_abs(dev, ABS_THROTTLE, ((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; + 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; + 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 \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 000000000000..d0d9b6c95a06 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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; + 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 000000000000..f17aeda24478 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +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 000000000000..1ac676fa79a1 --- /dev/null +++ b/drivers/char/joystick/analog.c @@ -0,0 +1,757 @@ +/* + * $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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +/* + * 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]; + 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 000000000000..f059a2ff6c42 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#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 000000000000..f9edd0755fc5 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +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 000000000000..a92ef58a997b --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +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 .\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 000000000000..288ac2f9edfa --- /dev/null +++ b/drivers/char/joystick/gameport.c @@ -0,0 +1,201 @@ +/* + * $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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +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 000000000000..cad8be16bd11 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..4cedd7892ac0 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +#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 000000000000..7104e5d49e8d --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 8c0ba6923a99..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct js_port* js_am_port __initdata = NULL; - -MODULE_AUTHOR("Vojtech Pavlik "); -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 f73ee8ded76a..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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 a1644350c2c7..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include - -#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 \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 469c6c8cee51..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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 \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 e56da540adde..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -MODULE_AUTHOR("Vojtech Pavlik "); -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<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; iport); - __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 .\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 b5ae4c0197d5..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 41169b12c027..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); -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 84a6def1667c..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 26c18856a168..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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 6044cbfb0ffd..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 cb14646e8567..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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:Kidx) 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 fbf9125e5115..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Raymond Ingles "); -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 da11598f4a41..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 - */ - -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 \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 3ace13642154..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 0fb4f4c7128a..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 56e043a592ec..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 c138a99d02ea..000000000000 --- 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 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -MODULE_AUTHOR("Vojtech Pavlik "); -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 232699859930..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 c9bd56ce361d..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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 "); -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 \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 000000000000..69dfd11121cb --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); + +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 000000000000..e8c77f48e14b --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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:Kdev; + 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 000000000000..f34a1864003e --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +#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 000000000000..0348ba08b373 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..9c98e9e85d32 --- /dev/null +++ b/drivers/char/joystick/serio.c @@ -0,0 +1,146 @@ +/* + * $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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +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) +{ + MOD_INC_USE_COUNT; + + 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--; + + MOD_DEC_USE_COUNT; +} + +void serio_register_device(struct serio_dev *dev) +{ + struct serio *serio = serio_list; + + MOD_INC_USE_COUNT; + + 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; + } + + MOD_DEC_USE_COUNT; +} + +int serio_open(struct serio *serio, struct serio_dev *dev) +{ + MOD_INC_USE_COUNT; + if (serio->open(serio)) { + MOD_DEC_USE_COUNT; + return -1; + } + serio->dev = dev; + + return 0; +} + +void serio_close(struct serio *serio) +{ + MOD_DEC_USE_COUNT; + 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 000000000000..453e674d7032 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +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 000000000000..8e060490ec3b --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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) +{ + 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 \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 000000000000..4f5059330c93 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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 000000000000..866e1ba50a52 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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 000000000000..f356f7dd5c2c --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..0e2dedcd008b --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +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 000000000000..7000b8560b92 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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/mem.c b/drivers/char/mem.c index ab6b65f0abe2..98ec4f5c69a9 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -652,13 +651,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/n_hdlc.c b/drivers/char/n_hdlc.c index 1d27b680d004..7a0df755ec66 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -663,7 +663,7 @@ static void n_hdlc_tty_receive(struct tty_struct *tty, #if LINUX_VERSION_CODE < VERSION(2,3,0) kill_fasync (n_hdlc->tty->fasync, SIGIO); #else - kill_fasync (n_hdlc->tty->fasync, SIGIO, POLL_IN); + kill_fasync(&n_hdlc->tty->fasync, SIGIO, POLL_IN); #endif } /* end of n_hdlc_tty_receive() */ diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 81d56274745a..f488d9124528 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -630,8 +630,7 @@ send_signal: put_tty_queue(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; - if (tty->fasync) - kill_fasync(tty->fasync, SIGIO, POLL_IN); + kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); return; @@ -735,8 +734,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, } if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { - if (tty->fasync) - kill_fasync(tty->fasync, SIGIO, POLL_IN); + kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); } diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c index b2370ed7154b..dfef66e39490 100644 --- a/drivers/char/pc110pad.c +++ b/drivers/char/pc110pad.c @@ -83,8 +83,7 @@ static struct semaphore reader_lock; static void wake_readers(void) { wake_up_interruptible(&queue); - if(asyncptr) - kill_fasync(asyncptr, SIGIO, POLL_IN); + kill_fasync(&asyncptr, SIGIO, POLL_IN); } diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index f215b9dd3ef5..c265b927f482 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -415,8 +415,7 @@ static inline void handle_mouse_event(unsigned char scancode) head = (head + 1) & (AUX_BUF_SIZE-1); if (head != queue->tail) { queue->head = head; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO, POLL_IN); + kill_fasync(&queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); } } diff --git a/drivers/char/qpmouse.c b/drivers/char/qpmouse.c index f221249284d9..6fd5a3d65038 100644 --- a/drivers/char/qpmouse.c +++ b/drivers/char/qpmouse.c @@ -133,8 +133,7 @@ static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) head &= QP_BUF_SIZE-1; } queue->head = head; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO, POLL_IN); + kill_fasync(&queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 4aca69903185..f227b5094b5a 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -179,8 +179,7 @@ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* Now do the rest of the actions */ wake_up_interruptible(&rtc_wait); - if (rtc_async_queue) - kill_fasync (rtc_async_queue, SIGIO, POLL_IN); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); } #endif @@ -779,8 +778,7 @@ static void rtc_dropped_irq(unsigned long data) /* Now we have new data */ wake_up_interruptible(&rtc_wait); - if (rtc_async_queue) - kill_fasync (rtc_async_queue, SIGIO, POLL_IN); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); } #endif diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e28f564e16e9..6ccf63d84334 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -43,13 +43,16 @@ * few races on freeing buffers too. * Alan Modra * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * * This module exports the following rs232 io functions: * * int rs_init(void); */ -static char *serial_version = "4.93"; -static char *serial_revdate = "2000-03-20"; +static char *serial_version = "5.01"; +static char *serial_revdate = "2000-05-29"; /* * Serial driver configuration section. Here are the various options: @@ -134,6 +137,8 @@ static char *serial_revdate = "2000-03-20"; #endif #endif +#define CONFIG_SERIAL_RSA + #define RS_STROBE_TIME (10*HZ) #define RS_ISR_PASS_LIMIT 256 @@ -277,9 +282,20 @@ static struct serial_uart_config uart_config[] = { UART_STARTECH }, { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, { 0, 0} }; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +#endif /* CONFIG_SERIAL_RSA */ + static struct serial_state rs_table[RS_TABLE_SIZE] = { SERIAL_PORT_DFNS /* Defined in serial.h */ }; @@ -293,11 +309,8 @@ static struct serial_state rs_table[RS_TABLE_SIZE] = { static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; static int serial_pci_board_idx = 0; #endif -#ifndef PCI_BASE_ADDRESS -#define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start) -#define PCI_BASE_REGION_SIZE(dev, r) ((dev)->resource[r].end - \ - (dev)->resource[r].start) -#define IS_PCI_REGION_IOPORT(dev, r) ((dev)->resource[r].flags & \ +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_IO) #endif #ifndef PCI_IRQ_RESOURCE @@ -314,7 +327,8 @@ static int serial_pci_board_idx = 0; #define ACTIVATE_FUNC(dev) (dev->activate) #define DEACTIVATE_FUNC(dev) (dev->deactivate) #endif - + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) static struct tty_struct *serial_table[NR_PORTS]; static struct termios *serial_termios[NR_PORTS]; @@ -996,6 +1010,9 @@ static void do_softint(void *private_) tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif } } @@ -1090,6 +1107,50 @@ static void figure_IRQ_timeout(int irq) IRQ_timeout[irq] = timeout ? timeout : 1; } +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} + +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +#endif /* CONFIG_SERIAL_RSA */ + static int startup(struct async_struct * info) { unsigned long flags; @@ -1127,7 +1188,7 @@ static int startup(struct async_struct * info) printk("starting up ttys%d (irq %d)...", info->line, state->irq); #endif - if (uart_config[info->state->type].flags & UART_STARTECH) { + if (uart_config[state->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); @@ -1145,7 +1206,7 @@ static int startup(struct async_struct * info) /* * For a XR16C850, we need to set the trigger levels */ - if (info->state->type == PORT_16850) { + if (state->type == PORT_16850) { serial_outp(info, UART_FCTR, UART_FCTR_TRGD | UART_FCTR_RX); serial_outp(info, UART_TRG, UART_TRG_96); @@ -1156,12 +1217,12 @@ static int startup(struct async_struct * info) serial_outp(info, UART_LCR, 0); } - if (info->state->type == PORT_16750) { + if (state->type == PORT_16750) { /* Wake up UART */ serial_outp(info, UART_IER, 0); } - if (info->state->type == PORT_16C950) { + if (state->type == PORT_16C950) { /* Wake up and initialize UART */ info->ACR = 0; serial_outp(info, UART_LCR, 0xBF); @@ -1174,6 +1235,20 @@ static int startup(struct async_struct * info) serial_outp(info, UART_LCR, 0); } +#ifdef CONFIG_SERIAL_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif + /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) @@ -1199,7 +1274,8 @@ static int startup(struct async_struct * info) * if it is, then bail out, because there's likely no UART * here. */ - if (serial_inp(info, UART_LSR) == 0xff) { + if (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { printk("LSR safety check engaged!\n"); if (capable(CAP_SYS_ADMIN)) { if (info->tty) @@ -1425,6 +1501,17 @@ static void shutdown(struct async_struct * info) UART_FCR_CLEAR_XMIT)); serial_outp(info, UART_FCR, 0); +#ifdef CONFIG_SERIAL_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif + + (void)serial_in(info, UART_RX); /* read data port to reset things */ if (info->tty) @@ -1522,6 +1609,12 @@ static void change_speed(struct async_struct *info, baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif baud_base = info->state->baud_base; if (info->state->type == PORT_16C950) { if (baud <= baud_base) @@ -1583,6 +1676,10 @@ static void change_speed(struct async_struct *info, if (uart_config[info->state->type].flags & UART_USE_FIFO) { if ((info->state->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } @@ -1810,6 +1907,9 @@ static void rs_flush_buffer(struct tty_struct *tty) info->xmit.head = info->xmit.tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); @@ -1912,6 +2012,10 @@ static int get_serial_info(struct async_struct * info, tmp.type = state->type; tmp.line = state->line; tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; tmp.irq = state->irq; tmp.flags = state->flags; tmp.xmit_fifo_size = state->xmit_fifo_size; @@ -1933,14 +2037,19 @@ static int set_serial_info(struct async_struct * info, struct serial_state old_state, *state; unsigned int i,change_irq,change_port; int retval = 0; + unsigned long new_port; if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; state = info->state; old_state = *state; - + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += new_serial.port_high << HIGH_BITS_OFFSET; + change_irq = new_serial.irq != state->irq; - change_port = (new_serial.port != state->port) || + change_port = (new_port != ((int) state->port)) || (new_serial.hub6 != state->hub6); if (!capable(CAP_SYS_ADMIN)) { @@ -1978,7 +2087,7 @@ static int set_serial_info(struct async_struct * info, if (new_serial.type) { for (i = 0 ; i < NR_PORTS; i++) if ((state != &rs_table[i]) && - (rs_table[i].port == new_serial.port) && + (rs_table[i].port == new_port) && rs_table[i].type) return -EADDRINUSE; } @@ -2005,8 +2114,14 @@ static int set_serial_info(struct async_struct * info, info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; - if ((state->type != PORT_UNKNOWN) && state->port) + if ((state->type != PORT_UNKNOWN) && state->port) { +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif release_region(state->port,8); + } state->type = new_serial.type; if (change_port || change_irq) { /* @@ -2015,15 +2130,22 @@ static int set_serial_info(struct async_struct * info, */ shutdown(info); state->irq = new_serial.irq; - info->port = state->port = new_serial.port; + info->port = state->port = new_port; info->hub6 = state->hub6 = new_serial.hub6; if (info->hub6) info->io_type = state->io_type = SERIAL_IO_HUB6; else if (info->io_type == SERIAL_IO_HUB6) info->io_type = state->io_type = SERIAL_IO_PORT; } - if ((state->type != PORT_UNKNOWN) && state->port) - request_region(state->port,8,"serial(set)"); + if ((state->type != PORT_UNKNOWN) && state->port) { +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif + request_region(state->port,8,"serial(set)"); + } check_and_exit: @@ -2462,6 +2584,9 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, /* note the counters on entry */ cprev = info->state->icount; restore_flags(flags); + /* Force modem status interrupts on */ + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ @@ -3348,6 +3473,16 @@ static void autoconfig_startech_uarts(struct async_struct *info, * LSR register (which serial_icr_read does) */ if (state->type == PORT_16550A) { + /* + * EFR [4] must be set else this test fails + * + * This shouldn't be necessary, but Mike Hudson + * (Exoray@isys.ca) claims that it's needed for 952 + * dual UART's (which are not recommended for new designs). + */ + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); /* Check for Oxford Semiconductor 16C950 */ scratch = serial_icr_read(info, UART_ID1); scratch2 = serial_icr_read(info, UART_ID2); @@ -3434,7 +3569,8 @@ static void autoconfig(struct serial_state * state) save_flags(flags); cli(); - if (!state->iomem_base) { + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. @@ -3459,8 +3595,9 @@ static void autoconfig(struct serial_state * state) serial_outp(info, UART_IER, scratch); if (scratch2 || scratch3 != 0x0F) { #ifdef SERIAL_DEBUG_AUTOCONF - printk("serial: ttyS%d: simple autoconfig failed\n", - state->line); + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); #endif restore_flags(flags); return; /* We failed; there's nothing here */ @@ -3545,6 +3682,25 @@ static void autoconfig(struct serial_state * state) } serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif serial_outp(info, UART_LCR, save_lcr); if (state->type == PORT_16450) { scratch = serial_in(info, UART_SCR); @@ -3564,12 +3720,23 @@ static void autoconfig(struct serial_state * state) return; } - if (info->port) - request_region(info->port,8,"serial(auto)"); + if (info->port) { +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif + request_region(info->port,8,"serial(auto)"); + } /* * Reset the UART. */ +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif serial_outp(info, UART_MCR, save_mcr); serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | @@ -3614,7 +3781,7 @@ static void __init printk_pnp_dev_id(unsigned short vendor, static _INLINE_ int get_pci_port(struct pci_dev *dev, struct pci_board *board, - struct serial_struct *state, + struct serial_struct *req, int idx) { unsigned long port; @@ -3626,24 +3793,28 @@ static _INLINE_ int get_pci_port(struct pci_dev *dev, base_idx += idx; if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = PCI_BASE_REGION_SIZE(dev, base_idx) / 8; + max_port = pci_resource_len(dev, base_idx) / 8; if (idx >= max_port) return 1; } - port = PCI_BASE_ADDRESS(dev, base_idx) + board->first_uart_offset; + port = pci_resource_start(dev, base_idx) + board->first_uart_offset; if ((board->flags & SPCI_FL_BASE_TABLE) == 0) port += idx * (board->uart_offset ? board->uart_offset : 8); if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - state->port = port; + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; + else + req->port_high = 0; return 0; } - state->io_type = SERIAL_IO_MEM; - state->iomem_base = ioremap(port, board->uart_offset); - state->iomem_reg_shift = board->reg_shift; - state->port = 0; + req->io_type = SERIAL_IO_MEM; + req->iomem_base = ioremap(port, board->uart_offset); + req->iomem_reg_shift = board->reg_shift; + req->port = 0; return 0; } @@ -3670,23 +3841,28 @@ static void __init start_pci_pnp_board(struct pci_dev *dev, struct pci_board *board) { int k, line; - struct serial_struct fake_state; + struct serial_struct serial_req; int base_baud; if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { - printk("SERIAL: PNP device '"); + printk("serial: PNP device '"); printk_pnp_dev_id(board->vendor, board->device); printk("' prepare failed\n"); return; } if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { - printk("SERIAL: PNP device '"); + printk("serial: PNP device '"); printk_pnp_dev_id(board->vendor, board->device); printk("' activate failed\n"); return; } + if (!(board->flags & SPCI_FL_ISPNP) && pci_enable_device(dev)) { + printk("serial: PCI device enable failed\n"); + return; + } + /* * Run the initialization function, if any */ @@ -3710,18 +3886,18 @@ static void __init start_pci_pnp_board(struct pci_dev *dev, base_baud = board->base_baud; if (!base_baud) base_baud = BASE_BAUD; - memset(&fake_state, 0, sizeof(fake_state)); + memset(&serial_req, 0, sizeof(serial_req)); for (k=0; k < board->num_ports; k++) { - fake_state.irq = get_pci_irq(dev, board, k); - if (get_pci_port(dev, board, &fake_state, k)) + serial_req.irq = get_pci_irq(dev, board, k); + if (get_pci_port(dev, board, &serial_req, k)) break; - fake_state.flags = ASYNC_SKIP_TEST; + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; #ifdef SERIAL_DEBUG_PCI printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", - fake_state.port, fake_state.irq, fake_state.io_type); + serial_req.port, serial_req.irq, serial_req.io_type); #endif - line = register_serial(&fake_state); + line = register_serial(&serial_req); if (line < 0) break; rs_table[line].baud_base = base_baud; @@ -3742,28 +3918,45 @@ __init #endif pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) { - u8 data, *p, scratch; + u8 data, *p, irq_config; + int pci_config; + irq_config = 0x41; + pci_config = PCI_COMMAND_MEMORY; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_VENDOR_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the deep + * FIFOs + */ + irq_config = 0x5b; + pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + } + pci_read_config_byte(dev, PCI_COMMAND, &data); if (enable) pci_write_config_byte(dev, PCI_COMMAND, - data | PCI_COMMAND_MEMORY); + data | pci_config); /* enable/disable interrupts */ - p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80); - scratch = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - scratch = 0x43; - writel(enable ? scratch : 0x00, (unsigned long)p + 0x4c); + p = ioremap(pci_resource_start(dev, 0), 0x80); + writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); iounmap(p); if (!enable) pci_write_config_byte(dev, PCI_COMMAND, - data & ~PCI_COMMAND_MEMORY); + data & ~pci_config); return 0; } + /* * SIIG serial cards have an PCI interface chip which also controls * the UART clocking frequency. Each UART can be clocked independently @@ -3796,7 +3989,7 @@ pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) if (!enable) return 0; - p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80); + p = ioremap(pci_resource_start(dev, 0), 0x80); switch (dev->device & 0xfff8) { case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ @@ -3841,6 +4034,36 @@ pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) return 0; } +/* Added for EKF Intel i960 serial boards */ +static int +#ifndef MODULE +__init +#endif +pci_inteli960ni_fn(struct pci_dev *dev, + struct pci_board *board, + int enable) +{ + unsigned long oldval; + + if (!(board->subdevice & 0x1000)) + return(-1); + + if (!enable) /* is there something to deinit? */ + return(0); + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n", + (unsigned long) board->subdevice); +#endif + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return(-1); + } + return(0); +} + /* * This is the configuration table for all of the PCI serial boards @@ -3851,8 +4074,9 @@ static struct pci_board pci_boards[] __initdata = { * Vendor ID, Device ID, * Subvendor ID, Subdevice ID, * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers - * Register shift to use for memory-mapped I/O + * Offset to get to next UART's registers, + * Register shift to use for memory-mapped I/O, + * Initialization function, first UART offset */ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, @@ -3942,6 +4166,10 @@ static struct pci_board pci_boards[] __initdata = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, PCI_ANY_ID, PCI_ANY_ID, SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, + /* VScom SPCOM800, from sl@s.pl */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2, 8, 921600 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_KEYSPAN, PCI_SUBDEVICE_ID_KEYSPAN_SX2, @@ -3979,6 +4207,12 @@ static struct pci_board pci_boards[] __initdata = { PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8, SPCI_FL_BASE2, 8, 460800 }, + /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ + /* (Exoray@isys.ca) */ + { PCI_VENDOR_ID_PLX, PCI_VENDOR_ID_PLX_ROMULUS, + 0x10b5, 0x106a, + SPCI_FL_BASE2, 4, 921600, + 0x20, 2, pci_plx9050_fn, 0x03 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, PCI_ANY_ID, PCI_ANY_ID, SPCI_FL_BASE1, 4, 115200 }, @@ -4164,7 +4398,7 @@ static struct pci_board pci_boards[] __initdata = { PCI_ANY_ID, PCI_ANY_ID, SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, 0, 0, pci_siig20x_fn }, - /* Computone devices submitted by Doug McNash dougm@computone.com */ + /* Computone devices submitted by Doug McNash dmcnash@computone.com */ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, SPCI_FL_BASE0, 4, 921600, /* IOMEM */ @@ -4198,6 +4432,15 @@ static struct pci_board pci_boards[] __initdata = { { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, PCI_ANY_ID, PCI_ANY_ID, SPCI_FL_BASE0, 4, 921600 }, + /* EKF addition for i960 Boards form EKF with serial port */ + { PCI_VENDOR_ID_INTEL, 0x1960, + 0xE4BF, PCI_ANY_ID, + SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ + 8<<2, 2, pci_inteli960ni_fn, 0x10000}, + /* RAStel 2 port modem, gerg@moreton.com.au */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, + SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* * Untested PCI modems, sent in from various folks... */ @@ -4221,12 +4464,12 @@ static struct pci_board pci_boards[] __initdata = { }; /* - * Given a complete unknown PCI device, try to use some hueristics to + * Given a complete unknown PCI device, try to use some heuristics to * guess what the configuration might be, based on the pitiful PCI * serial specs. Returns 0 on success, 1 on failure. */ -static int _INLINE_ serial_guess_board(struct pci_dev *dev, - struct pci_board *board) +static int _INLINE_ serial_pci_guess_board(struct pci_dev *dev, + struct pci_board *board) { int num_iomem = 0, num_port = 0, first_port = -1; int i; @@ -4281,13 +4524,6 @@ static void __init probe_serial_pci(void) printk(KERN_DEBUG "Entered probe_serial_pci()\n"); #endif - if (!pcibios_present()) { -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Leaving probe_serial_pci() (no pcibios)\n"); -#endif - return; - } - pci_for_each_dev(dev) { for (board = pci_boards; board->vendor; board++) { if (board->vendor != (unsigned short) PCI_ANY_ID && @@ -4305,7 +4541,7 @@ static void __init probe_serial_pci(void) break; } - if (board->vendor == 0 && serial_guess_board(dev, board)) + if (board->vendor == 0 && serial_pci_guess_board(dev, board)) continue; start_pci_pnp_board(dev, board); @@ -4321,58 +4557,280 @@ static void __init probe_serial_pci(void) #ifdef ENABLE_SERIAL_PNP -static struct pci_board pnp_devices[] __initdata = { - /* Motorola VoiceSURFR 56K Modem */ - { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, +struct pnp_board { + unsigned short vendor; + unsigned short device; +}; + +static struct pnp_board pnp_devices[] __initdata = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) }, /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_NO_SHIRQ | SPCI_FL_PNPDEFAULT, - 1, 115200 }, + { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) }, /* AZT3005 PnP SOUND DEVICE */ - { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) }, /* Best Data Products Inc. Smart One 336F PnP Modem */ - { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, /* Boca Research 33,600 ACF Modem */ - { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) }, + /* Creative */ /* Creative Modem Blaster Flash56 DI5601-1 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) }, /* Creative Modem Blaster V.90 DI5660 */ - { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) }, + /* Fujitsu FMV-FX431 Plug & Play */ + { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) }, + /* Hayes Accura 56K Fax Modem PnP */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) }, + /* Hayes 288, V.34 + FAX */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) }, + /* Intertex 33k6 56k Voice EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) }, + /* KXPro 33.6 Vocal ASVD PnP */ + { ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) }, + /* Lasat Safire 560 PnP */ + { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) }, + /* Lasat Safire 336 PnP */ + { ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) }, + /* Motorola TA210 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) }, + /* Motorola BitSURFR Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) }, + /* Motorola Lifestyle 28.8 Internal */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) }, + /* Motorola V.3400 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) }, + /* Motorola VoiceSURFR 56K External PnP */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) }, + /* Motorola ModemSURFR 56K External PnP */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) }, + /* Motorola ModemSURFR 56K Internal PnP */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) }, + /* PC Rider K56 Phone System PnP */ + { ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) }, /* Pace 56 Voice Internal Plug & Play Modem */ - { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - /* SupraExpress 28.8 Data/Fax PnP modem */ - { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - /* US Robotics Sporster 33600 Modem */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - /* U.S. Robotics 56K FAX INT */ - { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - /* Viking 56K FAX INT */ - { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - - /* These ID's are taken from M$ documentation */ - /* Compaq 14400 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, - /* Compaq 2400/9600 Modem */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) }, + /* Generic */ /* Generic standard PC COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) }, /* Generic 16550A-compatible COM port */ - { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501), 0, 0, - SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 }, + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) }, + /* Compaq 14400 Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) }, + /* Compaq 2400/9600 Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) }, + /* Standard 9600 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) }, + /* Standard 14400 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) }, + /* Standard 28800 bps Modem*/ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) }, + /* Standard Modem*/ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) }, + /* Standard 9600 bps Modem*/ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) }, + /* Standard 14400 bps Modem*/ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) }, + /* Standard 28800 bps Modem*/ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) }, + /* Standard Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) }, + /* Standard 9600 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) }, + /* Standard 14400 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) }, + /* Standard 28800 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) }, + /* Standard Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) }, + /* Standard 9600 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) }, + /* Standard 14400 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) }, + /* Standard 28800 bps Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) }, + /* Standard Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) }, + /* Standard PCMCIA Card Modem */ + { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) }, + /* Viking 56K FAX INT */ + { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) }, + /* U.S. Robotics 56K Voice INT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) }, + /* U.S. Robotics 56K Voice EXT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) }, + /* U.S. Robotics 56K FAX INT */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) }, + /* U.S. Robotics 56K Voice INT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) }, + /* U.S. Robotics 56K Voice EXT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) }, + /* U.S. Robotics 56K Voice INT PnP */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) }, + /* U.S. Robotics 56K Message */ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) }, + /* U.S. Robotics 56K FAX INT PnP*/ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) }, + /* U.S. Robotics 56K Voice INT PnP*/ + { ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) }, { 0, } }; @@ -4394,10 +4852,80 @@ static void inline avoid_irq_share(struct pci_dev *dev) irq->map = map; } +static char *modem_names[] __initdata = { + "MODEM", "Modem", "modem", "FAX", "Fax", "fax", + "56K", "56k", "K56", "33.6", "28.8", "14.4", + "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", + "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 +}; + +static int __init check_name(char *name) +{ + char **tmp = modem_names; + + while (*tmp) { + if (strstr(name, *tmp)) + return 1; + tmp++; + } + return 0; +} + +static int inline check_compatible_id(struct pci_dev *dev) +{ + int i; + for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) + if ((dev->vendor_compatible[i] == + ISAPNP_VENDOR('P', 'N', 'P')) && + (swab16(dev->device_compatible[i]) >= 0xc000) && + (swab16(dev->device_compatible[i]) <= 0xdfff)) + return 0; + return 1; +} + +/* + * Given a complete unknown ISA PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base adress + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int _INLINE_ serial_pnp_guess_board(struct pci_dev *dev, + struct pci_board *board) +{ + struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; + struct isapnp_resources *resa; + + if (!(check_name(dev->name) || check_name(dev->bus->name)) && + !(check_compatible_id(dev))) + return 1; + + if (!res || res->next) + return 1; + + for (resa = res->alt; resa; resa = resa->alt) { + struct isapnp_port *port; + for (port = res->port; port; port = port->next) + if ((port->size == 8) && + ((port->min == 0x2f8) || + (port->min == 0x3f8) || + (port->min == 0x2e8) || + (port->min == 0x3e8))) + return 0; + } + + return 1; +} + static void __init probe_serial_pnp(void) { struct pci_dev *dev = NULL; - struct pci_board *board; + struct pnp_board *pnp_board; + struct pci_board board; #ifdef SERIAL_DEBUG_PNP printk("Entered probe_serial_pnp()\n"); @@ -4409,13 +4937,35 @@ static void __init probe_serial_pnp(void) return; } - for (board = pnp_devices; board->vendor; board++) { - while ((dev = isapnp_find_dev(NULL, board->vendor, - board->device, dev))) { - if (board->flags & SPCI_FL_NO_SHIRQ) - avoid_irq_share(dev); - start_pci_pnp_board(dev, board); - } + isapnp_for_each_dev(dev) { + if (dev->active) + continue; + + memset(&board, 0, sizeof(board)); + board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT; + board.num_ports = 1; + board.base_baud = 115200; + + for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++) + if ((dev->vendor == pnp_board->vendor) && + (dev->device == pnp_board->device)) + break; + + if (pnp_board->vendor) { + board.vendor = pnp_board->vendor; + board.device = pnp_board->device; + /* Special case that's more efficient to hardcode */ + if ((board.vendor == ISAPNP_VENDOR('A', 'K', 'Y') && + board.device == ISAPNP_DEVICE(0x1021))) + board.flags |= SPCI_FL_NO_SHIRQ; + } else { + if (serial_pnp_guess_board(dev, &board)) + continue; + } + + if (board.flags & SPCI_FL_NO_SHIRQ) + avoid_irq_share(dev); + start_pci_pnp_board(dev, &board); } #ifdef SERIAL_DEBUG_PNP @@ -4477,7 +5027,7 @@ int __init rs_init(void) #if (LINUX_VERSION_CODE > 0x20100) serial_driver.driver_name = "serial"; #endif -#ifdef CONFIG_DEVFS_FS +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) serial_driver.name = "tts/%d"; #else serial_driver.name = "ttyS"; @@ -4525,7 +5075,7 @@ int __init rs_init(void) * major number and the subtype code. */ callout_driver = serial_driver; -#ifdef CONFIG_DEVFS_FS +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) callout_driver.name = "cua/%d"; #else callout_driver.name = "cua"; @@ -4615,13 +5165,24 @@ int register_serial(struct serial_struct *req) unsigned long flags; struct serial_state *state; struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += req->port_high << HIGH_BITS_OFFSET; save_flags(flags); cli(); for (i = 0; i < NR_PORTS; i++) { - if ((rs_table[i].port == req->port) && + if ((rs_table[i].port == port) && (rs_table[i].iomem_base == req->iomem_base)) break; } + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } if (i == NR_PORTS) { for (i = 0; i < NR_PORTS; i++) if ((rs_table[i].type == PORT_UNKNOWN) && @@ -4636,11 +5197,11 @@ int register_serial(struct serial_struct *req) if (rs_table[i].count) { restore_flags(flags); printk("Couldn't configure serial #%d (port=%ld,irq=%d): " - "device already open\n", i, req->port, req->irq); + "device already open\n", i, port, req->irq); return -1; } state->irq = req->irq; - state->port = req->port; + state->port = port; state->flags = req->flags; state->io_type = req->io_type; state->iomem_base = req->iomem_base; @@ -4648,7 +5209,7 @@ int register_serial(struct serial_struct *req) if (req->baud_base) state->baud_base = req->baud_base; if ((info = state->info) != NULL) { - info->port = req->port; + info->port = port; info->flags = req->flags; info->io_type = req->io_type; info->iomem_base = req->iomem_base; @@ -4721,10 +5282,10 @@ void rs_fini(void) timer_table[RS_TIMER].expires = 0; remove_bh(SERIAL_BH); if ((e1 = tty_unregister_driver(&serial_driver))) - printk("SERIAL: failed to unregister serial driver (%d)\n", + printk("serial: failed to unregister serial driver (%d)\n", e1); if ((e2 = tty_unregister_driver(&callout_driver))) - printk("SERIAL: failed to unregister callout driver (%d)\n", + printk("serial: failed to unregister callout driver (%d)\n", e2); restore_flags(flags); @@ -4733,8 +5294,15 @@ void rs_fini(void) rs_table[i].info = NULL; kfree_s(info, sizeof(struct async_struct)); } - if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) - release_region(rs_table[i].port, 8); + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif + release_region(rs_table[i].port, 8); + } #if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) if (rs_table[i].iomem_base) iounmap(rs_table[i].iomem_base); @@ -4881,9 +5449,6 @@ static int __init serial_console_setup(struct console *co, char *options) int cflag = CREAD | HUPCL | CLOCAL; int quot = 0; char *s; -#if defined(CONFIG_KDB) - extern int kdb_port; -#endif if (options) { baud = simple_strtoul(options, NULL, 10); @@ -4987,14 +5552,6 @@ static int __init serial_console_setup(struct console *co, char *options) if (serial_in(info, UART_LSR) == 0xff) return -1; -#if defined(CONFIG_KDB) - /* - * Remember I/O port for kdb - */ - if (kdb_port == 0 ) - kdb_port = ser->port; -#endif /* CONFIG_KDB */ - return 0; } @@ -5023,6 +5580,6 @@ void __init serial_console_init(void) /* Local variables: - compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" End: */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index fd4908f78d96..d702851d787e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1427,49 +1427,6 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait) return 0; } -/* - * fasync_helper() is used by some character device drivers (mainly mice) - * to set up the fasync queue. It returns negative on error, 0 if it did - * no changes and positive if it added/deleted the entry. - */ -int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) -{ - struct fasync_struct *fa, **fp; - unsigned long flags; - - for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { - if (fa->fa_file == filp) - break; - } - - if (on) { - if (fa) { - fa->fa_fd = fd; - return 0; - } - fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); - if (!fa) - return -ENOMEM; - fa->magic = FASYNC_MAGIC; - fa->fa_file = filp; - fa->fa_fd = fd; - save_flags(flags); - cli(); - fa->fa_next = *fapp; - *fapp = fa; - restore_flags(flags); - return 1; - } - if (!fa) - return 0; - save_flags(flags); - cli(); - *fp = fa->fa_next; - restore_flags(flags); - kfree(fa); - return 1; -} - static int tty_fasync(int fd, struct file * filp, int on) { struct tty_struct * tty; diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index d0f4257daab1..b425513b04cd 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 : + Cleaned up for kernel merge. Removed the back compatibility support Reformatted, fixing spelling etc as I went Removed IRQ 0-15 assumption - + + Jeff Garzik : + 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 #include @@ -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/irda/girbil.c b/drivers/net/irda/girbil.c index f4aafc3249a7..37d0291c8f43 100644 --- a/drivers/net/irda/girbil.c +++ b/drivers/net/irda/girbil.c @@ -201,13 +201,13 @@ static int girbil_reset(struct irda_task *task) self->set_dtr_rts(self->dev, TRUE, FALSE); irda_task_next_state(task, IRDA_TASK_WAIT1); /* Sleep at least 5 ms */ - ret = MSECS_TO_JIFFIES(10); + ret = MSECS_TO_JIFFIES(20); break; case IRDA_TASK_WAIT1: /* Set DTR and clear RTS to enter command mode */ self->set_dtr_rts(self->dev, FALSE, TRUE); irda_task_next_state(task, IRDA_TASK_WAIT2); - ret = MSECS_TO_JIFFIES(10); + ret = MSECS_TO_JIFFIES(20); break; case IRDA_TASK_WAIT2: /* Write control byte */ diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c index d8e76deefb28..d9b774f6fc54 100644 --- a/drivers/net/wan/comx.c +++ b/drivers/net/wan/comx.c @@ -81,6 +81,10 @@ extern int comx_proto_fr_init(void); static struct comx_hardware *comx_channels = NULL; static struct comx_protocol *comx_lines = NULL; +static int comx_mkdir(struct inode *, struct dentry *, int); +static int comx_rmdir(struct inode *, struct dentry *); +static struct dentry *comx_lookup(struct inode *, struct dentry *); + static struct inode_operations comx_root_inode_ops = { lookup: comx_lookup, mkdir: comx_mkdir, diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c index a945167c5abb..36d0a5d6a9e2 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/telephony/ixj.c b/drivers/telephony/ixj.c index 6a5af79437c8..043d57bbc897 100644 --- a/drivers/telephony/ixj.c +++ b/drivers/telephony/ixj.c @@ -446,8 +446,7 @@ static void ixj_timeout(unsigned long ptr) { j->m_hook = 0; j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of change } goto timer_end; } @@ -536,8 +535,7 @@ static void ixj_timeout(unsigned long ptr) j->proc_load = j->ssr.high << 8 | j->ssr.low; if (!j->m_hook) { j->m_hook = j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of change } } else { if (j->dsp.low == 0x21 && @@ -552,8 +550,7 @@ static void ixj_timeout(unsigned long ptr) if (j->m_hook) { j->m_hook = 0; j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of change } } } @@ -642,8 +639,7 @@ static void ixj_timeout(unsigned long ptr) } if (j->ex.bytes) { wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of change } } else { break; @@ -917,8 +913,7 @@ static int ixj_hookstate(int board) j->r_hook = fOffHook; if (j->port != PORT_POTS) { j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of change } } @@ -1045,8 +1040,6 @@ int ixj_open(struct phone_device *p, struct file *file_p) if (file_p->f_mode & FMODE_WRITE) j->writers++; - MOD_INC_USE_COUNT; - if (ixjdebug > 0) // printk(KERN_INFO "Opening board %d\n", NUM(inode->i_rdev)); printk(KERN_INFO "Opening board %d\n", p->board); @@ -1196,7 +1189,6 @@ int ixj_release(struct inode *inode, struct file *file_p) j->rec_frame_size = j->play_frame_size = 0; ixj_fasync(-1, file_p, 0); // remove from list of async notification - MOD_DEC_USE_COUNT; return 0; } @@ -1471,8 +1463,7 @@ static void ixj_read_frame(int board) wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of frame + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of frame } } @@ -1557,8 +1548,7 @@ static void ixj_write_frame(int board) wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of empty buffer + kill_fasync(&j->async_queue, SIGIO, POLL_IN); // Send apps notice of empty buffer #ifdef PERFMON_STATS ++j->frameswritten; #endif @@ -3949,6 +3939,7 @@ static int ixj_fasync(int fd, struct file *file_p, int mode) struct file_operations ixj_fops = { + owner: THIS_MODULE, read: ixj_enhanced_read, write: ixj_enhanced_write, poll: ixj_poll, @@ -4615,10 +4606,12 @@ int __init ixj_init(void) pci = pci_find_device(0x15E2, 0x0500, pci); if (!pci) break; + if (pci_enable_device(pci)) + break; { - ixj[cnt].DSPbase = pci->resource[0].start; + ixj[cnt].DSPbase = pci_resource_start(pci, 0); ixj[cnt].XILINXbase = ixj[cnt].DSPbase + 0x10; - ixj[cnt].serial = PCIEE_GetSerialNumber(pci->resource[2].start); + ixj[cnt].serial = (PCIEE_GetSerialNumber)pci_resource_start(pci, 2); result = check_region(ixj[cnt].DSPbase, 16); if (result) { diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 042f742da5ab..2975a51d4dcf 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -70,15 +70,18 @@ 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 ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB - dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $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 + dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 fi - dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_USB - dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_USB + dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV + dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV fi endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ec8687ae6af3..7d95c0c9892b 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/iforce.c b/drivers/usb/iforce.c new file mode 100644 index 000000000000..a61b7801f365 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +#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/wmforce.c b/drivers/usb/wmforce.c deleted file mode 100644 index dff963b74996..000000000000 --- 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 , or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR("Vojtech Pavlik "); - -#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/fs/adfs/dir.c b/fs/adfs/dir.c index a5fcc1a54183..21a27734426b 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -12,12 +12,7 @@ #include #include #include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) #include -#else -#include -#endif #include "adfs.h" diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index cec45c0980d7..bdcce1c51c40 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -11,12 +11,7 @@ #include #include #include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) #include -#else -#include -#endif #include "adfs.h" #include "dir_f.h" diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index afb770d0d5ae..21d719ba7476 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -9,12 +9,7 @@ #include #include #include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) #include -#else -#include -#endif #include "adfs.h" #include "dir_fplus.h" diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 0cd28ca47209..bdd2a4f18c14 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -29,24 +29,13 @@ #include "adfs.h" -/* - * We have mostly NULLs here: the current defaults are OK for - * the adfs filesystem. - */ struct file_operations adfs_file_operations = { read: generic_file_read, mmap: generic_file_mmap, fsync: file_fsync, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) write: generic_file_write, -#endif }; struct inode_operations adfs_file_inode_operations = { setattr: adfs_notify_change, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - &adfs_file_operations, /* default file operations */ - readpage: generic_readpage, - bmap: adfs_bmap, -#endif }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 6f9c389a4ca0..fa3655a12860 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -19,7 +19,6 @@ * Lookup/Create a block at offset 'block' into 'inode'. We currently do * not support creation of new blocks, so we return -EIO for this case. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) int adfs_get_block(struct inode *inode, long block, struct buffer_head *bh, int create) { @@ -79,16 +78,6 @@ static struct address_space_operations adfs_aops = { bmap: _adfs_bmap }; -#else -int adfs_bmap(struct inode *inode, int block) -{ - if (block >= inode->i_blocks) - return 0; - - return __adfs_block_map(inode->i_sb, inode->i_ino, block); -} -#endif - static inline unsigned int adfs_filetype(struct inode *inode) { diff --git a/fs/adfs/map.c b/fs/adfs/map.c index 0c6507411ab9..e74458e453db 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -7,12 +7,7 @@ #include #include #include - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) #include -#else -#include -#endif #include "adfs.h" diff --git a/fs/adfs/super.c b/fs/adfs/super.c index da2cc0788d47..7e56aeec7a63 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -318,7 +318,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile set_blocksize(dev, BLOCK_SIZE); if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { adfs_error(sb, "unable to read superblock"); - goto error_unlock; + goto error; } b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); @@ -354,7 +354,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile if (!bh) { adfs_error(sb, "couldn't read superblock on " "2nd try."); - goto error_unlock; + goto error; } b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); if (adfs_checkbblk(b_data)) { @@ -416,11 +416,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj)); -#else - sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj), NULL); -#endif if (!sb->s_root) { int i; @@ -428,14 +424,12 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile brelse(sb->u.adfs_sb.s_map[i].dm_bh); kfree(sb->u.adfs_sb.s_map); adfs_error(sb, "get root inode failed\n"); - goto error_dec_use; + goto error; } return sb; error_free_bh: brelse(bh); -error_unlock: -error_dec_use: error: return NULL; } diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 9551d701631d..2b47ab674295 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -1,7 +1,7 @@ /* * fs/bfs/inode.c * BFS superblock and inode operations. - * Copyright (C) 1999 Tigran Aivazian + * Copyright (C) 1999 Tigran Aivazian * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds. */ @@ -16,7 +16,7 @@ #include "bfs_defs.h" -MODULE_AUTHOR("Tigran A. Aivazian"); +MODULE_AUTHOR("Tigran A. Aivazian "); MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux"); EXPORT_NO_SYMBOLS; diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 68be7a69d3a8..eff2da6cd910 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -24,82 +24,48 @@ #include #include -static void coda_ccinsert(struct coda_cache *el, struct super_block *sb); -static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii); -static void coda_ccremove(struct coda_cache *el); -static void coda_cnremove(struct coda_cache *el); -static void coda_cache_create(struct inode *inode, int mask); -static struct coda_cache * coda_cache_find(struct inode *inode); - - -/* insert a acl-cache entry in sb list */ -static void coda_ccinsert(struct coda_cache *el, struct super_block *sb) +/* create a new acl cache entry and enlist it */ +static struct coda_cache *coda_cache_create(struct inode *inode) { - struct coda_sb_info *sbi = coda_sbp(sb); + struct coda_inode_info *cii = ITOC(inode); + struct coda_sb_info *sbi = coda_sbp(inode->i_sb); + struct coda_cache *cc = NULL; ENTRY; - /* third test verifies cc was initialized before adding it - to the sblist. Probably superfluous */ - if ( !sbi || !el || !list_empty(&el->cc_cclist) ) { - printk("coda_ccinsert: NULL sbi or el->cc_cclist not empty!\n"); - return ; + + if ( !sbi || !cii ) { + printk("coda_cache_create: NULL sbi or cii!\n"); + return NULL; } - list_add(&el->cc_cclist, &sbi->sbi_cchead); -} + CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc)); -/* insert a acl-cache entry in the inode list */ -static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii) -{ - ENTRY; - if ( !cii || !el || ! list_empty(&el->cc_cnlist)) { - printk("coda_cninsert: NULL cii or el->cc_cnlist not empty!\n"); - return ; + if ( !cc ) { + printk("Out of memory in coda_cache_create!\n"); + return NULL; } - list_add(&el->cc_cnlist, &cii->c_cnhead); -} -/* remove a cache entry from the superblock list */ -static void coda_ccremove(struct coda_cache *el) -{ - ENTRY; - if ( ! list_empty(&el->cc_cclist) ) - list_del(&el->cc_cclist); - else - printk("coda_ccremove: loose cc entry!"); -} + coda_load_creds(&cc->cc_cred); + cc->cc_mask = 0; -/* remove a cache entry from the inode's list */ -static void coda_cnremove(struct coda_cache *el) -{ - ENTRY; - if ( ! list_empty(&el->cc_cnlist) ) - list_del(&el->cc_cnlist); - else - printk("coda_cnremove: loose cn entry!"); + INIT_LIST_HEAD(&cc->cc_cclist); + INIT_LIST_HEAD(&cc->cc_cnlist); + list_add(&cc->cc_cclist, &sbi->sbi_cchead); + list_add(&cc->cc_cnlist, &cii->c_cnhead); + + return cc; } -/* create a new cache entry and enlist it */ -static void coda_cache_create(struct inode *inode, int mask) +/* destroy an acl cache entry */ +static void coda_cache_destroy(struct coda_cache *el) { - struct coda_inode_info *cii = ITOC(inode); - struct super_block *sb = inode->i_sb; - struct coda_cache *cc = NULL; ENTRY; - - CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc)); - - if ( !cc ) { - printk("Out of memory in coda_cache_enter!\n"); + if (list_empty(&el->cc_cclist) || list_empty(&el->cc_cnlist)) { + printk("coda_cache_destroy: loose entry!"); return; } - - INIT_LIST_HEAD(&cc->cc_cclist); - INIT_LIST_HEAD(&cc->cc_cnlist); - - coda_load_creds(&cc->cc_cred); - cc->cc_mask = mask; - coda_cninsert(cc, cii); - coda_ccinsert(cc, sb); + list_del(&el->cc_cclist); + list_del(&el->cc_cnlist); + CODA_FREE(el, sizeof(struct coda_cache)); } /* see if there is a match for the current @@ -107,11 +73,11 @@ static void coda_cache_create(struct inode *inode, int mask) static struct coda_cache * coda_cache_find(struct inode *inode) { struct coda_inode_info *cii = ITOC(inode); - struct list_head *lh, *le; + struct list_head *le; struct coda_cache *cc = NULL; - le = lh = &cii->c_cnhead; - while( (le = le->next ) != lh ) { + list_for_each(le, &cii->c_cnhead) + { /* compare name and creds */ cc = list_entry(le, struct coda_cache, cc_cnlist); if ( !coda_cred_ok(&cc->cc_cred) ) @@ -119,7 +85,7 @@ static struct coda_cache * coda_cache_find(struct inode *inode) CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); return cc; /* cache hit */ } - return NULL; + return NULL; } /* create or extend an acl cache hit */ @@ -129,11 +95,10 @@ void coda_cache_enter(struct inode *inode, int mask) cc = coda_cache_find(inode); - if ( cc ) { + if (!cc) + cc = coda_cache_create(inode); + if (cc) cc->cc_mask |= mask; - } else { - coda_cache_create(inode, mask); - } } /* remove all cached acl matches from an inode */ @@ -154,9 +119,7 @@ void coda_cache_clear_inode(struct inode *inode) while ( le != &cii->c_cnhead ) { cc = list_entry(le, struct coda_cache, cc_cnlist); le = le->next; - coda_cnremove(cc); - coda_ccremove(cc); - CODA_FREE(cc, sizeof(*cc)); + coda_cache_destroy(cc); } } @@ -172,16 +135,11 @@ void coda_cache_clear_all(struct super_block *sb) return; } - if ( list_empty(&sbi->sbi_cchead) ) - return; - le = sbi->sbi_cchead.next; while ( le != &sbi->sbi_cchead ) { cc = list_entry(le, struct coda_cache, cc_cclist); le = le->next; - coda_cnremove(cc); - coda_ccremove(cc); - CODA_FREE(cc, sizeof(*cc)); + coda_cache_destroy(cc); } } @@ -197,18 +155,12 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) return; } - if (list_empty(&sbi->sbi_cchead)) - return; - le = sbi->sbi_cchead.next; while ( le != &sbi->sbi_cchead ) { cc = list_entry(le, struct coda_cache, cc_cclist); le = le->next; - if ( coda_cred_eq(&cc->cc_cred, cred)) { - coda_cnremove(cc); - coda_ccremove(cc); - CODA_FREE(cc, sizeof(*cc)); - } + if ( coda_cred_eq(&cc->cc_cred, cred)) + coda_cache_destroy(cc); } } @@ -218,11 +170,11 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) int coda_cache_check(struct inode *inode, int mask) { struct coda_inode_info *cii = ITOC(inode); - struct list_head *lh, *le; + struct list_head *le; struct coda_cache *cc = NULL; - le = lh = &cii->c_cnhead; - while( (le = le->next ) != lh ) { + list_for_each(le, &cii->c_cnhead) + { /* compare name and creds */ cc = list_entry(le, struct coda_cache, cc_cnlist); if ( (cc->cc_mask & mask) != mask ) @@ -232,8 +184,8 @@ int coda_cache_check(struct inode *inode, int mask) CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); return 1; /* cache hit */ } - CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino ); - return 0; + CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino ); + return 0; } @@ -276,10 +228,9 @@ static void coda_flag_children(struct dentry *parent, int flag) struct list_head *child; struct dentry *de; - child = parent->d_subdirs.next; - while ( child != &parent->d_subdirs ) { + list_for_each(child, &parent->d_subdirs) + { de = list_entry(child, struct dentry, d_child); - child = child->next; /* don't know what to do with negative dentries */ if ( ! de->d_inode ) continue; @@ -307,17 +258,3 @@ void coda_flag_inode_children(struct inode *inode, int flag) dput(alias_de); } -/* this will not zap the inode away */ -void coda_flag_inode(struct inode *inode, int flag) -{ - struct coda_inode_info *cii; - - if ( !inode ) { - CDEBUG(D_CACHE, " no inode!\n"); - return; - } - - cii = ITOC(inode); - cii->c_flags |= flag; -} - diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 197855e1db25..7c554461271f 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -50,6 +50,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) } else if (S_ISLNK(inode->i_mode)) { inode->i_op = &coda_symlink_inode_operations; inode->i_data.a_ops = &coda_symlink_aops; + inode->i_mapping = &inode->i_data; } else init_special_inode(inode, inode->i_mode, attr->va_rdev); } @@ -65,13 +66,25 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, inode = iget(sb, ino); if ( !inode ) { CDEBUG(D_CNODE, "coda_iget: no inode\n"); - return NULL; + return ERR_PTR(-ENOMEM); } /* check if the inode is already initialized */ cii = ITOC(inode); - if (cii->c_magic == CODA_CNODE_MAGIC) + if (cii->c_magic == CODA_CNODE_MAGIC) { + /* see if it is the right one (might have an inode collision) */ + if ( !coda_fideq(fid, &cii->c_fid) ) { + printk("coda_iget: initialized inode old %s new %s!\n", + coda_f2s(&cii->c_fid), coda_f2s2(fid)); + iput(inode); + return ERR_PTR(-ENOENT); + } + /* replace the attributes, type might have changed */ + coda_fill_inode(inode, attr); goto out; + } + + /* new, empty inode found... initializing */ /* Initialize the Coda inode info structure */ memset(cii, 0, (int) sizeof(struct coda_inode_info)); @@ -90,15 +103,17 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, if ( coda_f2i(fid) == ino ) goto out; - /* check if we expect this weird fid */ - if ( !coda_fid_is_weird(fid) ) + /* check if we expected this weird fid */ + if ( !coda_fid_is_weird(fid) ) { printk("Coda: unknown weird fid: ino %ld, fid %s." "Tell Peter.\n", (long)ino, coda_f2s(&cii->c_fid)); + goto out; + } /* add the inode to a global list so we can find it back later */ list_add(&cii->c_volrootlist, &sbi->sbi_volroothead); CDEBUG(D_CNODE, "Added %ld, %s to volroothead\n", - (long)ino, coda_f2s(&cii->c_fid)); + (long)ino, coda_f2s(&cii->c_fid)); out: return inode; } @@ -111,7 +126,6 @@ out: */ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) { - struct coda_inode_info *cnp; struct coda_vattr attr; int error; @@ -125,32 +139,22 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) "coda_cnode_make: coda_getvattr returned %d for %s.\n", error, coda_f2s(fid)); *inode = NULL; + EXIT; return error; } *inode = coda_iget(sb, fid, &attr); - if ( !(*inode) ) { + if ( IS_ERR(*inode) ) { printk("coda_cnode_make: coda_iget failed\n"); - return -ENOMEM; - } - - cnp = ITOC(*inode); - /* see if it is the right one (we might have an inode collision) */ - if ( coda_fideq(fid, &cnp->c_fid) ) { - CDEBUG(D_DOWNCALL, - "Done making inode: ino %ld, count %d with %s\n", - (*inode)->i_ino, atomic_read(&(*inode)->i_count), - coda_f2s(&cnp->c_fid)); EXIT; - return 0; - } + return PTR_ERR(*inode); + } - /* collision */ - printk("coda_cnode_make on initialized inode %ld, old %s new %s!\n", - (*inode)->i_ino, coda_f2s(&cnp->c_fid), coda_f2s2(fid)); - iput(*inode); + CDEBUG(D_DOWNCALL, "Done making inode: ino %ld, count %d with %s\n", + (*inode)->i_ino, atomic_read(&(*inode)->i_count), + coda_f2s(&(*inode)->u.coda_i.c_fid)); EXIT; - return -ENOENT; + return 0; } @@ -168,7 +172,8 @@ void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, cnp->c_fid = *newfid; list_del(&cnp->c_volrootlist); - if ( !coda_fid_is_weird(newfid) ) + INIT_LIST_HEAD(&cnp->c_volrootlist); + if ( coda_fid_is_weird(newfid) ) list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); return; @@ -184,10 +189,9 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) { ino_t nr; struct inode *inode; - struct coda_inode_info *cnp; + struct coda_inode_info *cii; ENTRY; - if ( !sb ) { printk("coda_fid_to_inode: no sb!\n"); return NULL; @@ -201,7 +205,6 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) if ( coda_fid_is_weird(fid) ) { - struct coda_inode_info *cii; struct list_head *lh, *le; struct coda_sb_info *sbi = coda_sbp(sb); le = lh = &sbi->sbi_volroothead; @@ -209,19 +212,19 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) while ( (le = le->next) != lh ) { cii = list_entry(le, struct coda_inode_info, c_volrootlist); - CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n", + /* paranoia check, should never trigger */ + if ( cii->c_magic != CODA_CNODE_MAGIC ) + printk("coda_fid_to_inode: Bad magic in inode %x.\n", cii->c_magic); + + CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n", coda_f2s(&cii->c_fid), cii->c_vnode->i_ino); + if ( coda_fideq(&cii->c_fid, fid) ) { inode = cii->c_vnode; CDEBUG(D_INODE, "volume root, found %ld\n", inode->i_ino); - if ( cii->c_magic != CODA_CNODE_MAGIC ) - printk("%s: Bad magic in inode, tell Peter.\n", - __FUNCTION__); - iget(sb, inode->i_ino); return inode; } - } return NULL; } @@ -236,27 +239,27 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) } /* check if this inode is linked to a cnode */ - cnp = ITOC(inode); - if ( cnp->c_magic != CODA_CNODE_MAGIC ) { + cii = ITOC(inode); + if ( cii->c_magic != CODA_CNODE_MAGIC ) { CDEBUG(D_INODE, "uninitialized inode. Return.\n"); - iput(inode); - return NULL; + goto bad_inode; } - /* make sure fid is the one we want; - unfortunately Venus will shamelessly send us mount-symlinks. - These have the same inode as the root of the volume they - mount, but the fid will be wrong. - */ - if ( !coda_fideq(fid, &(cnp->c_fid)) ) { - /* printk("coda_fid2inode: bad cnode (ino %ld, fid %s)" - "Tell Peter.\n", nr, coda_f2s(fid)); */ - iput(inode); - return NULL; + /* make sure fid is the one we want */ + if ( !coda_fideq(fid, &(cii->c_fid)) ) { +#if 0 + printk("coda_fid2inode: bad cnode (ino %ld, fid %s)", nr, + coda_f2s(fid)); +#endif + goto bad_inode; } CDEBUG(D_INODE, "found %ld\n", inode->i_ino); return inode; + +bad_inode: + iput(inode); + return NULL; } /* the CONTROL inode is made without asking attributes from Venus */ @@ -276,3 +279,4 @@ int coda_cnode_makectl(struct inode **inode, struct super_block *sb) return error; } + diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 2031b4f6e039..0faf296631aa 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -48,11 +48,15 @@ static int coda_dentry_revalidate(struct dentry *de, int); static int coda_dentry_delete(struct dentry *); /* support routines */ +static void coda_prepare_fakefile(struct inode *coda_inode, + struct file *coda_file, + struct inode *open_inode, + struct file *open_file, + 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 coda_fsync(struct file *, struct dentry *dentry, int); -int coda_crossvol_rename = 0; int coda_hasmknod = 0; struct dentry_operations coda_dentry_operations = @@ -90,27 +94,24 @@ struct file_operations coda_dir_operations = { /* acces routines: lookup, readlink, permission */ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) { - struct coda_inode_info *dircnp; struct inode *res_inode = NULL; - struct ViceFid resfid; + struct ViceFid resfid = {0,0,0}; int dropme = 0; /* to indicate entry should not be cached */ - int type; + int type = 0; int error = 0; const char *name = entry->d_name.name; size_t length = entry->d_name.len; ENTRY; - dircnp = ITOC(dir); - if ( length > CODA_MAXNAMLEN ) { printk("name too long: lookup, %s (%*s)\n", - coda_f2s(&dircnp->c_fid), (int)length, name); + coda_i2s(dir), (int)length, name); return ERR_PTR(-ENAMETOOLONG); } CDEBUG(D_INODE, "name %s, len %ld in ino %ld, fid %s\n", - name, (long)length, dir->i_ino, coda_f2s(&dircnp->c_fid)); + name, (long)length, dir->i_ino, coda_i2s(dir)); /* control object, create inode on the fly */ if (coda_isroot(dir) && coda_iscontrol(name, length)) { @@ -118,10 +119,11 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) CDEBUG(D_SPECIAL, "Lookup on CTL object; dir ino %ld, count %d\n", dir->i_ino, atomic_read(&dir->i_count)); + dropme = 1; goto exit; } - error = venus_lookup(dir->i_sb, &(dircnp->c_fid), + error = venus_lookup(dir->i_sb, coda_i2f(dir), (const char *)name, length, &type, &resfid); res_inode = NULL; @@ -132,12 +134,17 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry) coda_f2s(&resfid)); dropme = 1; } + error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); - if (error) - return ERR_PTR(error); + if (error) return ERR_PTR(error); + + /* make sure we drop unexpected weird fid's */ + if (coda_f2i(&resfid) != res_inode->i_ino && + !coda_fid_is_weird(&resfid)) + dropme = 1; } else if (error != -ENOENT) { CDEBUG(D_INODE, "error for %s(%*s)%d\n", - coda_f2s(&dircnp->c_fid), (int)length, name, error); + coda_i2s(dir), (int)length, name, error); return ERR_PTR(error); } CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", @@ -149,7 +156,7 @@ exit: d_add(entry, res_inode); if ( dropme ) { d_drop(entry); - ITOC(res_inode)->c_flags |= C_VATTR; + coda_flag_inode(res_inode, C_VATTR); } EXIT; return NULL; @@ -158,16 +165,14 @@ exit: int coda_permission(struct inode *inode, int mask) { - struct coda_inode_info *cp = ITOC(inode); int error; ENTRY; coda_vfs_stat.permission++; coda_permission_stat.count++; - if ( mask == 0 ) { + if ( mask == 0 ) return 0; - } if ( coda_access_cache == 1 ) { if ( coda_cache_check(inode, mask) ) { @@ -176,28 +181,39 @@ int coda_permission(struct inode *inode, int mask) } } - cp = ITOC(inode); - CDEBUG(D_INODE, "mask is %o\n", mask); - error = venus_access(inode->i_sb, &(cp->c_fid), mask); + error = venus_access(inode->i_sb, coda_i2f(inode), mask); CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", - coda_f2s(&(cp->c_fid)), inode->i_ino, mask, error); + coda_i2s(inode), inode->i_ino, mask, error); - if ( error == 0 ) { + if (!error) coda_cache_enter(inode, mask); - } return error; } -/* creation routines: create, mknod, mkdir, link, symlink */ +static inline void coda_dir_changed(struct inode *dir, int link) +{ +#ifdef REQUERY_VENUS_FOR_MTIME + /* invalidate the directory cnode's attributes so we refetch the + * attributes from venus next time the inode is referenced */ + coda_flag_inode(dir, C_VATTR); +#else + /* optimistically we can also act as if our nose bleeds. The + * granularity of the mtime is coarse anyways so we might actually be + * right most of the time. Note: we only do this for directories. */ + dir->i_mtime = CURRENT_TIME; +#endif + if (link) + dir->i_nlink += link; +} +/* creation routines: create, mknod, mkdir, link, symlink */ static int coda_create(struct inode *dir, struct dentry *de, int mode) { int error=0; - struct coda_inode_info *dircnp; const char *name=de->d_name.name; int length=de->d_name.len; struct inode *result = NULL; @@ -207,14 +223,12 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) ENTRY; coda_vfs_stat.create++; - CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode); + CDEBUG(D_INODE, "name: %s, length %d, mode %o\n", name, length, mode); if (coda_isroot(dir) && coda_iscontrol(name, length)) return -EPERM; - dircnp = ITOC(dir); - - error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, + error = venus_create(dir->i_sb, coda_i2f(dir), name, length, 0, mode, 0, &newfid, &attrs); if ( error ) { @@ -232,15 +246,14 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) } /* invalidate the directory cnode's attributes */ - dircnp->c_flags |= C_VATTR; + coda_dir_changed(dir, 0); d_instantiate(de, result); return 0; -} +} static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) { int error=0; - struct coda_inode_info *dircnp; const char *name=de->d_name.name; int length=de->d_name.len; struct inode *result = NULL; @@ -258,9 +271,7 @@ static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) if (coda_isroot(dir) && coda_iscontrol(name, length)) return -EPERM; - dircnp = ITOC(dir); - - error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, + error = venus_create(dir->i_sb, coda_i2f(dir), name, length, 0, mode, rdev, &newfid, &attrs); if ( error ) { @@ -278,14 +289,13 @@ static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev) } /* invalidate the directory cnode's attributes */ - dircnp->c_flags |= C_VATTR; + coda_dir_changed(dir, 0); d_instantiate(de, result); return 0; } static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) { - struct coda_inode_info *dircnp; struct inode *inode; struct coda_vattr attr; const char *name = de->d_name.name; @@ -299,13 +309,11 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) if (coda_isroot(dir) && coda_iscontrol(name, len)) return -EPERM; - dircnp = ITOC(dir); - CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", - name, len, coda_f2s(&(dircnp->c_fid)), mode); + name, len, coda_i2s(dir), mode); attr.va_mode = mode; - error = venus_mkdir(dir->i_sb, &(dircnp->c_fid), + error = venus_mkdir(dir->i_sb, coda_i2f(dir), name, len, &newfid, &attr); if ( error ) { @@ -325,8 +333,7 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) } /* invalidate the directory cnode's attributes */ - dircnp->c_flags |= C_VATTR; - dir->i_nlink++; + coda_dir_changed(dir, 1); d_instantiate(de, inode); return 0; } @@ -338,7 +345,6 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, struct inode *inode = source_de->d_inode; const char * name = de->d_name.name; int len = de->d_name.len; - struct coda_inode_info *dir_cnp, *cnp; int error; ENTRY; @@ -347,28 +353,26 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) return -EPERM; - dir_cnp = ITOC(dir_inode); - cnp = ITOC(inode); - - CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid))); - CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid))); + CDEBUG(D_INODE, "old: fid: %s\n", coda_i2s(inode)); + CDEBUG(D_INODE, "directory: %s\n", coda_i2s(dir_inode)); - error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid), - (const char *)name, len); + error = venus_link(dir_inode->i_sb, coda_i2f(inode), + coda_i2f(dir_inode), (const char *)name, len); - if ( ! error ) { - dir_cnp->c_flags |= C_VATTR; - atomic_inc(&inode->i_count); - d_instantiate(de, inode); - inode->i_nlink++; - } else { + if (error) { d_drop(de); - return error; + goto out; } - CDEBUG(D_INODE, "link result %d\n",error); + coda_dir_changed(dir_inode, 0); + atomic_inc(&inode->i_count); + d_instantiate(de, inode); + inode->i_nlink++; + +out: + CDEBUG(D_INODE, "link result %d\n",error); EXIT; - return(error); + return(error); } @@ -377,7 +381,6 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, { const char *name = de->d_name.name; int len = de->d_name.len; - struct coda_inode_info *dir_cnp = ITOC(dir_inode); int symlen; int error=0; @@ -398,13 +401,12 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, * an inode for the entry we have to drop it. */ d_drop(de); - error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, + error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, symname, symlen); /* mtime is no good anymore */ - if ( !error ) { - dir_cnp->c_flags |= C_VATTR; - } + if ( !error ) + coda_dir_changed(dir_inode, 0); CDEBUG(D_INODE, "in symlink result %d\n",error); EXIT; @@ -414,7 +416,6 @@ static int coda_symlink(struct inode *dir_inode, struct dentry *de, /* destruction routines: unlink, rmdir */ int coda_unlink(struct inode *dir, struct dentry *de) { - struct coda_inode_info *dircnp = ITOC(dir); int error; const char *name = de->d_name.name; int len = de->d_name.len; @@ -423,16 +424,15 @@ int coda_unlink(struct inode *dir, struct dentry *de) coda_vfs_stat.unlink++; CDEBUG(D_INODE, " %s in %s, dirino %ld\n", name , - coda_f2s(&(dircnp->c_fid)), dir->i_ino); + coda_i2s(dir), dir->i_ino); - error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len); + error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { CDEBUG(D_INODE, "upc returned error %d\n", error); return error; } - /* cache management: mtime has changed, ask Venus */ - dircnp->c_flags |= C_VATTR; + coda_dir_changed(dir, 0); de->d_inode->i_nlink--; return 0; @@ -440,7 +440,6 @@ int coda_unlink(struct inode *dir, struct dentry *de) int coda_rmdir(struct inode *dir, struct dentry *de) { - struct coda_inode_info *dircnp; const char *name = de->d_name.name; int len = de->d_name.len; int error; @@ -448,19 +447,18 @@ int coda_rmdir(struct inode *dir, struct dentry *de) ENTRY; coda_vfs_stat.rmdir++; - dircnp = ITOC(dir); - if (!d_unhashed(de)) return -EBUSY; - error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len); + error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); if ( error ) { CDEBUG(D_INODE, "upc returned error %d\n", error); return error; } - if (de->d_inode->i_nlink) - de->d_inode->i_nlink --; + coda_dir_changed(dir, -1); + de->d_inode->i_nlink--; + d_delete(de); return 0; } @@ -473,43 +471,38 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, const char *new_name = new_dentry->d_name.name; int old_length = old_dentry->d_name.len; int new_length = new_dentry->d_name.len; - struct inode *new_inode = new_dentry->d_inode; - struct coda_inode_info *new_cnp, *old_cnp; int error; ENTRY; coda_vfs_stat.rename++; - old_cnp = ITOC(old_dir); - new_cnp = ITOC(new_dir); - - CDEBUG(D_INODE, "old: %s, (%d length, %ld strlen), new: %s" - "(%d length, %ld strlen).old:d_count: %d, new:d_count: %d\n", - old_name, old_length, (long)strlen(old_name), new_name, new_length, - (long)strlen(new_name),old_dentry->d_count, new_dentry->d_count); - - /* the C library will do unlink/create etc */ - if ( coda_crossvol_rename == 0 && - old_cnp->c_fid.Volume != new_cnp->c_fid.Volume ) - return -EXDEV; + CDEBUG(D_INODE, "old: %s, (%d length), new: %s" + "(%d length). old:d_count: %d, new:d_count: %d\n", + old_name, old_length, new_name, new_length, + old_dentry->d_count, new_dentry->d_count); - error = venus_rename(old_dir->i_sb, &(old_cnp->c_fid), - &(new_cnp->c_fid), old_length, new_length, + error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), + coda_i2f(new_dir), old_length, new_length, (const char *) old_name, (const char *)new_name); - if ( error ) { - CDEBUG(D_INODE, "returned error %d\n", error); - return error; - } + if ( !error ) { + if ( new_dentry->d_inode ) { + if ( S_ISDIR(new_dentry->d_inode->i_mode) ) { + old_dir->i_nlink--; + new_dir->i_nlink++; + } + coda_flag_inode(new_dentry->d_inode, C_VATTR); + } - coda_flag_inode(new_inode, C_VATTR); - coda_flag_inode(old_dir, C_VATTR); - coda_flag_inode(new_dir, C_VATTR); + /* coda_flag_inode(old_dir, C_VATTR); */ + /* coda_flag_inode(new_dir, C_VATTR); */ + old_dir->i_mtime = new_dir->i_mtime = CURRENT_TIME; + } CDEBUG(D_INODE, "result %d\n", error); EXIT; - return 0; + return error; } @@ -517,43 +510,70 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, int coda_readdir(struct file *file, void *dirent, filldir_t filldir) { int result = 0; - struct coda_inode_info *cnp; struct file open_file; struct dentry open_dentry; - struct inode *inode=file->f_dentry->d_inode; + struct inode *inode=file->f_dentry->d_inode, *container; ENTRY; coda_vfs_stat.readdir++; - cnp = ITOC(inode); - if ( !cnp->c_ovp ) { - CDEBUG(D_FILE, "open inode pointer = NULL.\n"); + if ( inode->i_mapping == &inode->i_data ) { + CDEBUG(D_FILE, "no container inode.\n"); return -EIO; } - coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file, - &open_dentry); - if ( S_ISREG(cnp->c_ovp->i_mode) ) { + container = (struct inode *)inode->i_mapping->host; + + coda_prepare_fakefile(inode, file, container, &open_file, &open_dentry); + + if ( S_ISREG(container->i_mode) ) { /* Venus: we must read Venus dirents from the file */ result = coda_venus_readdir(&open_file, dirent, filldir); } else { - /* potemkin case: we are handed a directory inode */ + /* potemkin case: we are handed a directory inode */ result = vfs_readdir(&open_file, filldir, dirent); } - coda_restore_codafile(inode, file, cnp->c_ovp, &open_file); + + /* we only have to restore the file position (and f_version?) */ + file->f_pos = open_file.f_pos; + file->f_version = open_file.f_version; + EXIT; return result; } +/* grab the ext2 inode of the container file */ +static int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind) +{ + struct super_block *sbptr; + + sbptr = get_super(dev); + + if ( !sbptr ) { + printk("coda_inode_grab: coda_find_super returns NULL.\n"); + return -ENXIO; + } + + *ind = NULL; + *ind = iget(sbptr, ino); + + if ( *ind == NULL ) { + printk("coda_inode_grab: iget(dev: %d, ino: %ld) " + "returns NULL.\n", dev, (long)ino); + return -ENOENT; + } + CDEBUG(D_FILE, "ino: %ld, ops at %p\n", (long)ino, (*ind)->i_op); + return 0; +} + /* ask venus to cache the file and return the inode of the container file, put this inode pointer in the cnode for future read/writes */ int coda_open(struct inode *i, struct file *f) { ino_t ino; dev_t dev; - struct coda_inode_info *cnp; int error = 0; - struct inode *cont_inode = NULL; + struct inode *cont_inode = NULL, *old_container; unsigned short flags = f->f_flags & (~O_EXCL); unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_cred *cred; @@ -564,10 +584,7 @@ int coda_open(struct inode *i, struct file *f) CDEBUG(D_SPECIAL, "OPEN inode number: %ld, count %d, flags %o.\n", f->f_dentry->d_inode->i_ino, f->f_dentry->d_count, flags); - cnp = ITOC(i); - - - error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); + error = venus_open(i->i_sb, coda_i2f(i), coda_flags, &ino, &dev); if (error) { CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n", dev, (long)ino, error); @@ -589,25 +606,25 @@ int coda_open(struct inode *i, struct file *f) coda_load_creds(cred); f->private_data = cred; - if ( cnp->c_ovp ) - iput(cnp->c_ovp); - - cnp->c_ovp = cont_inode; + if ( i->i_mapping != &i->i_data ) { + old_container = (struct inode *)i->i_mapping->host; + i->i_mapping = &i->i_data; + iput(old_container); + } i->i_mapping = cont_inode->i_mapping; - cnp->c_ocount++; - CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", + CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", error, atomic_read(&i->i_count), i->i_ino); - CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", - cnp->c_ovp->i_ino, atomic_read(&cnp->c_ovp->i_count), - (cnp->c_ovp->i_op)); + CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", + cont_inode->i_ino, atomic_read(&cont_inode->i_count), + cont_inode->i_op); EXIT; return 0; } int coda_release(struct inode *i, struct file *f) { - struct coda_inode_info *cnp; + struct inode *container = NULL; int error = 0; unsigned short flags = (f->f_flags) & (~O_EXCL); unsigned short cflags = coda_flags_to_cflags(flags); @@ -618,30 +635,15 @@ int coda_release(struct inode *i, struct file *f) cred = (struct coda_cred *)f->private_data; - cnp =ITOC(i); - CHECK_CNODE(cnp); - CDEBUG(D_FILE, - "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n", - i->i_ino, atomic_read(&i->i_count), - (cnp->c_ovp ? cnp->c_ovp->i_ino : 0), - (cnp->c_ovp ? atomic_read(&cnp->c_ovp->i_count) : -99)); + if (i->i_mapping != &i->i_data) + container = (struct inode *)i->i_mapping->host; + CDEBUG(D_FILE, "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n", + i->i_ino, atomic_read(&i->i_count), + (container ? container->i_ino : 0), + (container ? atomic_read(&container->i_count) : -99)); - /* even when c_ocount=0 we cannot put c_ovp to - * NULL since the file may be mmapped. - * See code in inode.c (coda_put_inode) for - * further handling of close. - */ - - --cnp->c_ocount; - - if ( flags & (O_WRONLY | O_RDWR) ) - --cnp->c_owrite; - - /* Venus closing a container file? don't bother making the upcall. */ - if ( current->pid != coda_upc_comm.vc_pid ) { - error = venus_release(i->i_sb, &(cnp->c_fid), cflags, cred); - } + error = venus_release(i->i_sb, coda_i2f(i), cflags, cred); f->private_data = NULL; if (cred) @@ -652,13 +654,29 @@ int coda_release(struct inode *i, struct file *f) } /* support routines */ + +/* instantiate a fake file and dentry to pass to coda_venus_readdir */ +static void coda_prepare_fakefile(struct inode *i, struct file *coda_file, + struct inode *cont_inode, + struct file *cont_file, + struct dentry *cont_dentry) +{ + cont_file->f_dentry = cont_dentry; + cont_file->f_dentry->d_inode = cont_inode; + cont_file->f_pos = coda_file->f_pos; + cont_file->f_version = coda_file->f_version; + cont_file->f_op = cont_inode->i_fop; + return ; +} + /* * this structure is manipulated by filldir in vfs layer. * the count holds the remaining amount of space in the getdents buffer, * beyond the current_dir pointer. + * + * What structure is this comment referring to?? -JH */ - /* should be big enough to hold any single directory entry */ #define DIR_BUFSIZE 2048 @@ -768,13 +786,12 @@ static int coda_dentry_revalidate(struct dentry *de, int flags) if (!inode) return 1; - - cii = ITOC(de->d_inode); if (coda_isroot(inode)) return 1; if (is_bad_inode(inode)) return 0; + cii = ITOC(de->d_inode); if (! (cii->c_flags & (C_PURGE | C_FLUSH)) ) return valid; @@ -808,7 +825,7 @@ static int coda_dentry_delete(struct dentry * dentry) if (!dentry->d_inode) return 0; - flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; + flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; if (is_bad_inode(dentry->d_inode) || flags) { CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, @@ -826,14 +843,13 @@ static int coda_dentry_delete(struct dentry * dentry) * cache manager Venus issues a downcall to the kernel when this * happens */ - int coda_revalidate_inode(struct dentry *dentry) { struct coda_vattr attr; int error = 0; int old_mode; ino_t old_ino; - struct inode *inode = dentry->d_inode; + struct inode *inode = dentry->d_inode, *container; struct coda_inode_info *cii = ITOC(inode); ENTRY; @@ -844,14 +860,6 @@ int coda_revalidate_inode(struct dentry *dentry) if ( cii->c_flags == 0 ) return 0; - /* Venus accessing a container file, don't try to revalidate */ - if ( current->pid == coda_upc_comm.vc_pid ) - return 0; - - /* Venus closed the device .... */ - if ( cii->c_flags & C_DYING ) - goto return_bad_inode; - if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) { error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); if ( error ) @@ -866,7 +874,6 @@ int coda_revalidate_inode(struct dentry *dentry) old_ino = inode->i_ino; coda_vattr_to_iattr(inode, &attr); - if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) { printk("Coda: inode %ld, fid %s changed type!\n", inode->i_ino, coda_f2s(&(cii->c_fid))); @@ -886,10 +893,10 @@ int coda_revalidate_inode(struct dentry *dentry) return 0; return_bad_inode: - if ( cii->c_ovp ) { - iput(cii->c_ovp); + if ( inode->i_mapping != &inode->i_data ) { + container = (struct inode *)inode->i_mapping->host; inode->i_mapping = &inode->i_data; - cii->c_ovp = NULL; + iput(container); } make_bad_inode(inode); return -EIO; diff --git a/fs/coda/file.c b/fs/coda/file.c index ab805cf11b8f..704b4d00b5cd 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -26,139 +26,58 @@ #include #include -/* file operations */ -static int coda_file_mmap(struct file * file, struct vm_area_struct * vma); - -/* also exported from this file (used for dirs) */ -int coda_fsync(struct file *, struct dentry *dentry); - -struct inode_operations coda_file_inode_operations = { - permission: coda_permission, - revalidate: coda_revalidate_inode, - setattr: coda_notify_change, -}; - -struct file_operations coda_file_operations = { - read: generic_file_read, - write: generic_file_write, - mmap: coda_file_mmap, - open: coda_open, - release: coda_release, - fsync: coda_fsync, -}; - -/* File operations */ - -static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) +static ssize_t +coda_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { - struct coda_inode_info *cii; - int res; + struct inode *inode = file->f_dentry->d_inode; + ssize_t n; - coda_vfs_stat.file_mmap++; + n = generic_file_write(file, buf, count, ppos); - ENTRY; - cii = ITOC(file->f_dentry->d_inode); - cii->c_mmcount++; - - res =generic_file_mmap(file, vma); - EXIT; - return res; + inode->i_size = ((struct inode*)inode->i_mapping->host)->i_size; + + return n; } -int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) +/* exported from this file (used for dirs) */ +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) { - struct coda_inode_info *cnp; - struct inode *coda_inode = coda_dentry->d_inode; - struct inode *cont_inode = NULL; - struct file cont_file; + struct inode *inode = coda_dentry->d_inode; struct dentry cont_dentry; - int result = 0; - ENTRY; + int result = 0; + ENTRY; coda_vfs_stat.fsync++; - if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || - S_ISLNK(coda_inode->i_mode))) + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) return -EINVAL; - lock_kernel(); - cnp = ITOC(coda_inode); - CHECK_CNODE(cnp); - - cont_inode = cnp->c_ovp; - if ( cont_inode == NULL ) { - printk("coda_file_write: cached inode is 0!\n"); - unlock_kernel(); + if ( inode->i_mapping == &inode->i_data ) { + printk("coda_fsync: no container inode!\n"); return -1; } - coda_prepare_openfile(coda_inode, coda_file, cont_inode, - &cont_file, &cont_dentry); - - down(&cont_inode->i_sem); + cont_dentry.d_inode = (struct inode *)inode->i_mapping->host; + + down(&cont_dentry.d_inode->i_sem); + result = file_fsync(NULL, &cont_dentry, datasync); + up(&cont_dentry.d_inode->i_sem); - result = file_fsync(&cont_file ,&cont_dentry); - if ( result == 0 ) { - result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid)); + if ( !datasync && result == 0 ) { + lock_kernel(); + result = venus_fsync(inode->i_sb, coda_i2f(inode)); + unlock_kernel(); } - up(&cont_inode->i_sem); - - coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); - unlock_kernel(); - return result; + return result; } -/* - * support routines - */ -/* instantiate the container file and dentry object to do io */ -void coda_prepare_openfile(struct inode *i, struct file *coda_file, - struct inode *cont_inode, struct file *cont_file, - struct dentry *cont_dentry) -{ - cont_file->f_pos = coda_file->f_pos; - cont_file->f_mode = coda_file->f_mode; - cont_file->f_flags = coda_file->f_flags; - atomic_set(&cont_file->f_count, atomic_read(&coda_file->f_count)); - cont_file->f_owner = coda_file->f_owner; - cont_file->f_op = cont_inode->i_fop; - cont_file->f_dentry = cont_dentry; - cont_file->f_dentry->d_inode = cont_inode; - return ; -} - -/* update the Coda file & inode after I/O */ -void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file, - struct inode *open_inode, struct file *open_file) -{ - coda_file->f_pos = open_file->f_pos; - /* XXX what about setting the mtime here too? */ - /* coda_inode->i_mtime = open_inode->i_mtime; */ - coda_inode->i_size = open_inode->i_size; - return; -} - -/* grab the ext2 inode of the container file */ -int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind) -{ - struct super_block *sbptr; - - sbptr = get_super(dev); - - if ( !sbptr ) { - printk("coda_inode_grab: coda_find_super returns NULL.\n"); - return -ENXIO; - } - - *ind = NULL; - *ind = iget(sbptr, ino); - - if ( *ind == NULL ) { - printk("coda_inode_grab: iget(dev: %d, ino: %ld) " - "returns NULL.\n", dev, (long)ino); - return -ENOENT; - } - CDEBUG(D_FILE, "ino: %ld, ops at %p\n", (long)ino, (*ind)->i_op); - return 0; -} +struct file_operations coda_file_operations = { + read: generic_file_read, + write: coda_file_write, + mmap: generic_file_mmap, + open: coda_open, + release: coda_release, + fsync: coda_fsync, +}; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 09b3c2eadb28..84191c494283 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -1,7 +1,7 @@ /* * Super block/filesystem wide operations * - * Copryright (C) 1996 Peter J. Braam and + * Copyright (C) 1996 Peter J. Braam and * Michael Callahan * * Rewritten for Linux 2.1. Peter Braam @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,7 @@ /* VFS super_block ops */ static struct super_block *coda_read_super(struct super_block *, void *, int); static void coda_read_inode(struct inode *); -static void coda_put_inode(struct inode *); -static void coda_delete_inode(struct inode *); +static void coda_clear_inode(struct inode *); static void coda_put_super(struct super_block *); static int coda_statfs(struct super_block *sb, struct statfs *buf); @@ -43,8 +43,7 @@ static int coda_statfs(struct super_block *sb, struct statfs *buf); struct super_operations coda_super_operations = { read_inode: coda_read_inode, - put_inode: coda_put_inode, - delete_inode: coda_delete_inode, + clear_inode: coda_clear_inode, put_super: coda_put_super, statfs: coda_statfs, }; @@ -144,83 +143,74 @@ static void coda_read_inode(struct inode *inode) return; } -static void coda_put_inode(struct inode *inode) +static void coda_clear_inode(struct inode *inode) { - ENTRY; - - CDEBUG(D_INODE,"ino: %ld, count %d\n", inode->i_ino, atomic_read(&inode->i_count)); - - if ( atomic_read(&inode->i_count) == 1 ) { - write_inode_now(inode); - inode->i_nlink = 0; - } -} - -static void coda_delete_inode(struct inode *inode) -{ - struct coda_inode_info *cii; + struct coda_inode_info *cii = ITOC(inode); struct inode *open_inode; ENTRY; CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", inode->i_ino, atomic_read(&inode->i_count)); - cii = ITOC(inode); - if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) { - clear_inode(inode); - return; - } + if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) + goto out; + + lock_kernel(); - if ( ! list_empty(&cii->c_volrootlist) ) { + if ( !list_empty(&cii->c_volrootlist) ) { list_del(&cii->c_volrootlist); INIT_LIST_HEAD(&cii->c_volrootlist); } - open_inode = cii->c_ovp; - if ( open_inode ) { + if ( inode->i_mapping != &inode->i_data ) { + open_inode = (struct inode *)inode->i_mapping->host; CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n", - open_inode->i_ino, atomic_read(&open_inode->i_count)); - cii->c_ovp = NULL; + open_inode->i_ino, atomic_read(&open_inode->i_count)); inode->i_mapping = &inode->i_data; - iput(open_inode); + iput(open_inode); } coda_cache_clear_inode(inode); + unlock_kernel(); + CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); +out: inode->u.coda_i.c_magic = 0; - clear_inode(inode); EXIT; } int coda_notify_change(struct dentry *de, struct iattr *iattr) { struct inode *inode = de->d_inode; - struct coda_inode_info *cii; struct coda_vattr vattr; int error; ENTRY; memset(&vattr, 0, sizeof(vattr)); - cii = ITOC(inode); - CHECK_CNODE(cii); coda_iattr_to_vattr(iattr, &vattr); vattr.va_type = C_VNON; /* cannot set type */ CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode); - error = venus_setattr(inode->i_sb, &cii->c_fid, &vattr); + /* Venus is responsible for truncating the container-file!!! */ + error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr); - if ( !error ) { + if ( !error ) { coda_vattr_to_iattr(inode, &vattr); coda_cache_clear_inode(inode); - } - CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", - inode->i_mode, error); + } + CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error); EXIT; - return error; + return error; } +struct inode_operations coda_file_inode_operations = { + permission: coda_permission, + revalidate: coda_revalidate_inode, + setattr: coda_notify_change, +}; + static int coda_statfs(struct super_block *sb, struct statfs *buf) { int error; @@ -244,7 +234,6 @@ static int coda_statfs(struct super_block *sb, struct statfs *buf) return 0; } - /* init_coda: used by filesystems.c to register coda */ DECLARE_FSTYPE( coda_fs_type, "coda", coda_read_super, 0); @@ -254,5 +243,3 @@ int init_coda_fs(void) return register_filesystem(&coda_fs_type); } - - diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index b88c602c6026..582ea7000d7c 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -71,7 +71,7 @@ static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) struct venus_comm *vcp = &coda_upc_comm; unsigned int mask = POLLOUT | POLLWRNORM; - poll_wait(file, &(vcp->vc_waitq), wait); + poll_wait(file, &vcp->vc_waitq, wait); if (!list_empty(&vcp->vc_pending)) mask |= POLLIN | POLLRDNORM; @@ -99,24 +99,24 @@ static int coda_psdev_ioctl(struct inode * inode, struct file * filp, */ static ssize_t coda_psdev_write(struct file *file, const char *buf, - size_t count, loff_t *off) + size_t nbytes, loff_t *off) { struct venus_comm *vcp = &coda_upc_comm; struct upc_req *req = NULL; struct upc_req *tmp; struct list_head *lh; struct coda_in_hdr hdr; + ssize_t retval = 0, count = 0; int error; - if ( !coda_upc_comm.vc_pid ) return -EIO; /* Peek at the opcode, uniquefier */ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) return -EFAULT; - CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), count %ld\n", - current->pid, hdr.opcode, hdr.unique, (long)count); + CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ld\n", + current->pid, hdr.opcode, hdr.unique, (long)nbytes); if (DOWNCALL(hdr.opcode)) { struct super_block *sb = NULL; @@ -125,41 +125,47 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, sb = coda_super_info.sbi_sb; if ( !sb ) { - printk("coda_psdev_write: downcall, no SB!\n"); - return count; + CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n"); + count = nbytes; + goto out; } CDEBUG(D_PSDEV, "handling downcall\n"); - if ( count < sizeof(struct coda_out_hdr) ) { + if ( nbytes < sizeof(struct coda_out_hdr) ) { printk("coda_downcall opc %ld uniq %ld, not enough!\n", hdr.opcode, hdr.unique); - return count; + count = nbytes; + goto out; } - CODA_ALLOC(dcbuf, union outputArgs *, size); - if ( count > size ) { + if ( nbytes > size ) { printk("Coda: downcall opc %ld, uniq %ld, too much!", hdr.opcode, hdr.unique); - count = size; + nbytes = size; + } + CODA_ALLOC(dcbuf, union outputArgs *, nbytes); + if (copy_from_user(dcbuf, buf, nbytes)) { + CODA_FREE(dcbuf, nbytes); + retval = -EFAULT; + goto out; } - if (copy_from_user(dcbuf, buf, count)) - return -EFAULT; /* what downcall errors does Venus handle ? */ lock_kernel(); error = coda_downcall(hdr.opcode, dcbuf, sb); unlock_kernel(); - if ( error) { - printk("psdev_write: coda_downcall error: %d\n", - error); - return 0; + CODA_FREE(dcbuf, nbytes); + if (error) { + printk("psdev_write: coda_downcall error: %d\n", error); + retval = error; + goto out; } - CODA_FREE(dcbuf, size); - return count; + count = nbytes; + goto out; } - /* Look for the message on the processing queue. */ + lock_kernel(); lh = &vcp->vc_processing; while ( (lh = lh->next) != &vcp->vc_processing ) { tmp = list_entry(lh, struct upc_req , uc_chain); @@ -171,31 +177,40 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, break; } } + unlock_kernel(); + if (!req) { printk("psdev_write: msg (%ld, %ld) not found\n", hdr.opcode, hdr.unique); - return(-ESRCH); + retval = -ESRCH; + goto out; } /* move data into response buffer. */ - if (req->uc_outSize < count) { + if (req->uc_outSize < nbytes) { printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n", - req->uc_outSize, (long)count, hdr.opcode, hdr.unique); - count = req->uc_outSize; /* don't have more space! */ + req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique); + nbytes = req->uc_outSize; /* don't have more space! */ + } + if (copy_from_user(req->uc_data, buf, nbytes)) { + req->uc_flags |= REQ_ABORT; + wake_up(&req->uc_sleep); + retval = -EFAULT; + goto out; } - if (copy_from_user(req->uc_data, buf, count)) - return -EFAULT; /* adjust outsize. is this usefull ?? */ - req->uc_outSize = count; + req->uc_outSize = nbytes; req->uc_flags |= REQ_WRITE; + count = nbytes; CDEBUG(D_PSDEV, "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n", (long)count, hdr.opcode, hdr.unique, &req); wake_up(&req->uc_sleep); - return(count); +out: + return(count ? count : retval); } /* @@ -203,45 +218,71 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, */ static ssize_t coda_psdev_read(struct file * file, char * buf, - size_t count, loff_t *off) + size_t nbytes, loff_t *off) { + DECLARE_WAITQUEUE(wait, current); struct venus_comm *vcp = &coda_upc_comm; struct upc_req *req; - int result = count ; + ssize_t retval = 0, count = 0; - CDEBUG(D_PSDEV, "count %ld\n", (long)count); - if (list_empty(&(vcp->vc_pending))) { - return -1; - } - - req = list_entry((vcp->vc_pending.next), struct upc_req, uc_chain); - list_del(&(req->uc_chain)); + if (nbytes == 0) + return 0; - /* Move the input args into userspace */ - if (req->uc_inSize <= count) - result = req->uc_inSize; + lock_kernel(); - if (count < req->uc_inSize) { + add_wait_queue(&vcp->vc_waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list_empty(&vcp->vc_pending)) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&vcp->vc_waitq, &wait); + + if (retval) + goto out; + + req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain); + list_del(&req->uc_chain); + + /* Move the input args into userspace */ + count = req->uc_inSize; + if (nbytes < req->uc_inSize) { printk ("psdev_read: Venus read %ld bytes of %d in message\n", - (long)count, req->uc_inSize); + (long)nbytes, req->uc_inSize); + count = nbytes; } - if ( copy_to_user(buf, req->uc_data, result)) - return -EFAULT; + if (copy_to_user(buf, req->uc_data, count)) { + retval = -EFAULT; + goto free_out; + } - /* If request was a signal, don't enqueue */ - if (req->uc_opcode == CODA_SIGNAL) { - CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", - req->uc_opcode, req->uc_unique); - CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); - CODA_FREE(req, sizeof(struct upc_req)); - return count; - } - - req->uc_flags |= REQ_READ; - list_add(&(req->uc_chain), vcp->vc_processing.prev); + /* If request was not a signal, enqueue and don't free */ + if (req->uc_opcode != CODA_SIGNAL) { + req->uc_flags |= REQ_READ; + list_add(&(req->uc_chain), vcp->vc_processing.prev); + goto out; + } + + CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", + req->uc_opcode, req->uc_unique); - return result; +free_out: + CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); + CODA_FREE(req, sizeof(struct upc_req)); +out: + unlock_kernel(); + return (count ? count : retval); } @@ -251,7 +292,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file) ENTRY; /* first opener: must be lento. Initialize & take its pid */ - if ( file->f_flags == O_RDWR ) { + if ( (file->f_flags & O_ACCMODE) == O_RDWR ) { if ( vcp->vc_pid ) { printk("Venus pid already set to %d!!\n", vcp->vc_pid); return -1; @@ -264,7 +305,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file) vcp->vc_inuse++; - if ( file->f_flags == O_RDWR ) { + if ( (file->f_flags & O_ACCMODE) == O_RDWR ) { vcp->vc_pid = current->pid; vcp->vc_seq = 0; INIT_LIST_HEAD(&vcp->vc_pending); @@ -334,6 +375,7 @@ static int coda_psdev_release(struct inode * inode, struct file * file) static struct file_operations coda_psdev_fops = { + owner: THIS_MODULE, read: coda_psdev_read, write: coda_psdev_write, poll: coda_psdev_poll, diff --git a/fs/coda/stats.c b/fs/coda/stats.c deleted file mode 100644 index d4a8b2e9b332..000000000000 --- a/fs/coda/stats.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * stats.c - * - * CODA operation statistics - * - * (c) March, 1998 Zhanyong Wan - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct coda_vfs_stats coda_vfs_stat; -struct coda_permission_stats coda_permission_stat; -struct coda_cache_inv_stats coda_cache_inv_stat; -struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS]; - -/* keep this in sync with coda.h! */ -char *coda_upcall_names[] = { - "totals ", /* 0 */ - "noop ", /* 1 */ - "root ", /* 2 */ - "sync ", /* 3 */ - "open ", /* 4 */ - "close ", /* 5 */ - "ioctl ", /* 6 */ - "getattr ", /* 7 */ - "setattr ", /* 8 */ - "access ", /* 9 */ - "lookup ", /* 10 */ - "create ", /* 11 */ - "remove ", /* 12 */ - "link ", /* 13 */ - "rename ", /* 14 */ - "mkdir ", /* 15 */ - "rmdir ", /* 16 */ - "readdir ", /* 17 */ - "symlink ", /* 18 */ - "readlink ", /* 19 */ - "fsync ", /* 20 */ - "inactive ", /* 21 */ - "vget ", /* 22 */ - "signal ", /* 23 */ - "replace ", /* 24 */ - "flush ", /* 25 */ - "purgeuser ", /* 26 */ - "zapfile ", /* 27 */ - "zapdir ", /* 28 */ - "zapvnode ", /* 28 */ - "purgefid ", /* 30 */ - "open_by_path" /* 31 */ -}; - - - - -void reset_coda_vfs_stats( void ) -{ - memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) ); -} - -#if 0 -static void reset_upcall_entry( struct coda_upcall_stats_entry * pentry ) -{ - pentry->count = 0; - pentry->time_sum = pentry->time_squared_sum = 0; -} -#endif - -void reset_coda_upcall_stats( void ) -{ - memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) ); -} - -void reset_coda_permission_stats( void ) -{ - memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) ); -} - -void reset_coda_cache_inv_stats( void ) -{ - memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) ); -} - - -void do_time_stats( struct coda_upcall_stats_entry * pentry, - unsigned long runtime ) -{ - - unsigned long time = runtime * 1000 /HZ; /* time in ms */ - CDEBUG(D_SPECIAL, "time: %ld\n", time); - - if ( pentry->count == 0 ) { - pentry->time_sum = pentry->time_squared_sum = 0; - } - - pentry->count++; - pentry->time_sum += time; - pentry->time_squared_sum += time*time; -} - - - -void coda_upcall_stats(int opcode, long unsigned runtime) -{ - struct coda_upcall_stats_entry * pentry; - - if ( opcode < 0 || opcode > CODA_NCALLS - 1) { - printk("Nasty opcode %d passed to coda_upcall_stats\n", - opcode); - return; - } - - pentry = &coda_upcall_stat[opcode]; - do_time_stats(pentry, runtime); - - /* fill in the totals */ - pentry = &coda_upcall_stat[0]; - do_time_stats(pentry, runtime); - -} - -unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry ) -{ - return ( pentry->count == 0 ) ? 0 : pentry->time_sum / pentry->count; -} - -static inline unsigned long absolute( unsigned long x ) -{ - return x >= 0 ? x : -x; -} - -static unsigned long sqr_root( unsigned long x ) -{ - unsigned long y = x, r; - int n_bit = 0; - - if ( x == 0 ) - return 0; - if ( x < 0) - x = -x; - - while ( y ) { - y >>= 1; - n_bit++; - } - - r = 1 << (n_bit/2); - - while ( 1 ) { - r = (r + x/r)/2; - if ( r*r <= x && x < (r+1)*(r+1) ) - break; - } - - return r; -} - -unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry ) -{ - unsigned long time_avg; - - if ( pentry->count <= 1 ) - return 0; - - time_avg = get_time_average( pentry ); - return - sqr_root( (pentry->time_squared_sum / pentry->count) - - time_avg * time_avg ); -} - -int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp, - void * buffer, size_t * lenp ) -{ - if ( write ) { - reset_coda_vfs_stats(); - } - - *lenp = 0; - return 0; -} - -int do_reset_coda_upcall_stats( ctl_table * table, int write, - struct file * filp, void * buffer, - size_t * lenp ) -{ - if ( write ) { - reset_coda_upcall_stats(); - } - - *lenp = 0; - return 0; -} - -int do_reset_coda_permission_stats( ctl_table * table, int write, - struct file * filp, void * buffer, - size_t * lenp ) -{ - if ( write ) { - reset_coda_permission_stats(); - } - - *lenp = 0; - return 0; -} - -int do_reset_coda_cache_inv_stats( ctl_table * table, int write, - struct file * filp, void * buffer, - size_t * lenp ) -{ - if ( write ) { - reset_coda_cache_inv_stats(); - } - - *lenp = 0; - return 0; -} - -int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset, - int length, int dummy ) -{ - int len=0; - off_t begin; - struct coda_vfs_stats * ps = & coda_vfs_stat; - - /* this works as long as we are below 1024 characters! */ - len += sprintf( buffer, - "Coda VFS statistics\n" - "===================\n\n" - "File Operations:\n" - "\tfile_read\t%9d\n" - "\tfile_write\t%9d\n" - "\tfile_mmap\t%9d\n" - "\topen\t\t%9d\n" - "\trelase\t\t%9d\n" - "\tfsync\t\t%9d\n\n" - "Dir Operations:\n" - "\treaddir\t\t%9d\n\n" - "Inode Operations\n" - "\tcreate\t\t%9d\n" - "\tlookup\t\t%9d\n" - "\tlink\t\t%9d\n" - "\tunlink\t\t%9d\n" - "\tsymlink\t\t%9d\n" - "\tmkdir\t\t%9d\n" - "\trmdir\t\t%9d\n" - "\trename\t\t%9d\n" - "\tpermission\t%9d\n" - "\treadpage\t%9d\n", - - /* file operations */ - ps->file_read, - ps->file_write, - ps->file_mmap, - ps->open, - ps->release, - ps->fsync, - - /* dir operations */ - ps->readdir, - - /* inode operations */ - ps->create, - ps->lookup, - ps->link, - ps->unlink, - ps->symlink, - ps->mkdir, - ps->rmdir, - ps->rename, - ps->permission, - ps->readpage ); - - begin = offset; - *start = buffer + begin; - len -= begin; - - if ( len > length ) - len = length; - if ( len < 0 ) - len = 0; - - return len; -} - -int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset, - int length, int dummy ) -{ - int len=0; - int i; - off_t begin; - off_t pos = 0; - char tmpbuf[80]; - int tmplen = 0; - - ENTRY; - /* this works as long as we are below 1024 characters! */ - if ( offset < 80 ) - len += sprintf( buffer,"%-79s\n", "Coda upcall statistics"); - if ( offset < 160) - len += sprintf( buffer + len,"%-79s\n", "======================"); - if ( offset < 240) - len += sprintf( buffer + len,"%-79s\n", "upcall\t\t count\tavg time(ms)\tstd deviation(ms)"); - if ( offset < 320) - len += sprintf( buffer + len,"%-79s\n", "------\t\t -----\t------------\t-----------------"); - pos = 320; - for ( i = 0 ; i < CODA_NCALLS ; i++ ) { - tmplen += sprintf(tmpbuf,"%s\t%9d\t%10ld\t%10ld", - coda_upcall_names[i], - coda_upcall_stat[i].count, - get_time_average(&coda_upcall_stat[i]), - coda_upcall_stat[i].time_squared_sum); - pos += 80; - if ( pos < offset ) - continue; - len += sprintf(buffer + len, "%-79s\n", tmpbuf); - if ( len >= length ) - break; - } - - begin = len- (pos - offset); - *start = buffer + begin; - len -= begin; - - if ( len > length ) - len = length; - if ( len < 0 ) - len = 0; - EXIT; - return len; -} - -int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset, - int length, int dummy ) -{ - int len=0; - off_t begin; - struct coda_permission_stats * ps = & coda_permission_stat; - - /* this works as long as we are below 1024 characters! */ - len += sprintf( buffer, - "Coda permission statistics\n" - "==========================\n\n" - "count\t\t%9d\n" - "hit count\t%9d\n", - - ps->count, - ps->hit_count ); - - begin = offset; - *start = buffer + begin; - len -= begin; - - if ( len > length ) - len = length; - if ( len < 0 ) - len = 0; - - return len; -} - -int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset, - int length, int dummy ) -{ - int len=0; - off_t begin; - struct coda_cache_inv_stats * ps = & coda_cache_inv_stat; - - /* this works as long as we are below 1024 characters! */ - len += sprintf( buffer, - "Coda cache invalidation statistics\n" - "==================================\n\n" - "flush\t\t%9d\n" - "purge user\t%9d\n" - "zap_dir\t\t%9d\n" - "zap_file\t%9d\n" - "zap_vnode\t%9d\n" - "purge_fid\t%9d\n" - "replace\t\t%9d\n", - ps->flush, - ps->purge_user, - ps->zap_dir, - ps->zap_file, - ps->zap_vnode, - ps->purge_fid, - ps->replace ); - - begin = offset; - *start = buffer + begin; - len -= begin; - - if ( len > length ) - len = length; - if ( len < 0 ) - len = 0; - - return len; -} - diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index 289f9417c38e..cbfff3e5b354 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -297,9 +297,6 @@ int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset, "Coda VFS statistics\n" "===================\n\n" "File Operations:\n" - "\tfile_read\t%9d\n" - "\tfile_write\t%9d\n" - "\tfile_mmap\t%9d\n" "\topen\t\t%9d\n" "\trelase\t\t%9d\n" "\tfsync\t\t%9d\n\n" @@ -314,13 +311,9 @@ int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset, "\tmkdir\t\t%9d\n" "\trmdir\t\t%9d\n" "\trename\t\t%9d\n" - "\tpermission\t%9d\n" - "\treadpage\t%9d\n", + "\tpermission\t%9d\n", /* file operations */ - ps->file_read, - ps->file_write, - ps->file_mmap, ps->open, ps->release, ps->fsync, @@ -337,9 +330,8 @@ int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset, ps->mkdir, ps->rmdir, ps->rename, - ps->permission, - ps->readpage ); - + ps->permission); + begin = offset; *start = buffer + begin; len -= begin; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 63586d05cb06..674c8cb3b5e2 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -40,19 +40,31 @@ #include -static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, +static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, union inputArgs *buffer); +static void *alloc_upcall(int opcode, int size) +{ + union inputArgs *inp; + + CODA_ALLOC(inp, union inputArgs *, size); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->ih.opcode = opcode; + inp->ih.pid = current->pid; + inp->ih.pgid = current->pgrp; + coda_load_creds(&(inp->ih.cred)); + + return (void*)inp; +} + #define UPARG(op)\ do {\ - CODA_ALLOC(inp, union inputArgs *, insize);\ - if ( !inp ) { return -ENOMEM; }\ - outp = (union outputArgs *) (inp);\ - inp->ih.opcode = (op);\ - inp->ih.pid = current->pid;\ - inp->ih.pgid = current->pgrp;\ - coda_load_creds(&(inp->ih.cred));\ - outsize = insize;\ + inp = (union inputArgs *)alloc_upcall(op, insize); \ + if (IS_ERR(inp)) { return PTR_ERR(inp); }\ + outp = (union outputArgs *)(inp); \ + outsize = insize; \ } while (0) static inline int max(int a, int b) @@ -84,12 +96,12 @@ int venus_rootfid(struct super_block *sb, ViceFid *fidp) if (error) { printk("coda_get_rootfid: error %d\n", error); } else { - *fidp = (ViceFid) outp->coda_root.VFid; + *fidp = (ViceFid) outp->coda_root.VFid; CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n", fidp->Volume, fidp->Vnode); } - if (inp) CODA_FREE(inp, insize); + CODA_FREE(inp, insize); EXIT; return error; } @@ -108,11 +120,9 @@ int venus_getattr(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if ( !error ) - *attr = outp->coda_getattr.attr; + *attr = outp->coda_getattr.attr; - if (inp) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); EXIT; return error; } @@ -124,7 +134,7 @@ int venus_setattr(struct super_block *sb, struct ViceFid *fid, union outputArgs *outp; int insize, outsize, error; - insize= SIZE(setattr); + insize = SIZE(setattr); UPARG(CODA_SETATTR); inp->coda_setattr.VFid = *fid; @@ -133,7 +143,7 @@ int venus_setattr(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); CDEBUG(D_SUPER, " result %d\n", error); - if ( inp ) CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -147,7 +157,7 @@ int venus_lookup(struct super_block *sb, struct ViceFid *fid, int offset; offset = INSIZE(lookup); - insize = max(offset + length +1, OUTSIZE(lookup)); + insize = max(offset + length +1, OUTSIZE(lookup)); UPARG(CODA_LOOKUP); inp->coda_lookup.VFid = *fid; @@ -159,12 +169,10 @@ int venus_lookup(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if ( !error ) { - *resfid = outp->coda_lookup.VFid; - *type = outp->coda_lookup.vtype; - } - if (inp) CODA_FREE(inp, insize); - + *resfid = outp->coda_lookup.VFid; + *type = outp->coda_lookup.vtype; + + CODA_FREE(inp, insize); return error; } @@ -189,8 +197,7 @@ int venus_release(struct super_block *sb, struct ViceFid *fid, int flags, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -209,17 +216,10 @@ int venus_open(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if ( !error ) { - *ino = outp->coda_open.inode; - *dev = outp->coda_open.dev; - } else { - *ino = 0; - *dev = 0; - } - - if (inp) - CODA_FREE(inp, insize); + *ino = outp->coda_open.inode; + *dev = outp->coda_open.dev; + CODA_FREE(inp, insize); return error; } @@ -248,8 +248,7 @@ int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid, *attrs = outp->coda_mkdir.attr; *newfid = outp->coda_mkdir.VFid; - if (inp) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -289,7 +288,7 @@ int venus_rename(struct super_block *sb, struct ViceFid *old_fid, (char *)inp + (int) inp->coda_rename.destname); error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -322,8 +321,7 @@ int venus_create(struct super_block *sb, struct ViceFid *dirfid, *attrs = outp->coda_create.attr; *newfid = outp->coda_create.VFid; - if (inp) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -345,8 +343,8 @@ int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, *((char *)inp + offset + length) = '\0'; error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if ( inp ) - CODA_FREE(inp, insize); + + CODA_FREE(inp, insize); return error; } @@ -367,8 +365,8 @@ int venus_remove(struct super_block *sb, struct ViceFid *dirfid, *((char *)inp + offset + length) = '\0'; error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if ( inp ) - CODA_FREE(inp, insize); + + CODA_FREE(inp, insize); return error; } @@ -398,9 +396,9 @@ int venus_readlink(struct super_block *sb, struct ViceFid *fid, *(buffer + retlen) = '\0'; } - if (inp) CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; + CODA_FREE(inp, insize); return error; } @@ -428,10 +426,9 @@ int venus_link(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) - CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; + CODA_FREE(inp, insize); return error; } @@ -466,10 +463,9 @@ int venus_symlink(struct super_block *sb, struct ViceFid *fid, error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) - CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; + CODA_FREE(inp, insize); return error; } @@ -486,8 +482,7 @@ int venus_fsync(struct super_block *sb, struct ViceFid *fid) error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), &outsize, inp); - if ( inp ) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -505,7 +500,7 @@ int venus_access(struct super_block *sb, struct ViceFid *fid, int mask) error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, insize); + CODA_FREE(inp, insize); EXIT; return error; } @@ -576,8 +571,7 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, } exit: - if (inp) - CODA_FREE(inp, insize); + CODA_FREE(inp, insize); return error; } @@ -602,9 +596,9 @@ int venus_statfs(struct super_block *sb, struct statfs *sfs) printk("coda_statfs: Venus returns: %d\n", error); } - if (inp) CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; + CODA_FREE(inp, insize); return error; } @@ -625,16 +619,20 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) add_wait_queue(&vmp->uc_sleep, &wait); for (;;) { - if ( coda_hard == 0 ) + if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) set_current_state(TASK_INTERRUPTIBLE); else set_current_state(TASK_UNINTERRUPTIBLE); + /* venus died */ + if ( !coda_upc_comm.vc_pid ) + break; + /* got a reply */ if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) break; - if ( !coda_hard && signal_pending(current) ) { + if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { /* if this process really wants to die, let it go */ if ( sigismember(&(current->signal), SIGKILL) || sigismember(&(current->signal), SIGINT) ) @@ -645,7 +643,6 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) break; } schedule(); - } remove_wait_queue(&vmp->uc_sleep, &wait); current->state = TASK_RUNNING; @@ -711,6 +708,7 @@ ENTRY; /* Append msg to pending queue and poke Venus. */ list_add(&(req->uc_chain), vcommp->vc_pending.prev); + CDEBUG(D_UPCALL, "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %p.zzz.\n", current->pid, req->uc_opcode, req->uc_unique, req); @@ -848,7 +846,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { /* Handle invalidation requests. */ if ( !sb || !sb->s_root || !sb->s_root->d_inode) { - printk("coda_downcall: opcode %d, no sb!\n", opcode); + CDEBUG(D_DOWNCALL, "coda_downcall: opcode %d, no sb!\n", opcode); return 0; } @@ -878,8 +876,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_ZAPDIR : { struct inode *inode; ViceFid *fid = &out->coda_zapdir.CodaFid; - CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", - coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid)); clstats(CODA_ZAPDIR); inode = coda_fid_to_inode(fid, sb); @@ -889,7 +886,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) coda_flag_inode_children(inode, C_PURGE); CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino); coda_flag_inode(inode, C_VATTR); - iput(inode); + iput(inode); } else CDEBUG(D_DOWNCALL, "zapdir: no inode\n"); @@ -900,14 +897,13 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) struct inode *inode; struct ViceFid *fid = &out->coda_zapfile.CodaFid; clstats(CODA_ZAPFILE); - CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", - coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid)); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", - inode->i_ino); + CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", + inode->i_ino); coda_flag_inode(inode, C_VATTR); - iput(inode); + iput(inode); } else CDEBUG(D_DOWNCALL, "zapfile: no inode\n"); return 0; @@ -916,61 +912,20 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_PURGEFID : { struct inode *inode; ViceFid *fid = &out->coda_purgefid.CodaFid; - CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", - coda_f2s(fid)); + CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid)); clstats(CODA_PURGEFID); inode = coda_fid_to_inode(fid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", - inode->i_ino); + CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", + inode->i_ino); coda_flag_inode_children(inode, C_PURGE); coda_purge_dentries(inode); - iput(inode); - }else + iput(inode); + } else CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); return 0; } - case CODA_MAKE_CINODE : { - struct inode *inode; - ViceFid *fid = &out->coda_make_cinode.CodaFid; - struct coda_vattr *attr = &out->coda_make_cinode.attr; - int fd = out->coda_make_cinode.fd; - struct file *file; - CDEBUG(D_DOWNCALL, "make_cinode: fid = %s, ino = %ld\n", - coda_f2s(fid), attr->va_fileid); - - inode = coda_iget(sb, fid, attr); - if ( !inode ) { - CDEBUG(D_DOWNCALL, "make_cinode: no inode\n"); - return -EINVAL; - } - - file = fget(fd); - if ( !file ) { - CDEBUG(D_DOWNCALL, "make_cinode: no file\n"); - iput(inode); - return -EINVAL; - } - - inode->u.coda_i.c_ovp = file->f_dentry->d_inode; - inode->i_mapping = file->f_dentry->d_inode->i_mapping; - file->f_dentry->d_inode = inode; - file->f_op = &coda_file_operations; - - /* - Unhash the dentry of the container file, as it is - still owned by the fs that stores the container - file. A more reliable solution would be to create - an new dentry owned by Coda, but that would require - knowledge of the internals of the dcache. - */ - d_drop(file->f_dentry); - - fput(file); - return 0; - } - case CODA_REPLACE : { struct inode *inode; ViceFid *oldfid = &out->coda_replace.OldFid; @@ -979,9 +934,10 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) CDEBUG(D_DOWNCALL, "CODA_REPLACE\n"); inode = coda_fid_to_inode(oldfid, sb); if ( inode ) { - CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino); + CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", + inode->i_ino); coda_replace_fid(inode, oldfid, newfid); - iput(inode); + iput(inode); }else CDEBUG(D_DOWNCALL, "purgefid: no inode\n"); diff --git a/fs/devfs/base.c b/fs/devfs/base.c index 9f1e8b06fd1c..8b6e58f9c32e 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -451,6 +451,27 @@ Added CONFIG_DEVFS_MOUNT. Work sponsored by SGI. v0.96 + 20000608 Richard Gooch + Disabled multi-mount capability (use VFS bindings instead). + Work sponsored by SGI. + v0.97 + 20000610 Richard Gooch + Switched to FS_SINGLE to disable multi-mounts. + 20000612 Richard Gooch + Removed module support. + Removed multi-mount code. + Removed compatibility macros: VFS has changed too much. + Work sponsored by SGI. + v0.98 + 20000614 Richard Gooch + Merged devfs inode into devfs entry. + Work sponsored by SGI. + v0.99 + 20000619 Richard Gooch + Removed dead code in which used to call + . + Work sponsored by SGI. + v0.100 */ #include #include @@ -485,29 +506,9 @@ #include #include -#define DEVFS_VERSION "0.96 (20000430)" - -#ifndef DEVFS_NAME -# define DEVFS_NAME "devfs" -#endif +#define DEVFS_VERSION "0.100 (20000619)" -/* 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 +605,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 +633,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 +647,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 +660,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 +673,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 +755,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 */ 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 +798,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 +844,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 +1044,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 +1054,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 +1064,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 +1193,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 */ @@ -1284,8 +1317,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) ) @@ -1687,13 +1718,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 +1783,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 +1996,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 +2057,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 +2088,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 +2112,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 +2147,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 +2240,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 +2260,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 +2304,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 +2315,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 +2367,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 +2381,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 +2414,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 +2428,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 +2458,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 +2479,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 +2504,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 +2557,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 +2599,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 +2614,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 +2649,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 +2662,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 +2698,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 +2722,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 to fail which in turns causes - 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 +2777,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 +2813,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 +2829,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 +2843,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 +2860,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 +2882,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 +2890,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 +2912,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 +2935,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 +2942,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 +2966,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 +2988,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 +3020,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 +3086,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); + if ( ( root_inode = get_vfs_inode (sb, root_entry, NULL) ) == NULL ) goto out_no_root; - } - if ( ( root_inode = get_vfs_inode (sb, di, 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 +3111,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 +3306,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 +3336,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/devpts/devpts_i.h b/fs/devpts/devpts_i.h index 7dcd8cfd26d9..da387ea1a932 100644 --- a/fs/devpts/devpts_i.h +++ b/fs/devpts/devpts_i.h @@ -19,8 +19,6 @@ struct devpts_sb_info { u32 magic; - struct super_block *next; - struct super_block **back; int setuid; int setgid; uid_t uid; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 6a26bf981cac..c3667b2086e2 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -27,7 +27,7 @@ #include "devpts_i.h" -static struct super_block *mounts = NULL; +static struct vfsmount *devpts_mnt; static void devpts_put_super(struct super_block *sb) { @@ -44,22 +44,17 @@ static void devpts_put_super(struct super_block *sb) iput(inode); } } - - *sbi->back = sbi->next; - if ( sbi->next ) - SBI(sbi->next)->back = sbi->back; - kfree(sbi->inodes); kfree(sbi); } static int devpts_statfs(struct super_block *sb, struct statfs *buf); -static void devpts_read_inode(struct inode *inode); +static int devpts_remount (struct super_block * sb, int * flags, char * data); static struct super_operations devpts_sops = { - read_inode: devpts_read_inode, put_super: devpts_put_super, statfs: devpts_statfs, + remount_fs: devpts_remount, }; static int devpts_parse_options(char *options, struct devpts_sb_info *sbi) @@ -112,108 +107,69 @@ static int devpts_parse_options(char *options, struct devpts_sb_info *sbi) return 0; } +static int devpts_remount(struct super_block * sb, int * flags, char * data) +{ + struct devpts_sb_info *sbi = sb->u.generic_sbp; + int res = devpts_parse_options(data,sbi); + if (res) { + printk("devpts: called with bogus options\n"); + return -EINVAL; + } + return 0; +} + struct super_block *devpts_read_super(struct super_block *s, void *data, int silent) { - struct inode * root_inode; - struct dentry * root; + struct inode * inode; struct devpts_sb_info *sbi; - /* Super block already completed? */ - if (s->s_root) - goto out_unlock; - sbi = (struct devpts_sb_info *) kmalloc(sizeof(struct devpts_sb_info), GFP_KERNEL); if ( !sbi ) - goto fail_unlock; + goto fail; sbi->magic = DEVPTS_SBI_MAGIC; sbi->max_ptys = unix98_max_ptys; sbi->inodes = kmalloc(sizeof(struct inode *) * sbi->max_ptys, GFP_KERNEL); - if ( !sbi->inodes ) { - kfree(sbi); - goto fail_unlock; - } + if ( !sbi->inodes ) + goto fail_free; memset(sbi->inodes, 0, sizeof(struct inode *) * sbi->max_ptys); + if ( devpts_parse_options(data,sbi) && !silent) { + printk("devpts: called with bogus options\n"); + goto fail_free; + } + + inode = get_empty_inode(); + if (!inode) + goto fail_free; + inode->i_sb = s; + inode->i_dev = s->s_dev; + inode->i_ino = 1; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_size = 0; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = inode->i_gid = 0; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &devpts_root_inode_operations; + inode->i_fop = &devpts_root_operations; + inode->i_nlink = 2; + s->u.generic_sbp = (void *) sbi; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = DEVPTS_SUPER_MAGIC; s->s_op = &devpts_sops; - s->s_root = NULL; - - /* - * Get the root inode and dentry, but defer checking for errors. - */ - root_inode = iget(s, 1); /* inode 1 == root directory */ - root = d_alloc_root(root_inode); - - /* - * Check whether somebody else completed the super block. - */ + s->s_root = d_alloc_root(inode); if (s->s_root) - goto out_dput; - - if (!root) - goto fail_iput; - - /* Can this call block? (It shouldn't) */ - if ( devpts_parse_options(data,sbi) ) { - printk("devpts: called with bogus options\n"); - goto fail_dput; - } - - /* - * Check whether somebody else completed the super block. - */ - if (s->s_root) - goto out_dec; - - /* - * Success! Install the root dentry now to indicate completion. - */ - s->s_root = root; - - sbi->next = mounts; - if ( sbi->next ) - SBI(sbi->next)->back = &(sbi->next); - sbi->back = &mounts; - mounts = s; - - return s; - - /* - * Success ... somebody else completed the super block for us. - */ -out_unlock: - goto out_dec; -out_dput: - if (root) - dput(root); - else - iput(root_inode); -out_dec: - return s; + return s; - /* - * Failure ... clear the s_dev slot and clean up. - */ -fail_dput: - /* - * dput() can block, so we clear the super block first. - */ - dput(root); - goto fail_free; -fail_iput: printk("devpts: get root dentry failed\n"); - /* - * iput() can block, so we clear the super block first. - */ - iput(root_inode); + iput(inode); fail_free: kfree(sbi); -fail_unlock: +fail: return NULL; } @@ -221,95 +177,64 @@ static int devpts_statfs(struct super_block *sb, struct statfs *buf) { buf->f_type = DEVPTS_SUPER_MAGIC; buf->f_bsize = 1024; - buf->f_bfree = 0; - buf->f_bavail = 0; - buf->f_ffree = 0; buf->f_namelen = NAME_MAX; return 0; } -static void devpts_read_inode(struct inode *inode) -{ - ino_t ino = inode->i_ino; - struct devpts_sb_info *sbi = SBI(inode->i_sb); - - inode->i_mode = 0; - inode->i_nlink = 0; - inode->i_size = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_blocks = 0; - inode->i_blksize = 1024; - inode->i_uid = inode->i_gid = 0; - - if ( ino == 1 ) { - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; - inode->i_op = &devpts_root_inode_operations; - inode->i_fop = &devpts_root_operations; - inode->i_nlink = 2; - return; - } - - ino -= 2; - if ( ino >= sbi->max_ptys ) - return; /* Bogus */ - - /* Gets filled in by devpts_pty_new() */ - init_special_inode(inode,S_IFCHR,0); - - return; -} - -static DECLARE_FSTYPE(devpts_fs_type, "devpts", devpts_read_super, 0); +static DECLARE_FSTYPE(devpts_fs_type, "devpts", devpts_read_super, FS_SINGLE); void devpts_pty_new(int number, kdev_t device) { - struct super_block *sb; - struct devpts_sb_info *sbi; + struct super_block *sb = devpts_mnt->mnt_sb; + struct devpts_sb_info *sbi = SBI(sb); struct inode *inode; - for ( sb = mounts ; sb ; sb = sbi->next ) { - sbi = SBI(sb); - - if ( sbi->inodes[number] ) { - continue; /* Already registered, this does happen */ - } + if ( sbi->inodes[number] ) + return; /* Already registered, this does happen */ - /* Yes, this looks backwards, but it is correct */ - inode = iget(sb, number+2); - if ( inode ) { - inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid; - inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid; - inode->i_mode = sbi->mode | S_IFCHR; - inode->i_rdev = device; - inode->i_nlink++; - sbi->inodes[number] = inode; - } + inode = get_empty_inode(); + if (!inode) + return; + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_ino = number+2; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid; + inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + init_special_inode(inode, S_IFCHR|sbi->mode, kdev_t_to_nr(device)); + + if ( sbi->inodes[number] ) { + iput(inode); + return; } + sbi->inodes[number] = inode; } void devpts_pty_kill(int number) { - struct super_block *sb; - struct devpts_sb_info *sbi; - struct inode *inode; - - for ( sb = mounts ; sb ; sb = sbi->next ) { - sbi = SBI(sb); - - inode = sbi->inodes[number]; + struct super_block *sb = devpts_mnt->mnt_sb; + struct devpts_sb_info *sbi = SBI(sb); + struct inode *inode = sbi->inodes[number]; - if ( inode ) { - sbi->inodes[number] = NULL; - inode->i_nlink--; - iput(inode); - } + if ( inode ) { + sbi->inodes[number] = NULL; + inode->i_nlink--; + iput(inode); } } int __init init_devpts_fs(void) { - return register_filesystem(&devpts_fs_type); - + int err = register_filesystem(&devpts_fs_type); + if (!err) { + devpts_mnt = kern_mount(&devpts_fs_type); + err = PTR_ERR(devpts_mnt); + if (!IS_ERR(devpts_mnt)) + err = 0; + } + return err; } #ifdef MODULE @@ -329,6 +254,7 @@ void cleanup_module(void) devpts_upcall_new = NULL; devpts_upcall_kill = NULL; unregister_filesystem(&devpts_fs_type); + kern_umount(devpts_mnt); } #endif diff --git a/fs/efs/super.c b/fs/efs/super.c index 7917bef5c6a3..04b5fe91d3c2 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -185,7 +185,6 @@ struct super_block *efs_read_super(struct super_block *s, void *d, int silent) { s->s_flags |= MS_RDONLY; } s->s_op = &efs_superblock_operations; - s->s_dev = dev; s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE)); if (!(s->s_root)) { @@ -193,11 +192,6 @@ struct super_block *efs_read_super(struct super_block *s, void *d, int silent) { goto out_no_fs; } - if (check_disk_change(s->s_dev)) { - printk(KERN_ERR "EFS: device changed\n"); - goto out_no_fs; - } - return(s); out_no_fs_ul: diff --git a/fs/fcntl.c b/fs/fcntl.c index ab758c5660be..f6e4e1651a27 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -330,7 +331,52 @@ out: read_unlock(&tasklist_lock); } -void kill_fasync(struct fasync_struct *fa, int sig, int band) +/* + * fasync_helper() is used by some character device drivers (mainly mice) + * to set up the fasync queue. It returns negative on error, 0 if it did + * no changes and positive if it added/deleted the entry. + */ +static rwlock_t fasync_lock = RW_LOCK_UNLOCKED; +int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) +{ + struct fasync_struct *fa, **fp; + struct fasync_struct *new = NULL; + int result = 0; + + if (on) { + new = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); + if (!new) + return -ENOMEM; + } + write_lock_irq(&fasync_lock); + for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { + if (fa->fa_file == filp) { + if(on) { + fa->fa_fd = fd; + kfree(new); + } else { + *fp = fa->fa_next; + kfree(fa); + result = 1; + } + goto out; + } + } + + if (on) { + new->magic = FASYNC_MAGIC; + new->fa_file = filp; + new->fa_fd = fd; + new->fa_next = *fapp; + *fapp = new; + result = 1; + } +out: + write_unlock_irq(&fasync_lock); + return result; +} + +void __kill_fasync(struct fasync_struct *fa, int sig, int band) { while (fa) { struct fown_struct * fown; @@ -348,3 +394,10 @@ void kill_fasync(struct fasync_struct *fa, int sig, int band) fa = fa->fa_next; } } + +void kill_fasync(struct fasync_struct **fp, int sig, int band) +{ + read_lock(&fasync_lock); + __kill_fasync(*fp, sig, band); + read_unlock(&fasync_lock); +} diff --git a/fs/ioctl.c b/fs/ioctl.c index 614cdaf67dcf..16ad5ec267c3 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -88,8 +88,12 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) /* Did FASYNC state change ? */ if ((flag ^ filp->f_flags) & FASYNC) { if (filp->f_op && filp->f_op->fasync) - filp->f_op->fasync(fd, filp, on); + error = filp->f_op->fasync(fd, filp, on); + else error = -ENOTTY; } + if (error != 0) + break; + if (on) filp->f_flags |= FASYNC; else diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index cc3025ee3268..a5ec6c774962 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -50,7 +50,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) memset(argp, 0, sizeof(*argp)); nlmclnt_next_cookie(&argp->cookie); argp->state = nsm_local_state; - lock->fh = *NFS_FH(fl->fl_file->f_dentry); + memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry), sizeof(struct nfs_fh)); lock->caller = system_utsname.nodename; lock->oh.data = req->a_owner; lock->oh.len = sprintf(req->a_owner, "%d@%s", diff --git a/fs/lockd/host.c b/fs/lockd/host.c index dcd33c19bf81..a18c2d109fdf 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -328,7 +328,7 @@ nlm_gc_hosts(void) if (host->h_monitored) nsm_unmonitor(host); if ((clnt = host->h_rpcclnt) != NULL) { - if (clnt->cl_users) { + if (atomic_read(&clnt->cl_users)) { printk(KERN_WARNING "lockd: active RPC handle\n"); clnt->cl_dead = 1; diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index bd8aa6b98031..910ffe095030 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -6,6 +6,7 @@ * Copyright (C) 1996-1997 Régis Duchesne * Copyright (C) 1998 Joseph Malicki * Copyright (C) 1999 Steve Dodd + * Copyright (C) 2000 Anton Altaparmakov */ #include "ntfstypes.h" @@ -551,11 +552,11 @@ int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset, dest->size=chunk; error=ntfs_getput_clusters(ino->vol,s_cluster, offset-s_vcn*clustersize,dest); - if(error)/* FIXME: maybe return failure */ + if(error) { ntfs_error("Read error\n"); dest->size=copied; - return 0; + return error; } l-=chunk; copied+=chunk; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 7f2a7fe8674b..3b7b5b2af555 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -221,7 +221,6 @@ ntfs_init_attrdef(ntfs_inode* attrdef) int ntfs_get_version(ntfs_inode* volume) { ntfs_attribute *volinfo; - int i; volinfo = ntfs_find_attr(volume, volume->vol->at_volume_information, 0); if (!volinfo) diff --git a/fs/open.c b/fs/open.c index 9135f0e30138..b2f6f6ffa0de 100644 --- a/fs/open.c +++ b/fs/open.c @@ -742,22 +742,6 @@ out: return error; } -inline void __put_unused_fd(struct files_struct *files, unsigned int fd) -{ - FD_CLR(fd, files->open_fds); - if (fd < files->next_fd) - files->next_fd = fd; -} - -inline void put_unused_fd(unsigned int fd) -{ - struct files_struct *files = current->files; - - write_lock(&files->file_lock); - __put_unused_fd(files, fd); - write_unlock(&files->file_lock); -} - asmlinkage long sys_open(const char * filename, int flags, int mode) { char * tmp; diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index 5717c4af9f99..0e1ba2d77df4 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -139,7 +139,7 @@ static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_s le32_to_cpu(linuxp->nr_sects)); linuxp ++; } - printk(" >\n"); + printk(" >"); /* * Prevent someone doing a mkswap or mkfs on this partition */ diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 4fda5fd5c06e..5981fa1201a9 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -33,7 +33,7 @@ #include "ibm.h" extern void device_init(void); -extern void md_setup_drive(void); +extern void md_run_setup(void); extern int *blk_size[]; extern void rd_load(void); extern void initrd_load(void); @@ -437,7 +437,7 @@ int __init partition_setup(void) rd_load(); #endif #ifdef CONFIG_BLK_DEV_MD - autodetect_raid(); + md_run_setup(); #endif return 0; } diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c index 53386a08d42b..29e455045406 100644 --- a/fs/partitions/msdos.c +++ b/fs/partitions/msdos.c @@ -438,6 +438,11 @@ check_table: continue; add_gd_partition(hd, minor, first_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size); +#if CONFIG_BLK_DEV_MD && CONFIG_AUTODETECT_RAID + if (SYS_IND(p) == LINUX_RAID_PARTITION) { + md_autodetect_dev(MKDEV(hd->major,minor)); + } +#endif if (is_extended_partition(p)) { printk(" <"); /* diff --git a/include/linux/coda.h b/include/linux/coda.h index cbd042242ef5..a085586285cd 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -331,9 +331,8 @@ struct coda_statfs { #if 0 #define CODA_KERNEL_VERSION 0 /* don't care about kernel version number */ #define CODA_KERNEL_VERSION 1 /* The old venus 4.6 compatible interface */ -#define CODA_KERNEL_VERSION 2 /* venus_lookup gets an extra parameter */ #endif -#define CODA_KERNEL_VERSION 3 /* added CODA_MAKE_CINODE downcall */ +#define CODA_KERNEL_VERSION 2 /* venus_lookup gets an extra parameter */ /* * Venus <-> Coda RPC arguments diff --git a/include/linux/coda_cache.h b/include/linux/coda_cache.h index e549b02e7c94..fe3b2f40c317 100644 --- a/include/linux/coda_cache.h +++ b/include/linux/coda_cache.h @@ -29,49 +29,6 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred); int coda_cache_check(struct inode *inode, int mask); /* for downcalls and attributes and lookups */ -void coda_flag_inode(struct inode *inode, int flag); void coda_flag_inode_children(struct inode *inode, int flag); - -/* - * Structure to contain statistics on the cache usage - */ - -struct cfsnc_statistics { - unsigned hits; - unsigned misses; - unsigned enters; - unsigned dbl_enters; - unsigned long_name_enters; - unsigned long_name_lookups; - unsigned long_remove; - unsigned lru_rm; - unsigned zapPfids; - unsigned zapFids; - unsigned zapFile; - unsigned zapUsers; - unsigned Flushes; - unsigned Sum_bucket_len; - unsigned Sum2_bucket_len; - unsigned Max_bucket_len; - unsigned Num_zero_len; - unsigned Search_len; -}; - - -#define CFSNC_FIND ((u_long) 1) -#define CFSNC_REMOVE ((u_long) 2) -#define CFSNC_INIT ((u_long) 3) -#define CFSNC_ENTER ((u_long) 4) -#define CFSNC_LOOKUP ((u_long) 5) -#define CFSNC_ZAPPFID ((u_long) 6) -#define CFSNC_ZAPFID ((u_long) 7) -#define CFSNC_ZAPVNODE ((u_long) 8) -#define CFSNC_ZAPFILE ((u_long) 9) -#define CFSNC_PURGEUSER ((u_long) 10) -#define CFSNC_FLUSH ((u_long) 11) -#define CFSNC_PRINTCFSNC ((u_long) 12) -#define CFSNC_PRINTSTATS ((u_long) 13) -#define CFSNC_REPLACE ((u_long) 14) - #endif _CFSNC_HEADER_ diff --git a/include/linux/coda_fs_i.h b/include/linux/coda_fs_i.h index 302a715b9c65..ac14c8e7fbf8 100644 --- a/include/linux/coda_fs_i.h +++ b/include/linux/coda_fs_i.h @@ -13,8 +13,6 @@ #include #include - - #define CODA_CNODE_MAGIC 0x47114711 /* * coda fs inode data @@ -22,10 +20,6 @@ struct coda_inode_info { struct ViceFid c_fid; /* Coda identifier */ u_short c_flags; /* flags (see below) */ - u_short c_ocount; /* count of openers */ - u_short c_owrite; /* count of open for write */ - u_short c_mmcount; /* count of mmappers */ - struct inode *c_ovp; /* open inode pointer */ struct list_head c_cnhead; /* head of cache entries */ struct list_head c_volrootlist; /* list of volroot cnoddes */ struct inode *c_vnode; /* inode associated with cnode */ @@ -34,14 +28,10 @@ struct coda_inode_info { /* flags */ #define C_VATTR 0x1 /* Validity of vattr in inode */ -#define C_PURGE 0x8 -#define C_ZAPDIR 0x10 -#define C_DYING 0x4 /* from venus (which died) */ -#define C_INITED 0x20 #define C_FLUSH 0x2 /* used after a flush */ +#define C_DYING 0x4 /* from venus (which died) */ +#define C_PURGE 0x8 -struct inode *coda_iget(struct super_block * sb, ViceFid * fid, - struct coda_vattr * attr); int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *); int coda_cnode_makectl(struct inode **inode, struct super_block *sb); struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb); diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index 08ccef4c45b2..556d1b60e146 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -48,6 +48,8 @@ extern int coda_access_cache; /* this file: heloers */ static __inline__ struct ViceFid *coda_i2f(struct inode *); +static __inline__ char *coda_i2s(struct inode *); +static __inline__ void coda_flag_inode(struct inode *, int flag); char *coda_f2s(ViceFid *f); char *coda_f2s2(ViceFid *f); int coda_isroot(struct inode *i); @@ -56,7 +58,6 @@ int coda_fid_is_weird(struct ViceFid *fid); int coda_iscontrol(const char *name, size_t length); void coda_load_creds(struct coda_cred *cred); -int coda_mycred(struct coda_cred *); void coda_vattr_to_iattr(struct inode *, struct coda_vattr *); void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *); unsigned short coda_flags_to_cflags(unsigned short); @@ -64,16 +65,6 @@ void print_vattr( struct coda_vattr *attr ); int coda_cred_ok(struct coda_cred *cred); int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2); -/* defined in file.c */ -void coda_prepare_openfile(struct inode *coda_inode, struct file *coda_file, - struct inode *open_inode, struct file *open_file, - struct dentry *open_dentry); -void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file, - struct inode *open_inode, struct file *open_file); -int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind); - -#define NB_SFS_SIZ 0x895440 - /* cache.c */ void coda_purge_children(struct inode *, int); void coda_purge_dentries(struct inode *); @@ -110,8 +101,6 @@ void coda_sysctl_clean(void); #define EXIT \ if(coda_print_entry) printk("Process %d leaving %s\n",current->pid,__FUNCTION__) -#define CHECK_CNODE(c) do { } while (0); - #define CODA_ALLOC(ptr, cast, size) \ do { \ if (size < 3000) { \ @@ -129,18 +118,24 @@ do { \ #define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %lx at %p.\n", (long) size, ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %lx at %p.\n", (long) size, ptr);} } while (0) -/* inode to cnode */ +/* inode to cnode access functions */ static __inline__ struct ViceFid *coda_i2f(struct inode *inode) { return &(inode->u.coda_i.c_fid); } -#define ITOC(inode) (&((inode)->u.coda_i)) - - - +static __inline__ char *coda_i2s(struct inode *inode) +{ + return coda_f2s(&(inode->u.coda_i.c_fid)); +} +/* this will not zap the inode away */ +static __inline__ void coda_flag_inode(struct inode *inode, int flag) +{ + inode->u.coda_i.c_flags |= flag; +} +#define ITOC(inode) (&((inode)->u.coda_i)) #endif diff --git a/include/linux/coda_opstats.h b/include/linux/coda_opstats.h deleted file mode 100644 index fdf3fac42f2d..000000000000 --- a/include/linux/coda_opstats.h +++ /dev/null @@ -1,94 +0,0 @@ - -/* - * Operation statistics for Coda. - * Copyright (C) 1997 Carnegie Mellon University - * - * Carnegie Mellon University encourages users of this software - * to contribute improvements to the Coda project. Contact Peter Braam - * . - */ - - - -#define CFS_MOUNT_STATS 0 -#define CFS_UMOUNT_STATS 1 -#define CFS_ROOT_STATS 2 -#define CFS_STATFS_STATS 3 -#define CFS_SYNC_STATS 4 -#define CFS_VGET_STATS 5 -#define CFS_VFSOPS_SIZE 6 - -/* vnodeops: - * open: all to venus - * close: all to venus - * rdrw: bogus. Maybe redirected to UFS. - * May call open/close for internal opens/closes - * (Does exec not call open?) - * ioctl: causes a lookupname - * passes through - * select: can't get there from here. - * getattr: can be satsified by cache - * setattr: all go through - * access: can be satisfied by cache - * readlink: can be satisfied by cache - * fsync: passes through - * inactive: passes through - * lookup: can be satisfied by cache - * create: passes through - * remove: passes through - * link: passes through - * rename: passes through - * mkdir: passes through - * rmdir: passes through - * symlink: passes through - * readdir: may be redirected to UFS - * may cause an "internal" open/close - */ - -#define CFS_OPEN_STATS 0 -#define CFS_CLOSE_STATS 1 -#define CFS_RDWR_STATS 2 -#define CFS_IOCTL_STATS 3 -#define CFS_SELECT_STATS 4 -#define CFS_GETATTR_STATS 5 -#define CFS_SETATTR_STATS 6 -#define CFS_ACCESS_STATS 7 -#define CFS_READLINK_STATS 8 -#define CFS_FSYNC_STATS 9 -#define CFS_INACTIVE_STATS 10 -#define CFS_LOOKUP_STATS 11 -#define CFS_CREATE_STATS 12 -#define CFS_REMOVE_STATS 13 -#define CFS_LINK_STATS 14 -#define CFS_RENAME_STATS 15 -#define CFS_MKDIR_STATS 16 -#define CFS_RMDIR_STATS 17 -#define CFS_SYMLINK_STATS 18 -#define CFS_READDIR_STATS 19 -#define CFS_VNODEOPS_SIZE 20 - - -/* - * I propose the following structres: - */ - - -struct cfs_op_stats { - int opcode; /* vfs opcode */ - long entries; /* number of times call attempted */ - long sat_intrn; /* number of times call satisfied by cache */ - long unsat_intrn; /* number of times call failed in cache, but - was not bounced to venus proper. */ - long gen_intrn; /* number of times call generated internally */ - /* (do we need that?) */ -}; - - -/* - * With each call to the minicache, we'll bump the counters whenver - * a call is satisfied internally (through the cache or through a - * redirect), and whenever an operation is caused internally. - * Then, we can add the total operations caught by the minicache - * to the world-wide totals, and leave a caveat for the specific - * graphs later. - */ diff --git a/include/linux/coda_proc.h b/include/linux/coda_proc.h index 99cee973f8a4..37dfad86adf3 100644 --- a/include/linux/coda_proc.h +++ b/include/linux/coda_proc.h @@ -39,9 +39,6 @@ void coda_upcall_stats(int opcode, unsigned long jiffies); struct coda_vfs_stats { /* file operations */ - int file_read; - int file_write; - int file_mmap; int open; int release; int fsync; @@ -59,7 +56,6 @@ struct coda_vfs_stats int rmdir; int rename; int permission; - int readpage; /* symlink operatoins*/ int follow_link; diff --git a/include/linux/file.h b/include/linux/file.h index 8ac96f574388..268fd27a18b4 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -74,6 +74,24 @@ static inline void fput(struct file * file) } extern void put_filp(struct file *); +extern int get_unused_fd(void); + +static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) +{ + FD_CLR(fd, files->open_fds); + if (fd < files->next_fd) + files->next_fd = fd; +} + +static inline void put_unused_fd(unsigned int fd) +{ + struct files_struct *files = current->files; + + write_lock(&files->file_lock); + __put_unused_fd(files, fd); + write_unlock(&files->file_lock); +} + /* * Install a file pointer in the fd array. * diff --git a/include/linux/fs.h b/include/linux/fs.h index 285f21f16234..8a1f8e9b69be 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -554,6 +554,15 @@ struct fasync_struct { struct file *fa_file; }; +#define FASYNC_MAGIC 0x4601 + +/* SMP safe fasync helpers: */ +extern int fasync_helper(int, struct file *, int, struct fasync_struct **); +/* can be called from interrupts */ +extern void kill_fasync(struct fasync_struct **, int, int); +/* only for net: no internal synchronization */ +extern void __kill_fasync(struct fasync_struct *, int, int); + struct nameidata { struct dentry *dentry; struct vfsmount *mnt; @@ -562,10 +571,6 @@ struct nameidata { int last_type; }; -#define FASYNC_MAGIC 0x4601 - -extern int fasync_helper(int, struct file *, int, struct fasync_struct **); - #define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */ #define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */ @@ -866,9 +871,6 @@ asmlinkage long sys_open(const char *, int, int); asmlinkage long sys_close(unsigned int); /* yes, it's really unsigned */ extern int do_close(unsigned int, int); /* yes, it's really unsigned */ extern int do_truncate(struct dentry *, loff_t start); -extern int get_unused_fd(void); -extern void __put_unused_fd(struct files_struct *, unsigned int); /* locked outside */ -extern void put_unused_fd(unsigned int); /* locked inside */ extern struct file *filp_open(const char *, int, int); extern struct file * dentry_open(struct dentry *, struct vfsmount *, int); @@ -878,7 +880,6 @@ extern char * getname(const char *); #define putname(name) free_page((unsigned long)(name)) enum {BDEV_FILE, BDEV_SWAP, BDEV_FS, BDEV_RAW}; -extern void kill_fasync(struct fasync_struct *, int, int); extern int register_blkdev(unsigned int, const char *, struct block_device_operations *); extern int unregister_blkdev(unsigned int, const char *); extern struct block_device *bdget(dev_t); diff --git a/include/linux/gameport.h b/include/linux/gameport.h new file mode 100644 index 000000000000..972dd0bfb727 --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include + +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/genhd.h b/include/linux/genhd.h index c07a473d1444..c0f69dbb4943 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -50,7 +50,6 @@ struct partition { struct hd_struct { long start_sect; long nr_sects; - int type; /* currently RAID or normal */ devfs_handle_t de; /* primary (master) devfs entry */ }; diff --git a/include/linux/input.h b/include/linux/input.h index 6d19fc2a9f66..a44e6f8e1bab 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/joystick.h b/include/linux/joystick.h index c0e148171507..1836aa95a281 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 -#include /* * 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 -#include - -#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 -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 -#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 - -#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/mc146818rtc.h b/include/linux/mc146818rtc.h index cd6bb91b002e..cab50dd9edba 100644 --- a/include/linux/mc146818rtc.h +++ b/include/linux/mc146818rtc.h @@ -10,22 +10,10 @@ #ifndef _MC146818RTC_H #define _MC146818RTC_H -#include -#include /* get the user-level API */ - -#ifndef RTC_PORT -#define RTC_PORT(x) (0x70 + (x)) -#define RTC_ALWAYS_BCD 1 -#endif -#define CMOS_READ(addr) ({ \ -outb_p((addr),RTC_PORT(0)); \ -inb_p(RTC_PORT(1)); \ -}) -#define CMOS_WRITE(val, addr) ({ \ -outb_p((addr),RTC_PORT(0)); \ -outb_p((val),RTC_PORT(1)); \ -}) +#include +#include /* get the user-level API */ +#include /* register access macros */ /********************************************************************** * register summary diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index c2cabe19b25a..152fb359bc73 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -78,7 +78,6 @@ extern int md_do_sync(mddev_t *mddev, mdp_disk_t *spare); extern void md_done_sync(mddev_t *mddev, int blocks, int ok); extern void md_recover_arrays (void); extern int md_check_ordering (mddev_t *mddev); -extern void autodetect_raid(void); extern struct gendisk * find_gendisk (kdev_t dev); extern int md_notify_reboot(struct notifier_block *this, unsigned long code, void *x); diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index b62d63750412..9f722b49ebf7 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -199,15 +199,16 @@ struct mddev_s int sb_dirty; mdu_param_t param; int ro; - unsigned long curr_resync; - unsigned long resync_start; + unsigned long curr_resync; /* blocks scheduled */ + unsigned long resync_mark; /* a recent timestamp */ + unsigned long resync_mark_cnt;/* blocks written at resync_mark */ char *name; int recovery_running; struct semaphore reconfig_sem; struct semaphore recovery_sem; struct semaphore resync_sem; - atomic_t recovery_active; + atomic_t recovery_active; /* blocks scheduled, but not written */ md_wait_queue_head_t recovery_wait; struct md_list_head all_mddevs; @@ -218,11 +219,9 @@ struct mdk_personality_s { char *name; int (*make_request)(request_queue_t *q, mddev_t *mddev, int rw, struct buffer_head * bh); - void (*end_request)(struct buffer_head * bh, int uptodate); int (*run)(mddev_t *mddev); int (*stop)(mddev_t *mddev); int (*status)(char *page, mddev_t *mddev); - int max_invalid_dev; int (*error_handler)(mddev_t *mddev, kdev_t dev); /* @@ -337,7 +336,8 @@ typedef struct mdk_thread_s { typedef struct dev_name_s { struct md_list_head list; kdev_t dev; - char name [MAX_DISKNAME_LEN]; + char namebuf [MAX_DISKNAME_LEN]; + char *name; } dev_name_t; diff --git a/include/linux/raid/raid1.h b/include/linux/raid/raid1.h index 1016bdaa498c..ffa65d20d64e 100644 --- a/include/linux/raid/raid1.h +++ b/include/linux/raid/raid1.h @@ -9,6 +9,7 @@ struct mirror_info { kdev_t dev; int next; int sect_limit; + int head_position; /* * State bits: @@ -34,6 +35,18 @@ struct raid1_private_data { struct mirror_info *spare; md_spinlock_t device_lock; + /* buffer pool */ + /* buffer_heads that we have pre-allocated have b_pprev -> &freebh + * and are linked into a stack using b_next + * raid1_bh that are pre-allocated have R1BH_PreAlloc set. + * All these variable are protected by device_lock + */ + struct buffer_head *freebh; + int freebh_cnt; /* how many are on the list */ + struct raid1_bh *freer1; + struct raid1_bh *freebuf; /* each bh_req has a page allocated */ + md_wait_queue_head_t wait_buffer; + /* for use when syncing mirrors: */ int start_active, start_ready, start_pending, start_future; @@ -68,12 +81,12 @@ struct raid1_bh { unsigned long state; mddev_t *mddev; struct buffer_head *master_bh; - struct buffer_head *mirror_bh [MD_SB_DISKS]; + struct buffer_head *mirror_bh_list; struct buffer_head bh_req; - struct buffer_head *next_retry; + struct raid1_bh *next_r1; /* next for retry or in free list */ }; /* bits for raid1_bh.state */ #define R1BH_Uptodate 1 #define R1BH_SyncPhase 2 - +#define R1BH_PreAlloc 3 /* this was pre-allocated, add to free list */ #endif diff --git a/include/linux/raid1.h b/include/linux/raid1.h deleted file mode 100644 index 4b031e6825cf..000000000000 --- a/include/linux/raid1.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _RAID1_H -#define _RAID1_H - -#include - -struct mirror_info { - int number; - int raid_disk; - kdev_t dev; - int next; - int sect_limit; - - /* - * State bits: - */ - int operational; - int write_only; - int spare; -}; - -struct raid1_data { - struct md_dev *mddev; - struct mirror_info mirrors[MD_SB_DISKS]; /* RAID1 devices, 2 to MD_SB_DISKS */ - int raid_disks; - int working_disks; /* Number of working disks */ - int last_used; - unsigned long next_sect; - int sect_count; - int resync_running; -}; - -/* - * this is our 'private' 'collective' RAID1 buffer head. - * it contains information about what kind of IO operations were started - * for this RAID5 operation, and about their status: - */ - -struct raid1_bh { - unsigned int remaining; - int cmd; - unsigned long state; - struct md_dev *mddev; - struct buffer_head *master_bh; - struct buffer_head *mirror_bh [MD_SB_DISKS]; - struct buffer_head bh_req; - struct buffer_head *next_retry; -}; - -#endif diff --git a/include/linux/raid5.h b/include/linux/raid5.h deleted file mode 100644 index 655d41d54ad1..000000000000 --- a/include/linux/raid5.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef _RAID5_H -#define _RAID5_H - -#ifdef __KERNEL__ -#include -#include - -struct disk_info { - kdev_t dev; - int operational; - int number; - int raid_disk; - int write_only; - int spare; -}; - -struct stripe_head { - struct stripe_head *hash_next, **hash_pprev; /* hash pointers */ - struct stripe_head *free_next; /* pool of free sh's */ - struct buffer_head *buffer_pool; /* pool of free buffers */ - struct buffer_head *bh_pool; /* pool of free bh's */ - struct raid5_data *raid_conf; - struct buffer_head *bh_old[MD_SB_DISKS]; /* disk image */ - struct buffer_head *bh_new[MD_SB_DISKS]; /* buffers of the MD device (present in buffer cache) */ - struct buffer_head *bh_copy[MD_SB_DISKS]; /* copy on write of bh_new (bh_new can change from under us) */ - struct buffer_head *bh_req[MD_SB_DISKS]; /* copy of bh_new (only the buffer heads), queued to the lower levels */ - int cmd_new[MD_SB_DISKS]; /* READ/WRITE for new */ - int new[MD_SB_DISKS]; /* buffer added since the last handle_stripe() */ - unsigned long sector; /* sector of this row */ - int size; /* buffers size */ - int pd_idx; /* parity disk index */ - int nr_pending; /* nr of pending cmds */ - unsigned long state; /* state flags */ - int cmd; /* stripe cmd */ - int count; /* nr of waiters */ - int write_method; /* reconstruct-write / read-modify-write */ - int phase; /* PHASE_BEGIN, ..., PHASE_COMPLETE */ - wait_queue_head_t wait; /* processes waiting for this stripe */ -}; - -/* - * Phase - */ -#define PHASE_BEGIN 0 -#define PHASE_READ_OLD 1 -#define PHASE_WRITE 2 -#define PHASE_READ 3 -#define PHASE_COMPLETE 4 - -/* - * Write method - */ -#define METHOD_NONE 0 -#define RECONSTRUCT_WRITE 1 -#define READ_MODIFY_WRITE 2 - -/* - * Stripe state - */ -#define STRIPE_LOCKED 0 -#define STRIPE_ERROR 1 - -/* - * Stripe commands - */ -#define STRIPE_NONE 0 -#define STRIPE_WRITE 1 -#define STRIPE_READ 2 - -struct raid5_data { - struct stripe_head **stripe_hashtbl; - struct md_dev *mddev; - struct md_thread *thread, *resync_thread; - struct disk_info disks[MD_SB_DISKS]; - struct disk_info *spare; - int buffer_size; - int chunk_size, level, algorithm; - int raid_disks, working_disks, failed_disks; - int sector_count; - unsigned long next_sector; - atomic_t nr_handle; - struct stripe_head *next_free_stripe; - int nr_stripes; - int resync_parity; - int max_nr_stripes; - int clock; - int nr_hashed_stripes; - int nr_locked_stripes; - int nr_pending_stripes; - int nr_cached_stripes; - - /* - * Free stripes pool - */ - int nr_free_sh; - struct stripe_head *free_sh_list; - wait_queue_head_t wait_for_stripe; -}; - -#endif - -/* - * Our supported algorithms - */ -#define ALGORITHM_LEFT_ASYMMETRIC 0 -#define ALGORITHM_RIGHT_ASYMMETRIC 1 -#define ALGORITHM_LEFT_SYMMETRIC 2 -#define ALGORITHM_RIGHT_SYMMETRIC 3 - -#endif diff --git a/include/linux/serial.h b/include/linux/serial.h index da10e19edca2..ce404ca1a9d7 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -10,6 +10,7 @@ #ifndef _LINUX_SERIAL_H #define _LINUX_SERIAL_H +#ifdef __KERNEL__ #include /* @@ -27,10 +28,12 @@ struct async_icount { */ #define SERIAL_XMIT_SIZE PAGE_SIZE +#endif + struct serial_struct { int type; int line; - unsigned long port; + unsigned int port; int irq; int flags; int xmit_fifo_size; @@ -44,7 +47,8 @@ struct serial_struct { unsigned short closing_wait2; /* no longer used... */ unsigned char *iomem_base; unsigned short iomem_reg_shift; - int reserved[2]; + unsigned int port_high; + int reserved[1]; }; /* @@ -70,7 +74,8 @@ struct serial_struct { #define PORT_16C950 10 /* Oxford Semiconductor */ #define PORT_16654 11 #define PORT_16850 12 -#define PORT_MAX 12 +#define PORT_RSA 13 /* RSA-DV II/S card */ +#define PORT_MAX 13 #define SERIAL_IO_PORT 0 #define SERIAL_IO_HUB6 1 @@ -115,7 +120,10 @@ struct serial_uart_config { #define ASYNC_LOW_LATENCY 0x2000 /* Request low latency behaviour */ -#define ASYNC_FLAGS 0x3FFF /* Possible legal async flags */ +#define ASYNC_BUGGY_UART 0x4000 /* This is a buggy UART, skip some safety + * checks. Note: can be dangerous! */ + +#define ASYNC_FLAGS 0x7FFF /* Possible legal async flags */ #define ASYNC_USR_MASK 0x3430 /* Legal flags that non-privileged * users can set or reset */ @@ -127,7 +135,9 @@ struct serial_uart_config { #define ASYNC_CLOSING 0x08000000 /* Serial port is closing */ #define ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */ #define ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */ -#define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards */ +#define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards + --- no longer used */ +#define ASYNC_AUTOPROBE 0x00800000 /* Port was autoprobed */ #define ASYNC_INTERNAL_FLAGS 0xFF000000 /* Internal flags */ diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 8329b4580e45..d1f90126211d 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -24,6 +24,11 @@ #include #include #include +#if (LINUX_VERSION_CODE < 0x020300) +/* Unfortunate, but Linux 2.2 needs async_icount defined here and + * it got moved in 2.3 */ +#include +#endif struct serial_state { int magic; @@ -190,6 +195,9 @@ struct pci_board_inst { /* Do not use irq sharing for this device */ #define SPCI_FL_NO_SHIRQ 0x1000 -#define SPCI_FL_PNPDEFAULT (SPCI_FL_IRQRESOURCE) +/* This is a PNP device */ +#define SPCI_FL_ISPNP 0x2000 + +#define SPCI_FL_PNPDEFAULT (SPCI_FL_IRQRESOURCE|SPCI_FL_ISPNP) #endif /* _LINUX_SERIAL_H */ diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h index 1787be41f9ca..09feb9501bd7 100644 --- a/include/linux/serial_reg.h +++ b/include/linux/serial_reg.h @@ -229,5 +229,54 @@ #define UART_TRG_120 0x78 #define UART_TRG_128 0x80 +/* + * These definitions are for the RSA-DV II/S card, from + * + * Kiyokazu SUTO + */ + +#define UART_RSA_BASE (-8) + +#define UART_RSA_MSR ((UART_RSA_BASE) + 0) /* I/O: Mode Select Register */ + +#define UART_RSA_MSR_SWAP (1 << 0) /* Swap low/high 8 bytes in I/O port addr */ +#define UART_RSA_MSR_FIFO (1 << 2) /* Enable the external FIFO */ +#define UART_RSA_MSR_FLOW (1 << 3) /* Enable the auto RTS/CTS flow control */ +#define UART_RSA_MSR_ITYP (1 << 4) /* Level (1) / Edge triger (0) */ + +#define UART_RSA_IER ((UART_RSA_BASE) + 1) /* I/O: Interrupt Enable Register */ + +#define UART_RSA_IER_Rx_FIFO_H (1 << 0) /* Enable Rx FIFO half full int. */ +#define UART_RSA_IER_Tx_FIFO_H (1 << 1) /* Enable Tx FIFO half full int. */ +#define UART_RSA_IER_Tx_FIFO_E (1 << 2) /* Enable Tx FIFO empty int. */ +#define UART_RSA_IER_Rx_TOUT (1 << 3) /* Enable char receive timeout int */ +#define UART_RSA_IER_TIMER (1 << 4) /* Enable timer interrupt */ + +#define UART_RSA_SRR ((UART_RSA_BASE) + 2) /* IN: Status Read Register */ + +#define UART_RSA_SRR_Tx_FIFO_NEMP (1 << 0) /* Tx FIFO is not empty (1) */ +#define UART_RSA_SRR_Tx_FIFO_NHFL (1 << 1) /* Tx FIFO is not half full (1) */ +#define UART_RSA_SRR_Tx_FIFO_NFUL (1 << 2) /* Tx FIFO is not full (1) */ +#define UART_RSA_SRR_Rx_FIFO_NEMP (1 << 3) /* Rx FIFO is not empty (1) */ +#define UART_RSA_SRR_Rx_FIFO_NHFL (1 << 4) /* Rx FIFO is not half full (1) */ +#define UART_RSA_SRR_Rx_FIFO_NFUL (1 << 5) /* Rx FIFO is not full (1) */ +#define UART_RSA_SRR_Rx_TOUT (1 << 6) /* Character reception timeout occured (1) */ +#define UART_RSA_SRR_TIMER (1 << 7) /* Timer interrupt occured */ + +#define UART_RSA_FRR ((UART_RSA_BASE) + 2) /* OUT: FIFO Reset Register */ + +#define UART_RSA_TIVSR ((UART_RSA_BASE) + 3) /* I/O: Timer Interval Value Set Register */ + +#define UART_RSA_TCR ((UART_RSA_BASE) + 4) /* OUT: Timer Control Register */ + +#define UART_RSA_TCR_SWITCH (1 << 0) /* Timer on */ + +/* + * The RSA DSV/II board has two fixed clock frequencies. One is the + * standard rate, and the other is 8 times faster. + */ +#define SERIAL_RSA_BAUD_BASE (921600) +#define SERIAL_RSA_BAUD_BASE_LO (SERIAL_RSA_BAUD_BASE / 8) + #endif /* _LINUX_SERIAL_REG_H */ diff --git a/include/linux/serio.h b/include/linux/serio.h new file mode 100644 index 000000000000..9cb8bb52576e --- /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 , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +/* + * The serial port set type ioctl. + */ + +#include +#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/shm.h b/include/linux/shm.h index bc56c5e20ba0..e1b335cbe990 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -2,7 +2,7 @@ #define _LINUX_SHM_H_ #include -#include +#include /* * SHMMAX, SHMMNI and SHMALL are upper limits are defaults which can diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 0d09eb48ad51..4cc0f16ad649 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -30,7 +30,7 @@ struct rpc_portmap { * The high-level client handle */ struct rpc_clnt { - unsigned int cl_users; /* number of references */ + atomic_t cl_users; /* number of references */ struct rpc_xprt * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ u32 cl_maxproc; /* max procedure number */ diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index c82289283263..eb55e776c832 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -9,13 +9,17 @@ #ifndef _LINUX_SUNRPC_DEBUG_H_ #define _LINUX_SUNRPC_DEBUG_H_ +#include + #include #include /* * Enable RPC debugging/profiling. */ +#ifdef CONFIG_SYSCTL #define RPC_DEBUG +#endif /* #define RPC_PROFILE */ /* diff --git a/init/main.c b/init/main.c index f1cb9e938e0c..88de7a27994f 100644 --- a/init/main.c +++ b/init/main.c @@ -492,12 +492,12 @@ asmlinkage void __init start_kernel(void) printk(linux_banner); setup_arch(&command_line); printk("Kernel command line: %s\n", saved_command_line); + parse_options(command_line); trap_init(); init_IRQ(); sched_init(); time_init(); softirq_init(); - parse_options(command_line); /* * HACK ALERT! This is early. We're enabling the console before @@ -699,7 +699,8 @@ static void __init do_basic_setup(void) #ifdef CONFIG_BLK_DEV_INITRD root_mountflags = real_root_mountflags; - if (mount_initrd && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { + if (mount_initrd && ROOT_DEV != real_root_dev + && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { int error; int i, pid; @@ -709,7 +710,7 @@ static void __init do_basic_setup(void) if (MAJOR(real_root_dev) != RAMDISK_MAJOR || MINOR(real_root_dev) != 0) { #ifdef CONFIG_BLK_DEV_MD - autodetect_raid(); + md_run_setup(); #endif error = change_root(real_root_dev,"/initrd"); if (error) diff --git a/ipc/shm.c b/ipc/shm.c index d6aaf031125f..f1b638acfda1 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -16,11 +16,9 @@ * * The filesystem has the following restrictions/bugs: * 1) It only can handle one directory. - * 2) Because the directory is represented by the SYSV shm array it - * can only be mounted one time. - * 3) Private writeable mappings are not supported - * 4) Read and write are not implemented (should they?) - * 5) No special nodes are supported + * 2) Private writeable mappings are not supported + * 3) Read and write are not implemented (should they?) + * 4) No special nodes are supported * * There are the following mount options: * - nr_blocks (^= shmall) is the number of blocks of size PAGE_SIZE @@ -335,7 +333,7 @@ static inline struct shmid_kernel *shm_rmid(int id) return (struct shmid_kernel *)ipc_rmid(&shm_ids,id); } -static __inline__ int shm_addid(struct shmid_kernel *shp) +static inline int shm_addid(struct shmid_kernel *shp) { return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1); } @@ -544,13 +542,37 @@ static int shm_unlink (struct inode *dir, struct dentry *dent) return 0; } -#define SHM_ENTRY(shp, index) (shp)->shm_dir[(index)/PTRS_PER_PTE][(index)%PTRS_PER_PTE] +/* + * We cannot use kmalloc for shm_alloc since this restricts the + * maximum size of the segments. + * + * We also cannot use vmalloc, since this uses too much of the vmalloc + * space and we run out of this on highend machines. + * + * So we have to use this complicated indirect scheme to alloc the shm + * page tables. + * + */ + +#ifdef PTE_INIT +static inline void init_ptes (pte_t *pte, int number) { + while (number--) + PTE_INIT (pte++); +} +#else +static inline void init_ptes (pte_t *pte, int number) { + memset (pte, 0, number*sizeof(*pte)); +} +#endif + +#define PTES_PER_PAGE (PAGE_SIZE/sizeof(pte_t)) +#define SHM_ENTRY(shp, index) (shp)->shm_dir[(index)/PTES_PER_PAGE][(index)%PTES_PER_PAGE] static pte_t **shm_alloc(unsigned long pages, int doacc) { - unsigned short dir = pages / PTRS_PER_PTE; - unsigned short last = pages % PTRS_PER_PTE; - pte_t **ret, **ptr, *pte; + unsigned short dir = pages / PTES_PER_PAGE; + unsigned short last = pages % PTES_PER_PAGE; + pte_t **ret, **ptr; if (pages == 0) return NULL; @@ -564,8 +586,7 @@ static pte_t **shm_alloc(unsigned long pages, int doacc) *ptr = (pte_t *)__get_free_page (GFP_KERNEL); if (!*ptr) goto free; - for (pte = *ptr; pte < *ptr + PTRS_PER_PTE; pte++) - pte_clear (pte); + init_ptes (*ptr, PTES_PER_PAGE); } /* The last one is probably not of PAGE_SIZE: we use kmalloc */ @@ -573,8 +594,7 @@ static pte_t **shm_alloc(unsigned long pages, int doacc) *ptr = kmalloc (last*sizeof(pte_t), GFP_KERNEL); if (!*ptr) goto free; - for (pte = *ptr; pte < *ptr + last; pte++) - pte_clear (pte); + init_ptes (*ptr, last); } if (doacc) { shm_lockall(); @@ -597,14 +617,14 @@ nomem: static void shm_free(pte_t** dir, unsigned long pages, int doacc) { int i, rss, swp; - pte_t **ptr = dir+pages/PTRS_PER_PTE; + pte_t **ptr = dir+pages/PTES_PER_PAGE; if (!dir) return; for (i = 0, rss = 0, swp = 0; i < pages ; i++) { pte_t pte; - pte = dir[i/PTRS_PER_PTE][i%PTRS_PER_PTE]; + pte = dir[i/PTES_PER_PAGE][i%PTES_PER_PAGE]; if (pte_none(pte)) continue; if (pte_present(pte)) { @@ -617,7 +637,7 @@ static void shm_free(pte_t** dir, unsigned long pages, int doacc) } /* first the last page */ - if (pages%PTRS_PER_PTE) + if (pages%PTES_PER_PAGE) kfree (*ptr); /* now the whole pages */ while (--ptr >= dir) @@ -663,10 +683,10 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr) BUG(); error = -ENOSPC; if (shm_tot - shp->shm_npages >= shm_ctlall) - goto out; + goto size_out; error = 0; if (shp->shm_segsz == attr->ia_size) - goto out; + goto size_out; /* Now we set them to the real values */ old_dir = shp->shm_dir; old_pages = shp->shm_npages; @@ -674,8 +694,8 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr) pte_t *swap; int i,j; i = old_pages < new_pages ? old_pages : new_pages; - j = i % PTRS_PER_PTE; - i /= PTRS_PER_PTE; + j = i % PTES_PER_PAGE; + i /= PTES_PER_PAGE; if (j) memcpy (new_dir[i], old_dir[i], j * sizeof (pte_t)); while (i--) { @@ -687,10 +707,21 @@ static int shm_setattr (struct dentry *dentry, struct iattr *attr) shp->shm_dir = new_dir; shp->shm_npages = new_pages; shp->shm_segsz = attr->ia_size; -out: +size_out: shm_unlock(inode->i_ino); shm_free (old_dir, old_pages, 1); + set_attr: + if (!(shp = shm_lock(inode->i_ino))) + BUG(); + if (attr->ia_valid & ATTR_MODE) + shp->shm_perm.mode = attr->ia_mode; + if (attr->ia_valid & ATTR_UID) + shp->shm_perm.uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + shp->shm_perm.gid = attr->ia_gid; + shm_unlock (inode->i_ino); + inode_setattr(inode, attr); return error; } @@ -1073,6 +1104,9 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) case IPC_SET: { + struct dentry * dentry; + char name[SHM_FMT_LEN+1]; + if ((shmid % SEQ_MULTIPLIER)== zero_id) return -EINVAL; @@ -1098,7 +1132,29 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) shp->shm_flags = (shp->shm_flags & ~S_IRWXUGO) | (setbuf.mode & S_IRWXUGO); shp->shm_ctim = CURRENT_TIME; - break; + shm_unlock(shmid); + up(&shm_ids.sem); + + sprintf (name, SHM_FMT, shmid); + lock_kernel(); + dentry = lookup_one(name, lock_parent(shm_sb->s_root)); + unlock_dir(shm_sb->s_root); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto bad_dentry; + err = -ENOENT; + if (dentry->d_inode) { + struct inode *ino = dentry->d_inode; + ino->i_uid = setbuf.uid; + ino->i_gid = setbuf.gid; + ino->i_mode = (setbuf.mode & S_IRWXUGO) | (ino->i_mode & ~S_IALLUGO);; + ino->i_atime = ino->i_mtime = ino->i_ctime = CURRENT_TIME; + err = 0; + } + dput (dentry); + bad_dentry: + unlock_kernel(); + return err; } default: @@ -1142,6 +1198,7 @@ static int shm_mmap(struct file * file, struct vm_area_struct * vma) */ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) { + struct shmid_kernel *shp; unsigned long addr; struct file * file; int err; @@ -1169,13 +1226,25 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) if (shmflg & SHM_RDONLY) { prot = PROT_READ; o_flags = O_RDONLY; - acc_mode = MAY_READ; + acc_mode = S_IRUGO; } else { prot = PROT_READ | PROT_WRITE; o_flags = O_RDWR; - acc_mode = MAY_READ | MAY_WRITE; + acc_mode = S_IRUGO | S_IWUGO; } + /* + * We cannot rely on the fs check since SYSV IPC does have an + * aditional creator id... + */ + shp = shm_lock(shmid); + if(shp==NULL) + return -EINVAL; + err = ipcperms(&shp->shm_perm, acc_mode); + shm_unlock(shmid); + if (err) + return -EACCES; + sprintf (name, SHM_FMT, shmid); lock_kernel(); @@ -1188,9 +1257,6 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) err = -ENOENT; if (!dentry->d_inode) goto bad_file; - err = permission(dentry->d_inode, acc_mode); - if (err) - goto bad_file1; file = dentry_open(dentry, shm_fs_type.kern_mnt, o_flags); err = PTR_ERR(file); if (IS_ERR (file)) @@ -1492,7 +1558,7 @@ int shm_swap (int prio, int gfp_mask) shm_lockall(); check_id: shp = shm_get(swap_id); - if(shp==NULL || shp->shm_flags & SHM_LOCKED) { + if(shp==NULL || shp->shm_flags & PRV_LOCKED) { next_id: swap_idx = 0; if (++swap_id > shm_ids.max_id) { diff --git a/kernel/exit.c b/kernel/exit.c index b6e3e633bbfd..6b7d65ad3079 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -432,6 +432,8 @@ NORET_TYPE void do_exit(long code) printk("Aiee, killing interrupt handler\n"); if (!tsk->pid) panic("Attempted to kill the idle task!"); + if (tsk->pid == 1) + panic("Attempted to kill init!"); tsk->flags |= PF_EXITING; del_timer_sync(&tsk->real_timer); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 0274d48fc9c6..5e10801fbd71 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -44,6 +44,7 @@ #include #include #include +#include #if defined(CONFIG_PROC_FS) #include @@ -80,6 +81,7 @@ EXPORT_SYMBOL(exec_usermodehelper); #ifdef CONFIG_MODULES EXPORT_SYMBOL(get_module_symbol); +EXPORT_SYMBOL(put_module_symbol); EXPORT_SYMBOL(try_inc_mod_count); #endif @@ -491,6 +493,7 @@ EXPORT_SYMBOL(fs_overflowgid); /* all busmice */ EXPORT_SYMBOL(fasync_helper); +EXPORT_SYMBOL(kill_fasync); #ifdef CONFIG_BLK_DEV_MD EXPORT_SYMBOL(disk_name); /* for md.c */ diff --git a/kernel/sched.c b/kernel/sched.c index c42402e9546c..f85cc4213b8b 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); @@ -920,16 +920,11 @@ asmlinkage long sys_sched_getscheduler(pid_t pid) if (pid < 0) goto out_nounlock; - read_lock(&tasklist_lock); - retval = -ESRCH; + read_lock(&tasklist_lock); p = find_process_by_pid(pid); - if (!p) - goto out_unlock; - - retval = p->policy; - -out_unlock: + if (p) + retval = p->policy & ~SCHED_YIELD; read_unlock(&tasklist_lock); out_nounlock: diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 00e62aa76c25..6155ebccfd91 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -440,7 +440,7 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig wake_up_interruptible(sk->sleep); if (sock && sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) - kill_fasync(sock->fasync_list, sig, + __kill_fasync(sock->fasync_list, sig, (sig == SIGURG) ? POLL_PRI : POLL_IN); } read_unlock(&sk->callback_lock); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e6dbaa296fe9..a14c984d7b50 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.82 2000/05/03 06:37:07 davem Exp $ + * Version: $Id: udp.c,v 1.83 2000/06/09 07:35:49 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -718,6 +718,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len, sin->sin_family = AF_INET; sin->sin_port = skb->h.uh->source; sin->sin_addr.s_addr = skb->nh.iph->saddr; + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } if (sk->protinfo.af_inet.cmsg_flags) ip_cmsg_recv(msg, skb); diff --git a/net/netsyms.c b/net/netsyms.c index 7eeab3412d61..c209ff9910f2 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -252,6 +252,8 @@ EXPORT_SYMBOL(ip_defrag); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); EXPORT_SYMBOL(devinet_ioctl); +EXPORT_SYMBOL(register_inetaddr_notifier); +EXPORT_SYMBOL(unregister_inetaddr_notifier); /* needed for ip_gre -cw */ EXPORT_SYMBOL(ip_statistics); @@ -522,7 +524,7 @@ EXPORT_SYMBOL(dev_mc_delete); EXPORT_SYMBOL(dev_mc_upload); EXPORT_SYMBOL(n_tty_ioctl); EXPORT_SYMBOL(tty_register_ldisc); -EXPORT_SYMBOL(kill_fasync); +EXPORT_SYMBOL(__kill_fasync); EXPORT_SYMBOL(if_port_text); diff --git a/net/socket.c b/net/socket.c index de849d3f4e25..b0978fb321db 100644 --- a/net/socket.c +++ b/net/socket.c @@ -747,10 +747,10 @@ int sock_wake_async(struct socket *sock, int how, int band) /* fall through */ case 0: call_kill: - kill_fasync(sock->fasync_list, SIGIO, band); + __kill_fasync(sock->fasync_list, SIGIO, band); break; case 3: - kill_fasync(sock->fasync_list, SIGURG, band); + __kill_fasync(sock->fasync_list, SIGURG, band); } return 0; } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 475cc4229c04..ce93ab71c27f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -34,7 +34,7 @@ #include -#define RPC_SLACK_SPACE 1024 /* total overkill */ +#define RPC_SLACK_SPACE 512 /* total overkill */ #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_CALL @@ -90,6 +90,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, if (!clnt) goto out_no_clnt; memset(clnt, 0, sizeof(*clnt)); + atomic_set(&clnt->cl_users, 0); clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; @@ -139,16 +140,16 @@ rpc_shutdown_client(struct rpc_clnt *clnt) { dprintk("RPC: shutting down %s client for %s\n", clnt->cl_protname, clnt->cl_server); - while (clnt->cl_users) { + while (atomic_read(&clnt->cl_users)) { #ifdef RPC_DEBUG dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n", - clnt->cl_protname, clnt->cl_users); + clnt->cl_protname, atomic_read(&clnt->cl_users)); #endif /* Don't let rpc_release_client destroy us */ clnt->cl_oneshot = 0; clnt->cl_dead = 0; rpc_killall_tasks(clnt); - sleep_on(&destroy_wait); + sleep_on_timeout(&destroy_wait, 1*HZ); } return rpc_destroy_client(clnt); } @@ -181,14 +182,10 @@ void rpc_release_client(struct rpc_clnt *clnt) { dprintk("RPC: rpc_release_client(%p, %d)\n", - clnt, clnt->cl_users); - if (clnt->cl_users) { - if (--(clnt->cl_users) > 0) - return; - } else - printk("rpc_release_client: %s client already free??\n", - clnt->cl_protname); + clnt, atomic_read(&clnt->cl_users)); + if (!atomic_dec_and_test(&clnt->cl_users)) + return; wake_up(&destroy_wait); if (clnt->cl_oneshot || clnt->cl_dead) rpc_destroy_client(clnt); @@ -445,7 +442,7 @@ call_allocate(struct rpc_task *task) * auth->au_wslack */ bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE; - if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL) + if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL) return; printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); @@ -479,11 +476,11 @@ call_encode(struct rpc_task *task) /* Default buffer setup */ bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE; - req->rq_svec[0].iov_base = task->tk_buffer; + req->rq_svec[0].iov_base = (void *)task->tk_buffer; req->rq_svec[0].iov_len = bufsiz; req->rq_slen = 0; req->rq_snr = 1; - req->rq_rvec[0].iov_base = task->tk_buffer; + req->rq_rvec[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz); req->rq_rvec[0].iov_len = bufsiz; req->rq_rlen = bufsiz; req->rq_rnr = 1; @@ -655,9 +652,11 @@ call_timeout(struct rpc_task *task) if (req) printk(KERN_NOTICE "%s: server %s not responding, still trying\n", clnt->cl_protname, clnt->cl_server); +#ifdef RPC_DEBUG else printk(KERN_NOTICE "%s: task %d can't get a request slot\n", clnt->cl_protname, task->tk_pid); +#endif } if (clnt->cl_autobind) clnt->cl_port = 0; @@ -773,12 +772,13 @@ call_header(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; struct rpc_xprt *xprt = clnt->cl_xprt; - u32 *p = task->tk_buffer; + struct rpc_rqst *req = task->tk_rqstp; + u32 *p = req->rq_svec[0].iov_base; /* FIXME: check buffer size? */ if (xprt->stream) *p++ = 0; /* fill in later */ - *p++ = task->tk_rqstp->rq_xid; /* XID */ + *p++ = req->rq_xid; /* XID */ *p++ = htonl(RPC_CALL); /* CALL */ *p++ = htonl(RPC_VERSION); /* RPC version */ *p++ = htonl(clnt->cl_prog); /* program number */ @@ -793,7 +793,7 @@ call_header(struct rpc_task *task) static u32 * call_verify(struct rpc_task *task) { - u32 *p = task->tk_buffer, n; + u32 *p = task->tk_rqstp->rq_rvec[0].iov_base, n; p += 1; /* skip XID */ @@ -859,7 +859,7 @@ garbage: task->tk_client->cl_stats->rpcgarbage++; if (task->tk_garb_retry) { task->tk_garb_retry--; - printk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid); + dprintk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid); task->tk_action = call_encode; return NULL; } diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index 026edcd708b8..45b775103036 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -31,6 +31,7 @@ static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int); static void pmap_getport_done(struct rpc_task *); extern struct rpc_program pmap_program; +spinlock_t pmap_lock = SPIN_LOCK_UNLOCKED; /* * Obtain the port for a given RPC service on a given host. This one can @@ -49,11 +50,14 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) task->tk_pid, clnt->cl_server, map->pm_prog, map->pm_vers, map->pm_prot); + spin_lock(&pmap_lock); if (clnt->cl_binding) { rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0); + spin_unlock(&pmap_lock); return; } clnt->cl_binding = 1; + spin_unlock(&pmap_lock); task->tk_status = -EACCES; /* why set this? returns -EIO below */ if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) @@ -74,8 +78,10 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) return; bailout: + spin_lock(&pmap_lock); clnt->cl_binding = 0; rpc_wake_up(&clnt->cl_bindwait); + spin_unlock(&pmap_lock); task->tk_status = -EIO; task->tk_action = NULL; } @@ -129,8 +135,10 @@ pmap_getport_done(struct rpc_task *task) clnt->cl_port = htons(clnt->cl_port); clnt->cl_xprt->addr.sin_port = clnt->cl_port; } + spin_lock(&pmap_lock); clnt->cl_binding = 0; rpc_wake_up(&clnt->cl_bindwait); + spin_unlock(&pmap_lock); } /* diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index b1e75b87f223..9dc2d1247b67 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -669,8 +669,10 @@ __rpc_schedule(void) if (task->tk_lock) { spin_unlock_bh(&rpc_queue_lock); printk(KERN_ERR "RPC: Locked task was scheduled !!!!\n"); +#ifdef RPC_DEBUG rpc_debug = ~0; rpc_show_tasks(); +#endif break; } __rpc_remove_wait_queue(task); @@ -778,7 +780,7 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, spin_unlock(&rpc_sched_lock); if (clnt) - clnt->cl_users++; + atomic_inc(&clnt->cl_users); #ifdef RPC_DEBUG task->tk_magic = 0xf00baa; @@ -823,8 +825,8 @@ cleanup: /* Check whether to release the client */ if (clnt) { printk("rpc_new_task: failed, users=%d, oneshot=%d\n", - clnt->cl_users, clnt->cl_oneshot); - clnt->cl_users++; /* pretend we were used ... */ + atomic_read(&clnt->cl_users), clnt->cl_oneshot); + atomic_inc(&clnt->cl_users); /* pretend we were used ... */ rpc_release_client(clnt); } goto out; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 385c0f30b677..051a643acf92 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -273,8 +273,8 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp) if (prog != progp->pg_prog) goto err_bad_prog; - versp = progp->pg_vers[vers]; - if (!versp || vers >= progp->pg_nvers) + if (vers >= progp->pg_nvers || + !(versp = progp->pg_vers[vers])) goto err_bad_vers; procp = versp->vs_proc + proc; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index f646531200a8..e0a13d725926 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -301,7 +301,7 @@ svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen) mm_segment_t oldfs; struct msghdr msg; struct socket *sock; - int len; + int len, alen; rqstp->rq_addrlen = sizeof(rqstp->rq_addr); sock = rqstp->rq_sock->sk_sock; @@ -319,6 +319,13 @@ svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen) len = sock_recvmsg(sock, &msg, buflen, MSG_DONTWAIT); set_fs(oldfs); + /* sock_recvmsg doesn't fill in the name/namelen, so we must.. + * possibly we should cache this in the svc_sock structure + * at accept time. FIXME + */ + alen = sizeof(rqstp->rq_addr); + sock->ops->getname(sock, (struct sockaddr *)&rqstp->rq_addr, &alen, 1); + dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n", rqstp->rq_sock, iov[0].iov_base, iov[0].iov_len, len); @@ -539,15 +546,15 @@ svc_tcp_accept(struct svc_sock *svsk) } /* Ideally, we would want to reject connections from unauthorized - * hosts here, but we have no generic client tables. For now, - * we just punt connects from unprivileged ports. */ + * hosts here, but when we get encription, the IP of the host won't + * tell us anything. For now just warn about unpriv connections. + */ if (ntohs(sin.sin_port) >= 1024) { if (net_ratelimit()) printk(KERN_WARNING - "%s: connect from unprivileged port: %u.%u.%u.%u:%d", + "%s: connect from unprivileged port: %u.%u.%u.%u:%d\n", serv->sv_name, NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); - goto failed; } dprintk("%s: connect from %u.%u.%u.%u:%04x\n", serv->sv_name, @@ -584,7 +591,7 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; struct svc_buf *bufp = &rqstp->rq_argbuf; - int len, ready; + int len, ready, used; dprintk("svc: tcp_recv %p data %d conn %d close %d\n", svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close); @@ -618,6 +625,11 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) svsk->sk_reclen = ntohl(svsk->sk_reclen); if (!(svsk->sk_reclen & 0x80000000)) { + /* FIXME: technically, a record can be fragmented, + * and non-terminal fragments will not have the top + * bit set in the fragment length header. + * But apparently no known nfs clients send fragmented + * records. */ /* FIXME: shutdown socket */ printk(KERN_NOTICE "RPC: bad TCP reclen %08lx", (unsigned long) svsk->sk_reclen); @@ -633,11 +645,21 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) goto error; if (len < svsk->sk_reclen) { + /* FIXME: if sk_reclen > window-size, then we will + * never be able to receive the record, so should + * shutdown the connection + */ dprintk("svc: incomplete TCP record (%d of %d)\n", len, svsk->sk_reclen); svc_sock_received(svsk, ready); return -EAGAIN; /* record not complete */ } + /* if we think there is only one more record to read, but + * it is bigger than we expect, then two records must have arrived + * together, so pretend we aren't using the record.. */ + if (len > svsk->sk_reclen && ready == 1) + used = 0; + else used = 1; /* Frob argbuf */ bufp->iov[0].iov_base += 4; @@ -664,7 +686,7 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) svsk->sk_reclen = 0; svsk->sk_tcplen = 0; - svc_sock_received(svsk, 1); + svc_sock_received(svsk, used); if (serv->sv_stats) serv->sv_stats->nettcpcnt++; @@ -692,6 +714,7 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp) { struct svc_buf *bufp = &rqstp->rq_resbuf; + int sent; /* Set up the first element of the reply iovec. * Any other iovecs that may be in use have been taken @@ -701,7 +724,17 @@ svc_tcp_sendto(struct svc_rqst *rqstp) bufp->iov[0].iov_len = bufp->len << 2; bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4)); - return svc_sendto(rqstp, bufp->iov, bufp->nriov); + sent = svc_sendto(rqstp, bufp->iov, bufp->nriov); + if (sent != bufp->len<<2) { + printk(KERN_NOTICE "rpc-srv/tcp: %s: sent only %d bytes of %d - should shutdown socket\n", + rqstp->rq_sock->sk_server->sv_name, + sent, bufp->len << 2); + /* FIXME: should shutdown the socket, or allocate more memort + * or wait and try again or something. Otherwise + * client will get confused + */ + } + return sent; } static int diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index b353aa37a4cc..7534288db33e 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -290,11 +290,12 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) { unsigned long cwnd = xprt->cwnd; + spin_lock_bh(&xprt_sock_lock); if (xprt->nocong) - return; + goto out; if (result >= 0) { if (xprt->cong < cwnd || time_before(jiffies, xprt->congtime)) - return; + goto out; /* The (cwnd >> 1) term makes sure * the result gets rounded properly. */ cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd; @@ -317,6 +318,8 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) } xprt->cwnd = cwnd; + out: + spin_unlock_bh(&xprt_sock_lock); } /* @@ -1294,15 +1297,18 @@ xprt_reserve(struct rpc_task *task) dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n", task->tk_pid, xprt->cong, xprt->cwnd); - if (!RPCXPRT_CONGESTED(xprt) && xprt->free) { - xprt_reserve_status(task); + spin_lock_bh(&xprt_sock_lock); + xprt_reserve_status(task); + if (task->tk_rqstp) { task->tk_timeout = 0; } else if (!task->tk_timeout) { task->tk_status = -ENOBUFS; } else { dprintk("RPC: xprt_reserve waiting on backlog\n"); - rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL); + task->tk_status = -EAGAIN; + rpc_sleep_on(&xprt->backlog, task, NULL, NULL); } + spin_unlock_bh(&xprt_sock_lock); dprintk("RPC: %4d xprt_reserve returns %d\n", task->tk_pid, task->tk_status); return task->tk_status; @@ -1323,25 +1329,20 @@ xprt_reserve_status(struct rpc_task *task) /* NOP */ } else if (task->tk_rqstp) { /* We've already been given a request slot: NOP */ - } else if (!RPCXPRT_CONGESTED(xprt) && xprt->free) { + } else { + if (RPCXPRT_CONGESTED(xprt) || !(req = xprt->free)) + goto out_nofree; /* OK: There's room for us. Grab a free slot and bump * congestion value */ - spin_lock(&xprt_lock); - if (!(req = xprt->free)) { - spin_unlock(&xprt_lock); - goto out_nofree; - } xprt->free = req->rq_next; req->rq_next = NULL; - spin_unlock(&xprt_lock); xprt->cong += RPC_CWNDSCALE; task->tk_rqstp = req; xprt_request_init(task, xprt); if (xprt->free) xprt_clear_backlog(xprt); - } else - goto out_nofree; + } return; @@ -1388,24 +1389,21 @@ xprt_release(struct rpc_task *task) dprintk("RPC: %4d release request %p\n", task->tk_pid, req); - spin_lock(&xprt_lock); - req->rq_next = xprt->free; - xprt->free = req; - /* remove slot from queue of pending */ if (task->tk_rpcwait) { printk("RPC: task of released request still queued!\n"); -#ifdef RPC_DEBUG - printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait)); -#endif rpc_remove_wait_queue(task); } - spin_unlock(&xprt_lock); + + spin_lock_bh(&xprt_sock_lock); + req->rq_next = xprt->free; + xprt->free = req; /* Decrease congestion value. */ xprt->cong -= RPC_CWNDSCALE; xprt_clear_backlog(xprt); + spin_unlock_bh(&xprt_sock_lock); } /* diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 0a2a58c34c1d..2f37380941a1 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.96 2000/05/12 23:51:26 davem Exp $ + * Version: $Id: af_unix.c,v 1.98 2000/06/19 06:24:59 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -662,21 +662,44 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (sunaddr->sun_path[0]) { lock_kernel(); err = 0; + /* + * Get the parent directory, calculate the hash for last + * component. + */ if (path_init(sunaddr->sun_path, LOOKUP_PARENT, &nd)) err = path_walk(sunaddr->sun_path, &nd); if (err) goto out_mknod_parent; + /* + * Yucky last component or no last component at all? + * (foo/., foo/.., /////) + */ err = -EEXIST; if (nd.last_type != LAST_NORM) goto out_mknod; + /* + * Lock the directory. + */ down(&nd.dentry->d_inode->i_sem); + /* + * Do the final lookup. + */ dentry = lookup_hash(&nd.last, nd.dentry); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_mknod_unlock; err = -ENOENT; + /* + * Special case - lookup gave negative, but... we had foo/bar/ + * From the vfs_mknod() POV we just have a negative dentry - + * all is fine. Let's be bastards - you had / on the end, you've + * been asking for (non-existent) directory. -ENOENT for you. + */ if (nd.last.name[nd.last.len] && !dentry->d_inode) goto out_mknod_dput; + /* + * All right, let's create it. + */ err = vfs_mknod(nd.dentry->d_inode, dentry, S_IFSOCK|sock->inode->i_mode, 0); if (err) @@ -772,12 +795,16 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, * If it was connected, reconnect. */ if (unix_peer(sk)) { - sock_put(unix_peer(sk)); - unix_peer(sk)=NULL; + struct sock *old_peer = unix_peer(sk); + unix_peer(sk)=other; + unix_state_wunlock(sk); + + sock_put(old_peer); + } else { + unix_peer(sk)=other; + unix_state_wunlock(sk); } - unix_peer(sk)=other; - unix_state_wunlock(sk); - return 0; + return 0; out_unlock: unix_state_wunlock(sk); @@ -1089,9 +1116,8 @@ static void unix_destruct_fds(struct sk_buff *skb) unix_detach_fds(&scm, skb); /* Alas, it calls VFS */ - lock_kernel(); + /* So fscking what? fput() had been SMP-safe since the last Summer */ scm_destroy(&scm); - unlock_kernel(); sock_wfree(skb); } @@ -1188,11 +1214,14 @@ restart: err = 0; unix_state_wlock(sk); if (unix_peer(sk) == other) { - sock_put(other); unix_peer(sk)=NULL; + unix_state_wunlock(sk); + + sock_put(other); err = -ECONNREFUSED; + } else { + unix_state_wunlock(sk); } - unix_state_wunlock(sk); other = NULL; if (err) @@ -1330,8 +1359,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, return sent; pipe_err_free: - kfree_skb(skb); unix_state_runlock(other); + kfree_skb(skb); pipe_err: if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL)) send_sig(SIGPIPE,current,0); -- 2.39.5