## Automatically generated incremental diff
## From:   linux-2.2.14-pre15
## To:     linux-2.2.14-pre16
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.2.14-pre15/CREDITS linux-2.2.14-pre16/CREDITS
--- linux-2.2.14-pre15/CREDITS	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/CREDITS	2003-08-15 15:09:55.000000000 -0700
@@ -1541,10 +1541,11 @@
 D: improved memory detection code.
 
 N: Vojtech Pavlik
-E: vojtech@ucw.cz
+E: vojtech@suse.cz
 D: Joystick driver
 D: arcnet-hardware readme
 D: Minor ARCnet hacking
+D: USB development
 S: Ucitelska 1576
 S: Prague 8
 S: 182 00 Czech Republic
diff -urN linux-2.2.14-pre15/Documentation/Configure.help linux-2.2.14-pre16/Documentation/Configure.help
--- linux-2.2.14-pre15/Documentation/Configure.help	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/Documentation/Configure.help	2003-08-15 15:09:55.000000000 -0700
@@ -9325,83 +9325,117 @@
 
 Joystick support
 CONFIG_JOYSTICK
-  If you have a joystick, you can say Y here to enable generic
-  joystick support. You will also need to say Y or M to at least one
-  of the hardware specific joystick drivers. This will make the
-  joysticks 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.
+  If you have a joystick, 6dof controller, gamepad, steering wheel,
+  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.
 
-  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.
-
-Classic PC analog joysticks and gamepads
+Classic PC analog
 CONFIG_JOY_ANALOG
-  Say Y here if you have an analog joystick or gamepad 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,
+  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
 
-FPGaming and MadCatz A3D controllers
-CONFIG_JOY_ASSASIN
-  Say Y here if you have an FPGaming Assasin 3D, MadCatz Panther or
-  MadCatz Panther XL. For more information on how to use the driver
-  please read Documentation/joystick.txt
+FPGaming and MadCatz A3D
+CONFIG_JOY_ASSASSIN
+  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
 
-Gravis GrIP joysticks and gamepads
+Gravis GrIP
 CONFIG_JOY_GRAVIS
-  Say Y here if you have a Gravis GamePad Pro, Gravis Xterminator or
-  Gravis Blackhawk Digital. For more information on how to use the
-  driver please read Documentation/joystick.txt
+  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
+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
+
+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
+
+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
+
+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
 
-PDPI Lightning 4 gamecards
+PDPI Lightning 4 card
 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
   use the driver please read Documentation/joystick.txt
 
-Logitech Digital joysticks and gamepads
-CONFIG_JOY_LOGITECH
-  Say Y here if you have a Logitech WingMan Extreme Digital,
-  Logitech ThunderPad Digital or Logitech CyberMan 2. For more
+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
+
+Magellan and Space Mouse
+CONFIG_JOY_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
+
+SpaceTec SpaceOrb 360 and SpaceBall Avenger
+CONFIG_JOY_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
   Documentation/joystick.txt
 
-Microsoft SideWinder, Genius Digital joysticks and gamepads
-CONFIG_JOY_SIDEWINDER
-  Say Y here if you have a Microsoft SideWinder 3d Pro, Microsoft
-  SideWinder Precision Pro, Microsoft SideWinder Force Feedback Pro,
-  Microsoft Sidewinder GamePad or Genius Flight2000 F-23 Digital. For
-  more information on how to use the driver please read
+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
 
-ThrustMaster DirectConnect joysticks and gamepads
-CONFIG_JOY_THRUSTMASTER
-  Say Y here if you have a ThrustMaster Millenium 3D Inceptor or a
-  ThrustMaster 3D Rage Pad. For more information on how to use the
-  driver please read Documentation/joystick.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
 
-NES, SNES, PSX, Multisystem joysticks and gamepads
+NES, SNES, N64, PSX, Multi
 CONFIG_JOY_CONSOLE
   Say Y here if you have a Nintendo Entertainment System gamepad,
-  Super Nintendo Entertainment System gamepad, Sony PlayStation
-  gamepad or a Multisystem -- Atari, Amiga, Commodore, Amstrad CPC
-  joystick. For more information on how to use the driver please read
+  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
 
-Sega, Multisystem joysticks and gamepads
+Sega, Multi
 CONFIG_JOY_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. For more information on how to use
-  the driver please read Documentation/joystick.txt and
-  Documentation/joystick-parport.txt
+  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
 
-TurboGraFX Multisystem joystick interface
+TurboGraFX 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,
diff -urN linux-2.2.14-pre15/Documentation/joystick-api.txt linux-2.2.14-pre16/Documentation/joystick-api.txt
--- linux-2.2.14-pre15/Documentation/joystick-api.txt	1998-10-21 08:43:33.000000000 -0700
+++ linux-2.2.14-pre16/Documentation/joystick-api.txt	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,7 @@
 		      Joystick API Documentation                -*-Text-*-
 
 		       Ragnar Hojland Espinosa
-		     <ragnar@lightside.ddns.org>
+		     <ragnar@lightside.dhis.org>
 
 			      7 Aug 1998
 
@@ -75,7 +75,7 @@
 	...and so on
 
 Hats vary from one joystick type to another. Some can be moved in 8
-directions, some only in 4, however, the driver always reports a hat as two
+directions, some only in 4, The driver, however, always reports a hat as two
 independent axis, even if the hardware doesn't allow independent movement.
 
 
@@ -85,7 +85,7 @@
 For an axis, ``value'' is a signed integer between -32767 and +32767
 representing the position of the joystick along that axis. If you
 don't read a 0 when the joystick is `dead', or if it doesn't span the
-full range, you should recalibrate (with, for example, jscal).
+full range, you should recalibrate it (with, for example, jscal).
 
 For a button, ``value'' for a press button event is 1 and for a release
 button event is 0.
@@ -93,16 +93,16 @@
 Though this
 
 	if (js_event.type == JS_EVENT_BUTTON) {
-	   buttons_state ^= (1 << js_event.number);
+		buttons_state ^= (1 << js_event.number);
 	}
 
 may work well if you handle JS_EVENT_INIT events separately,
 
 	if ((js_event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) {
-	   if (js_event.value)
-	      buttons_state |= (1 << js_event.number);
-	   else
-	      buttons_state &= ~(1 << js_event.number);
+		if (js_event.value)
+	        	buttons_state |= (1 << js_event.number);
+	   	else
+	      		buttons_state &= ~(1 << js_event.number);
 	}
 
 is much safer since it can't lose sync with the driver. As you would
@@ -112,6 +112,7 @@
 
 2.4 js_event.time
 ~~~~~~~~~~~~~~~~~
+
 The time an event was generated is stored in ``js_event.time''. It's a time
 in miliseconds since ... well, since sometime in the past.  This eases the
 task of detecting double clicks, figuring out if movement of axis and button
@@ -144,14 +145,14 @@
 For example,
 
 	while (1) {
-	   while (read (fd, &e, sizeof(struct js_event)) > 0) {
-	      process_event (e);
-	   }
-	   /* EAGAIN is returned when the queue is empty */
-	   if (errno != EAGAIN) {
-	      /* error */
-	   }
-	   /* do something interesting with processed events */
+		while (read (fd, &e, sizeof(struct js_event)) > 0) {
+	        	process_event (e);
+	   	}
+	   	/* EAGAIN is returned when the queue is empty */
+	   	if (errno != EAGAIN) {
+	      		/* error */
+	   	}
+	   	/* do something interesting with processed events */
 	}
 
 One reason for emptying the queue is that if it gets full you'll start
@@ -219,6 +220,7 @@
 	#ifdef JS_VERSION
 	#if JS_VERSION > 0xsomething
 
+
 4.2 JSIOCGNAME
 ~~~~~~~~~~~~~~
 
@@ -232,6 +234,7 @@
 		strncpy(name, "Unknown", sizeof(name));
 	printf("Name: %s\n", name);
 
+
 4.3 JSIOC[SG]CORR
 ~~~~~~~~~~~~~~~~~
 
@@ -266,10 +269,10 @@
 
 	struct JS_DATA_TYPE js;
 	while (1) {
-	   if (read (fd, &js, JS_RETURN) != JS_RETURN) {
-	      /* error */
-	   }
-	   usleep (1000);
+		if (read (fd, &js, JS_RETURN) != JS_RETURN) {
+	      		/* error */
+	   	}
+	   	usleep (1000);
 	}
 
 As you can figure out from the example, the read returns immediately,
@@ -296,9 +299,10 @@
 center, and 255 maximum value.
 
 The v0.8.0.2 driver also had an interface for 'digital joysticks', (now
-called Multisystem joystick in this driver), under /dev/djsX. This driver
+called Multisystem joysticks in this driver), under /dev/djsX. This driver
 doesn't try to be compatible with that interface.
 
+
 6. Final Notes
 ~~~~~~~~~~~~~~
 
diff -urN linux-2.2.14-pre15/Documentation/joystick-parport.txt linux-2.2.14-pre16/Documentation/joystick-parport.txt
--- linux-2.2.14-pre15/Documentation/joystick-parport.txt	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/Documentation/joystick-parport.txt	2003-08-15 15:09:55.000000000 -0700
@@ -1,6 +1,7 @@
 		  Linux Joystick parport drivers v1.2 BETA
-		  (c) 1998 Vojtech Pavlik <vojtech@ucw.cz>
+	       (c) 1998-1999 Vojtech Pavlik <vojtech@suse.cz>
 	       (c) 1998 Andree Borrmann <a.borrmann@tu-bs.de>
+			     Sponsored by SuSE
 ----------------------------------------------------------------------------
 
 0. Disclaimer
@@ -44,7 +45,8 @@
   The main problem with PC parallel ports is that they don't have +5V power
 source on any of their pins. So, if you want a reliable source of power
 for your pads, use either keyboard or joystick port, and make a pass-through
-cable.
+cable. You can also pull the power directly from the power supply (the red
+wire is +5V).
 
   If you want to use the parallel port only, you can take the power is from
 some data pin. For most gamepad and parport implementations only one pin is
@@ -298,27 +300,29 @@
 
   The PSX controller is supported by the joy-console.c.
 
-Pinout of the PSX controller:
+Pinout of the PSX controller (compatible with DirectPadPro):
 
   +---------+---------+---------+
 9 | o  o  o | o  o  o | o  o  o | 1               parallel
    \________|_________|________/                  port pins
     |  |      |  |  |   |
-    |  |      |  |  |   +-------->  Clock    ---  (1)
-    |  |      |  |  +------------>  Select   ---  (17)
-    |  |      |  +--------------->  Power    ---  (16)
+    |  |      |  |  |   +-------->  Clock    ---  (4)
+    |  |      |  |  +------------>  Select   ---  (3)
+    |  |      |  +--------------->  Power    ---  (5-9)
     |  |      +------------------>  Ground   ---  (18-25)
-    |  +------------------------->  Command  ---  (14)
+    |  +------------------------->  Command  ---  (2)
     +---------------------------->  Data     ---  (10,11,12,13,15) one only...
 
   You may have to add pull up/down resistors. Maybe your pad also won't like
 the 5V (PSX uses 3.7V).
 
-  Currently the driver supports only ONE psx pad and only one type of
-controller: The normal PSX controller. NEGCON support is planned for the
-next release. ANALOG controller may be too (I do not recommend to connect
-the "force feedback"/"rumble pack" version... it (may) use too much
-power...)
+  Currently the driver supports only one psx pad per parallel port, and these
+controllers:
+
+ * Standard PSX Pad
+ * NegCon PSX Pad
+ * Analog PSX Pad (red mode)
+ * Analog PSX Pad (green mode)
 
 2.4 Sega
 ~~~~~~~~
@@ -437,13 +441,15 @@
 	  4  | Multisystem 1-button joystick
 	  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_db9_3 as additional command line parameters for two
-more parallel ports.
+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)
@@ -463,6 +469,9 @@
     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
 ~~~~~~~~~~~~~
@@ -475,6 +484,10 @@
 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).
 
+  Caveat here: This driver only works on bidirectional parallel ports. If
+your parallel port is recent enough, you should have no trouble with this.
+Old parallel ports may not have this feature.
+
   'Type' is the type of joystick or pad attached:
 
 	Type | Joystick/Pad
@@ -484,9 +497,10 @@
 	  2  | Multisystem 2-button joystick
 	  3  | Genesis pad (3+1 buttons)
 	  5  | Genesis pad (5+1 buttons)
-	  6  | Genesis pad (6+1 buttons)
-	  7  | Saturn pad
+	  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) 
 
   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
@@ -501,6 +515,8 @@
     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
 ~~~~~~~~~~~~~~~~~~~~
@@ -519,6 +535,27 @@
 use js_tg_2 and js_tg_3 as additional command line parameters for two more
 interfaces.
 
-3.4 End
+3.4 PC parallel port pinout
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		  .----------------------------------------.
+   At the PC:     \ 13 12 11 10  9  8  7  6  5  4  3  2  1 /
+                   \  25 24 23 22 21 20 19 18 17 16 15 14 /
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+	  Pin | Name    | Description
+	~~~~~~|~~~~~~~~~|~~~~~~~~~~
+	    1 | /STROBE | Strobe
+	  2-9 | D0-D7   | Data Bit 0-7
+	   10 | /ACK    | Acknowledge
+	   11 | BUSY    | Busy
+	   12 | PE      | Paper End
+	   13 | SELIN   | Select In
+	   14 | /AUTOFD | Autofeed
+	   15 | /ERROR  | Error
+	   16 | /INIT   | Initialize
+	   17 | /SEL    | Select
+	18-25 | GND     | Signal Ground
+
+3.5 End
 ~~~~~~~
   That's all, folks! Have fun!
diff -urN linux-2.2.14-pre15/Documentation/joystick.txt linux-2.2.14-pre16/Documentation/joystick.txt
--- linux-2.2.14-pre15/Documentation/joystick.txt	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/Documentation/joystick.txt	2003-08-15 15:09:55.000000000 -0700
@@ -1,5 +1,6 @@
-		     Linux Joystick driver v1.2.13
-	       (c) 1996-1998 Vojtech Pavlik <vojtech@ucw.cz>
+		       Linux Joystick driver v1.2.15
+	       (c) 1996-1999 Vojtech Pavlik <vojtech@suse.cz>
+			     Sponsored by SuSE
 ----------------------------------------------------------------------------
 
 0. Disclaimer
@@ -19,7 +20,7 @@
 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
   Should you need to contact me, the author, you can do so either by e-mail
-- mail your message to <vojtech@ucw.cz>, or by paper mail: Vojtech Pavlik,
+- mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
 Ucitelska 1576, Prague 8, 182 00 Czech Republic
 
   For your convenience, the GNU General Public License version 2 is included
@@ -38,18 +39,18 @@
 
   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, Genius, Gravis, Logitech, MadCatz, Microsoft and
-ThrustMaster. Creative Labs protocol support is still to be done.
+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), Sony gamepads (PSX).
-Support for N64, Atari Jaguar, Atari 2600, NES FourScore, SNES MultiTap,
-PSX NegCon and others might be added later.
+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.
+Amiga Linux port.
 
   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
@@ -57,21 +58,16 @@
 
   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://artax.karlin.mff.cuni.cz/pub/linux/joystick/
-
-  The joystick driver is also included in the Linux 2.1 kernels:
-
-	ftp://linux.kernel.org/pub/linux/kernel/v2.1/
+	ftp://ftp.gts.cz/pub/linux/joystick/
 
   And a homepage of the driver is at:
 
+	http://www.suse.cz/development/joystick/
 	http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/
-	http://artax.karlin.mff.cuni.cz/~vojtech/joystick/
-
-  A mirror of the homepage is at:
-
 	http://www.trylinux.com/projects/joystick/
+	http://www.linuxgames.com/joystick/
 
   There is also a mailing list for the driver at:
 
@@ -92,7 +88,21 @@
   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). Then you compile it
+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
 
@@ -163,6 +173,14 @@
   where 'something' is the type of your joystick. See below for more precise
 explanation.
 
+  Alternately you can add the lines
+
+	alias char-major-15 joy-something
+	options joy-something js_xx=x,x,x,x,...
+
+  to the /etc/conf.modules file, so that the joystick module will be loaded
+automatically when the /dev/js devices are accessed.
+
 2.5 Verifying that it works
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   For testing the joystick driver functionality, there is the jstest
@@ -197,14 +215,14 @@
 calibration using the jstest command, and if you do, you then can save the
 correction coefficients into a file
 
-	jscal -s /dev/js0 > /etc/joystick.cal
+	jscal -p /dev/js0 > /etc/joystick.cal
 
   And add a line to your rc script executing that file
 
 	source /etc/joystick.cal
 
   This way, after the next reboot your joystick will remain calibrated. You
-can also add the jscal -s line to your shutdown script.
+can also add the jscal -p line to your shutdown script.
 
 
 3. HW specific driver information
@@ -222,7 +240,7 @@
 
 * 2-axis, 4-button joystick
 * 3-axis, 4-button joystick
-* Two 2-axis, 2-button joysticks on an Y-cable
+* 4-axis, 4-button joystick
 
   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
@@ -261,6 +279,8 @@
 
 	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
@@ -268,9 +288,11 @@
 	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
@@ -280,8 +302,7 @@
 	Joystick				| 'm' value
 	----------------------------------------------------
 	Gravis GamePad Pro - analog mode	| 0x00f3
-	Genius Flight2000 F-23 - CHF mode	| 0x02ff
-	Genius Flight2000 F-23 - FCS mode	| 0x08f7
+	Genius Flight2000 F-23			| 0x02ff
 	Microsoft SideWinder 3D Pro - CHF mode	| 0x02ff
 	Microsoft SideWinder 3D Pro - FCS mode	| 0x08f7
 
@@ -302,70 +323,85 @@
   And it would do the same as the above explained command line. Use
 whichever way you like best.
 
-3.2 Microsoft SideWinder and Genius Digital joysticks
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  SideWinder and Genius Digital joysticks are supported by the
+3.2 Microsoft SideWinder joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  Microsoft 'Digital Overdrive' protocol is supported by the
 joy-sidewinder.c module. All currently supported joysticks:
 
-* SideWinder 3d Pro
-* SideWinder Precision Pro 
+* SideWinder 3D Pro
 * SideWinder Force Feedback Pro
-* SideWinder Game Pad (up to four, chained together)
-* Genius Flight2000 Digital F-23
+* SideWinder Force Feedback Wheel
+* SideWinder FreeStyle Pro
+* SideWinder GamePad (up to four, chained together)
+* SideWinder Precision Pro 
 
   are autodetected, and thus no module parameters are needed.
 
+  There is one caveat with the 3D Pro. There are 9 buttons reported,
+although the joystick has only 8. The 9th button is the mode switch on the
+rear side of the joystick. However, moving it, you'll reset the joystick,
+and make it unresponsive for about a one third of a second. Furthermore, the
+joystick will also re-center itself, taking the position it was in during
+this time as a new center position. Use it if you want, but think first.
+
   The SideWinder Standard is not a digital joystick, and thus is supported
-by the analog driver described above. SideWinder FreeStyle Pro and
-SideWinder Force Feedback Wheel are not supported yet.
+by the analog driver described above. 
 
-3.3 Logitech Digital joysticks
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  Logitech Digital joysticks are supported by the joy-logitech.c module. It
-currently supports these devices:
+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 Wingman Extreme Digital
 * Logitech CyberMan 2
 * Logitech ThunderPad Digital
-
-  All three are autodetected, and thus no parameters to the module are
-needed.
-
-  Logitech Wingman is not a digital joystick and is handled by the analog
-driver described above. Logitech Wingman Warrior communicates through a
-serial port and is not supported yet. Logitech Wingman Force, Wingman
-Formula, Wingman Formula Force, Wingman Gamepad, Wingman Interceptor are USB
-joysticks, with optional serial port connection, and are not supported yet.
+* Logitech WingMan Extreme Digital
+* Logitech WingMan Formula
+* Logitech WingMan Interceptor
+* Logitech WingMan GamePad
+* Logitech WingMan GamePad USB
+* Logitech WingMan GamePad Extreme
+* Logitech WingMan Extreme Digital 3D
+
+  ADI devices are autodetected, and the driver supports up to two (any
+combination of) devices on a single gameport, using an Y-cable or chained
+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.
 
 3.4 Gravis GrIP
 ~~~~~~~~~~~~~~~
-  Gravis GrIP gamepads are supported by the joy-gravis.c module. It
+  Gravis GrIP protocol is supported by the joy-gravis.c module. It
 currently supports:
 
 * Gravis GamePad Pro
 * Gravis Xterminator
-* Gravis Blackhawk Digital
+* Gravis BlackHawk Digital
 
-  All these pads 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 support is in the works. Gravis Xcalibur, ArcadeXtreme,
-GamePad Pro/M are joysticks/pads that probably never reached mass
-production. Gravis Stinger is a serial device and hopefully will be
-supported in the future.
+  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.
 
 3.5 FPGaming A3D and MadCatz A3D
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  The Assasin 3D protocol created by FPGaming, is used both by FPGaming
+  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-assasin.c module. It currently supports:
+joy-assassin.c module. It currently supports:
 
-* FPGaming Assasin 3D
+* FPGaming Assassin 3D
 * MadCatz Panther
 * MadCatz Panther XL
 
-  All these devices are autodetected. Because the Assasin 3D and the Panther
+  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.
@@ -376,20 +412,33 @@
 3.6 ThrustMaster DirectConnect (BSP)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   The TM DirectConnect (BSP) protocol is supported by the joy-thrustmaster.c
-module. It currently supports:
+module. This includes, but is not limited to:
 
 * ThrustMaster Millenium 3D Inceptor
 * ThrustMaster 3D Rage Pad
+* ThrustMaster Fusion Digital Game Pad
 
-  Both these drvices are autodetected, and thus no parameters to the module
+  Devices not directly supported, but hopefully working are:
+
+* ThrustMaster FragMaster
+* ThrustMaster Attack Throttle
+
+  If you have one of these, contact me.
+
+  BSP devices are autodetected, and thus no parameters to the module
 are needed.
 
-  The Millenium and Rage Pad should work fine now. TM WCS III won't work,
-because important parts of code for that are missing. I'm not sure if it was
-ever mass produced.
+3.7 Creative Labs Blaster
+~~~~~~~~~~~~~~~~~~~~~~~~~
+  The Blaster protocol is supported by the joy-creative.c module. It
+currently supports only the:
+
+* Creative Blaster GamePad Cobra
+
+  Up to two of these can be used on a single gameport, using an Y-cable.
 
-3.7 PDPI Lightning 4 gamecards
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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
@@ -420,8 +469,49 @@
   See the description of analog joystick driver for explanations of m0 and
 n0 values.
 
-3.8 Amiga
-~~~~~~~~~
+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.
+
+  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:
+
+	js_pci=0,0,0x207,0,1,1,0,0,3,0,0x33,0xcc
+
+3.10 Amiga
+~~~~~~~~~~
   Amiga joysticks, connected to an Amiga, are supported by the joy-amiga.c
 driver. Since they can't be autodetected, the driver has a command line.
 
@@ -438,10 +528,75 @@
   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.9 Game console and 8-bit pads and joysticks
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3.11 Game console and 8-bit pads and joysticks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 See joystick-parport.txt for more info.
 
+3.12 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:
+
+* SpaceTec SpaceBall Avenger
+* SpaceTec SpaceOrb 360
+
+Devices currently supported by joy-spaceball 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:
+
+	jsattach --spaceorb /dev/ttySx &
+or
+	jsattach --sball4 /dev/ttySx &
+
+where /dev/ttySx 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
+side of the orb, although reported as an ordinary button, causes internal
+recentering of the spaceorb, moving the zero point to the position in which
+the ball is at the moment of pressing the button. So, think first before
+you bind it to some other function.
+
+SpaceTec SpaceBall 2003 FLX and 3003 FLX are not supported yet. 
+
+3.13 Logitech SWIFT devices
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The SWIFT serial protocol is supported by the joy-warrior module. It
+currently supports only the:
+
+* Logitech WingMan Warrior
+
+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 &
+
+ttySx is the serial port your Warrior is attached to.
+
+3.14 Magellan / Space Mouse
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  The Magellan (or Space Mouse), manufactured by LogiCad3d (formerly Space
+Systems), for many other companies (Logitech, HP, ...) is supported by the
+joy-magellan module. It currently supports only the:
+
+* Magellan 3D
+* Space Mouse
+
+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 &
+
+command. After that the Magellan will be detected, initialized, will beep,
+and the /dev/jsX device should become useable.
+
 4. Troubleshooting
 ~~~~~~~~~~~~~~~~~~
   There is quite a high probability that you run into some problems. For
@@ -452,9 +607,10 @@
 	jstest --normal /dev/js0
 	jstest --old    /dev/js0
 
-  If your trouble stems from the fact the drivers can't detect the attached
-joystick, and/or you decide you need my help (which I will gladly provide),
-please use the joydump utility first. It's created just by typing
+  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
 
@@ -465,64 +621,73 @@
 
   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. Please send me the results along
-with your problem report.
+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.
 
   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.
-
-  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 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: 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'.
-
-  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
-PC?
-  A: Yes, it is possible, but it'll burn your serial port or the pad. It
-won't work, of course.
-
-  Q: My joystick doesnt work with Quake / Quake 2. What's the cause?
-  A: Quake / Quake 2 don't support joystick. Use joy2key to simulate keypresses
-for them.
+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'.
+
+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
+   PC?
+A: Yes, it is possible, but it'll burn your serial port or the pad. It
+   won't work, of course.
+
+Q: My joystick doesn't work with Quake / Quake 2. What's the cause?
+A: Quake / Quake 2 don't support joystick. Use joy2key to simulate keypresses
+   for them.
 
 6. Programming Interface
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -565,23 +730,31 @@
 	Brian Gerst		<bgerst@quark.vpplus.com>
 	Andree Borrmann		<a.borrmann@tu-bs.de>
 	Martin Giguere		<zefrench@hotmail.com>
+	David Thompson		<dcthomp@mail.utexas.edu>
+	Justin Wake		<spectre@telefragged.com>
+	Benoit Triquet		<benoit@adsl-216-100-248-201.dsl.pacbell.net>
+	John Dahlstrom		<jodaman@bradley.edu>
+	Dan Gohman		<gohmandj@mrs.umn.edu>
+	Joe Krahn		<krahn@niehs.nih.gov>
+	David Kuder		<alphagod@penguinpowered.com>
+	Raymond Ingles		<sorceror@tir.com>
 
   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 compatible with
-Microsoft SideWinder 3d Pro.
+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, and to Trystan A Larey-williams <axe@mail.wsu.edu>, who wrote an
-attempt of a driver for them.
+protocol specifications, and to Trystan A Larey-Williams <axe@mail.wsu.edu>,
+who wrote an attempt of a driver for them.
 
   Thanks to Creative Labs Europe, and Ifor Powell <ipowell@cle.creaf.com>,
-who provided me with docs for their first generation gamepad.
+who provided me with docs for their first generation Blaster GamePad.
 
   Special thanks go to FP-Gaming, Inc. and James C Barnes <james@fpgaming.com>,
-who provided me with help and detailed information about the Assasin 3D
+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.
 
@@ -590,10 +763,20 @@
 code for their L4 gamecard, and sending me the card to test my driver with
 it.
 
+  Thanks to LogiCad3D for their support, for having the specifications
+online and for the nice music on their telephone.
+
+  Special thanks to Logitech, Jerry de Raad <Jerry_de_Raad@logitech.com>,
+Thomas Burgel <Thomas_Burgel@logitech.com>, Avinash Shinde
+<Avinash_Shinde@logitech.com> for providing me with a lot of documentation
+for their devices, and also for a big box, containing a CyberMan2, Wingman
+Extreme, Magellan, Wingman Warrior, two MouseMan mice, and a NewTouch
+keyboard.
+
   Thanks to everyone else who helped me develop this package of drivers!
 
-  No thanks to Microsoft, Logitech, and Gravis, who don't release a word
-about their hardware .... :(
+  No thanks to Microsoft and Gravis, who don't release a word about their
+hardware .... :(
 
 8. ChangeLog
 ~~~~~~~~~~~~
@@ -602,50 +785,3 @@
 9. To do
 ~~~~~~~~
   See the TODO file for the list of things planned.
-
-10. Current driver status
-~~~~~~~~~~~~~~~~~~~~~~~~~
-  OK means tested and not touched till this driver revision, unknown means
-that the driver was changed since last test, broken means doesn't work,
-incomplete means can't work, because vital parts of support are missing.
-
-joystick.c:		2.1.x kernel interface	- OK
-joy-amiga.c:		Multi1 stick		- unknown
-joy-analog.c:		standard joysticks	- OK
-			FCS hats		- OK
-			CHF hats & buttons	- OK
-			XY buttons		- OK
-			UV buttons		- OK
-joy-assasin.c:		MadCatz Panther XL	- OK
-			MadCatz PXL rudder	- OK
-			MadCatz Panther		- OK
-			FPG Assasin 3D		- OK
-joy-console.c:		NES pad			- OK
-			SNES pad		- OK
-			Multi1 stick		- OK
-			Multi2 stick		- OK
-			PSX			- SW OK, HW unreliable
-joy-db9.c:		Multi1 stick		- OK
-			Multi2 stick		- OK
-			Sega Genesis pad	- OK
-			Sega Genesis 5 pad	- OK
-			Sega Genesis 6 pad	- OK
-			Sega Saturn pad		- unknown
-joy-gravis.c		Gravis GamePad Pro	- OK
-			Gravis Xterminator	- OK
-			Gravis Blackhawk Digital- OK
-joy-lightning.c		PDPI Lightning 4	- OK
-joy-logitech.c		WingMan Extreme Digital	- OK
-			CyberMan 2		- OK
-			Thunder Pad Digital	- unknown
-joy-sidewinder.c	SW 3D Pro		- OK
-			Genius F-23		- OK
-			SW GP			- OK
-			SW PP			- OK
-			SW FFP			- OK
-joy-thrustmaster.c	Millenium 3D Inceptor	- OK
-			3D-Rage Pad		- OK
-joy-turbografx.c	Multi1 stick		- OK
-
-Please help me and send me success / failure reports for the drivers,
-I need to know what works, and what needs to be debugged. Thank you.
diff -urN linux-2.2.14-pre15/drivers/block/ide.c linux-2.2.14-pre16/drivers/block/ide.c
--- linux-2.2.14-pre15/drivers/block/ide.c	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/drivers/block/ide.c	2003-08-15 15:09:55.000000000 -0700
@@ -2711,47 +2711,16 @@
 	if (!drive)
 		return 0;
 
-	if (drive->forced_geom) {
-		/*
-		 * Update the current 3D drive values.
-		 */
-		drive->id->cur_cyls	= drive->bios_cyl;
-		drive->id->cur_heads	= drive->bios_head;
-		drive->id->cur_sectors	= drive->bios_sect;
+	if (drive->forced_geom)
 		return 0;
-	}
 
-	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) {
-		/*
-		 * Update the current 3D drive values.
-		 */
-		drive->id->cur_cyls	= drive->bios_cyl;
-		drive->id->cur_heads	= drive->bios_head;
-		drive->id->cur_sectors	= drive->bios_sect;
+	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
 		return 0;		/* we already have a translation */
-	}
 
 	printk("%s ", msg);
 
-	if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) {
-		/*
-		 * Update the current 3D drive values.
-		 */
-		drive->id->cur_cyls	= drive->bios_cyl;
-		drive->id->cur_heads	= drive->bios_head;
-		drive->id->cur_sectors	= drive->bios_sect;
+	if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63))
 		return 0;		/* small disk: no translation needed */
-	}
-
-	if (drive->id) {
-		drive->cyl  = drive->id->cyls;
-		drive->head = drive->id->heads;
-		drive->sect = drive->id->sectors;
-	}
-	drive->bios_cyl  = drive->cyl;
-	drive->bios_head = drive->head;
-	drive->bios_sect = drive->sect;
-	drive->special.b.set_geometry = 1;
 
 	tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
 	drive->bios_sect = 63;
@@ -2779,12 +2748,7 @@
 	}
 	drive->part[0].nr_sects = current_capacity(drive);
 	printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
-	/*
-	 * Update the current 3D drive values.
-	 */
-	drive->id->cur_cyls    = drive->bios_cyl;
-	drive->id->cur_heads   = drive->bios_head;
-	drive->id->cur_sectors = drive->bios_sect;
+
 	return 1;
 }
 
diff -urN linux-2.2.14-pre15/drivers/block/ide-disk.c linux-2.2.14-pre16/drivers/block/ide-disk.c
--- linux-2.2.14-pre15/drivers/block/ide-disk.c	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/drivers/block/ide-disk.c	2003-08-15 15:09:55.000000000 -0700
@@ -128,10 +128,20 @@
 	int i;
 	unsigned int msect, nsect;
 	struct request *rq;
-
+#if 0
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
 		return ide_error(drive, "read_intr", stat);
 	}
+#else	/* new way for dealing with premature shared PCI interrupts */
+	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+		if (stat & (ERR_STAT|DRQ_STAT)) {
+			return ide_error(drive, "read_intr", stat);
+		}
+		/* no data yet, so wait for another interrupt */
+		ide_set_handler(drive, &read_intr, WAIT_CMD);
+		return ide_started;
+	}
+#endif
 	msect = drive->mult_count;
 	
 read_next:
@@ -152,7 +162,7 @@
 	rq->buffer += nsect<<9;
 	rq->errors = 0;
 	i = (rq->nr_sectors -= nsect);
-	if ((rq->current_nr_sectors -= nsect) <= 0)
+	if (((long)(rq->current_nr_sectors -= nsect)) <= 0)
 		ide_end_request(1, HWGROUP(drive));
 	if (i > 0) {
 		if (msect)
@@ -187,7 +197,7 @@
 			rq->errors = 0;
 			i = --rq->nr_sectors;
 			--rq->current_nr_sectors;
-			if (rq->current_nr_sectors <= 0)
+			if (((long)rq->current_nr_sectors) <= 0)
 				ide_end_request(1, hwgroup);
 			if (i > 0) {
 				idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
@@ -196,7 +206,7 @@
 			}
 			return ide_stopped;
 		}
-		printk("%s: write_intr error2: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat);
+		return ide_stopped;     /* the original code did this here (?) */ 
 	}
 	return ide_error(drive, "write_intr", stat);
 }
@@ -242,7 +252,7 @@
 		 *	Completed ?
 		 */
 		 
-		if ((rq->nr_sectors -= nsect) <= 0)
+		if (((long)(rq->nr_sectors -= nsect)) <= 0)
 		{
 			spin_unlock_irqrestore(&io_request_lock, flags);
 			break;
@@ -266,7 +276,7 @@
 				 *	deep crap
 				 */
 				spin_unlock_irqrestore(&io_request_lock, flags);
-				printk("%s: buffer list corrupted (%d, %d, %d)\n", drive->name,
+				printk("%s: buffer list corrupted (%ld, %ld, %d)\n", drive->name,
 					rq->current_nr_sectors, rq->nr_sectors, nsect);
 				ide_end_request(0, hwgroup);
 				return 1;
@@ -316,6 +326,7 @@
 				return ide_stopped;
 			}
 		}
+		return ide_stopped;     /* the original code did this here (?) */ 
 	}
 	return ide_error(drive, "multwrite_intr", stat);
 }
diff -urN linux-2.2.14-pre15/drivers/block/ide-probe.c linux-2.2.14-pre16/drivers/block/ide-probe.c
--- linux-2.2.14-pre15/drivers/block/ide-probe.c	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/drivers/block/ide-probe.c	2003-08-15 15:09:55.000000000 -0700
@@ -386,12 +386,20 @@
 	/* Extract drive geometry from CMOS+BIOS if not already setup */
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
-		if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
-			drive->cyl   = drive->bios_cyl  = *(unsigned short *)BIOS;
-			drive->head  = drive->bios_head = *(BIOS+2);
-			drive->sect  = drive->bios_sect = *(BIOS+14);
-			drive->ctl   = *(BIOS+8);
-			drive->present = 1;
+		if ((cmos_disks & (0xf0 >> (unit*4)))
+		    && !drive->present && !drive->nobios) {
+			unsigned short cyl = *(unsigned short *)BIOS;
+			unsigned char head = *(BIOS+2);
+			unsigned char sect = *(BIOS+14);
+			if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
+				drive->cyl   = drive->bios_cyl  = cyl;
+				drive->head  = drive->bios_head = head;
+				drive->sect  = drive->bios_sect = sect;
+				drive->ctl   = *(BIOS+8);
+				drive->present = 1;
+			} else
+				printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n",
+				       cyl, head, sect);
 		}
 		BIOS += 16;
 	}
diff -urN linux-2.2.14-pre15/drivers/char/Config.in linux-2.2.14-pre16/drivers/char/Config.in
--- linux-2.2.14-pre15/drivers/char/Config.in	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/drivers/char/Config.in	2003-08-15 15:09:55.000000000 -0700
@@ -74,6 +74,8 @@
 	endmenu
 fi
 
+source drivers/char/joystick/Config.in
+
 tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE
 if [ "$CONFIG_QIC02_TAPE" != "n" ]; then
   bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
@@ -181,15 +183,6 @@
 
 endmenu
 
-mainmenu_option next_comment
-comment 'Joystick support'
-
-tristate 'Joystick support' CONFIG_JOYSTICK
-if [ "$CONFIG_JOYSTICK" != "n" ]; then
-  source drivers/char/joystick/Config.in
-fi
-endmenu
-
 tristate 'Double Talk PC internal speech card support' CONFIG_DTLK
 
 mainmenu_option next_comment
diff -urN linux-2.2.14-pre15/drivers/char/joystick/Config.in linux-2.2.14-pre16/drivers/char/joystick/Config.in
--- linux-2.2.14-pre15/drivers/char/joystick/Config.in	1998-12-16 13:38:18.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/Config.in	2003-08-15 15:09:55.000000000 -0700
@@ -1,19 +1,34 @@
 #
-# Joystick lowlevel driver configuration
+# Joystick driver
 #
 
-dep_tristate '   Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK
-dep_tristate '   FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK
-dep_tristate '   Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK
-dep_tristate '   Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK
-dep_tristate '   Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK
-dep_tristate '   ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK
-dep_tristate '   PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK
-if [ "$CONFIG_PARPORT" != "n" ]; then
-  dep_tristate '   NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT
-  dep_tristate '   Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT
-  dep_tristate '   TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT
-fi  
-if [ "$CONFIG_AMIGA" = "y" ]; then
-  dep_tristate '   Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK
+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
+   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  
+   if [ "$CONFIG_AMIGA" = "y" ]; then
+      dep_tristate '  Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK
+   fi
 fi
+
+endmenu
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-amiga.c linux-2.2.14-pre16/drivers/char/joystick/joy-amiga.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-amiga.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-amiga.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-amiga.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -25,7 +27,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -36,13 +38,14 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <asm/amigahw.h>
+#include <linux/init.h>
 
 static struct js_port* js_am_port __initdata = NULL;
 
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_am, "1-2i");
 
-static int js_am[]={0,0};
+static int __initdata js_am[] = { 0, 0 };
 
 /*
  * js_am_read() reads and Amiga joystick data.
@@ -69,7 +72,7 @@
 
 	axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
 	data = ~(data ^ (data << 1));
-	axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1);
+	axes[0][1] = ((data >> 1) & 1) - ((data >> 9) & 1);
 
 	return 0;
 }
@@ -114,11 +117,14 @@
 }
 
 #ifndef MODULE
-void __init js_am_setup(char *str, int *ints)
+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
@@ -148,8 +154,8 @@
 #ifdef MODULE
 void cleanup_module(void)
 {
-	while (js_am_port != NULL) {
-		if (js_am_port->devs[0] != NULL)
+	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);
 	}
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-analog.c linux-2.2.14-pre16/drivers/char/joystick/joy-analog.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-analog.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-analog.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-analog.c  Version 1.2
  *
- *  Copyright (c) 1996-1998 Vojtech Pavlik
+ *  Copyright (c) 1996-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -25,74 +27,129 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
 #include <asm/io.h>
+#include <asm/param.h>
+#include <asm/system.h>
+#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/joystick.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/init.h>
 
-#define JS_AN_MAX_TIME 3000
+#define JS_AN_MAX_TIME		3000	/* 3 ms */
+#define JS_AN_LOOP_TIME		2000	/* 2 t */
 
 static int js_an_port_list[] __initdata = {0x201, 0};
 static struct js_port* js_an_port __initdata = NULL;
 
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_an, "2-24i");
 
-static int 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,-1,0,0};
+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_an_info *info = xinfo;
+	struct js_ax_info *info = xinfo;
+	struct js_an_info *an = &info->an;
+	int io = info->io;
+	unsigned long flags;
 	unsigned char buf[4];
-	int time[4];
-	unsigned char u, v, a;
-	unsigned int t, t1;
+	unsigned int time[4];
+	unsigned char u, v, w;
+	unsigned int p, q, r, s, t;
 	int i, j;
-	int timeout;
-	int io = info->io;
-
-	timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10;
 
-	info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4;
+	an->buttons = ~inb(io) >> 4;
 
 	i = 0;
-	u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS)
-	      | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4);
-
+	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);
-	t = js_get_time_a();
+	GET_TIME(r);
+	__restore_flags(flags);
+	t = r;
+	v = w;
 	do {
-		v = inb(io) & a;
-		t1 = js_get_time_a();
-		if (u ^ v) {
-			time[i] = js_delta_a(t1,t);
+		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;
-			u = v;
 			i++;
 		}
-	} while (v && js_delta_a(t1,t) < timeout);
+	} while (v && (i < 4) && (DELTA(t,r) < q));
+
+	v <<= 4;
 
-	for (--i; i >= 0; i--)
+	for (--i; i >= 0; i--) {
+		v |= buf[i];
 		for (j = 0; j < 4; j++)
-			if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a;
+			if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed;
+	}
 
-	js_an_decode(info, axes, buttons);
+	js_an_decode(an, axes, buttons);
 
-	return 0;
+	return -(v != w);
 }
 
 /*
@@ -116,12 +173,53 @@
 }
 
 /*
+ * 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_an_info info;
+	struct js_ax_info info, *ax;
 	int i, numdev;
 	unsigned char u;
 
@@ -129,7 +227,6 @@
 
 	if (check_region(io, 1)) return port;
 
-	if (((u = inb(io)) & 3) == 3) return port;
 	outb(0xff,io);
 	u = inb(io);
 	udelay(JS_AN_MAX_TIME);
@@ -138,31 +235,41 @@
 	if (!u) return port;
 	if (u & 0xf0) return port;
 
-	if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0)
+	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_an_info), js_an_read);
+	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\n",
-			js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info),
-				js_an_name(i, &info), js_an_open, js_an_close),
-			js_an_name(i, &info), info.io);
+		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), js_an_open, js_an_close),
+			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(port->info, port->axes, port->buttons);
-	js_an_init_corr(port->info, port->axes, port->corr, 8);
+	js_an_read(ax, port->axes, port->buttons);
+	js_an_init_corr(&ax->an, port->axes, port->corr, 8);
 
 	return port;
 }
 
 #ifndef MODULE
-void __init js_an_setup(char *str, int *ints)
+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
@@ -193,11 +300,11 @@
 void cleanup_module(void)
 {
 	int i;
-	struct js_an_info *info;
+	struct js_ax_info *info;
 
-	while (js_an_port != NULL) {
+	while (js_an_port) {
 		for (i = 0; i < js_an_port->ndevs; i++)
-			if (js_an_port->devs[i] != NULL)
+			if (js_an_port->devs[i])
 				js_unregister_device(js_an_port->devs[i]);
 		info = js_an_port->info;
 		release_region(info->io, 1);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-analog.h linux-2.2.14-pre16/drivers/char/joystick/joy-analog.h
--- linux-2.2.14-pre15/drivers/char/joystick/joy-analog.h	1998-10-08 17:25:16.000000000 -0700
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-analog.h	2003-08-15 15:09:55.000000000 -0700
@@ -1,13 +1,15 @@
 /*
  *  joy-analog.h  Version 1.2
  *
- *  Copyright (c) 1996-1998 Vojtech Pavlik
+ *  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-assasin.c, and joy-lightning.c
+ * is: joy-analog.c, joy-assassin.c, and joy-lightning.c
  */
 
 /*
@@ -26,10 +28,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
+#include <linux/bitops.h>
+
 #define JS_AN_AXES_STD		0x0f
 #define JS_AN_BUTTONS_STD	0xf0
 
@@ -53,7 +57,6 @@
 } js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}};
 
 struct js_an_info {
-	int io;
 	unsigned char mask[2];
 	unsigned int extensions;
 	int axes[4];
@@ -75,7 +78,7 @@
 	if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0;
 
 	if (info->extensions & JS_AN_ANY_CHF) {
-		switch (info->buttons) {
+		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;
@@ -134,19 +137,6 @@
 	}
 }
 
-/*
- * js_an_count_bits() counts set bits in a byte.
- */
-
-static inline int js_an_count_bits(unsigned long c)
-{
-	int i = 0;
-	while (c) {
-		i += c & 1;
-		c >>= 1;
-	}
-	return i;
-}
 
 /*
  * js_an_init_corr() initializes the correction values for
@@ -158,7 +148,7 @@
 	int i, j, t;
 
 	for (i = 0; i < 2; i++)
-	for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) {
+	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)) {
@@ -175,9 +165,9 @@
 		corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1);
 	}
 
-	i = js_an_count_bits(info->mask[0] & 0xf);
+	i = hweight8(info->mask[0] & 0xf);
 
-	for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) {
+	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;
@@ -204,6 +194,7 @@
 		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);
@@ -212,7 +203,7 @@
 		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_U);
+			info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_V);
 		}
 		if (info->extensions & JS_AN_ANY_CHF) {
 			info->mask[0] |= 0xf0;
@@ -233,12 +224,11 @@
 				info->mask[0] = 0xcc; /* joystick 1 */
 				break;
 			case 0xf:
-				info->mask[0] = 0x33; /* joysticks 0 and 1 */
-				info->mask[1] = 0xcc;
+				info->mask[0] = 0xff; /* 4-axis 4-button joystick */
 				break;
 			default:
 				printk(KERN_WARNING "joy-analog: Unknown joystick device detected "
-					"(data=%#x), contact <vojtech@ucw.cz>\n", exist);
+					"(data=%#x), contact <vojtech@suse.cz>\n", exist);
 				return -1;
 		}
 	}
@@ -252,7 +242,7 @@
 
 static inline int js_an_axes(int i, struct js_an_info *info)
 {
-	return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2;
+	return hweight8(info->mask[i] & 0x0f) + hweight8(info->extensions & JS_AN_HATS_ALL) * 2;
 }
 
 /*
@@ -261,9 +251,9 @@
 
 static inline int js_an_buttons(int i, struct js_an_info *info)
 {
-	return js_an_count_bits(info->mask[i] & 0xf0) +
+	return hweight8(info->mask[i] & 0xf0) +
 	       (info->extensions & JS_AN_BUTTONS_CHF) * 2 +
-	       js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY);
+	       hweight8(info->extensions & JS_AN_BUTTONS_PXY);
 }
 
 /*
@@ -276,13 +266,13 @@
 {
 
 	sprintf(js_an_name_buf, "Analog %d-axis %d-button",
-		js_an_count_bits(info->mask[i] & 0x0f),
+		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,
-			js_an_count_bits(info->extensions & JS_AN_HATS_ALL));
+			hweight8(info->extensions & JS_AN_HATS_ALL));
 
 	strcat(js_an_name_buf, " joystick");
 
@@ -291,7 +281,7 @@
 			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-button" : "");
+			info->extensions & JS_AN_BUTTONS_PXY ? " XY/UV" : "");
 
 	return js_an_name_buf;
 }
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-assasin.c linux-2.2.14-pre16/drivers/char/joystick/joy-assasin.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-assasin.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-assasin.c	1969-12-31 16:00:00.000000000 -0800
@@ -1,423 +0,0 @@
-/*
- *  joy-assasin.c  Version 1.2
- *
- *  Copyright (c) 1998 Vojtech Pavlik
- */
-
-/*
- * This is a module for the Linux joystick driver, supporting
- * joysticks using FP-Gaming's Assasin 3D protocol.
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or 
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * 
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
- */
-
-#include <asm/io.h>
-#include <asm/system.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/joystick.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-
-#define JS_AS_MAX_START		250
-#define JS_AS_MAX_STROBE	50
-#define JS_AS_MAX_TIME		2400
-#define JS_AS_MAX_LENGTH	40
-
-#define JS_AS_MODE_A3D		1	/* Assasin 3D */
-#define JS_AS_MODE_PAN		2	/* Panther */
-#define JS_AS_MODE_OEM		3	/* Panther OEM version */
-#define JS_AS_MODE_PXL		4	/* Panther XL */
-
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_PARM(js_as, "2-24i");
-
-static int 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,-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 Assasin 3D packet.
- */
-
-static int js_as_read_packet(int io, int length, char *data)
-{
-	unsigned char u, v;
-	int i;
-	unsigned int t, t1;
-	unsigned long flags;
-
-	int start = (js_time_speed * JS_AS_MAX_START) >> 10;
-	int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10;
-
-	i = 0;
-
-	__save_flags(flags);
-	__cli();
-	outb(0xff,io);
-
-	u = inb(io);
-	t = js_get_time();
-
-	do {
-		v = inb(io);
-		t1 = js_get_time();
-	} while (u == v && js_delta(t1, t) < start);
-
-	t = t1;
-
-	do {
-		v = inb(io);
-		t1 = js_get_time();
-		if ((u ^ v) & u & 0x10) {
-			data[i++] = v >> 5;
-			t = t1;
-		}
-		u = v;
-	} while (i < length && js_delta(t1,t) < strobe);
-
-	__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;
-
-		default:
-			printk("Error.\n");
-			return -1;
-	}
-}
-
-/*
- * js_as_open() is a callback from the file open routine.
- */
-
-static int js_as_open(struct js_dev *jd)
-{
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/*
- * js_as_close() is a callback from the file release routine.
- */
-
-static int js_as_close(struct js_dev *jd)
-{
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/*
- * 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 Assasin.
- */
-
-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;
-	if (((u = inb(io)) & 3) == 3) return port;
-	outb(0xff,io);
-	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
-
-	if (js_as_read_packet(io, 1, data) != 1) return port;
-
-	if (data[0] && data[0] <= 4) {
-		info->mode = data[0];
-		info->io = io;
-		request_region(io, 1, "joystick (assasin)");
-		port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read);
-		info = port->info;
-	} else {
-		printk(KERN_WARNING "joy-assasin: unknown joystick device detected "
-			"(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", io, data[0]);
-		return port;
-	}
-
-	udelay(JS_AS_MAX_TIME);
-
-	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", js_as_open, js_as_close),
-				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", js_as_open, js_as_close));
-				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 Assasin 3D"; break;
-		case JS_AS_MODE_PAN: name = "MadCatz Panther"; break;
-		case JS_AS_MODE_OEM: name = "OEM Assasin 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, js_as_open, js_as_close),
-		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), js_as_open, js_as_close),
-			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
-void __init js_as_setup(char *str, int *ints)
-{
-	int i;
-	for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1];
-}
-#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-assasin: no joysticks found\n");
-#endif
-
-	return -ENODEV;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-	int i;
-	struct js_as_info *info;
-
-	while (js_as_port != NULL) {
-		for (i = 0; i < js_as_port->ndevs; i++)
-			if (js_as_port->devs[i] != NULL)
-				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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-assassin.c linux-2.2.14-pre16/drivers/char/joystick/joy-assassin.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-assassin.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-assassin.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,416 @@
+/*
+ *  joy-assassin.c  Version 1.2
+ *
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * joysticks using FP-Gaming's Assassin 3D protocol.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#define JS_AS_MAX_START		1000
+#define JS_AS_DELAY_READ	3000
+#define JS_AS_MAX_LENGTH	40
+
+#define JS_AS_MODE_A3D		1	/* Assassin 3D */
+#define JS_AS_MODE_PAN		2	/* Panther */
+#define JS_AS_MODE_OEM		3	/* Panther OEM version */
+#define JS_AS_MODE_PXL		4	/* Panther XL */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_PARM(js_as, "2-24i");
+
+static int __initdata js_as[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 };
+
+static int js_as_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_as_port __initdata = NULL;
+
+#include "joy-analog.h"
+
+struct js_as_info {
+	int io;
+	char mode;
+	char rudder;
+	struct js_an_info an;
+};
+
+/*
+ * js_as_read_packet() reads an Assassin 3D packet.
+ */
+
+static int js_as_read_packet(int io, int length, char *data)
+{
+	unsigned char u, v;
+	int i;
+	unsigned int t, p;
+	unsigned long flags;
+
+	i = 0;
+
+	__save_flags(flags);
+	__cli();
+
+	outb(0xff,io);
+	v = inb(io);
+	t = p = JS_AS_MAX_START;
+
+	while (t > 0 && i < length) {
+		t--;
+		u = v; v = inb(io);
+		if (~v & u & 0x10) {
+			data[i++] = v >> 5;
+			p = t = (p - t) << 3;
+		}
+	}
+
+	__restore_flags(flags);
+
+	return i;
+}
+
+/*
+ * js_as_csum() computes checksum of triplet packet
+ */
+
+static int js_as_csum(char *data, int count)
+{
+	int i, csum = 0;
+	for (i = 0; i < count - 2; i++) csum += data[i];
+	return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+/*
+ * js_as_read() reads and analyzes A3D joystick data.
+ */
+
+static int js_as_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_as_info *info = xinfo;
+	char data[JS_AS_MAX_LENGTH];
+
+	switch (info->mode) {
+
+		case JS_AS_MODE_A3D:
+		case JS_AS_MODE_OEM:
+		case JS_AS_MODE_PAN:
+
+			if (js_as_read_packet(info->io, 29, data) != 29) return -1;
+			if (data[0] != info->mode) return -1;
+			if (js_as_csum(data, 29)) return -1;
+
+			axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7);
+			axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7);
+
+			buttons[0][0] = (data[2] << 2) | (data[3] >> 1);
+
+			info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+			info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+			info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+			info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+			info->an.buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+			js_an_decode(&info->an, axes + 1, buttons + 1);
+
+			return 0;
+
+		case JS_AS_MODE_PXL:
+
+			if (js_as_read_packet(info->io, 33, data) != 33) return -1;
+			if (data[0] != info->mode) return -1;
+			if (js_as_csum(data, 33)) return -1;
+
+			axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128;
+			axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128;
+			info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128;
+			axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128;
+
+			axes[0][3] = ( data[5]       & 1) - ((data[5] >> 2) & 1);
+			axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1);
+			axes[0][5] = ((data[4] >> 1) & 1) - ( data[3]       & 1);
+			axes[0][6] = ((data[4] >> 2) & 1) - ( data[4]       & 1);
+
+			axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7);
+			axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7);
+
+			buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8];
+
+			if (info->rudder) axes[1][0] = info->an.axes[0];
+
+			return 0;
+	}
+	return -1;
+}
+
+/*
+ * js_as_open() is a callback from the file open routine.
+ */
+
+static int js_as_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_as_close() is a callback from the file release routine.
+ */
+
+static int js_as_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_as_pxl_init_corr() initializes the correction values for
+ * the Panther XL.
+ */
+
+static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = axes[0][i] - 4;
+		corr[0][i].coef[1] = axes[0][i] + 4;
+		corr[0][i].coef[2] = (1 << 29) / (127 - 32);
+		corr[0][i].coef[3] = (1 << 29) / (127 - 32);
+	}
+
+	corr[0][2].type = JS_CORR_BROKEN;
+	corr[0][2].prec = 0;
+	corr[0][2].coef[0] = 127 - 4;
+	corr[0][2].coef[1] = 128 + 4;
+	corr[0][2].coef[2] = (1 << 29) / (127 - 6);
+	corr[0][2].coef[3] = (1 << 29) / (127 - 6);
+
+	for (i = 3; i < 7; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = 0;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (1 << 29);
+		corr[0][i].coef[3] = (1 << 29);
+	}
+
+	for (i = 7; i < 9; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = -1;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (104 << 14);
+		corr[0][i].coef[3] = (104 << 14);
+	}
+}
+
+/*
+ * js_as_as_init_corr() initializes the correction values for
+ * the Panther and Assassin.
+ */
+
+static void __init js_as_as_init_corr(struct js_corr **corr)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		corr[0][i].type = JS_CORR_BROKEN;
+		corr[0][i].prec = -1;
+		corr[0][i].coef[0] = 0;
+		corr[0][i].coef[1] = 0;
+		corr[0][i].coef[2] = (104 << 14);
+		corr[0][i].coef[3] = (104 << 14);
+	}
+}
+
+/*
+ * js_as_rudder_init_corr() initializes the correction values for
+ * the Panther XL connected rudder.
+ */
+
+static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes)
+{
+	corr[1][0].type = JS_CORR_BROKEN;
+	corr[1][0].prec = 0;
+	corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3);
+	corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3);
+	corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+	corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1);
+}
+
+/*
+ * js_as_probe() probes for A3D joysticks.
+ */
+
+static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port)
+{
+	struct js_as_info iniinfo;
+	struct js_as_info *info = &iniinfo;
+	char *name;
+	char data[JS_AS_MAX_LENGTH];
+	unsigned char u;
+	int i;
+	int numdev;
+
+	memset(info, 0, sizeof(struct js_as_info));
+
+	if (io < 0) return port;
+
+	if (check_region(io, 1)) return port;
+
+	i = js_as_read_packet(io, JS_AS_MAX_LENGTH, data);
+
+	printk("%d\n", i);
+
+	if (!i) return port;
+	if (js_as_csum(data, i)) return port;
+
+	if (data[0] && data[0] <= 4) {
+		info->mode = data[0];
+		info->io = io;
+		request_region(io, 1, "joystick (assassin)");
+		port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read);
+		info = port->info;
+	} else {
+		printk(KERN_WARNING "joy-assassin: unknown joystick device detected "
+			"(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io, data[0]);
+		return port;
+	}
+
+	udelay(JS_AS_DELAY_READ);
+
+	if (info->mode == JS_AS_MODE_PXL) {
+			printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n",
+				js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close),
+				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", js_as_open, js_as_close));
+				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, js_as_open, js_as_close),
+		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), js_as_open, js_as_close),
+			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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-console.c linux-2.2.14-pre16/drivers/char/joystick/joy-console.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-console.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-console.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,14 +1,19 @@
 /*
- * joy-console.c  Version 0.11V
+ *  joy-console.c  Version 0.14V
  *
- * Copyright (c) 1998 Andree Borrmann
+ *  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, Multi1, Multi2, PSX) gamepads connected
- * via parallel port. Up to five such controllers can be
- * connected to one parallel port.
+ * console (NES, SNES, N64, Multi1, Multi2, PSX) gamepads 
+ * connected via parallel port. Up to five such controllers 
+ * can be connected to one parallel port.
  */
 
 /*
@@ -25,6 +30,10 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
 #include <asm/io.h>
@@ -36,12 +45,13 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 
-MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_console, "2-6i");
-MODULE_PARM(js_console2,"2-6i");
-MODULE_PARM(js_console3,"2-6i");
+MODULE_PARM(js_console_2,"2-6i");
+MODULE_PARM(js_console_3,"2-6i");
 
 
 #define JS_NO_PAD	0
@@ -51,29 +61,29 @@
 #define JS_MULTI_STICK	4
 #define JS_MULTI2_STICK	5
 #define JS_PSX_PAD	6
-
-#define JS_MAX_PAD	JS_PSX_PAD
+#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 {
-#ifdef USE_PARPORT
 	struct pardevice *port;	/* parport device */
-#else
-	int port;		/* hw port */
-#endif
 	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;		/* Normal PSX controllers */
-	int negcon;		/* PSX NEGCON controllers */
+	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_console2[] __initdata = { -1, 0, 0, 0, 0, 0 };
-static int js_console3[] __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 };
 
@@ -109,7 +119,7 @@
 #define JS_SNES_L	10
 #define JS_SNES_R	11
 
-#define JS_NES_POWER	0xf8
+#define JS_NES_POWER	0xfc
 #define JS_NES_CLOCK	0x01
 #define JS_NES_LATCH	0x02
 
@@ -123,17 +133,99 @@
 {
 	int i;
 
-	JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port);
+	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);
+	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);
+		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.
+ */
+
 }
 
 /*
@@ -161,7 +253,9 @@
 	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");
 }
 
 /*
@@ -169,27 +263,28 @@
  */
 
 #define JS_PSX_DELAY	10
+#define JS_PSX_LENGTH	8    /* talk to the controller in bytes */
 
-#define JS_PSX_LENGTH	8
-
-#define JS_PSX_NORMAL	0x41
-#define JS_PSX_NEGCON	0x23
-#define JS_PSX_MOUSE	0x12
-
-#define JS_PSX_SELBUT	0x01
+#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
+#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	0x01
-#define JS_PSX_COMMAND	0x02
-#define JS_PSX_POWER	0xf8
-#define JS_PSX_NOPOWER	0x04
-#define JS_PSX_SELECT	0x08
-
-#define JS_PSX_CTRL_OUT(X,Y)	JS_PAR_CTRL_OUT((X)^0x0f, Y)
+#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
@@ -202,11 +297,11 @@
 
 	cmd = (b&1)?JS_PSX_COMMAND:0;
 	for (i=0; i<8; i++) {
-		JS_PSX_CTRL_OUT(cmd, info->port);
+		JS_PAR_DATA_OUT(cmd | JS_PSX_POWER, info->port);
 		udelay(JS_PSX_DELAY);
 		ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0;
 		cmd = (b&1)?JS_PSX_COMMAND:0;
-		JS_PSX_CTRL_OUT(JS_PSX_CLOCK | cmd, info->port);
+		JS_PAR_DATA_OUT(cmd | JS_PSX_CLOCK | JS_PSX_POWER, info->port);
 		udelay(JS_PSX_DELAY);
 		b >>= 1;
 	}
@@ -228,7 +323,7 @@
 
 	JS_PAR_DATA_OUT(JS_PSX_POWER, info->port);
 
-	JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port);	/* Select pad */
+	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 */
@@ -237,7 +332,7 @@
 			data[i]=js_psx_command(info, 0);
 	else ret = -1;
 
-	JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port);
+	JS_PAR_DATA_OUT(JS_PSX_SELECT | JS_PSX_CLOCK | JS_PSX_POWER, info->port);
 	__restore_flags(flags);
 
 	return ret;
@@ -248,14 +343,14 @@
  * js_console_read() reads and analyzes console pads data.
  */
 
-#define JS_MAX_LENGTH JS_SNES_LENGTH
+#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, s;
+	int i, j, s;
 	int n = 0;
 
 /*
@@ -268,24 +363,68 @@
 
 		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);
-
-				n++;
+				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);
-				n++;
+				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];
+
 			}
 		}
 	}
@@ -300,21 +439,18 @@
 
 		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;
-
-				n++;
 			} 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;
-
-				n++;
 			}
 		}
 	}
@@ -323,18 +459,44 @@
  * PSX controllers
  */
 
-	if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) {	/* FIXME? >1 PSX pads? */
+	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)) {
 
-		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);
+			case JS_PSX_ANALOGR:
 
-		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);
+				buttons[n][0] |= (data[0]&JS_PSX_JOYL?0:0x800) | (data[0]&JS_PSX_JOYR?0:0x400);
 
-		n++;
+			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 -(n != info->pads);
+	return 0;
 }
 
 /*
@@ -343,10 +505,8 @@
 
 int js_console_open(struct js_dev *dev)
 {
-#ifdef USE_PARPORT
 	struct js_console_info *info = dev->port->info;
 	if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY;
-#endif
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -357,13 +517,9 @@
 
 int js_console_close(struct js_dev *dev)
 {
-#ifdef USE_PARPORT
 	struct js_console_info *info = dev->port->info;
-#endif
 	MOD_DEC_USE_COUNT;
-#ifdef USE_PARPORT
 	if (!MOD_IN_USE) parport_release(info->port);
-#endif
 	return 0;
 }
 
@@ -373,16 +529,12 @@
 	struct js_console_info *info;
 	int i;
 
-	while (js_console_port != NULL) {
+	while (js_console_port) {
 		for (i = 0; i < js_console_port->ndevs; i++)
-			if (js_console_port->devs[i] != NULL)
+			if (js_console_port->devs[i])
 				js_unregister_device(js_console_port->devs[i]);
 		info = js_console_port->info;
-#ifdef USE_PARPORT
 		parport_unregister_device(info->port);
-#else
-		release_region(info->port, 3);
-#endif
 		js_console_port = js_unregister_port(js_console_port);
 	}
 }
@@ -393,7 +545,7 @@
  * console gamepads.
  */
 
-static void __init js_console_init_corr(int num_axes, struct js_corr *corr)
+static void __init js_console_init_corr(int num_axes, int type, struct js_corr *corr)
 {
 	int i;
 
@@ -405,6 +557,28 @@
 		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);
+		}
+	}
 }
 
 /*
@@ -415,45 +589,40 @@
 static struct js_port __init *js_console_probe(int *config, struct js_port *port)
 {
 	char *name[5];
-	int i, psx, axes[5], buttons[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;
 
-#ifdef USE_PARPORT
-	{
-		struct parport *pp;
-
-		if (config[0] > 0x10)
-			for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
-		else
-			for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
-
-		if (pp == NULL) {
-			printk(KERN_ERR "joy-console: no such parport\n");
-			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]--;
 
-		info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
-		if (!info.port)
-			return port;
+	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;
 	}
-#else
-	info.port = config[0];
-	if (check_region(info.port, 3)) return port;
-	request_region(info.port, 3, "joystick (console pad)");
-#endif
 
-	for (i = 0; i < 5; i++)
+	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:
@@ -478,6 +647,23 @@
 				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;
@@ -497,31 +683,58 @@
 				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 controller";
+						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:
-						printk(KERN_WARNING "joy-console: NegCon not yet supported...\n");
+						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-console: PSX mouse not supported...\n");
+						printk(KERN_WARNING "joy-psx: PSX mouse not supported...\n");
 						break;
+
 					case -1:
-						printk(KERN_ERR "joy-console: no PSX controller found...\n");
+						printk(KERN_ERR "joy-psx: no PSX controller found...\n");
 						break;
+
 					default:
-						printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx);
+						printk(KERN_WARNING "joy-psx: PSX controller unknown: 0x%x,"
+							" please report to <vojtech@suse.cz>.\n", psx);
 				}
 				break;
 
@@ -529,52 +742,53 @@
 
 				printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]);
 		}
+	}
 
 	if (!info.pads) {
-#ifdef USE_PARPORT
 		parport_release(info.port);
 		parport_unregister_device(info.port);
-#else
-		release_region(info.port, 3);
-#endif
 		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++) {
-#ifdef USE_PARPORT
 		printk(KERN_INFO "js%d: %s on %s\n",
 			js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
 			name[i], info.port->port->name);
-#else
-		printk(KERN_INFO "js%d: %s at %#x\n",
-			js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close),
-			name[i], info.port);
-#endif
 
-		js_console_init_corr(axes[i], port->corr[i]);
+		js_console_init_corr(axes[i], type[i], port->corr[i]);
 	}
 
-#ifdef USE_PARPORT
 	parport_release(info.port);
-#endif
 	return port;
 }
 
 #ifndef MODULE
-void __init js_console_setup(char *str, int *ints)
+int __init js_console_setup(SETUP_PARAM)
 {
 	int i;
-
-	if (!strcmp(str,"js_console"))
-		for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1];
-	if (!strcmp(str,"js_console2"))
-		for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1];
-	if (!strcmp(str,"js_console3"))
-		for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1];
-
+	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
@@ -584,8 +798,8 @@
 #endif
 {
 	js_console_port = js_console_probe(js_console, js_console_port);
-	js_console_port = js_console_probe(js_console2, js_console_port);
-	js_console_port = js_console_probe(js_console3, 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;
 
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-creative.c linux-2.2.14-pre16/drivers/char/joystick/joy-creative.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-creative.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-creative.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,287 @@
+/*
+ *  joy-creative.c  Version 1.2
+ *
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * Creative Labs Blaster gamepad family.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#define JS_CR_MAX_STROBE	100	/* 100 us max wait for first strobe */
+#define JS_CR_LENGTH		36
+
+#define JS_CR_MODE_BGPC		8
+
+static int js_cr_port_list[] __initdata = {0x201, 0};
+static struct js_port* js_cr_port __initdata = NULL;
+
+struct js_cr_info {
+	int io;
+	unsigned char mode[2];
+};
+
+/*
+ * js_cr_read_packet() reads a Blaster gamepad packet.
+ */
+
+static int js_cr_read_packet(int io, unsigned int *data)
+{
+	unsigned long flags;
+	unsigned char u, v, w;
+	__u64 buf[2];
+	int r[2], t[2], p[2];
+	int i, j, ret;
+
+	for (i = 0; i < 2; i++); {
+		r[i] = buf[i] = 0;
+		p[i] = t[i] = JS_CR_MAX_STROBE;
+		p[i] += JS_CR_MAX_STROBE;
+	}
+	
+	__save_flags(flags);
+	__cli();
+
+	u = inb(io);
+
+	do {
+		t[0]--; t[1]--;
+		v = inb(io);
+		for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
+			if (w & 0x30) {
+				if ((w & 0x30) < 0x30 && r[i] < JS_CR_LENGTH && t[i] > 0) {
+					buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
+					p[i] = t[i] = (p[i] - t[i]) << 1;
+					u = v;
+				} else t[i] = 0;
+			}
+	} while (t[0] > 0 || t[1] > 0);
+
+	__restore_flags(flags);
+
+
+	ret = 0;
+
+	for (i = 0; i < 2; i++) {
+
+		if (r[i] != JS_CR_LENGTH) continue;
+
+		for (j = 0; j < JS_CR_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
+			buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (JS_CR_LENGTH - 1));
+
+		if (j < JS_CR_LENGTH) ret |= (1 << i);
+
+		data[i] = ((buf[i] >>  7) & 0x000001f) | ((buf[i] >>  8) & 0x00003e0)
+			| ((buf[i] >>  9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
+			| ((buf[i] >> 11) & 0x1f00000);
+
+	}
+
+	return ret;
+}
+
+/*
+ * js_cr_read() reads and analyzes Blaster gamepad data.
+ */
+
+static int js_cr_read(void *xinfo, int **axes, int **buttons)
+{
+	struct js_cr_info *info = xinfo;
+	unsigned int data[2];
+	int i, r;
+
+	if (!(r = js_cr_read_packet(info->io, data)))
+		return -1;
+
+	for (i = 0; i < 2; i++)
+		if (r & (1 << i)) {
+			switch (info->mode[i]) {
+
+				case JS_CR_MODE_BGPC:
+
+					axes[i][0] = ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1);
+					axes[i][1] = ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1);
+
+					buttons[i][0] = ((data[i] >> 12) & 0x007) | ((data[i] >> 6) & 0x038)
+						      | ((data[i] >>  1) & 0x0c0) | ((data[i] >> 7) & 0x300)
+						      | ((data[i] <<  5) & 0xc00);
+
+					break;
+
+				default:
+					break;
+
+			}
+		}
+
+	return 0;
+}
+
+/*
+ * js_cr_open() is a callback from the file open routine.
+ */
+
+static int js_cr_open(struct js_dev *jd)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * js_cr_close() is a callback from the file release routine.
+ */
+
+static int js_cr_close(struct js_dev *jd)
+{
+	MOD_DEC_USE_COUNT;
+	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]], js_cr_open, js_cr_close),
+				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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-db9.c linux-2.2.14-pre16/drivers/char/joystick/joy-db9.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-db9.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-db9.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,10 @@
 /*
- * joy-db9.c  Version 0.5V
+ *  joy-db9.c  Version 0.6V
  *
- * Copyright (c) 1998 Andree Borrmann
+ *  Copyright (c) 1998 Andree Borrmann
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -24,6 +27,10 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
 #include <asm/io.h>
@@ -35,8 +42,9 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
-MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_db9, "2i");
 MODULE_PARM(js_db9_2, "2i");
 MODULE_PARM(js_db9_3, "2i");
@@ -48,7 +56,8 @@
 #define JS_GENESIS6_PAD	0x06
 #define JS_SATURN_PAD	0x07
 #define JS_MULTI_0802	0x08
-#define JS_MAX_PAD	0x09
+#define JS_MULTI_0802_2	0x09
+#define JS_MAX_PAD	0x0A
 
 #define JS_DB9_UP	0x01
 #define JS_DB9_DOWN	0x02
@@ -59,8 +68,8 @@
 #define JS_DB9_FIRE3	0x40
 #define JS_DB9_FIRE4	0x80
 
-#define JS_DB9_NORMAL	0x22
-#define JS_DB9_NOSELECT	0x20
+#define JS_DB9_NORMAL	0x2a
+#define JS_DB9_NOSELECT	0x28
 
 #define JS_DB9_SATURN0	0x20
 #define JS_DB9_SATURN1	0x22
@@ -76,11 +85,7 @@
 static int js_db9_3[] __initdata = { -1, 0 };
 
 struct js_db9_info {
-#ifdef USE_PARPORT
 	struct pardevice *port;	/* parport device */
-#else
-	int port;		/* hw port */
-#endif
 	int mode;		/* pad mode */
 };
 
@@ -95,6 +100,15 @@
 
 	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;
@@ -185,11 +199,12 @@
 		udelay(JS_GENESIS6_DELAY);
 		JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */
 		udelay(JS_GENESIS6_DELAY);
-		JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port);
 		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);
+		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);
@@ -236,11 +251,11 @@
 	struct js_db9_info *info = dev->port->info;
 
 	if (!MOD_IN_USE) {
-#ifdef USE_PARPORT
 		if (parport_claim(info->port)) return -EBUSY;
-#endif
 
-		JS_PAR_ECTRL_OUT(0x35,info->port);		/* enable PS/2 mode: */
+		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 */
 	}
 		
@@ -260,12 +275,11 @@
 
 	if (!MOD_IN_USE) {
 
-		JS_PAR_CTRL_OUT(0x00,info->port);	/* normal direction */
-		JS_PAR_ECTRL_OUT(0x15,info->port);	/* enable normal mode */
+		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 */
 
-#ifdef USE_PARPORT
 		parport_release(info->port);
-#endif
 	}
 	return 0;
 }
@@ -274,16 +288,15 @@
 void cleanup_module(void)
 {
 	struct js_db9_info *info;
+	int i;
 
-	while (js_db9_port != NULL) {
-		js_unregister_device(js_db9_port->devs[0]);
+	while (js_db9_port) {
 		info = js_db9_port->info;
-#ifdef USE_PARPORT
+
+		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);
-#else
-		release_region(info->port, 3);
-		release_region(info->port+0x402, 1);
-#endif
 		js_db9_port = js_unregister_port(js_db9_port);
 	}
 
@@ -295,17 +308,17 @@
  * db9 gamepads.
  */
 
-static void __init js_db9_init_corr(struct js_corr **corr)
+static void __init js_db9_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);
+		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);
 	}
 }
 
@@ -316,75 +329,76 @@
 static struct js_port __init *js_db9_probe(int *config, struct js_port *port)
 {
 	struct js_db9_info info;
-	char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8,1};
+	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"};
+					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;
 
-#ifdef USE_PARPORT
-	{
-		struct parport *pp;
+	info.mode = config[1];
 
-		if (config[0] > 0x10)
-			for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
-		else
-			for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
-
-		if (pp == NULL) {
-			printk(KERN_ERR "joy-db9: no such parport\n");
-			return port;
-		}
-
-		if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) {
-			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;
+	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;
 	}
-#else
-	info.port = config[0];
-	if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port;
-	request_region(info.port, 3, "joystick (db9)");
-	request_region(info.port+0x402, 1, "joystick (db9)");
-#endif
 
-	info.mode = config[1];
+	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;
+	}
 
-	port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read);
+	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], js_db9_open, js_db9_close),
+			name[info.mode], info.port->port->name);
 
-#ifdef USE_PARPORT
-	printk(KERN_INFO "js%d: %s on %s\n",
-		js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
-		name[info.mode], info.port->port->name);
-#else
-	printk(KERN_INFO "js%d: %s at %#x\n",
-		js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close),
-		name[info.mode], info.port);
-#endif
+		js_db9_init_corr(port->corr[i]);
+	}
 
-	js_db9_init_corr(port->corr);
 
 	return port;
 }
 
 #ifndef MODULE
-void __init js_db9_setup(char *str, int *ints)
+int __init js_db9_setup(SETUP_PARAM)
 {
 	int i;
-
-	if (!strcmp(str,"js_db9"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1];
-	if (!strcmp(str,"js_db9_2"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1];
-	if (!strcmp(str,"js_db9_3"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1];
-
+	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
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-gravis.c linux-2.2.14-pre16/drivers/char/joystick/joy-gravis.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-gravis.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-gravis.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-gravis.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -25,7 +27,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -37,15 +39,16 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#include <linux/init.h>
 
 #define JS_GR_MODE_GPP		1
 #define JS_GR_LENGTH_GPP	24
-#define JS_GR_STROBE_GPP	75
+#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		30
+#define JS_GR_STROBE_XT		200
 #define JS_GR_MAX_CHUNKS_XT	10	
 #define JS_GR_MAX_BITS_XT	30	
 
@@ -63,37 +66,36 @@
 
 static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data)
 {
-	unsigned int t, t1;
+	unsigned long flags;
 	unsigned char u, v;
+	unsigned int t, p;
 	int i;
-	unsigned long flags;
-
-	int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10;
 
 	i = 0;
 	data[0] = 0;
+	p = t = JS_GR_STROBE_GPP;
+	p += JS_GR_STROBE_GPP;
 
 	__save_flags(flags);
 	__cli();
-	u = inb(io) >> shift;
-	t = js_get_time();
+
+	v = inb(io) >> shift;
 
 	do {
-		v = (inb(io) >> shift) & 3;
-		t1 = js_get_time();
-		if ((u ^ v) & u & 1) {
+		t--;
+		u = v; v = (inb(io) >> shift) & 3;
+		if (~v & u & 1) {
 			data[0] |= (v >> 1) << i++;
-			t = t1;
+			p = t = (p - t) << 1;
 		}
-		u = v;
-	} while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe);
+	} 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) << 23;
+		data[0] = data[0] >> 1 | (data[0] & 1) << (JS_GR_LENGTH_GPP - 1);
 
 	return -(i == JS_GR_LENGTH_GPP);
 }
@@ -104,35 +106,36 @@
 
 static int js_gr_xt_read_packet(int io, int shift, unsigned int *data)
 {
-	unsigned int t, t1;
-	unsigned char u, v, w;
 	unsigned int i, j, buf, crc;
+	unsigned char u, v, w;
 	unsigned long flags;
+	unsigned int t, p;
 	char status;
 
-	int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10;
-
 	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;
-	t = js_get_time();
 
 	do {
+		t--;
 		u = (inb(io) >> shift) & 3;
-		t1 = js_get_time();
 
 		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)) {
@@ -145,12 +148,11 @@
 				i = 0;
 			}
 
-			t = t1;
 			w = v;
 			v = u;
 		}
 
-	} while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe);
+	} while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && t > 0);
 
 	__restore_flags(flags);
 
@@ -320,7 +322,7 @@
 }
 
 /*
- * js_gr_probe() probes fro GrIP joysticks.
+ * js_gr_probe() probes for GrIP joysticks.
  */
 
 static struct js_port __init *js_gr_probe(int io, struct js_port *port)
@@ -389,9 +391,9 @@
 	int i;
 	struct js_gr_info *info;
 
-	while (js_gr_port != NULL) {
+	while (js_gr_port) {
 		for (i = 0; i < js_gr_port->ndevs; i++)
-			if (js_gr_port->devs[i] != NULL)
+			if (js_gr_port->devs[i])
 				js_unregister_device(js_gr_port->devs[i]);
 		info = js_gr_port->info;
 		release_region(info->io, 1);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-lightning.c linux-2.2.14-pre16/drivers/char/joystick/joy-lightning.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-lightning.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-lightning.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-lightning.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -26,7 +28,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -38,6 +40,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#include <linux/init.h>
 
 #define JS_L4_PORT		0x201
 #define JS_L4_SELECT_ANALOG	0xa4
@@ -52,10 +55,10 @@
 
 static struct js_port* __initdata js_l4_port = NULL;
 
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_l4, "2-24i");
 
-static int 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,-1,0,0};
+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"
 
@@ -70,11 +73,10 @@
 
 static int js_l4_wait_ready(void)
 {
-	unsigned int t, t1, timeout;
-	timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10;
-	t = t1 = js_get_time();
-	while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout));
-	return -(js_delta(t1, t) >= timeout);
+	unsigned int t;
+	t = JS_L4_TIMEOUT;
+	while ((inb(JS_L4_PORT) & JS_L4_BUSY) && t > 0) t--;
+	return -(t<=0);
 }
 
 /*
@@ -255,14 +257,14 @@
 
 	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), js_l4_open, js_l4_close),
 			js_an_name(i, &info->an), info->port);
 
-	info = port->info;
-
 	js_l4_calibrate(info);
 	js_l4_read(info, port->axes, port->buttons);
 	js_an_init_corr(&info->an, port->axes, port->corr, 0);
@@ -300,18 +302,21 @@
 
 		cards[i] = rev; 
 
-		printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n",
+		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
-void __init js_l4_setup(char *str, int *ints)
+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
@@ -333,7 +338,7 @@
 			js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port);
 	}
 
-	if (js_l4_port == NULL) {
+	if (!js_l4_port) {
 #ifdef MODULE
 		printk(KERN_WARNING "joy-lightning: no joysticks found\n");
 #endif
@@ -352,9 +357,9 @@
 	int cal[4] = {59, 59, 59, 59};
 	struct js_l4_info *info;
 
-	while (js_l4_port != NULL) {
+	while (js_l4_port) {
 		for (i = 0; i < js_l4_port->ndevs; i++)
-			if (js_l4_port->devs[i] != NULL)
+			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);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-logitech.c linux-2.2.14-pre16/drivers/char/joystick/joy-logitech.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-logitech.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-logitech.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,12 +1,14 @@
 /*
  *  joy-logitech.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
  * This is a module for the Linux joystick driver, supporting
- * Logitech Digital joystick family.
+ * Logitech ADI joystick family.
  */
 
 /*
@@ -25,7 +27,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -38,113 +40,175 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+/*
+ * Times array sizes, flags, ids.
+ */
+
+#undef JS_LT_DEBUG
+
+#define JS_LT_MAX_START		400	/* Trigger to packet timeout [400us] */
+
+#define JS_LT_MAX_LENGTH	256
+#define JS_LT_MIN_LENGTH	8
+#define JS_LT_MIN_LEN_LENGTH	10
+#define JS_LT_MIN_ID_LENGTH	66
+#define JS_LT_MAX_NAME_LENGTH	16
 
-#define JS_LT_MAX_START		250
-#define JS_LT_MAX_STROBE	25
-#define JS_LT_MAX_LENGTH	72
+#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_MAX_DELAY		12000
+#define JS_LT_FLAG_HAT		0x04
+#define JS_LT_FLAG_10BIT	0x08
 
-#define JS_LT_MODE_WMED		1
-#define JS_LT_MODE_CM2		2
-#define JS_LT_MODE_TPD		3
+#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
 
-static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 };
-static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 };
+#define JS_LT_BUG_BUTTONS	0x01
+#define JS_LT_BUG_LONGID	0x02
+#define JS_LT_BUG_LONGDATA	0x04
+#define JS_LT_BUG_IGNTRIG	0x08
 
-static int js_lt_port_list[] __initdata = {0x201, 0};
+/*
+ * 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;
-	unsigned char mode;
+	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 packet.
+ * js_lt_read_packet() reads a Logitech ADI packet.
  */
 
-static int js_lt_read_packet(int io, __u64 *data)
+static void js_lt_read_packet(struct js_lt_info *info)
 {
-
-	static unsigned char buf[JS_LT_MAX_LENGTH];
-	unsigned char u, v, w, mask = 0;
-	int i;
+	unsigned char u, v, w, x, z;
+	int t[2], s[2], p[2], i;
 	unsigned long flags;
-	unsigned int t, t1;
-
-	int start = (js_time_speed * JS_LT_MAX_START) >> 10;
-	int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10;
-
-	u = inb(io) >> 4;
-
-	if (u == 0xc) mask = 0x10;
-	if (u == 0x0) mask = 0x50;
-	if (!mask) return 0;
 
-	i = 0;
+	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,io);
+	outb(0xff, info->io);
+	v = z = inb(info->io);
 
-	u = inb(io);
-	t = js_get_time();
+	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);
 
-	if ((u & 0xc) != 0xc) mask = 0x10;
+	__restore_flags(flags);
 
-	do {
-		u = inb(io);
-		t1 = js_get_time();
-	} while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start);
+	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
 
-	t = t1;
+	return;
+}
 
-	do {
-		v = inb(io);
-		t1 = js_get_time();
-		w = u ^ v;
-		if ((((w >> 1) ^ w) & mask) == mask) {
-			buf[i++] = w;
-			t = t1;
-			u = v;
-		}
-	} while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe);
+/*
+ * js_lt_move_bits() detects a possible 2-stream mode, and moves
+ * the bits accordingly. 
+ */
 
-	__restore_flags(flags);
+static void js_lt_move_bits(struct js_lt_info *info, int length)
+{
+	int i;
 
-	t = i;
-	*data = 0;
+ 	info->idx[0] = info->idx[1] = 0;
 
-	if (mask == 0x10) {
-		for (i = 0; i < t; i++)
-			*data = ((buf[i] >> 5) & 1) | (*data << 1);
-		return t;
-	}
-	if (mask == 0x50) {
-		for (i = 0; i < t; i++)
-			*data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1);
-		return t << 1;
-	}
-	return 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_reverse() reverses the order of bits in a byte.
+ * js_lt_get_bits() gathers bits from the data packet.
  */
 
-static unsigned char js_lt_reverse(unsigned char u)
+static inline int js_lt_get_bits(struct js_lt_info *info, int device, int count)
 {
-	u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f);
-	u = ((u & 0x33) << 2) | ((u >> 2) & 0x33);
-	u = ((u & 0x55) << 1) | ((u >> 1) & 0x55);
-	return u;
+	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;
 }
 
 /*
@@ -154,54 +218,61 @@
 static int js_lt_read(void *xinfo, int **axes, int **buttons)
 {
 	struct js_lt_info *info = xinfo;
-	__u64 data;
-	int hat;
-
-	switch (info->mode) {
-
-		case JS_LT_MODE_TPD:
-
-			if (js_lt_read_packet(info->io, &data) != 20) return -1;
-
-			axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1);
-			axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1);
-
-			buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0));
-
-			return 0;
-
-		case JS_LT_MODE_WMED:
+	int i, j, k, l, t;
+	int ret = 0;
 
-			if (js_lt_read_packet(info->io, &data) != 42) return -1;
-			if ((hat = data & 0xf) > 8) return -1;
+	js_lt_read_packet(info);
+	js_lt_move_bits(info, info->length[0]);
 
-			axes[0][0] = (data >> 26) & 0xff;
-			axes[0][1] = (data >> 18) & 0xff;
-			axes[0][2] = (data >> 10) & 0xff;
-			axes[0][3] = js_lt_hat_to_axis[hat].x;
-			axes[0][4] = js_lt_hat_to_axis[hat].y;
+	for (i = 0; i < 2; i++) {
 
-			buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc);
-
-			return 0;
-
-		case JS_LT_MODE_CM2:
-
-			if (js_lt_read_packet(info->io, &data) != 64) return -1;
+		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;
+		}
 
-			axes[0][0] = (data >> 48) & 0xff;
-			axes[0][1] = (data >> 40) & 0xff;
-			axes[0][2] = (data >> 32) & 0xff;
-			axes[0][3] = (data >> 24) & 0xff;
-			axes[0][4] = (data >> 16) & 0xff;
-			axes[0][5] = (data >>  8) & 0xff;
+		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++;
+		}
 
-			buttons[0][0] = js_lt_reverse(data & 0xff);
+		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;
+		}
 
-			return 0;
+		for (j = 63; j < info->buttons[i]; j++) {
+			buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f);
+			l++;
+		}
 	}
 
-	return -1;
+	return ret;
 }
 
 /*
@@ -225,15 +296,18 @@
 }
 
 /*
- * js_lt_trigger_sequence() sends a trigger & delay sequence
- * to reset/initialize a Logitech joystick.
+ * js_lt_init_digital() sends a trigger & delay sequence
+ * to reset and initialize a Logitech joystick into digital mode.
  */
 
-static void __init js_lt_trigger_sequence(int io, int *seq)
+static void __init js_lt_init_digital(int io)
 {
-	while (*seq) {
+	int seq[] = { 3, 2, 3, 10, 6, 11, 7, 9, 11, 0 };
+	int i;
+
+	for (i = 0; seq[i]; i++) {
 		outb(0xff,io);
-		udelay(*seq++);
+		mdelay(seq[i]);
 	}
 }
 
@@ -242,35 +316,45 @@
  * Logitech joysticks.
  */
 
-static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr)
+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 < num_axes; j++) {
-		corr[0][j].type = JS_CORR_BROKEN;
-		corr[0][j].prec = 2;
-		corr[0][j].coef[0] = axes[0][j] - 8;
-		corr[0][j].coef[1] = axes[0][j] + 8;
-		corr[0][j].coef[2] = (1 << 29) / (127 - 32);
-		corr[0][j].coef[3] = (1 << 29) / (127 - 32);
-	}
-
-	switch (mode) {
-		case JS_LT_MODE_TPD:  j = 0; break;
-		case JS_LT_MODE_WMED: j = 3; break;
-		case JS_LT_MODE_CM2:  j = 6; break;
-		default:	      j = 0; 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);
+	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);
+	}
 }
 
 /*
@@ -279,61 +363,157 @@
 
 static struct js_port __init *js_lt_probe(int io, struct js_port *port)
 {
-	struct js_lt_info info;
-	char *name;
-	int axes, buttons, i;
-	__u64 data;
-	unsigned char u;
+	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;
 
-	if (((u = inb(io)) & 3) == 3) return port;
-	outb(0xff,io);
-	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
-
-	if (!(i = js_lt_read_packet(io, &data))) {
-		udelay(JS_LT_MAX_DELAY);
-		js_lt_trigger_sequence(io, js_lt_seq_reset);
-		js_lt_trigger_sequence(io, js_lt_seq_init);
-		i = js_lt_read_packet(io, &data);
-	}
-
-	switch (i) {
-		case 0:
-			return port;
-		case 20:
-			info.mode = JS_LT_MODE_TPD;
-			axes = 2; buttons = 8; name = "Logitech ThunderPad Digital";
-			break;
-		case 42:
-			info.mode = JS_LT_MODE_WMED;
-			axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital";
-			break;
-		case 64:
-			info.mode = JS_LT_MODE_CM2;
-			axes = 6; buttons = 8; name = "Logitech CyberMan 2";
-			break;
-		case 72:
-		case 144:
-			return port;
-		default:
-			printk(KERN_WARNING "joy-logitech: unknown joystick device detected "
-				"(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
-				io, i, (int)(data >> 32), (int)(data & 0xffffffff));
-			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]);
+		}
+
 	}
 
-	info.io = io;
+	if (!info->length[0] && !info->length[1])
+		return port;
 
 	request_region(io, 1, "joystick (logitech)");
-	port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read);
-	printk(KERN_INFO "js%d: %s at %#x\n",
-		js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io);
 
-	udelay(JS_LT_MAX_DELAY);
+	port = js_register_port(port, info, 2, sizeof(struct js_lt_info), js_lt_read);
+	info = port->info;
 
-	js_lt_read(port->info, port->axes, port->buttons);
-	js_lt_init_corr(axes, info.mode, port->axes, port->corr);
+	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, js_lt_open, js_lt_close), 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;
 }
@@ -359,10 +539,13 @@
 #ifdef MODULE
 void cleanup_module(void)
 {
+	int i;
 	struct js_lt_info *info;
 
-	while (js_lt_port != NULL) {
-		js_unregister_device(js_lt_port->devs[0]);
+	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);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-magellan.c linux-2.2.14-pre16/drivers/char/joystick/joy-magellan.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-magellan.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-magellan.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,397 @@
+/*
+ *  joy-magellan.c  Version 0.1
+ *
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * the Magellan and Space Mouse 6dof controllers.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define	N_JOYSTICK_MAG		14
+#define JS_MAG_MAX_LENGTH	64
+
+/*
+ * List of Magellans.
+ */
+
+static struct js_port* js_mag_port = NULL;
+
+/*
+ * Per-Magellan data.
+ */
+
+struct js_mag_info {
+	struct tty_struct* tty;
+	struct js_port* port;
+	int idx;
+	unsigned char data[JS_MAG_MAX_LENGTH];
+	unsigned char name[JS_MAG_MAX_LENGTH];
+	char ack;
+	char used;
+};
+
+/*
+ * js_mag_crunch_nibbles() verifies that the bytes sent from the Magellan
+ * have correct upper nibbles for the lower ones, if not, the packet will
+ * be thrown away. It also strips these upper halves to simplify further
+ * processing.
+ */
+
+static int js_mag_crunch_nibbles(unsigned char *data, int count)
+{
+	static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?";
+
+	do {
+		if (data[count] == nibbles[data[count] & 0xf])
+			data[count] = data[count] & 0xf;
+		else
+			return -1;
+	} while (--count);
+
+	return 0;
+}
+
+/*
+ * js_mag_process_packet() decodes packets the driver receives from the
+ * Magellan. It updates the data accordingly, and sets an ACK flag
+ * to the type of last packet received, if received OK.
+ */
+
+static void js_mag_process_packet(struct js_mag_info* info)
+{
+	int i;
+
+	if (!info->idx) return;
+
+	switch (info->data[0]) {
+
+		case 'd':				/* Axis data */
+			if (info->idx != 25) return;
+			if (js_mag_crunch_nibbles(info->data, 24)) return;
+			if (!info->port->devs[0]) return;
+			for (i = 0; i < 6; i++) {
+				info->port->axes[0][i] = 
+					( info->data[(i << 2) + 1] << 12 | info->data[(i << 2) + 2] << 8 |
+					  info->data[(i << 2) + 3] <<  4 | info->data[(i << 2) + 4] )
+					 - 32768;
+			}
+			break;
+
+		case 'e':				/* Error packet */
+			if (info->idx != 4) return;
+			if (js_mag_crunch_nibbles(info->data, 3)) return;
+			switch (info->data[1]) {
+				case 1:
+					printk(KERN_ERR "joy-magellan: Received command error packet. Failing command byte: %c\n",
+						info->data[2] | (info->data[3] << 4));
+					break;
+				case 2:
+					printk(KERN_ERR "joy-magellan: Received framing error packet.\n");
+					break;
+				default:
+					printk(KERN_ERR "joy-magellan: Received unknown error packet.\n");
+			}
+			break;
+
+		case 'k':				/* Button data */
+			if (info->idx != 4) return;
+			if (js_mag_crunch_nibbles(info->data, 3)) return;
+			if (!info->port->devs[0]) return;
+			info->port->buttons[0][0] = (info->data[1] << 1) | (info->data[2] << 5) | info->data[3];
+			break;
+
+		case 'm':				/* Mode */
+			if (info->idx != 2) return;
+			if (js_mag_crunch_nibbles(info->data, 1)) return; 
+			break;
+
+		case 'n':				/* Null radius */
+			if (info->idx != 2) return;
+			if (js_mag_crunch_nibbles(info->data, 1)) return; 
+			break;
+
+		case 'p':				/* Data rate */
+			if (info->idx != 3) return;
+			if (js_mag_crunch_nibbles(info->data, 2)) return;
+			break;
+
+		case 'q':				/* Sensitivity */
+			if (info->idx != 3) return;
+			if (js_mag_crunch_nibbles(info->data, 2)) return; 
+			break;
+
+		case 'v':				/* Version string */
+			info->data[info->idx] = 0;
+			for (i = 1; i < info->idx && info->data[i] == ' '; i++);
+			memcpy(info->name, info->data + i, info->idx - i);
+			break;
+
+		case 'z':				/* Zero position */
+			break;
+
+		default:
+			printk("joy-magellan: Unknown packet %d length %d:", info->data[0], info->idx);
+			for (i = 0; i < info->idx; i++) printk(" %02x", info->data[i]);
+			printk("\n");
+			return;
+	}
+
+	info->ack = info->data[0];
+}
+
+/*
+ * js_mag_command() sends a command to the Magellan, and waits for
+ * acknowledge.
+ */
+
+static int js_mag_command(struct js_mag_info *info, char *command, int timeout)
+{
+	info->ack = 0;
+	if (info->tty->driver.write(info->tty, 0, command, strlen(command)) != strlen(command)) return -1;
+	while (!info->ack && timeout--) mdelay(1);
+	return -(info->ack != command[0]);
+}
+
+/*
+ * js_mag_setup() initializes the Magellan to sane state. Also works as
+ * a probe for Magellan existence.
+ */
+
+static int js_mag_setup(struct js_mag_info *info)
+{
+
+	if (js_mag_command(info, "vQ\r", 800))	/* Read version */
+		return -1;
+	if (js_mag_command(info, "m3\r", 50))	/* Set full 3d mode */
+		return -1;
+	if (js_mag_command(info, "pBB\r", 50))	/* Set 16 reports/second (max) */
+		return -1;
+	if (js_mag_command(info, "z\r", 50))	/* Set zero position */
+		return -1;
+
+	return 0;
+}
+
+/*
+ * js_mag_read() updates the axis and button data upon startup.
+ */
+
+static int js_mag_read(struct js_mag_info *info)
+{
+	memset(info->port->axes[0],0, sizeof(int) * 6);		/* Axes are 0 after zero postition cmd */ 
+
+	if (js_mag_command(info, "kQ\r", 50))			/* Read buttons */
+		return -1;
+
+	return 0;
+}
+
+/*
+ * js_mag_open() is a callback from the joystick device open routine.
+ */
+
+static int js_mag_open(struct js_dev *jd)
+{
+	struct js_mag_info *info = jd->port->info;
+	info->used++;	
+	MOD_INC_USE_COUNT;
+	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);
+	}
+	MOD_DEC_USE_COUNT;
+	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", 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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-pci.c linux-2.2.14-pre16/drivers/char/joystick/joy-pci.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-pci.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-pci.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,272 @@
+/*
+ *  joy-pci.c  Version 0.4.0
+ *
+ *  Copyright (c) 1999 Raymond Ingles
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting the
+ * gameports on Trident 4DWave and Aureal Vortex soundcards, and
+ * analog joysticks connected to them.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Raymond Ingles <sorceror@tir.com>");
+MODULE_PARM(js_pci, "3-32i");
+
+#define NUM_CARDS              8
+static int js_pci[NUM_CARDS * 4] __initdata = { -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0,
+						-1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0 };
+
+static struct js_port * js_pci_port __initdata = NULL;
+
+#include "joy-analog.h"
+
+struct js_pci_info;
+typedef void (*js_pci_func)(struct js_pci_info *);
+
+struct js_pci_data {
+	int vendor;	/* PCI Vendor ID */
+	int model;	/* PCI Model ID */
+	int size;	/* Memory / IO region size */
+	int lcr;	/* Aureal Legacy Control Register */
+	int gcr;	/* Gameport control register */
+	int buttons;	/* Buttons location */
+	int axes;	/* Axes start */
+	int axsize;	/* Axis field size */
+	int axmax;	/* Axis field max value */
+	js_pci_func init;	
+	js_pci_func cleanup;	
+	char *name;
+};
+
+struct js_pci_info {
+        unsigned char *base;
+	struct pci_dev *pci_p;
+	__u32 lcr;
+	struct js_pci_data *data;
+        struct js_an_info an;
+};
+
+/*
+ * js_pci_*_init() sets the info->base field, disables legacy gameports,
+ * and enables the enhanced ones.
+ */
+
+static void js_pci_4dwave_init(struct js_pci_info *info)
+{
+	info->base = ioremap(BASE_ADDRESS(info->pci_p, 1), info->data->size);
+	pci_read_config_word(info->pci_p, info->data->lcr, (unsigned short *)&info->lcr);
+	pci_write_config_word(info->pci_p, info->data->lcr, info->lcr & ~0x20);
+	writeb(0x80, info->base + info->data->gcr);
+}
+
+static void js_pci_vortex_init(struct js_pci_info *info)
+{
+	info->base = ioremap(BASE_ADDRESS(info->pci_p, 0), info->data->size);
+	info->lcr = readl(info->base + info->data->lcr);
+	writel(info->lcr & ~0x8, info->base + info->data->lcr);
+	writel(0x40, info->base + info->data->gcr);
+}
+
+/*
+ * js_pci_*_cleanup does the opposite of the above functions.
+ */
+
+static void js_pci_4dwave_cleanup(struct js_pci_info *info)
+{
+	pci_write_config_word(info->pci_p, info->data->lcr, info->lcr);
+	writeb(0x00, info->base + info->data->gcr);
+	iounmap(info->base);
+}
+
+static void js_pci_vortex_cleanup(struct js_pci_info *info)
+{
+	writel(info->lcr, info->base + info->data->lcr);
+	writel(0x00, info->base + info->data->gcr);
+	iounmap(info->base);
+}
+
+static struct js_pci_data js_pci_data[] =
+{{ PCI_VENDOR_ID_TRIDENT, 0x2000, 0x10000, 0x00044 ,0x00030, 0x00031, 0x00034, 2, 0xffff,
+	js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave DX" },
+ { PCI_VENDOR_ID_TRIDENT, 0x2001, 0x10000, 0x00044, 0x00030, 0x00031, 0x00034, 2, 0xffff,
+	js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave NX" },
+ { PCI_VENDOR_ID_AUREAL,  0x0001, 0x40000, 0x1280c, 0x1100c, 0x11008, 0x11010, 4, 0x1fff,
+	js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex1" },
+ { PCI_VENDOR_ID_AUREAL,  0x0002, 0x40000, 0x2a00c, 0x2880c, 0x28808, 0x28810, 4, 0x1fff,
+	js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex2" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL }};
+
+/*
+ * js_pci_read() reads data from a PCI gameport.
+ */
+
+static int js_pci_read(void *xinfo, int **axes, int **buttons)
+{
+        struct js_pci_info *info = xinfo;
+	int i;
+
+	info->an.buttons = ~readb(info->base + info->data->buttons) >> 4;
+
+	for (i = 0; i < 4; i++)
+		info->an.axes[i] = readw(info->base + info->data->axes + i * info->data->axsize);
+
+        js_an_decode(&info->an, axes, buttons);
+        
+        return 0;
+}
+	
+/*
+ * js_pci_open() is a callback from the file open routine.
+ */
+
+static int js_pci_open(struct js_dev *jd)
+{
+        MOD_INC_USE_COUNT;
+        return 0;
+}
+
+/*
+ * js_pci_close() is a callback from the file release routine.
+ */
+
+static int js_pci_close(struct js_dev *jd)
+{
+        MOD_DEC_USE_COUNT;
+        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), js_pci_open, js_pci_close), 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++)
+			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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-sidewinder.c linux-2.2.14-pre16/drivers/char/joystick/joy-sidewinder.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-sidewinder.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-sidewinder.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-sidewinder.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -25,7 +27,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -38,18 +40,36 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#include <linux/init.h>
+
+/*
+ * These are really magic values. Changing them can make a problem go away,
+ * as well as break everything.
+ */
+
+#undef JS_SW_DEBUG
+
+#define JS_SW_START		400	/* The time we wait for the first bit [400 us] */
+#define JS_SW_STROBE		45	/* Max time per bit [45 us] */
+#define JS_SW_TIMEOUT		4000	/* Wait for everything to settle [4 ms] */
+#define JS_SW_KICK		45	/* Wait after A0 fall till kick [45 us] */
+#define JS_SW_END		8	/* Number of bits before end of packet to kick */
+#define JS_SW_FAIL		16	/* Number of packet read errors to fail and reinitialize */
+#define JS_SW_BAD		2	/* Number of packet read errors to switch off 3d Pro optimization */
+#define JS_SW_OK		64	/* Number of packet read successes to switch optimization back on */
+#define JS_SW_LENGTH		512	/* Max number of bits in a packet */
 
-#define JS_SW_MAX_START		250
-#define JS_SW_MIN_STROBE	25
-#define JS_SW_EXT_STROBE	45
-#define JS_SW_MIN_TIME		1500
-#define JS_SW_MAX_TIME		4000
-
-#define JS_SW_MAX_LENGTH	72
-
-#define JS_SW_MODE_3DP		1
-#define JS_SW_MODE_PP		2
-#define JS_SW_MODE_GP		3
+/*
+ * 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;
@@ -61,208 +81,398 @@
 
 struct js_sw_info {
 	int io;
-	unsigned char mode;
+	int length;
+	int speed;
+	unsigned char type;
+	unsigned char bits;
 	unsigned char number;
-	unsigned char optimize;
+	unsigned char fail;
+	unsigned char ok;
 };
 
 /*
- * js_sw_init_digital() switches a SideWinder into digital mode.
+ * Gameport speed.
+ */
+
+unsigned int js_sw_io_speed = 0;
+
+/*
+ * js_sw_measure_speed() measures the gameport i/o speed.
  */
 
-static void __init js_sw_init_digital(int io)
+static int __init js_sw_measure_speed(int io)
 {
-	unsigned int t;
-	unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10;
-        int delays[] = {140, 140+726, 140+300, 0};
-	int i = 0;
+#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;
 
-	__save_flags(flags);
-	__cli();
-	do {
-		outb(0xff,io);
-		t = js_get_time();
-		while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout));
-		udelay(delays[i]);
-	} while (delays[i++]);
-	__restore_flags(flags);
+	tx = 1 << 30;
 
-	for (i = 0; i < 4; i++) {
-		udelay(300);
-		outb(0xff, io);
+	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;
+	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() reads a SideWinder packet.
+ * js_sw_read_packet() is a function which reads either a data packet, or an
+ * identification packet from a SideWinder joystick. Better don't try to
+ * understand this, since all the ugliness of the Microsoft Digital
+ * Overdrive protocol is concentrated in this function. If you really want
+ * to know how this works, first go watch a couple horror movies, so that
+ * you are well prepared, read US patent #5628686 and then e-mail me,
+ * and I'll send you an explanation.
+ *					Vojtech <vojtech@suse.cz>
  */
 
-static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data)
+static int js_sw_read_packet(int io, int speed, unsigned char *buf, int length, int id)
 {
-	static unsigned char buf[JS_SW_MAX_LENGTH];
-	unsigned char u, v;
-	int i;
 	unsigned long flags;
-	unsigned int t, t1;
-
-	int length = l1 < l2 ? l2 : l1;
-	int start = (js_time_speed * JS_SW_MAX_START) >> 10;
-	strobe = (js_time_speed * strobe) >> 10;
+	int timeout, bitout, sched, i, kick, start, strobe;
+	unsigned char pending, u, v;
 
-	i = 0;
+	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);
-	__cli();
-	outb(0xff,io);
+        __save_flags(flags);					/* Quiet, please */
+        __cli();
 
+	outb(0xff, io);						/* Trigger */
 	v = inb(io);
-	t = js_get_time();
 
 	do {
+		bitout--;
 		u = v;
 		v = inb(io);
-		t1 = js_get_time();
-	} while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start);
+	} while (!(~v & u & 0x10) && (bitout > 0));		/* Wait for first falling edge on clock */
 
-	t = t1;
+	if (bitout > 0) bitout = strobe;			/* Extend time if not timed out */
+
+	while ((timeout > 0 || bitout > 0) && (i < length)) {
+
+		timeout--;
+		bitout--;					/* Decrement timers */
+		sched--;
 
-	do {
-		v = inb(io);
-		t1 = js_get_time();
-		if ((u ^ v) & v & 0x10) {
-			buf[i++] = v >> 5;
-			t = t1;
-		}
 		u = v;
-	} while (i < length && js_delta(t1,t) < strobe);
+		v = inb(io);
 
-	__restore_flags(flags);
+		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 */ 
+		}
+	}
 
-	*data = 0;
+	__restore_flags(flags);					/* Done - relax */
 
-	if (i == l1) {
-		t = i > 64 ? 64 : i;
-		for (i = 0; i < t; i++)
-			*data |= (__u64) (buf[i] & 1) << i;
-		return t;
-	}
-	if (i == l2) {
-		t = i > 22 ? 22 : i;
-		for (i = 0; i < t; i++)
-			*data |= (__u64) buf[i] << (3 * i);
-		return t * 3;
+#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_parity computes parity of __u64
+ * 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)
 {
-	t ^= t >> 32;
-	t ^= t >> 16;
-	t ^= t >> 8;
-	t ^= t >> 4;
-	t ^= t >> 2;
-	t ^= t >> 1;
-	return t & 1;
+	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_csum() computes checksum of nibbles in __u64
+ * js_sw_ccheck() checks synchronization bits and computes checksum of nibbles.
  */
 
-static int js_sw_csum(__u64 t)
+static int js_sw_check(__u64 t)
 {
 	char sum = 0;
-	while (t) {
+
+	if ((t & 0x8080808080808080ULL) ^ 0x80)			/* Sync */
+		return -1;
+
+	while (t) {						/* Sum */
 		sum += t & 0xf;
 		t >>= 4;
 	}
+
 	return sum & 0xf;
 }
 
 /*
- * js_sw_read() reads and analyzes SideWinder joystick data.
+ * js_sw_parse() analyzes SideWinder joystick data, and writes the results into
+ * the axes and buttons arrays.
  */
 
-static int js_sw_read(void *xinfo, int **axes, int **buttons)
+static int js_sw_parse(unsigned char *buf, struct js_sw_info *info, int **axes, int **buttons)
 {
-	struct js_sw_info *info = xinfo;
-	__u64 data;
 	int hat, i;
 
-	switch (info->mode) {
-
-		case JS_SW_MODE_3DP:
+	switch (info->type) {
 
-			if (info->optimize) {
-				i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data);
-			} else {
-				i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data);
-				if (i == 198) info->optimize = 1;
-			}
+		case JS_SW_TYPE_3DP:
+		case JS_SW_TYPE_F23:
 
-			if (i < 60) {
-				js_sw_init_digital(info->io);
-				info->optimize = 0;
-				return -1;
-			}
+			if (js_sw_check(GB(0,64,0)) || (hat = GB(6,1,3) | GB(60,3,0))  > 8) return -1;
 
-			if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) ||
-				(hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) {
-				info->optimize = 0;
-				return -1;
-			}
-			axes[0][0] = ((data <<  4) & 0x380) | ((data >> 16) & 0x07f);
-			axes[0][1] = ((data <<  7) & 0x380) | ((data >> 24) & 0x07f);
-			axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f);
-			axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f);
+			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] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f);
+			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_MODE_PP:
+		case JS_SW_TYPE_PP:
+		case JS_SW_TYPE_FFP:
 
-			if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1;
-			if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1;
+			if (!js_sw_parity(GB(0,48,0)) || (hat = GB(42,4,0)) > 8) return -1;
 
-			axes[0][0] = (data >>  9) & 0x3ff;
-			axes[0][1] = (data >> 19) & 0x3ff;
-			axes[0][2] = (data >> 29) & 0x07f;
-			axes[0][3] = (data >> 36) & 0x03f;
+			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] = ~data & 0x1ff;
+			buttons[0][0] = ~GB(0,9,0);
 
 			return 0;
 
-		case JS_SW_MODE_GP:
+		case JS_SW_TYPE_FSP:
 
-			if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number,
-				JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1;
-			if (js_sw_parity(data)) return -1;
-
-			for (i = 0; i < info->number; i++) {
-				axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1);
-				axes[i][1] = ( data       & 1) - ((data >> 1) & 1);
-				buttons[i][0] = (~data >> 4) & 0x3ff;
-				data >>= 15;
-			}
+			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 */
 
-		default:
-			return -1;
+		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;
 }
 
 /*
@@ -290,7 +500,7 @@
  * SideWinders.
  */
 
-static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr)
+static void __init js_sw_init_corr(int num_axes, int type, int number, struct js_corr **corr)
 {
 	int i, j;
 
@@ -305,9 +515,10 @@
 			corr[i][j].coef[3] = (1 << 29) / (511 - 32);
 		}
 
-		switch (mode) {
+		switch (type) {
 
-			case JS_SW_MODE_3DP:
+			case JS_SW_TYPE_3DP:
+			case JS_SW_TYPE_F23:
 
 				corr[i][2].type = JS_CORR_BROKEN;
 				corr[i][2].prec = 4;
@@ -320,33 +531,73 @@
 
 			break;
 
-			case JS_SW_MODE_PP:
+			case JS_SW_TYPE_PP:
+			case JS_SW_TYPE_FFP:
 
 				corr[i][2].type = JS_CORR_BROKEN;
-				corr[i][2].prec = 1;
-				corr[i][2].coef[0] = 63 - 4;
-				corr[i][2].coef[1] = 64 + 4;
-				corr[i][2].coef[2] = (1 << 29) / (63 - 4);
-				corr[i][2].coef[3] = (1 << 29) / (63 - 4);
+				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 = 0;
-				corr[i][3].coef[0] = 31 - 2;
-				corr[i][3].coef[1] = 32 + 2;
-				corr[i][3].coef[2] = (1 << 29) / (31 - 2);
-				corr[i][3].coef[3] = (1 << 29) / (31 - 2);
+				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++) {
+		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;
@@ -358,83 +609,211 @@
 }
 
 /*
+ * 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 *name;
-	int i, j, axes, buttons;
-	__u64 data;
-	unsigned char u;
+	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;
-	if (((u = inb(io)) & 3) == 3) return port;
-	outb(0xff,io);
-	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
-
-	i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
-
-	if (!i) {
-		udelay(JS_SW_MIN_TIME);
-		js_sw_init_digital(io);
-		udelay(JS_SW_MAX_TIME);
-		i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
-	}
-
-	switch (i) {
-		case 0:
-			return port;
-		case 5:
-		case 10:
-		case 15:
-		case 20:
-		case 30:
-		case 45:
-		case 60:
-			info.mode = JS_SW_MODE_GP;
-			outb(0xff,io);							/* Kick into 3-bit mode */
-			udelay(JS_SW_MAX_TIME);
-			i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data);	/* Get total length */
-			udelay(JS_SW_MIN_TIME);
-			j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data);	/* Get subpacket length */
-			if (!i || !j) {
-				printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d),"
-							" but not idenfitied.\n", i, j);
-				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;
 			}
-			info.number = i / j;
-			axes = 2; buttons = 10; name = "SideWinder GamePad";
-			break;
-		case 16:
-		case 48:
-			info.mode = JS_SW_MODE_PP; info.number = 1;
-			axes = 6; buttons = 9; name = "SideWinder Precision Pro";
-			break;
-		case 64:
-		case 66:
-			info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0;
-			axes = 6; buttons = 8; name = "SideWinder 3D Pro";
-			break;
-		case 72:
-			return port;
-		default:
-			printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected "
-				"(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
-				io, i, (int)(data >> 32), (int)(data & 0xffffffff));
-			return port;
+		}
+
+	} while (k && !info.type);
+
+	if (!info.type) {
+		printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected "
+			"(io=%#x), contact <vojtech@suse.cz>\n", io);
+		js_sw_print_packet("ID", j * 3, idbuf, 3);
+		js_sw_print_packet("Data", i * m, buf, m);
+		return port;
 	}
 
-	info.io = io;
+#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 at %#x\n",
-			js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io);
-	js_sw_init_corr(axes, info.mode, info.number, port->corr);
+		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], js_sw_open, js_sw_close), 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;
 }
@@ -463,9 +842,9 @@
 	int i;
 	struct js_sw_info *info;
 
-	while (js_sw_port != NULL) {
+	while (js_sw_port) {
 		for (i = 0; i < js_sw_port->ndevs; i++)
-			if (js_sw_port->devs[i] != NULL)
+			if (js_sw_port->devs[i])
 				js_unregister_device(js_sw_port->devs[i]);
 		info = js_sw_port->info;
 		release_region(info->io, 1);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-spaceball.c linux-2.2.14-pre16/drivers/char/joystick/joy-spaceball.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-spaceball.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-spaceball.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,347 @@
+/*
+ *  joy-spaceball.c  Version 0.1
+ *
+ *  Copyright (c) 1998 David Thompson
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *  Copyright (c) 1999 Joseph Krahn
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * the SpaceTec SpaceBall 4000 FLX.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define	N_JOYSTICK_SBALL	12
+#define JS_SBALL_MAX_LENGTH	128
+
+/*
+ * List of SpaceBalls.
+ */
+
+static struct js_port* js_sball_port = NULL;
+
+/*
+ * Per-Ball data.
+ */
+
+struct js_sball_info {
+	struct tty_struct* tty;
+	struct js_port* port;
+	int idx;
+	unsigned char data[JS_SBALL_MAX_LENGTH];
+	int js;
+	char used;
+};
+
+/*
+ * js_sball_process_packet() decodes packets the driver receives from the
+ * SpaceBall.
+ */
+
+static void js_sball_process_packet(struct js_sball_info* info)
+{
+	int i,b;
+	int **axes = info->port->axes;
+	int **buttons = info->port->buttons;
+	unsigned char *data = info->data;
+
+	if (info->idx < 2) return;
+
+	switch (info->data[0]) {
+
+		case '@':					/* Reset packet */
+			info->data[info->idx - 1] = 0;
+			for (i = 1; i < info->idx && info->data[i] == ' '; i++);
+
+			printk(KERN_INFO "js%d: SpaceBall 4000FLX [%s] on %s%d\n",
+				info->js, info->data + i, info->tty->driver.name,
+				MINOR(info->tty->device) - info->tty->driver.minor_start);
+
+			memset(axes[0], 0, sizeof(int) * 6);	/* All axes, buttons should be zero */
+			buttons[0][0] = 0;
+			break;
+
+		case 'D':					/* Ball data */
+			if (info->idx != 16) return;
+			if (!info->port->devs[0]) return;
+			axes[0][0] = ((data[3] << 8) | data[4] );
+			axes[0][1] = ((data[5] << 8) | data[6] );
+			axes[0][2] = ((data[7] << 8) | data[8] );
+			axes[0][3] = ((data[9] << 8) | data[10]);
+			axes[0][4] = ((data[11]<< 8) | data[12]);
+			axes[0][5] = ((data[13]<< 8) | data[14]);
+			for(i = 0; i < 6; i ++) if (axes[0][i] & 0x8000) axes[0][i] -= 0x10000;
+			break;
+
+		case 'K':				/* Button data, part1 */
+			/* We can ignore this packet for the SB 4000FLX. */
+			break;
+
+		case '.':				/* Button data, part2 */
+			if (info->idx != 4) return;
+			if (!info->port->devs[0]) return;
+			b = (data[1] & 0xbf) << 8 | (data[2] & 0xbf);
+			buttons[0][0] = ((b & 0x1f80) >> 1 | (b & 0x3f));
+			break;
+
+		case '?':				/* Error packet */
+			info->data[info->idx - 1] = 0;
+			printk(KERN_ERR "joy-spaceball: Device error. [%s]\n",info->data+1);
+			break;
+
+		case 'A':				/* reply to A command (ID# report) */
+		case 'B':				/* reply to B command (beep) */
+		case 'H':				/* reply to H command (firmware report) */
+		case 'S':				/* reply to S command (single beep) */
+		case 'Y':				/* reply to Y command (scale flag) */
+		case '"':				/* reply to "n command (report info, part n) */
+			break;
+
+		case 'P':				/* Pulse (update) speed */
+			if (info->idx != 3) return;     /* data[2],data[3] = hex digits for speed 00-FF */
+			break;
+
+		default:
+			printk("joy-spaceball: Unknown packet %d length %d:", data[0], info->idx);
+			for (i = 0; i < info->idx; i++) printk(" %02x", data[i]);
+			printk("\n");
+			return;
+	}
+}
+
+/*
+ * js_sball_open() is a callback from the joystick device open routine.
+ */
+
+static int js_sball_open(struct js_dev *jd)
+{
+	struct js_sball_info *info = jd->port->info;
+	info->used++;
+	MOD_INC_USE_COUNT;
+	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);
+	}
+	MOD_DEC_USE_COUNT;
+	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;
+
+	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", js_sball_open, js_sball_close);
+
+	js_sball_init_corr(js_sball_port->corr);
+
+	MOD_INC_USE_COUNT;
+
+	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,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+	name:		"spaceball",
+#endif
+	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 -urN linux-2.2.14-pre15/drivers/char/joystick/joy-spaceorb.c linux-2.2.14-pre16/drivers/char/joystick/joy-spaceorb.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-spaceorb.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-spaceorb.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,305 @@
+/*
+ *  joy-spaceorb.c  Version 0.1
+ *
+ *  Copyright (c) 1998 David Thompson
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * the SpaceTec SpaceOrb 360 and SpaceBall Avenger 6dof controllers.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define	N_JOYSTICK_ORB		15
+#define JS_ORB_MAX_LENGTH	64
+
+/*
+ * List of SpaceOrbs.
+ */
+
+static struct js_port* js_orb_port = NULL;
+
+/*
+ * Per-Orb data.
+ */
+
+struct js_orb_info {
+	struct tty_struct* tty;
+	struct js_port* port;
+	int idx;
+	unsigned char data[JS_ORB_MAX_LENGTH];
+	int js;
+	char used;
+};
+
+static unsigned char js_orb_xor[] = "SpaceWare";
+
+static unsigned char *js_orb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
+		"Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; 
+
+/*
+ * js_orb_process_packet() decodes packets the driver receives from the
+ * SpaceOrb.
+ */
+
+static void js_orb_process_packet(struct js_orb_info* info)
+{
+	int i;
+	int **axes = info->port->axes;
+	int **buttons = info->port->buttons;
+	unsigned char *data = info->data;
+	unsigned char c = 0;
+
+	if (info->idx < 2) return;
+	for (i = 0; i < info->idx; i++) c ^= data[i];
+	if (c) return;
+
+	switch (info->data[0]) {
+
+		case 'R':				/* Reset packet */
+			info->data[info->idx - 1] = 0;
+			for (i = 1; i < info->idx && info->data[i] == ' '; i++);
+			printk(KERN_INFO "js%d: SpaceOrb 360 [%s] on %s%d\n",
+				info->js, info->data + i, info->tty->driver.name,
+				MINOR(info->tty->device) - info->tty->driver.minor_start);
+			break;
+
+		case 'D':				/* Ball + button data */
+			if (info->idx != 12) return;
+			if (!info->port->devs[0]) return;
+			for (i = 0; i < 9; i++) info->data[i+2] ^= js_orb_xor[i]; 
+			axes[0][0] = ( data[2]         << 3) | (data[ 3] >> 4);
+			axes[0][1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
+			axes[0][2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
+			axes[0][3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
+			axes[0][4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
+			axes[0][5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
+			for(i = 0; i < 6; i ++) if (axes[0][i] & 0x200) axes[0][i] -= 1024;
+			buttons[0][0] = data[1];
+			break;
+
+		case 'K':				/* Button data */
+			if (info->idx != 5) return;
+			if (!info->port->devs[0]) return;
+			buttons[0][0] = data[2];
+			break;
+
+		case 'E':				/* Error packet */
+			if (info->idx != 4) return;
+			printk(KERN_ERR "joy-spaceorb: Device error. [ ");
+			for (i = 0; i < 7; i++)
+				if (data[1] & (1 << i))
+					printk("%s ", js_orb_errors[i]);
+			printk("]\n");
+			break;
+
+		case 'N':				/* Null region */
+			if (info->idx != 3) return;
+			break;
+
+		case 'P':				/* Pulse (update) speed */
+			if (info->idx != 4) return;
+			break;
+
+		default:
+			printk("joy-spaceorb: Unknown packet %d length %d:", data[0], info->idx);
+			for (i = 0; i < info->idx; i++) printk(" %02x", data[i]);
+			printk("\n");
+			return;
+	}
+}
+
+/*
+ * js_orb_open() is a callback from the joystick device open routine.
+ */
+
+static int js_orb_open(struct js_dev *jd)
+{
+	struct js_orb_info *info = jd->port->info;
+	info->used++;
+	MOD_INC_USE_COUNT;
+	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);
+	}
+	MOD_DEC_USE_COUNT;
+	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;
+
+	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", js_orb_open, js_orb_close);
+
+	js_orb_init_corr(js_orb_port->corr);
+
+	MOD_INC_USE_COUNT;
+
+	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,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+	name:		"spaceorb",
+#endif
+	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 -urN linux-2.2.14-pre15/drivers/char/joystick/joystick.c linux-2.2.14-pre16/drivers/char/joystick/joystick.c
--- linux-2.2.14-pre15/drivers/char/joystick/joystick.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joystick.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
- * joystick.c  Version 1.2
+ *  joystick.c  Version 1.2
  *
- * Copyright (c) 1996-1998 Vojtech Pavlik
+ *  Copyright (c) 1996-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -26,14 +28,13 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/segment.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/joystick.h>
@@ -42,10 +43,9 @@
 #include <linux/malloc.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#include <asm/spinlock.h>
 #include <linux/poll.h>
-#endif
+#include <linux/config.h>
+#include <linux/init.h>
 
 /*
  * Configurable parameters.
@@ -54,6 +54,15 @@
 #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.
  */
 
@@ -74,263 +83,13 @@
 static int js_use_count = 0;
 
 /*
- * Exported variables.
- */
-
-unsigned int js_time_speed = 0;
-js_time_func js_get_time;
-js_delta_func js_delta;
-
-unsigned int js_time_speed_a = 0;
-js_time_func js_get_time_a;
-js_delta_func js_delta_a;
-
-/*
  * Module info.
  */
 
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_SUPPORTED_DEVICE("js");
 
 /*
- * js_get_time_*() are different functions to get current time.
- * js_delta_*() are functions to compute time difference.
- */
-
-#ifdef __i386__
-
-static unsigned int js_get_time_rdtsc(void)
-{
-	unsigned int x;
-	__asm__ __volatile__ ( "rdtsc" : "=A" (x) );
-	return x;
-}
-
-static unsigned int js_get_time_pit(void)
-{
-	unsigned long flags;
-	unsigned int x;
-
-	__save_flags(flags);
-	__cli();
-	outb(0, 0x43);
-	x = inb(0x40);
-	x |= inb(0x40) << 8;
-	__restore_flags(flags);
-
-	return x;
-}
-
-static int js_delta_pit(unsigned int x, unsigned int y)
-{
-	return y - x + ( y < x ? 1193180L / HZ : 0 );
-}
-
-static unsigned int js_get_time_counter(void)
-{
-	static int time_counter = 0;
-	return time_counter++;
-}
-
-#else
-#ifdef __alpha__
-
-static unsigned int js_get_time_rpcc(void)
-{
-	unsigned int x;
-	__asm__ __volatile__ ( "rpcc %0" : "=r" (x) );
-	return x;
-}
-
-#else
-
-#ifndef MODULE
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-static unsigned int js_get_time_system(void)
-{
-	static struct timeval js_tv;
-	get_fast_time(&js_tv);
-	return js_tv.tv_sec * 1000000L + js_tv.tv_usec;
-}
-#endif
-#endif
-
-#endif
-#endif
-
-static int js_delta_normal(unsigned int x, unsigned int y)
-{
-	return x - y;
-}
-
-/*
- * js_calibrate_time() calibrates a given timer.
- */
-
-static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta)
-{
-	unsigned int t1, t2, t3;
-	unsigned long flags;
-
-	__save_flags(flags);
-	__cli();
-	t1 = get_time();
-	udelay(1000);
-	t2 = get_time();
-	t3 = get_time();
-	__restore_flags(flags);
-
-	return delta(t2, t1) - delta(t3, t2);
-}
-
-/*
- * js_calibrate_time_counter() calibrates the counter timer, which can't
- * be calibrated using the above function.
- */
-
-#ifdef __i386__
-
-static int __init js_calibrate_time_counter(void)
-{
-	unsigned int i, j, t1, t2, t3;
-
-	j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies);
-	j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies);
-
-	j = (t2 - t1) * HZ / 1000;
-
-	t1 = js_get_time_pit();
-	for (i = 0; i < 1000; i++) {
-		inb(0x201);
-		js_get_time_counter();
-	}
-	t2 = js_get_time_pit();
-	t3 = js_get_time_pit();
-
-	i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2));
-
-	if (DIFF(i,j) > 5)
-		printk(KERN_WARNING "js: Counter timer calibration unsure,"
-			" pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i);
-
-	return (i + j) >> 1;
-}
-
-#endif
-
-/*
- * js_setup_time chooses the best available timers
- * on the system and calibrates them.
- */
-
-static int __init js_setup_time(void)
-{
-	int t;
-	char *name, *name_a;
-
-	name = "";
-	name_a = "";
-	js_time_speed = 0;
-	js_time_speed_a = 0;
-
-#ifdef __i386__
-
-	t = js_calibrate_time(js_get_time_pit, js_delta_pit);
-
-	if (DIFF(t, 1193) > 5)
-		printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n"
-		       KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n",
-			t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193));
-
-	if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) {
-
-		js_time_speed_a = t;
-		js_get_time_a = js_get_time_rdtsc;
-		js_delta_a = js_delta_normal;
-		js_time_speed = t;
-		js_get_time = js_get_time_rdtsc;
-		js_delta = js_delta_normal;
-		name = "RDTSC";
-
-	} else {
-
-		js_time_speed_a = t;
-		js_get_time_a = js_get_time_pit;
-		js_delta_a = js_delta_pit;
-		name_a = "PIT";
-
-		t = js_calibrate_time_counter();
-
-		js_time_speed = t;
-		js_get_time = js_get_time_counter;
-		js_delta = js_delta_normal;
-		name = "counter";
-
-	}
-
-#else
-#ifdef __alpha__
-
-	t = js_calibrate_time(js_get_time_rpcc, js_delta_normal);
-
-	js_time_speed_a = t;
-	js_get_time_a = js_get_time_rpcc;
-	js_delta_a = js_delta_normal;
-	js_time_speed = t;
-	js_get_time = js_get_time_rpcc;
-	js_delta = js_delta_normal;
-	name = "RPCC";
-
-#else
-
-#ifndef MODULE
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-	t = js_calibrate_time(js_get_time_system, js_delta_normal);
-
-	js_time_speed_a = t;
-	js_get_time_a = js_get_time_system;
-	js_delta_a = js_delta_normal;
-	js_time_speed = t;
-	js_get_time = js_get_time_system;
-	js_delta = js_delta_normal;
-	name = "system";
-#endif
-#endif
-
-#endif
-#endif
-
-	printk(KERN_INFO "js: Version %d.%d.%d ",
-		JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
-
-	if (js_time_speed_a <= 0 || js_time_speed <= 0) {
-		printk("\n");
-		return -1;
-	}
-
-	printk("using ");
-
-	if (js_time_speed > 10000) {
-		t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500);
-		printk("%d MHz ", t);
-	} else {
-		t = js_time_speed / 10 + (js_time_speed % 10 >= 5);
-		printk("%d.%02d MHz ", t / 100, t % 100);
-	}
-
-	if (js_get_time_a != js_get_time) {
-		t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5);
-		printk("%s timer and %d.%02d MHz %s timer.\n",
-			name, t / 100, t % 100, name_a);
-	} else {
-		printk("%s timer.\n", name);
-	}
-
-	return 0;
-}
-
-
-/*
  * js_correct() performs correction of raw joystick data.
  */
 
@@ -364,7 +123,6 @@
 	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.
@@ -457,14 +215,17 @@
 	struct js_dev *curd = js_dev;
 	unsigned long flags;
 
-	while (curp != NULL) {
-		curp->read(curp->info, curp->axes, curp->buttons);
+	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 != NULL) {
+	while (curd) {
 		if (data) {
 			js_process_data(curd);
 			js_sync_buff(curd);
@@ -485,13 +246,9 @@
  * space.
  */
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
-#else
-static int js_read(struct inode *inode, struct file *file, char *buf, int count)
-#endif
 {
-	struct wait_queue wait = { current, NULL };
+	DECLARE_WAITQUEUE(wait, current);
 	struct js_event *buff = (void *) buf;
 	struct js_list *curl;
 	struct js_dev *jd;
@@ -525,8 +282,8 @@
 
 		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);
-			current->state = TASK_INTERRUPTIBLE;
 
 			while (GOF(curl->tail) == jd->bhead) {
 
@@ -575,13 +332,8 @@
 
 			tmpevent.time = jiffies * (1000/HZ);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 			if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event)))
 				retval = -EFAULT;
-#else
-			if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event))))
-				memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event));
-#endif
 
 			curl->startup++;
 			written++;
@@ -593,17 +345,11 @@
 
 		while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) {
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 			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;
-#else
-			if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) {
-				memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event));
-				put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time);
-			}
-#endif
+
 			curl->tail = new_tail;
 			written++;
 		}
@@ -624,15 +370,9 @@
 		data.y = jd->num_axes < 2 ? 0 :
 			((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 		retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
-#else
-		if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) {
-			memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE));
-		}
-#endif
 
-		curl->startup = 0;
+		curl->startup = jd->num_axes + jd->num_buttons;
 		curl->tail = GOB(jd->bhead);
 		if (!retval) retval = sizeof(struct JS_DATA_TYPE);
 	}
@@ -644,12 +384,12 @@
 	if (orig_tail == jd->tail) {
 		new_tail = curl->tail;
 		curl = jd->list;
-		while (curl != NULL && curl->tail != jd->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 == NULL) jd->tail = new_tail;
+		if (!curl) jd->tail = new_tail;
 	}
 
 	spin_unlock_irqrestore(&js_lock, flags);
@@ -661,8 +401,6 @@
  * js_poll() does select() support.
  */
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-
 static unsigned int js_poll(struct file *file, poll_table *wait)
 {
 	struct js_list *curl = file->private_data;
@@ -676,20 +414,6 @@
 	return retval;
 }
 
-#else
-
-static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
-{
-	struct js_list *curl = file->private_data;
-	if (sel_type == SEL_IN) {
-		if (GOF(curl->tail) != curl->dev->bhead) return 1;
-		select_wait(&curl->dev->wait, wait);
-	}
-	return 0;
-}
-
-#endif
-
 /*
  * js_ioctl handles misc ioctl calls.
  */
@@ -703,8 +427,6 @@
 	curl = file->private_data;
 	jd = curl->dev;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-
 	switch (cmd) {
 
 /*
@@ -757,95 +479,6 @@
 			}
 	}
 
-#else
-
-	switch (cmd) {
-
-/*
- * 0.x compatibility
- */
-
-		case JS_SET_CAL:
-			if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg,
-				sizeof(struct JS_DATA_TYPE))) return -EFAULT;
-			memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg,
-				sizeof(struct JS_DATA_TYPE));
-			return 0;
-		case JS_GET_CAL:
-			if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg,
-				sizeof(struct JS_DATA_TYPE))) return -EFAULT;
-			memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR,
-				sizeof(struct JS_DATA_TYPE));
-			return 0;
-		case JS_SET_TIMEOUT:
-			if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT;
-			js_comp_glue.JS_TIMEOUT = get_user((int *) arg);
-			return 0;
-		case JS_GET_TIMEOUT:
-			if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT;
-			put_user(js_comp_glue.JS_TIMEOUT, (int *) arg);
-			return 0;
-		case JS_SET_TIMELIMIT:
-			if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT;
-			js_comp_glue.JS_TIMELIMIT = get_user((long *) arg);
-			return 0;
-		case JS_GET_TIMELIMIT:
-			if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT;
-			put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg);
-			return 0;
-		case JS_SET_ALL:
-			if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg,
-				sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
-			memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg,
-				sizeof(struct JS_DATA_SAVE_TYPE));
-			return 0;
-		case JS_GET_ALL:
-			if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg,
-				sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT;
-			memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue,
-				sizeof(struct JS_DATA_SAVE_TYPE));
-			return 0;
-
-/*
- * 1.x ioctl calls
- */
-
-		case JSIOCGVERSION:
-			if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT;
-			put_user(JS_VERSION, (__u32 *) arg);
-			return 0;
-		case JSIOCGAXES:
-			if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
-			put_user(jd->num_axes, (__u8 *) arg);
-			return 0;
-		case JSIOCGBUTTONS:
-			if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT;
-			put_user(jd->num_buttons, (__u8 *) arg);
-			return 0;
-		case JSIOCSCORR:
-			if (verify_area(VERIFY_READ, (struct js_corr *) arg,
-				sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
-			memcpy_fromfs(jd->corr, (struct js_corr *) arg,
-				sizeof(struct js_corr) * jd->num_axes);
-			return 0;
-		case JSIOCGCORR:
-			if (verify_area(VERIFY_WRITE, (struct js_corr *) arg,
-				sizeof(struct js_corr) * jd->num_axes)) return -EFAULT;
-			memcpy_tofs((struct js_corr *) arg,
-				jd->corr, sizeof(struct js_corr) * jd->num_axes);
-			return 0;
-		default:
-			if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
-				len = strlen(jd->name) + 1;
-				if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT;
-				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
-				memcpy_tofs((char *) arg, jd->name, len);
-				return len;
-			}
-	}
-
-#endif
-
 	return -EINVAL;
 }
 
@@ -867,21 +500,20 @@
 
 	spin_lock_irqsave(&js_lock, flags);
 
-	while (i > 0 && jd != NULL) {
+	while (i > 0 && jd) {
 		jd = jd->next;
 		i--;
 	}
 
 	spin_unlock_irqrestore(&js_lock, flags);
 
-	if (jd == NULL) return -ENODEV;
+	if (!jd) return -ENODEV;
 
 	if ((result = jd->open(jd))) return result;
 
-	MOD_INC_USE_COUNT;
-	if (!js_use_count++) js_do_timer(0);
+	if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL))) {
 
-	if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL)) != NULL) {
+		MOD_INC_USE_COUNT;
 
 		spin_lock_irqsave(&js_lock, flags);
 
@@ -896,6 +528,8 @@
 
 		spin_unlock_irqrestore(&js_lock, flags);
 
+		if (!js_use_count++) js_do_timer(0);
+
 	} else {
 		result = -ENOMEM;
 	}
@@ -908,11 +542,7 @@
  * used by it.
  */
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 static int js_release(struct inode *inode, struct file *file)
-#else
-static void js_release(struct inode *inode, struct file *file)
-#endif
 {
 	struct js_list *curl = file->private_data;
 	struct js_dev *jd = curl->dev;
@@ -925,11 +555,11 @@
 	while (*curp && (*curp != curl)) curp = &((*curp)->next);
 	*curp = (*curp)->next;
 
-	if (jd->list != NULL)
+	if (jd->list)
 	if (curl->tail == jd->tail) {
 		curl = jd->list;
 		new_tail = curl->tail;
-		while (curl != NULL && curl->tail != jd->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;
@@ -946,9 +576,7 @@
 
 	jd->close(jd);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 	return 0;
-#endif
 }
 
 /*
@@ -967,7 +595,7 @@
 	printk(",--- Dumping Devices:\n");
 	printk("| js_dev = %x\n", (int) js_dev);
 
-	while (curd != NULL) {
+	while (curd) {
 		printk("|  %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n",
 			curd->next ? "|":"`",
 			(int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io);
@@ -977,7 +605,7 @@
 	printk(">--- Dumping ports:\n");
 	printk("| js_port = %x\n", (int) js_port);
 
-	while (curp != NULL) {
+	while (curp) {
 		printk("|  %s-port %x, next %x, io %#x, devices %d\n",
 			curp->next ? "|":"`",
 			(int) curp, (int) curp->next, curp->io, curp->ndevs);
@@ -1009,7 +637,7 @@
 	int i;
 	unsigned long flags;
 
-	if ((all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)) == NULL)
+	if (!(all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)))
 		return NULL;
 
 	curp = all;
@@ -1018,6 +646,8 @@
 	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;
@@ -1035,7 +665,7 @@
 
 	spin_lock_irqsave(&js_lock, flags);
 
-	while (*ptrp != NULL) ptrp=&((*ptrp)->next);
+	while (*ptrp) ptrp=&((*ptrp)->next);
 	*ptrp = curp;
 
 	spin_unlock_irqrestore(&js_lock, flags);
@@ -1051,7 +681,9 @@
 
 	spin_lock_irqsave(&js_lock, flags);
 
-	while (*curp != NULL && (*curp != port)) curp = &((*curp)->next);
+	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);
@@ -1071,9 +703,9 @@
 	int i = 0;
 	unsigned long flags;
 
-	if ((all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) +
+	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)) == NULL)
+			axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL)))
 		return -1;
 
 	curd = all;
@@ -1081,10 +713,11 @@
 	curd->next = NULL;
 	curd->list = NULL;
 	curd->port = port;
-	curd->wait = NULL;
 	curd->open = open;
 	curd->close = close;
 
+	init_waitqueue_head(&curd->wait);
+
 	curd->ahead = 0;
 	curd->bhead = 0;
 	curd->tail = JS_BUFF_SIZE - 1;
@@ -1107,7 +740,7 @@
 
 	spin_lock_irqsave(&js_lock, flags);
 
-	while (*ptrd != NULL) { ptrd=&(*ptrd)->next; i++; }
+	while (*ptrd) { ptrd=&(*ptrd)->next; i++; }
 	*ptrd = curd;
 
 	spin_unlock_irqrestore(&js_lock, flags);	
@@ -1122,7 +755,7 @@
 
 	spin_lock_irqsave(&js_lock, flags);
 
-	while (*curd != NULL && (*curd != dev)) curd = &((*curd)->next);
+	while (*curd && (*curd != dev)) curd = &((*curd)->next);
 	*curd = (*curd)->next;
 
 	spin_unlock_irqrestore(&js_lock, flags);	
@@ -1137,11 +770,7 @@
 static struct file_operations js_fops =
 {
 	read:		js_read,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 	poll:		js_poll,
-#else
-	select:		js_select,
-#endif
 	ioctl:		js_ioctl,
 	open:		js_open,
 	release:	js_release,
@@ -1158,15 +787,15 @@
 int __init js_init(void)
 #endif
 {
-	int result;
-
-	js_setup_time();
 
 	if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
 		printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
 		return -EBUSY;
 	}
 
+	printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n",
+		JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
+
 	spin_lock_init(&js_lock);
 
 	init_timer(&js_timer);
@@ -1177,44 +806,61 @@
 	js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT;
 	js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT;
 
-#ifdef MODULE
-	result = 0;
-#else
-	result = -ENODEV;
+#ifndef MODULE
+#ifdef CONFIG_JOY_PCI
+	js_pci_init();
+#endif
 #ifdef CONFIG_JOY_LIGHTNING
-	if (!js_l4_init()) result = 0;
+	js_l4_init();
 #endif
 #ifdef CONFIG_JOY_SIDEWINDER
-	if (!js_sw_init()) result = 0;
+	js_sw_init();
 #endif
-#ifdef CONFIG_JOY_ASSASIN
-	if (!js_as_init()) result = 0;
+#ifdef CONFIG_JOY_ASSASSIN
+	js_as_init();
 #endif
 #ifdef CONFIG_JOY_LOGITECH
-	if (!js_lt_init()) result = 0;
+	js_lt_init();
 #endif
 #ifdef CONFIG_JOY_THRUSTMASTER
-	if (!js_tm_init()) result = 0;
+	js_tm_init();
 #endif
 #ifdef CONFIG_JOY_GRAVIS
-	if (!js_gr_init()) result = 0;
+	js_gr_init();
+#endif
+#ifdef CONFIG_JOY_CREATIVE
+	js_cr_init();
 #endif
 #ifdef CONFIG_JOY_ANALOG
-	if (!js_an_init()) result = 0;
+	js_an_init();
 #endif
 #ifdef CONFIG_JOY_CONSOLE
-	if (!js_console_init()) result = 0;
+	js_console_init();
 #endif
 #ifdef CONFIG_JOY_DB9
-	if (!js_db9_init()) result = 0;
+	js_db9_init();
+#endif
+#ifdef CONFIG_JOY_TURBOGRAFX
+	js_tg_init();
 #endif
 #ifdef CONFIG_JOY_AMIGA
-	if (!js_am_init()) result = 0;
+	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
-	if (result) printk(KERN_ERR "js: no joysticks found\n");
 #endif
 
-	return result;
+	return 0;
 }
 
 /*
@@ -1229,3 +875,4 @@
 		printk(KERN_ERR "js: can't unregister device\n");
 }
 #endif
+
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-thrustmaster.c linux-2.2.14-pre16/drivers/char/joystick/joy-thrustmaster.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-thrustmaster.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-thrustmaster.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-thrustmaster.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -25,7 +27,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -38,28 +40,15 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/string.h>
+#include <linux/init.h>
 
 #define JS_TM_MAX_START		400
-#define JS_TM_MAX_STROBE	25
+#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_WCS3		4
-
-#define JS_TM_MODE_MAX		5	/* Last mode + 1 */
-
-#define JS_TM_BYTE_A0		0
-#define JS_TM_BYTE_A1		1
-#define JS_TM_BYTE_A2		3
-#define JS_TM_BYTE_A3		4
-#define JS_TM_BYTE_A4		6
-#define JS_TM_BYTE_A5		7
-
-#define JS_TM_BYTE_D0		2
-#define JS_TM_BYTE_D1		5
-#define JS_TM_BYTE_D2		8
-#define JS_TM_BYTE_D3		9
+#define JS_TM_MODE_FGP		163
 
 #define JS_TM_BYTE_ID		10
 #define JS_TM_BYTE_REV		11
@@ -68,48 +57,39 @@
 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;
 };
 
-static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x00, 0x22, 0x00};
-
 /*
  * js_tm_read_packet() reads a ThrustMaster packet.
  */
 
 static int js_tm_read_packet(int io, unsigned char *data)
 {
-	unsigned int t, t1;
+	unsigned int t, p;
 	unsigned char u, v, error;
 	int i, j;
 	unsigned long flags;
 
-	int start = (js_time_speed * JS_TM_MAX_START) >> 10;
-	int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10;
-
 	error = 0;
 	i = j = 0;
+	p = t = JS_TM_MAX_START;
 
 	__save_flags(flags);
 	__cli();
 	outb(0xff,io);
-
-	t = js_get_time();
+	
+	v = inb(io) >> 4;
 
 	do {
-		u = inb(io);
-		t1 = js_get_time();
-	} while ((u & 1) && js_delta(t1, t) < start);
-
-	t = t1;
-	u >>= 4;
-
-	do {
-		v = inb(io) >> 4;
-		t1 = js_get_time();
-		if ((u ^ v) & u & 2) {
+		t--;
+		u = v; v = inb(io) >> 4;
+		if (~v & u & 2) {
 			if (j) {
 				if (j < 9) {				/* Data bit */
 					data[i] |= (~v & 1) << (j - 1);
@@ -124,10 +104,9 @@
 				error |= ~v & 1;
 				j++;
 			}
-			t = t1;
+			p = t = (p - t) << 1;
 		}
-		u = v;
-	} while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe);
+	} while (!error && i < JS_TM_MAX_LENGTH && t > 0);
 
 	__restore_flags(flags);
 
@@ -142,46 +121,39 @@
 {
 	struct js_tm_info *info = xinfo;
 	unsigned char data[JS_TM_MAX_LENGTH];
+	int i;
 
-	if (js_tm_read_packet(info->io, data)) {
-		printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n");
-		return -1;
-	}
-	if (data[JS_TM_BYTE_ID] != info->mode) {
-		printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n",
-			data[JS_TM_BYTE_ID], info->mode);
-		return -1;
-	}
-	if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) {
-		printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n",
-			data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]);
-		return -1;
-	}
+	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][0] = data[JS_TM_BYTE_A0];
-			axes[0][1] = data[JS_TM_BYTE_A1];
-			axes[0][2] = data[JS_TM_BYTE_A2];
-			axes[0][3] = data[JS_TM_BYTE_A3];
-
-			axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1);
-			axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0]       & 1);
+			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_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06)
-				      | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30);
+			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:
 
-			axes[0][0] = data[JS_TM_BYTE_A0];
-			axes[0][1] = data[JS_TM_BYTE_A1];
+			buttons[0][0] = 0;
 
-			buttons[0][0] = ( data[JS_TM_BYTE_D0]       & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0)
-				      | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300);
+			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;
 
@@ -217,9 +189,9 @@
 
 static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr)
 {
-	int j;
+	int j = 0;
 
-	for (j = 0; j < num_axes; j++) {
+	for (; j < num_axes; j++) {
 		corr[0][j].type = JS_CORR_BROKEN;
 		corr[0][j].prec = 0;
 		corr[0][j].coef[0] = 127 - 2;
@@ -230,8 +202,7 @@
 
 	switch (mode) {
 		case JS_TM_MODE_M3DI: j = 4; break;
-		case JS_TM_MODE_3DRP: j = 2; break;
-		default:	      j = 0; break;
+		default: break;
 	}
 
 	for (; j < num_axes; j++) {
@@ -252,48 +223,46 @@
 static struct js_port __init *js_tm_probe(int io, struct js_port *port)
 {
 	struct js_tm_info info;
-	char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", NULL,
-						"ThrustMaster Rage 3D Gamepad", "ThrustMaster WCS III" };
-	char axes[JS_TM_MODE_MAX] = { 0, 6, 0, 2, 0 };
-	char buttons[JS_TM_MODE_MAX] = { 0, 5, 0, 10, 0 };
-
+	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 u;
+	unsigned char a, b;
+	int i;
 
 	if (check_region(io, 1)) return port;
 
-	if (((u = inb(io)) & 3) == 3) return port;
-	outb(0xff,io);
-	if (!((inb(io) ^ u) & ~u & 0xf)) return port;
-
-	if(js_tm_read_packet(io, data)) {
-		printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n");
-		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;
 
-	if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) {
-		printk(KERN_WARNING "joy-thrustmaster: unknown device detected "
-				    "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n",
-					io, info.mode);
-		return port;
-	}
+	for (i = 0; models[i].id && models[i].id != info.mode; i++);
 
-	if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) {
-		printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n",
-				data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]);
+	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, axes[info.mode], buttons[info.mode],
-				names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io);
-	js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr);
+		js_register_device(port, 0, a, b, name, js_tm_open, js_tm_close), name, data[JS_TM_BYTE_REV], io);
+	js_tm_init_corr(a, info.mode, port->axes, port->corr);
 
 	return port;
 }
@@ -321,7 +290,7 @@
 {
 	struct js_tm_info *info;
 
-	while (js_tm_port != NULL) {
+	while (js_tm_port) {
 		js_unregister_device(js_tm_port->devs[0]);
 		info = js_tm_port->info;
 		release_region(info->io, 1);
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-turbografx.c linux-2.2.14-pre16/drivers/char/joystick/joy-turbografx.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-turbografx.c	1998-12-01 19:05:05.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-turbografx.c	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,9 @@
 /*
  *  joy-turbografx.c  Version 1.2
  *
- *  Copyright (c) 1998 Vojtech Pavlik
+ *  Copyright (c) 1998-1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
  */
 
 /*
@@ -26,7 +28,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -39,9 +41,10 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_PARM(js_tg, "2-8i");
 MODULE_PARM(js_tg_2, "2-8i");
 MODULE_PARM(js_tg_3, "2-8i");
@@ -57,18 +60,14 @@
 #define JS_TG_BUTTON4	0x01
 #define JS_TG_BUTTON5	0x08
 
-static struct js_port* js_tg_port = NULL;
+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 {
-#ifdef USE_PARPORT
 	struct pardevice *port;	/* parport device */
-#else
-	int port;		/* hw port */
-#endif
 	int sticks;		/* joysticks connected */
 };
 
@@ -109,9 +108,7 @@
 	struct js_tg_info *info = dev->port->info;
 
 	if (!MOD_IN_USE) {
-#ifdef USE_PARPORT
 		if (parport_claim(info->port)) return -EBUSY; 
-#endif
 		JS_PAR_CTRL_OUT(0x04, info->port);
 	}
 	MOD_INC_USE_COUNT;
@@ -129,9 +126,7 @@
         MOD_DEC_USE_COUNT;
 	if (!MOD_IN_USE) {
 		JS_PAR_CTRL_OUT(0x00, info->port);
-#ifdef USE_PARPORT
         	parport_release(info->port);
-#endif
 	}
         return 0;
 }
@@ -142,16 +137,12 @@
 	struct js_tg_info *info;
 	int i;
 
-	while (js_tg_port != NULL) {
+	while (js_tg_port) {
 		for (i = 0; i < js_tg_port->ndevs; i++)
-			if (js_tg_port->devs[i] != NULL)
+			if (js_tg_port->devs[i])
 				js_unregister_device(js_tg_port->devs[i]);
 		info = js_tg_port->info;
-#ifdef USE_PARPORT
 		parport_unregister_device(info->port);
-#else
-		release_region(info->port, 3);
-#endif
 		js_tg_port = js_unregister_port(js_tg_port);
 	}
 }
@@ -186,33 +177,25 @@
 {
 	struct js_tg_info iniinfo;
 	struct js_tg_info *info = &iniinfo;
+	struct parport *pp;
 	int i;
 
 	if (config[0] < 0) return port;
 
-#ifdef USE_PARPORT
-	{
-		struct parport *pp;
-
-		if (config[0] > 0x10)
-			for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next);
-		else
-			for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--;
-
-		if (pp == NULL) {
-			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;
+	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;
 	}
-#else
-	info->port = config[0];
-	if (check_region(info->port, 3)) return port;
-	request_region(info->port, 3, "joystick (turbografx)");
-#endif
+
+	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;
@@ -221,24 +204,14 @@
 
 	for (i = 0; i < 7; i++)
 		if (config[i+1] > 0 && config[i+1] < 6) {
-#ifdef USE_PARPORT
 			printk(KERN_INFO "js%d: Multisystem joystick on %s\n",
 				js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close),
 				info->port->port->name);
-#else
-			printk(KERN_INFO "js%d: Multisystem joystick at %#x\n",
-				js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close),
-				info->port);
-#endif
 			info->sticks |= (1 << i);
 		}
 
         if (!info->sticks) {
-#ifdef USE_PARPORT
 		parport_unregister_device(info->port);
-#else
-		release_region(info->port, 3);
-#endif
 		return port;
         }
 		
@@ -248,18 +221,30 @@
 }
 
 #ifndef MODULE
-void __init js_tg_setup(char *str, int *ints)
+int __init js_tg_setup(SETUP_PARAM)
 {
 	int i;
-
-	if (!strcmp(str,"js_tg"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1];
-	if (!strcmp(str,"js_tg_2"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1];
-	if (!strcmp(str,"js_tg_3"))
-		for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1];
-
+	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
diff -urN linux-2.2.14-pre15/drivers/char/joystick/joy-warrior.c linux-2.2.14-pre16/drivers/char/joystick/joy-warrior.c
--- linux-2.2.14-pre15/drivers/char/joystick/joy-warrior.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.2.14-pre16/drivers/char/joystick/joy-warrior.c	2003-08-15 15:09:55.000000000 -0700
@@ -0,0 +1,318 @@
+/*
+ *  joy-warrior.c  Version 0.1
+ *
+ *  Copyright (c) 1998 David Thompson
+ *  Copyright (c) 1999 Vojtech Pavlik
+ *
+ *  Sponsored by SuSE
+ */
+
+/*
+ * This is a module for the Linux joystick driver, supporting
+ * the Logitech WingMan Warrior joystick.
+ */
+
+/*
+ * This program is free warftware; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ *  Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define	N_JOYSTICK_WAR		13
+#define JS_WAR_MAX_LENGTH	16
+
+/*
+ * List of Warriors.
+ */
+
+static struct js_port* js_war_port = NULL;
+
+static char js_war_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; 
+
+/*
+ * Per-Warrior data.
+ */
+
+struct js_war_info {
+	struct tty_struct* tty;
+	struct js_port* port;
+	int idx;
+	int len;
+	unsigned char data[JS_WAR_MAX_LENGTH];
+	char used;
+};
+
+/*
+ * js_war_process_packet() decodes packets the driver receives from the
+ * Warrior. It updates the data accordingly.
+ */
+
+static void js_war_process_packet(struct js_war_info* info)
+{
+	int **axes = info->port->axes;
+	int **buttons = info->port->buttons;
+	unsigned char *data = info->data;
+	int i;
+
+	if (!info->idx) return;
+
+	switch ((data[0] >> 4) & 7) {
+
+		case 1:					/* Button data */
+			if (!info->port->devs[0]) return;
+			buttons[0][0] = ((data[3] & 0xa) >> 1) | ((data[3] & 0x5) << 1);
+			return;
+		case 2:					/* Static status (Send !S to get one) */
+#if 0
+			printk("joy-warrior: Static status:");
+			for (i = 0; i < 12; i++)
+				printk(" %02x", info->data[i]);
+			printk("\n");
+#endif
+			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 4:					/* Dynamic status */
+#if 0
+			printk("joy-warrior: Dynamic status:");
+			for (i = 0; i < 4; i++)
+				printk(" %02x", info->data[i]);
+			printk("\n");
+#endif
+			return;
+		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;
+		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++;
+	MOD_INC_USE_COUNT;
+	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);
+	}
+	MOD_DEC_USE_COUNT;
+	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;
+
+	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", js_war_open, js_war_close),
+		tty->driver.name, MINOR(tty->device) - tty->driver.minor_start);
+
+	js_war_init_corr(js_war_port->corr);
+
+	MOD_INC_USE_COUNT;
+
+	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,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+        name:           "warrior",
+#endif
+	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 -urN linux-2.2.14-pre15/drivers/char/joystick/Makefile linux-2.2.14-pre16/drivers/char/joystick/Makefile
--- linux-2.2.14-pre15/drivers/char/joystick/Makefile	1998-10-08 17:25:16.000000000 -0700
+++ linux-2.2.14-pre16/drivers/char/joystick/Makefile	2003-08-15 15:09:55.000000000 -0700
@@ -1,23 +1,18 @@
 #
 # Makefile for the joystick drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
 
 O_TARGET := js.o
+OX_OBJS  :=
 O_OBJS   :=
+MX_OBJS  :=
 M_OBJS   :=
 
 ifeq ($(CONFIG_JOYSTICK),y)
-O_OBJS += joystick.o
+OX_OBJS += joystick.o
 else
   ifeq ($(CONFIG_JOYSTICK),m)
-  M_OBJS += joystick.o
+  MX_OBJS += joystick.o
   endif
 endif
 
@@ -37,11 +32,11 @@
   endif
 endif
 
-ifeq ($(CONFIG_JOY_ASSASIN),y)
-O_OBJS += joy-assasin.o
+ifeq ($(CONFIG_JOY_ASSASSIN),y)
+O_OBJS += joy-assassin.o
 else
-  ifeq ($(CONFIG_JOY_ASSASIN),m)
-  M_OBJS += joy-assasin.o
+  ifeq ($(CONFIG_JOY_ASSASSIN),m)
+  M_OBJS += joy-assassin.o
   endif
 endif
 
@@ -53,6 +48,14 @@
   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
@@ -85,6 +88,22 @@
   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
@@ -93,6 +112,22 @@
   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 
@@ -109,4 +144,12 @@
   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
+
 include $(TOPDIR)/Rules.make
diff -urN linux-2.2.14-pre15/drivers/isdn/hisax/avm_pci.c linux-2.2.14-pre16/drivers/isdn/hisax/avm_pci.c
--- linux-2.2.14-pre15/drivers/isdn/hisax/avm_pci.c	1999-10-19 17:14:00.000000000 -0700
+++ linux-2.2.14-pre16/drivers/isdn/hisax/avm_pci.c	2003-08-15 15:09:56.000000000 -0700
@@ -269,18 +269,26 @@
 	int hdlc = bcs->channel;
 
 	if (cs->debug & L1_DEB_HSCX)
-		debugl1(cs, "hdlc %c mode %d ichan %d",
-			'A' + hdlc, mode, bc);
-	bcs->mode = mode;
-	bcs->channel = bc;
+		debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d",
+			'A' + hdlc, bcs->mode, mode, hdlc, bc);
 	bcs->hw.hdlc.ctrl.ctrl = 0;
 	switch (mode) {
+		case (-1): /* used for init */
+			bcs->mode = 1;
+			bcs->channel = bc;
+			bc = 0;
 		case (L1_MODE_NULL):
+			if (bcs->mode == L1_MODE_NULL)
+				return;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
 			write_ctrl(bcs, 5);
+			bcs->mode = L1_MODE_NULL;
+			bcs->channel = bc;
 			break;
 		case (L1_MODE_TRANS):
+			bcs->mode = mode;
+			bcs->channel = bc;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
 			write_ctrl(bcs, 5);
@@ -290,6 +298,8 @@
 			hdlc_sched_event(bcs, B_XMTBUFREADY);
 			break;
 		case (L1_MODE_HDLC):
+			bcs->mode = mode;
+			bcs->channel = bc;
 			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
 			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
 			write_ctrl(bcs, 5);
@@ -695,8 +705,8 @@
 	cs->bcs[1].BC_SetStack = setstack_hdlc;
 	cs->bcs[0].BC_Close = close_hdlcstate;
 	cs->bcs[1].BC_Close = close_hdlcstate;
-	modehdlc(cs->bcs, 0, 0);
-	modehdlc(cs->bcs + 1, 0, 0);
+	modehdlc(cs->bcs, -1, 0);
+	modehdlc(cs->bcs + 1, -1, 1);
 }
 
 static void
diff -urN linux-2.2.14-pre15/drivers/net/tulip.c linux-2.2.14-pre16/drivers/net/tulip.c
--- linux-2.2.14-pre15/drivers/net/tulip.c	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/drivers/net/tulip.c	2003-08-15 15:09:56.000000000 -0700
@@ -433,7 +433,7 @@
 
 /* The bits in the CSR5 status registers, mostly interrupt sources. */
 enum status_bits {
-	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+	TimerInt=0x800, SytemError=0x2000, TPLnkFail=0x1000, TPLnkPass=0x10,
 	NormalIntr=0x10000, AbnormalIntr=0x8000,
 	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
 	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
@@ -530,6 +530,9 @@
 	int cur_index;						/* Current media index. */
 	int saved_if_port;
 	unsigned char pci_bus, pci_devfn;
+	int ttimer;
+	int susp_rx;
+	unsigned long nir;
 	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
@@ -548,6 +551,7 @@
 static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
+static int tulip_refill_rx(struct device *dev);
 static int tulip_rx(struct device *dev);
 static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
@@ -2484,6 +2488,9 @@
 	tp->tx_full = 0;
 	tp->cur_rx = tp->cur_tx = 0;
 	tp->dirty_rx = tp->dirty_tx = 0;
+	tp->susp_rx = 0;
+	tp->ttimer = 0;
+	tp->nir = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		tp->rx_ring[i].status = 0x00000000;
@@ -2577,7 +2584,15 @@
 	struct device *dev = (struct device *)dev_instance;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	long ioaddr = dev->base_addr;
-	int csr5, work_budget = max_interrupt_work;
+	int csr5;
+	int entry;
+	int missed;
+	int rx = 0;
+	int tx = 0;
+	int oi = 0;
+	int maxrx = RX_RING_SIZE;
+	int maxtx = TX_RING_SIZE;
+	int maxoi = TX_RING_SIZE;
 
 #if defined(__i386__) && defined(SMP_CHECK)
 	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
@@ -2595,6 +2610,8 @@
 	dev->interrupt = 1;
 #endif
 
+	tp->nir++;
+
 	do {
 		csr5 = inl(ioaddr + CSR5);
 		/* Acknowledge all of the current interrupt sources ASAP. */
@@ -2607,10 +2624,12 @@
 		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0)
 			break;
 
-		if (csr5 & (RxIntr | RxNoBuf))
-			work_budget -= tulip_rx(dev);
+		if (csr5 & (RxIntr | RxNoBuf)) {
+			rx += tulip_rx(dev);
+			tulip_refill_rx(dev);
+		}
 
-		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
+		if (csr5 & (TxNoBuf | TxDied | TxIntr | TimerInt)) {
 			unsigned int dirty_tx;
 
 			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0;
@@ -2623,7 +2642,7 @@
 				/* Check for Rx filter setup frames. */
 				if (tp->tx_skbuff[entry] == NULL)
 				  continue;
-
+				
 				if (status & 0x8000) {
 					/* There was an major error, log it. */
 #ifndef final_version
@@ -2655,6 +2674,7 @@
 				/* Free the original skb. */
 				dev_free_skb(tp->tx_skbuff[entry]);
 				tp->tx_skbuff[entry] = 0;
+				tx++;
 			}
 
 #ifndef final_version
@@ -2697,39 +2717,72 @@
 				/* Restart the transmit process. */
 				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
 				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+				outl(0, ioaddr + CSR1);
 			}
 			if (csr5 & RxDied) {		/* Missed a Rx frame. */
 				tp->stats.rx_errors++;
 				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 			}
-			if (csr5 & TimerInt) {
-				if (tulip_debug > 2)
-					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
-						   dev->name, csr5);
-				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
-			}
 			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
 				if (tp->link_change)
 					(tp->link_change)(dev, csr5);
 			}
+			if (csr5 & SytemError) {
+				printk(KERN_ERR "%s: (%lu) System Error occured\n", dev->name, tp->nir);
+			}
 			/* Clear all error sources, included undocumented ones! */
 			outl(0x0800f7ba, ioaddr + CSR5);
+			oi++;
 		}
-		if (--work_budget < 0) {
+		if (csr5 & TimerInt) {
+#if 0
+			if (tulip_debug > 2)
+				printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
+					   dev->name, csr5);
+			outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+#endif
+			tp->ttimer = 0;
+			oi++;
+		}
+		if (tx > maxtx || rx > maxrx || oi > maxoi) {
+#if 0
 			if (tulip_debug > 1)
+#endif
 				printk(KERN_WARNING "%s: Too much work during an interrupt, "
-					   "csr5=0x%8.8x.\n", dev->name, csr5);
+					   "csr5=0x%8.8x. (%lu) (%d,%d,%d)\n", dev->name, csr5, tp->nir, tx, rx, oi);
 			/* Acknowledge all interrupt sources. */
-			outl(0x8001ffff, ioaddr + CSR5);
+#if 0
 			/* Clear all interrupting sources, set timer to re-enable. */
-			outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt,
+			outl(((~csr5) & 0x0001ebef) | NormalIntr | AbnormalIntr | TimerInt,
 				 ioaddr + CSR7);
 			outl(12, ioaddr + CSR11);
+			tp->ttimer = 1;
+#endif
 			break;
 		}
 	} while (1);
 
+	tulip_refill_rx(dev);
+
+	/* check if we card is in suspend mode */
+	entry = tp->dirty_rx % RX_RING_SIZE;
+	if (tp->rx_skbuff[entry] == NULL) {
+		printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
+		if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) {
+			printk(KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n", dev->name, tp->nir);
+			outl(tulip_tbl[tp->chip_id].valid_intrs | TimerInt,
+				ioaddr + CSR7);
+			outl(TimerInt, ioaddr + CSR5);
+			outl(12, ioaddr + CSR11);
+			tp->ttimer = 1;
+		}
+	}
+
+	if ((missed = inl(ioaddr + CSR8) & 0x1ffff)) {
+		tp->stats.rx_dropped += missed & 0x10000 ? 0x10000 : missed;
+	}
+
 	if (tulip_debug > 4)
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
@@ -2742,12 +2795,35 @@
 	return;
 }
 
+static int tulip_refill_rx(struct device *dev)
+{
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int entry;
+	int refilled = 0;
+
+	/* Refill the Rx ring buffers. */
+	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
+		entry = tp->dirty_rx % RX_RING_SIZE;
+		if (tp->rx_skbuff[entry] == NULL) {
+			struct sk_buff *skb;
+			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
+			if (skb == NULL)
+				break;
+			skb->dev = dev;			/* Mark as being used by this device. */
+			tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
+			refilled++;
+		}
+		tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
+	}
+	return refilled;
+}
+
 static int tulip_rx(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int entry = tp->cur_rx % RX_RING_SIZE;
 	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
-	int work_done = 0;
+	int received = 0;
 
 	if (tulip_debug > 4)
 		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
@@ -2808,7 +2884,6 @@
 				memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail,
 					   pkt_len);
 #endif
-				work_done++;
 			} else { 	/* Pass up the skb already on the Rx ring. */
 				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
 				tp->rx_skbuff[entry] = NULL;
@@ -2829,25 +2904,11 @@
 			tp->stats.rx_bytes += pkt_len;
 #endif
 		}
+		received++;
 		entry = (++tp->cur_rx) % RX_RING_SIZE;
 	}
 
-	/* Refill the Rx ring buffers. */
-	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
-		entry = tp->dirty_rx % RX_RING_SIZE;
-		if (tp->rx_skbuff[entry] == NULL) {
-			struct sk_buff *skb;
-			skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
-			if (skb == NULL)
-				break;
-			skb->dev = dev;			/* Mark as being used by this device. */
-			tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
-			work_done++;
-		}
-		tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
-	}
-
-	return work_done;
+	return received;
 }
 
 static int tulip_close(struct device *dev)
diff -urN linux-2.2.14-pre15/drivers/scsi/sg.c linux-2.2.14-pre16/drivers/scsi/sg.c
--- linux-2.2.14-pre15/drivers/scsi/sg.c	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/drivers/scsi/sg.c	2003-08-15 15:09:56.000000000 -0700
@@ -16,7 +16,7 @@
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.36 (991008)";
+ static char * sg_version_str = "Version: 2.1.36 (991218)";
  static int sg_version_num = 20136; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
@@ -691,7 +691,6 @@
     Sg_device * sdp;
     Sg_fd * sfp;
     Sg_request * srp = NULL;
-    int closed = 0;
     static const int min_sb_len = 
                 SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ? 
                         sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE;
@@ -793,21 +792,24 @@
     scsi_release_command(SCpnt);
     SCpnt = NULL;
     if (sfp->closed) { /* whoops this fd already released, cleanup */
-        closed = 1;
         SCSI_LOG_TIMEOUT(1,
                printk("sg__done: already closed, freeing ...\n"));
 /* should check if module is unloaded <<<<<<< */
         sg_finish_rem_req(srp, NULL, 0);
+        srp = NULL;
         if (NULL == sfp->headrp) { 
             SCSI_LOG_TIMEOUT(1,
                 printk("sg__done: already closed, final cleanup\n"));
             sg_remove_sfp(sdp, sfp);
+            sfp = NULL;
         }
     }
 /* Now wake up any sg_read() that is waiting for this packet. */
-    wake_up_interruptible(&sfp->read_wait);
-    if ((sfp->async_qp) && (! closed))
-        kill_fasync(sfp->async_qp, SIGPOLL);
+    if (sfp && srp) {
+        wake_up_interruptible(&sfp->read_wait);
+        if (sfp->async_qp)
+            kill_fasync(sfp->async_qp, SIGPOLL);
+    }
 }
 
 static void sg_debug_all(const Sg_fd * sfp)
diff -urN linux-2.2.14-pre15/drivers/sound/nm256_audioc# linux-2.2.14-pre16/drivers/sound/nm256_audioc#
--- linux-2.2.14-pre15/drivers/sound/nm256_audioc#	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/drivers/sound/nm256_audioc#	1969-12-31 16:00:00.000000000 -0800
@@ -1,106 +0,0 @@
-***************
-*** 812,847 ****
-      return 0;
-  }
-  
-- /* See if the signature left by the NM256 BIOS is intact; if so, we use
--    the associated address as the end of our buffer. */
-  static void
-- nm256_peek_for_sig (struct nm256_info *card, u32 port1addr)
-  {
--     char *temp = ioremap_nocache (port1addr + card->port1_end - 0x0400, 16);
-      u32 sig;
-  
-      if (temp == NULL) {
-  	printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n");
-  	return;
-      }
--     memcpy_fromio (&sig, temp, sizeof (u32));
--     if ((sig & 0xffff0000) == 0x4e4d0000) {
-- 	memcpy_fromio (&(card->bufend), temp + 4, sizeof (u32));
-  	printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n",
-- 		card->bufend);
-      }
-  
-      release_region ((unsigned long) temp, 16);
-  }
-  
-- /* Install a driver for the soundcard referenced by PCIDEV. */
-  
-  static int
-  nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr)
-  {
-      struct nm256_info *card;
--     u32 port1addr = pcidev->resource[0].start;
--     u32 port2addr = pcidev->resource[1].start;
-      int x;
-  
-      card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL);
---- 982,1048 ----
-      return 0;
-  }
-  
-+ /* Perform a full reset on the hardware; this is invoked when an APM
-+    resume event occurs.  */
-+ static void
-+ nm256_full_reset (struct nm256_info *card)
-+ {
-+     nm256_initHw (card);
-+     ac97_reset (&(card->mdev));
-+ }
-+ 
-+ /* 
-+  * See if the signature left by the NM256 BIOS is intact; if so, we use
-+  * the associated address as the end of our audio buffer in the video
-+  * RAM.
-+  */
-+ 
-  static void
-+ nm256_peek_for_sig (struct nm256_info *card)
-  {
-+     u32 port1offset 
-+ 	= card->port[0].physaddr + card->port[0].end_offset - 0x0400;
-+     /* The signature is located 1K below the end of video RAM.  */
-+     char *temp = ioremap_nocache (port1offset, 16);
-+     /* Default buffer end is 5120 bytes below the top of RAM.  */
-+     u32 default_value = card->port[0].end_offset - 0x1400;
-      u32 sig;
-  
-+     /* Install the default value first, so we don't have to repeatedly
-+        do it if there is a problem.  */
-+     card->port[0].end_offset = default_value;
-+ 
-      if (temp == NULL) {
-  	printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n");
-  	return;
-      }
-+     sig = readl (temp);
-+     if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
-+ 	u32 pointer = readl (temp + 4);
-+ 
-+ 	/*
-+ 	 * If it's obviously invalid, don't use it (the port already has a
-+ 	 * suitable default value set).
-+ 	 */
-+ 	if (pointer != 0xffffffff)
-+ 	    card->port[0].end_offset = pointer;
-+ 
-  	printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n",
-+ 		pointer);
-      }
-  
-      release_region ((unsigned long) temp, 16);
-  }
-  
-+ /* 
-+  * Install a driver for the PCI device referenced by PCIDEV.
-+  * VERSTR is a human-readable version string.
-+  */
-  
-  static int
-  nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr)
-  {
-      struct nm256_info *card;
-      int x;
-  
-      card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL);
diff -urN linux-2.2.14-pre15/drivers/telephony/ixj.c linux-2.2.14-pre16/drivers/telephony/ixj.c
--- linux-2.2.14-pre15/drivers/telephony/ixj.c	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/drivers/telephony/ixj.c	2003-08-15 15:09:56.000000000 -0700
@@ -99,6 +99,8 @@
 static void ixj_read_frame(int board);
 static void ixj_write_frame(int board);
 static void ixj_init_timer(void);
+static void ixj_add_timer(void);
+static void ixj_del_timer(void);
 static void ixj_timeout(unsigned long ptr);
 static int read_filters(int board);
 static int LineMonitor(int board);
@@ -346,302 +348,308 @@
 	return retval;
 }
 
-void ixj_init_timer(void)
+static void ixj_init_timer(void)
 {
 	init_timer(&ixj_timer);
 	ixj_timer.function = ixj_timeout;
 	ixj_timer.data = (int) NULL;
+}
+
+static void ixj_add_timer(void)
+{
 	ixj_timer.expires = jiffies + (hertz / samplerate);
 	add_timer(&ixj_timer);
 }
 
+static void ixj_del_timer(void)
+{
+	del_timer(&ixj_timer);
+}
+
+static void ixj_tone_timeout(int board)
+{
+	IXJ *j = &ixj[board];
+	IXJ_TONE ti;
+	
+	j->tone_state++;
+	if (j->tone_state == 3) {
+		j->tone_state = 0;
+		if (j->cadence_t) {
+			j->tone_cadence_state++;
+			if (j->tone_cadence_state >= j->cadence_t->elements_used) 
+			{
+				switch (j->cadence_t->termination) 
+				{
+					case PLAY_ONCE:
+						ixj_cpt_stop(board);
+							break;
+					case REPEAT_LAST_ELEMENT:
+						j->tone_cadence_state--;
+						ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index);
+						break;
+					case REPEAT_ALL:
+						j->tone_cadence_state = 0;
+						if (j->cadence_t->ce[j->tone_cadence_state].freq0) 
+						{
+							ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
+							ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
+							ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
+							ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
+							ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
+							ixj_init_tone(board, &ti);
+						}
+						ixj_set_tone_on(j->cadence_t->ce[0].tone_on_time, board);
+						ixj_set_tone_off(j->cadence_t->ce[0].tone_off_time, board);
+						ixj_play_tone(board, j->cadence_t->ce[0].index);
+						break;
+				}
+			} else {
+				if (j->cadence_t->ce[j->tone_cadence_state].gain0) 
+				{
+					ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
+					ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
+					ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
+					ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
+					ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
+					ixj_init_tone(board, &ti);
+				}
+				ixj_set_tone_on(j->cadence_t->ce[j->tone_cadence_state].tone_on_time, board);
+				ixj_set_tone_off(j->cadence_t->ce[j->tone_cadence_state].tone_off_time, board);
+				ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index);
+			}
+		}
+	}
+}
+
 static void ixj_timeout(unsigned long ptr)
 {
 	int board;
 	unsigned long jifon;
 	IXJ *j;
-	IXJ_TONE ti;
-	static int mutex = 0;
 
-	/* What is this trying to do ?? */
-	/* This is a safeguard in case the loop does not complete before the
-	   next timer tick.  Since all the work is done during this timer loop,
-	   we do not want the next iteration of the loop to start before the
-	   last one finished. */
-
-	if (!mutex) {
-		mutex++;
-		for (board = 0; board < IXJMAX; board++) {
-			j = &ixj[board];
+	for (board = 0; board < IXJMAX; board++) 
+	{
+		j = &ixj[board];
 
-			if (j->DSPbase) {
+		if (j->DSPbase) 
+		{
 #ifdef PERFMON_STATS
-				j->timerchecks++;
+			j->timerchecks++;
 #endif
-				if (j->tone_state) {
-
-					if (!ixj_hookstate(board)) {
-						ixj_cpt_stop(board);
-						if (j->m_hook) {
-							j->m_hook = 0;
-							j->ex.bits.hookstate = 1;
-							if (j->async_queue)
-								kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
-
-						}
-						goto timer_end;
+			if (j->tone_state) 
+			{
+				if (!ixj_hookstate(board)) 
+				{
+					ixj_cpt_stop(board);
+					if (j->m_hook) 
+					{
+						j->m_hook = 0;
+						j->ex.bits.hookstate = 1;
+						if (j->async_queue)
+							kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
 					}
-					if (j->tone_state == 1)
-						jifon = (hertz * j->tone_on_time * 25 / 100000);
-					else
-						jifon = (hertz * j->tone_on_time * 25 / 100000) +
-						    (hertz * j->tone_off_time * 25 / 100000);
-					if (jiffies < j->tone_start_jif + jifon) {
-						if (j->tone_state == 1) {
-							ixj_play_tone(board, j->tone_index);
-							if (j->dsp.low == 0x20) {
-								goto timer_end;
-							}
-						} else {
-							ixj_play_tone(board, 0);
-							if (j->dsp.low == 0x20) {
-								goto timer_end;
-							}
+					goto timer_end;
+				}
+				if (j->tone_state == 1)
+					jifon = (hertz * j->tone_on_time * 25 / 100000);
+				else
+					jifon = (hertz * j->tone_on_time * 25 / 100000) +
+					    (hertz * j->tone_off_time * 25 / 100000);
+				if (jiffies < j->tone_start_jif + jifon) {
+					if (j->tone_state == 1) {
+						ixj_play_tone(board, j->tone_index);
+						if (j->dsp.low == 0x20) {
+							goto timer_end;
 						}
 					} else {
-						j->tone_state++;
-						if (j->tone_state == 3) {
-							j->tone_state = 0;
-							if (j->cadence_t) {
-								j->tone_cadence_state++;
-								if (j->tone_cadence_state == j->cadence_t->elements_used) {
-									switch (j->cadence_t->termination) {
-									case PLAY_ONCE:
-										ixj_cpt_stop(board);
-										break;
-									case REPEAT_LAST_ELEMENT:
-										j->tone_cadence_state--;
-										ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index);
-										break;
-									case REPEAT_ALL:
-										j->tone_cadence_state = 0;
-										if (j->cadence_t->ce[j->tone_cadence_state].freq0) {
-											ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
-											ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
-											ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
-											ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
-											ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
-											ixj_init_tone(board, &ti);
-										}
-										ixj_set_tone_on(j->cadence_t->ce[0].tone_on_time, board);
-										ixj_set_tone_off(j->cadence_t->ce[0].tone_off_time, board);
-										ixj_play_tone(board, j->cadence_t->ce[0].index);
-										break;
-									}
-								} else {
-									if (j->cadence_t->ce[j->tone_cadence_state].gain0) {
-										ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
-										ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
-										ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
-										ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
-										ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
-										ixj_init_tone(board, &ti);
-									}
-									ixj_set_tone_on(j->cadence_t->ce[j->tone_cadence_state].tone_on_time, board);
-									ixj_set_tone_off(j->cadence_t->ce[j->tone_cadence_state].tone_off_time, board);
-									ixj_play_tone(board, j->cadence_t->ce[j->tone_cadence_state].index);
-								}
-							}
+						ixj_play_tone(board, 0);
+						if (j->dsp.low == 0x20) {
+							goto timer_end;
 						}
-						if (j->flags.dialtone) {
-							ixj_dialtone(board);
-						}
-						if (j->flags.busytone) {
-							ixj_busytone(board);
-							if (j->dsp.low == 0x20) {
-								goto timer_end;
-							}
-						}
-						if (j->flags.ringback) {
-							ixj_ringback(board);
-							if (j->dsp.low == 0x20) {
-								goto timer_end;
-							}
-						}
-						if (!j->tone_state) {
-							if (j->dsp.low == 0x20 || (j->play_mode == -1 && j->rec_mode == -1))
-								idle(board);
-							if (j->dsp.low == 0x20 && j->play_mode != -1)
-								ixj_play_start(board);
-							if (j->dsp.low == 0x20 && j->rec_mode != -1)
-								ixj_record_start(board);
+					}
+				} else {
+					ixj_tone_timeout(board);
+					if (j->flags.dialtone) {
+						ixj_dialtone(board);
+					}
+					if (j->flags.busytone) {
+						ixj_busytone(board);
+						if (j->dsp.low == 0x20) {
+							goto timer_end;
 						}
 					}
-				}
-				if (!j->tone_state || j->dsp.low != 0x20) {
-					if (IsRxReady(board)) {
-						ixj_read_frame(board);
+					if (j->flags.ringback) {
+						ixj_ringback(board);
+						if (j->dsp.low == 0x20) {
+							goto timer_end;
+						}
 					}
-					if (IsTxReady(board)) {
-						ixj_write_frame(board);
+					if (!j->tone_state) {
+						if (j->dsp.low == 0x20 || (j->play_mode == -1 && j->rec_mode == -1))
+							idle(board);
+						if (j->dsp.low == 0x20 && j->play_mode != -1)
+							ixj_play_start(board);
+						if (j->dsp.low == 0x20 && j->rec_mode != -1)
+							ixj_record_start(board);
 					}
 				}
-				if (j->flags.cringing) {
-					if (ixj_hookstate(board) & 1) {
-						j->flags.cringing = 0;
-						ixj_ring_off(board);
+			}
+			if (!j->tone_state || j->dsp.low != 0x20) {
+				if (IsRxReady(board)) {
+					ixj_read_frame(board);
+				}
+				if (IsTxReady(board)) {
+					ixj_write_frame(board);
+				}
+			}
+			if (j->flags.cringing) {
+				if (ixj_hookstate(board) & 1) {
+					j->flags.cringing = 0;
+					ixj_ring_off(board);
+				} else {
+					if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) {
+						j->ring_cadence_t--;
+						if (j->ring_cadence_t == -1)
+							j->ring_cadence_t = 15;
+						j->ring_cadence_jif = jiffies;
+					}
+					if (j->ring_cadence & 1 << j->ring_cadence_t) {
+						ixj_ring_on(board);
 					} else {
-						if (jiffies - j->ring_cadence_jif >= (.5 * hertz)) {
-							j->ring_cadence_t--;
-							if (j->ring_cadence_t == -1)
-								j->ring_cadence_t = 15;
-							j->ring_cadence_jif = jiffies;
-						}
-						if (j->ring_cadence & 1 << j->ring_cadence_t) {
-							ixj_ring_on(board);
-						} else {
-							ixj_ring_off(board);
-						}
-						goto timer_end;
+						ixj_ring_off(board);
 					}
+					goto timer_end;
 				}
-				if (!j->flags.ringing) {
-					if (ixj_hookstate(board)) {
-						if (j->dsp.low == 0x21 &&
-						    j->pld_slicr.bits.state != PLD_SLIC_STATE_ACTIVE)
-                // Internet LineJACK
-						 {
-							SLIC_SetState(PLD_SLIC_STATE_ACTIVE, board);
-						}
-						LineMonitor(board);
-						read_filters(board);
-						ixj_WriteDSPCommand(0x511B, board);
-						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);	// Send apps notice of change
-
-						}
-					} else {
-						if (j->dsp.low == 0x21 &&
-						    j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE)
-                // Internet LineJACK
-						 {
-							SLIC_SetState(PLD_SLIC_STATE_STANDBY, board);
-						}
-						if (j->ex.bits.dtmf_ready) {
-							j->dtmf_wp = j->dtmf_rp = j->ex.bits.dtmf_ready = 0;
-						}
-						if (j->m_hook) {
-							j->m_hook = 0;
-							j->ex.bits.hookstate = 1;
-							if (j->async_queue)
-								kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
-
-						}
+			}
+			if (!j->flags.ringing) {
+				if (ixj_hookstate(board)) {
+					if (j->dsp.low == 0x21 &&
+					    j->pld_slicr.bits.state != PLD_SLIC_STATE_ACTIVE)
+               // Internet LineJACK
+					{
+						SLIC_SetState(PLD_SLIC_STATE_ACTIVE, board);
+					}
+					LineMonitor(board);
+					read_filters(board);
+					ixj_WriteDSPCommand(0x511B, board);
+					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);	// Send apps notice of change
+					}
+				} else {
+					if (j->dsp.low == 0x21 &&
+					    j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE)
+               // Internet LineJACK
+					{
+						SLIC_SetState(PLD_SLIC_STATE_STANDBY, board);
+					}
+					if (j->ex.bits.dtmf_ready) {
+						j->dtmf_wp = j->dtmf_rp = j->ex.bits.dtmf_ready = 0;
+					}
+					if (j->m_hook) {
+						j->m_hook = 0;
+						j->ex.bits.hookstate = 1;
+						if (j->async_queue)
+							kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
 					}
 				}
-				if (j->cardtype == 300) {
-					if (j->flags.pstn_present) {
-						j->pld_scrr.byte = inb_p(j->XILINXbase);
-						if (jiffies >= j->pstn_sleeptil && j->pld_scrr.bits.daaflag) {
-							daa_int_read(board);
-							if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.RING) {
-								if (!j->flags.pstn_ringing) {
-									j->flags.pstn_ringing = 1;
-									if (j->daa_mode != SOP_PU_RINGING)
-										daa_set_mode(board, SOP_PU_RINGING);
-								}
+			}
+			if (j->cardtype == 300) {
+				if (j->flags.pstn_present) {
+					j->pld_scrr.byte = inb_p(j->XILINXbase);
+					if (jiffies >= j->pstn_sleeptil && j->pld_scrr.bits.daaflag) {
+						daa_int_read(board);
+						if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.RING) {
+							if (!j->flags.pstn_ringing) {
+								j->flags.pstn_ringing = 1;
+								if (j->daa_mode != SOP_PU_RINGING)
+									daa_set_mode(board, SOP_PU_RINGING);
 							}
-							if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) {
-								j->pstn_winkstart = 0;
-								if (j->flags.pstn_ringing && !j->pstn_envelope) {
-									j->ex.bits.pstn_ring = 0;
-									j->pstn_envelope = 1;
-									j->pstn_ring_start = jiffies;
-								}
-							} else {
-								if (j->flags.pstn_ringing && j->pstn_envelope &&
-								    jiffies > j->pstn_ring_start + ((hertz * 15) / 10)) {
-									j->ex.bits.pstn_ring = 1;
-									j->pstn_envelope = 0;
-								} else if (j->daa_mode == SOP_PU_CONVERSATION) {
-									if (!j->pstn_winkstart) {
-										j->pstn_winkstart = jiffies;
-									} else if (jiffies > j->pstn_winkstart + (hertz * j->winktime / 1000)) {
-										daa_set_mode(board, SOP_PU_SLEEP);
-										j->pstn_winkstart = 0;
-										j->ex.bits.pstn_wink = 1;
-									}
-								} else {
-									j->ex.bits.pstn_ring = 0;
-								}
+						}
+						if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) {
+							j->pstn_winkstart = 0;
+							if (j->flags.pstn_ringing && !j->pstn_envelope) {
+								j->ex.bits.pstn_ring = 0;
+								j->pstn_envelope = 1;
+								j->pstn_ring_start = jiffies;
 							}
-							if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Cadence) {
-								if (j->daa_mode == SOP_PU_RINGING) {
+						} else {
+							if (j->flags.pstn_ringing && j->pstn_envelope &&
+							    jiffies > j->pstn_ring_start + ((hertz * 15) / 10)) {
+								j->ex.bits.pstn_ring = 1;
+								j->pstn_envelope = 0;
+							} else if (j->daa_mode == SOP_PU_CONVERSATION) {
+								if (!j->pstn_winkstart) {
+									j->pstn_winkstart = jiffies;
+								} else if (jiffies > j->pstn_winkstart + (hertz * j->winktime / 1000)) {
 									daa_set_mode(board, SOP_PU_SLEEP);
-									j->flags.pstn_ringing = 0;
-									j->ex.bits.pstn_ring = 0;
+									j->pstn_winkstart = 0;
+									j->ex.bits.pstn_wink = 1;
 								}
+							} else {
+								j->ex.bits.pstn_ring = 0;
 							}
-							if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Caller_ID) {
-								if (j->daa_mode == SOP_PU_RINGING && j->flags.pstn_ringing) {
-									j->pstn_cid_intr = 1;
-									j->pstn_cid_recieved = jiffies;
-								}
+						}
+						if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Cadence) {
+							if (j->daa_mode == SOP_PU_RINGING) {
+								daa_set_mode(board, SOP_PU_SLEEP);
+								j->flags.pstn_ringing = 0;
+								j->ex.bits.pstn_ring = 0;
 							}
-						} else {
-							if (j->pld_scrr.bits.daaflag) {
-								daa_int_read(board);
+						}
+						if (j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Caller_ID) {
+							if (j->daa_mode == SOP_PU_RINGING && j->flags.pstn_ringing) {
+								j->pstn_cid_intr = 1;
+								j->pstn_cid_recieved = jiffies;
 							}
-							j->ex.bits.pstn_ring = 0;
-							if (j->pstn_cid_intr && jiffies > j->pstn_cid_recieved + (hertz * 3)) {
-								if (j->daa_mode == SOP_PU_RINGING) {
-									ixj_daa_cid_read(board);
-									j->ex.bits.caller_id = 1;
-								}
-								j->pstn_cid_intr = 0;
-							} else {
-								j->ex.bits.caller_id = 0;
+						}
+					} else {
+						if (j->pld_scrr.bits.daaflag) {
+							daa_int_read(board);
+						}
+						j->ex.bits.pstn_ring = 0;
+						if (j->pstn_cid_intr && jiffies > j->pstn_cid_recieved + (hertz * 3)) {
+							if (j->daa_mode == SOP_PU_RINGING) {
+								ixj_daa_cid_read(board);
+								j->ex.bits.caller_id = 1;
 							}
-							if (!j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) {
-								if (j->flags.pstn_ringing && j->pstn_envelope) {
-									j->ex.bits.pstn_ring = 1;
-									j->pstn_envelope = 0;
-								} else if (j->daa_mode == SOP_PU_CONVERSATION) {
-									if (!j->pstn_winkstart) {
-										j->pstn_winkstart = jiffies;
-									} else if (jiffies > j->pstn_winkstart + (hertz * 320 / 1000)) {
-										daa_set_mode(board, SOP_PU_SLEEP);
-										j->pstn_winkstart = 0;
-										j->ex.bits.pstn_wink = 1;
-									}
+							j->pstn_cid_intr = 0;
+						} else {
+							j->ex.bits.caller_id = 0;
+						}
+						if (!j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK) {
+							if (j->flags.pstn_ringing && j->pstn_envelope) {
+								j->ex.bits.pstn_ring = 1;
+								j->pstn_envelope = 0;
+							} else if (j->daa_mode == SOP_PU_CONVERSATION) {
+								if (!j->pstn_winkstart) {
+									j->pstn_winkstart = jiffies;
+								} else if (jiffies > j->pstn_winkstart + (hertz * 320 / 1000)) {
+									daa_set_mode(board, SOP_PU_SLEEP);
+									j->pstn_winkstart = 0;
+									j->ex.bits.pstn_wink = 1;
 								}
 							}
 						}
 					}
 				}
-				if ((j->ex.bits.f0 || j->ex.bits.f1 || j->ex.bits.f2 || j->ex.bits.f3)
-				    && j->filter_cadence) {
-				}
-				if (j->ex.bytes) {
-					wake_up_interruptible(&j->poll_q);	// Wake any blocked selects
-
-					if (j->async_queue)
-						kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
-
-				}
-			} else {
-				break;
 			}
+			if ((j->ex.bits.f0 || j->ex.bits.f1 || j->ex.bits.f2 || j->ex.bits.f3)
+			    && j->filter_cadence) {
+			}
+			if (j->ex.bytes) {
+				wake_up_interruptible(&j->poll_q);	// Wake any blocked selects
+				if (j->async_queue)
+					kill_fasync(j->async_queue, SIGIO);	// Send apps notice of change
+			}
+		} else {
+			break;
 		}
 	}
-      timer_end:
-	if (mutex == 1) {
-		ixj_init_timer();
-		mutex--;
-	}
+timer_end:
+	ixj_add_timer();
 }
 
 static int ixj_status_wait(int board)
@@ -845,7 +853,7 @@
 	if (j->dsp.low == 0x20)	// Internet PhoneJACK
 	 {
 		if (ixjdebug > 0)
-			printk(KERN_INFO "IXJ Ring On /dev/ixj%d\n", board);
+			printk(KERN_INFO "IXJ Ring On /dev/phone%d\n", board);
 
 		j->gpio.bytes.high = 0x0B;
 		j->gpio.bytes.low = 0x00;
@@ -879,12 +887,17 @@
 	case 500:
 		SLIC_GetState(board);
 		if (j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE ||
-		    j->pld_slicr.bits.state == PLD_SLIC_STATE_STANDBY) {
-			if (j->flags.ringing) {
-				det = jiffies + (hertz / 50);
-				while (time_before(jiffies, det)) {
-					current->state = TASK_INTERRUPTIBLE;
-					schedule_timeout(1);
+		    j->pld_slicr.bits.state == PLD_SLIC_STATE_STANDBY) 
+		{
+			if (j->flags.ringing) 
+			{
+				if(!in_interrupt())
+				{
+					det = jiffies + (hertz / 50);
+					while (time_before(jiffies, det)) {
+						current->state = TASK_INTERRUPTIBLE;
+						schedule_timeout(1);
+					}
 				}
 				SLIC_GetState(board);
 				if (j->pld_slicr.bits.state == PLD_SLIC_STATE_RINGING) {
@@ -989,6 +1002,8 @@
 			}
 			current->state = TASK_INTERRUPTIBLE;
 			schedule_timeout(1);
+			if(signal_pending(current))
+				break;
 		}
 		jif = jiffies + (3 * hertz);
 		ixj_ring_off(board);
@@ -998,6 +1013,8 @@
 				while (time_before(jiffies, det)) {
 					current->state = TASK_INTERRUPTIBLE;
 					schedule_timeout(1);
+					if(signal_pending(current))
+						break;
 				}
 				if (ixj_hookstate(board) & 1) {
 					j->flags.ringing = 0;
@@ -1006,6 +1023,8 @@
 			}
 			current->state = TASK_INTERRUPTIBLE;
 			schedule_timeout(1);
+			if(signal_pending(current))
+				break;
 		}
 	}
 	ixj_ring_off(board);
@@ -1810,7 +1829,7 @@
 	}
 	if (!j->read_buffer) {
 		if (!j->read_buffer)
-			j->read_buffer = kmalloc(j->rec_frame_size * 2, GFP_KERNEL);
+			j->read_buffer = kmalloc(j->rec_frame_size * 2, GFP_ATOMIC);
 		if (!j->read_buffer) {
 			printk("Read buffer allocation for ixj board %d failed!\n", board);
 			return -ENOMEM;
@@ -2165,7 +2184,7 @@
 			return -1;
 	}
 	if (!j->write_buffer) {
-		j->write_buffer = kmalloc(j->play_frame_size * 2, GFP_KERNEL);
+		j->write_buffer = kmalloc(j->play_frame_size * 2, GFP_ATOMIC);
 		if (!j->write_buffer) {
 			printk("Write buffer allocation for ixj board %d failed!\n", board);
 			return -ENOMEM;
@@ -3341,6 +3360,7 @@
 
 	j->tone_state = 0;
 
+	ixj_del_timer();
 	if (j->cadence_t) {
 		if (j->cadence_t->ce) {
 			kfree(j->cadence_t->ce);
@@ -3348,6 +3368,7 @@
 		kfree(j->cadence_t);
 		j->cadence_t = NULL;
 	}
+	ixj_add_timer();
 	if (j->dsp.low == 0x20 || (j->play_mode == -1 && j->rec_mode == -1))
 		idle(board);
 	if (j->play_mode != -1)
@@ -3394,6 +3415,12 @@
 	if (copy_from_user(lcep, lcp->ce, sizeof(IXJ_CADENCE_ELEMENT) * lcp->elements_used))
 		return -EFAULT;
 
+	if(j->cadence_t)
+	{
+		kfree(j->cadence_t->ce);
+		kfree(j->cadence_t);
+	}
+	
 	lcp->ce = (void *) lcep;
 	j->cadence_t = lcp;
 	j->tone_cadence_state = 0;
@@ -3535,7 +3562,7 @@
 	int retval = 0;
 
 	if (ixjdebug > 1)
-		printk(KERN_DEBUG "ixj%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
+		printk(KERN_DEBUG "phone%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
 	if (minor >= IXJMAX)
 		return -ENODEV;
 
@@ -4575,7 +4602,8 @@
 	}
 	return len;
 }
-struct proc_dir_entry ixj_proc_entry =
+
+static struct proc_dir_entry ixj_proc_entry =
 {
 	0,
 	3, "ixj",
@@ -4586,12 +4614,7 @@
 	&ixj_read_proc
 };
 
-#ifdef MODULE
-
-MODULE_DESCRIPTION("Internet PhoneJACK/Internet LineJACK module - www.quicknet.net");
-MODULE_AUTHOR("Ed Okerson <eokerson@quicknet.net>");
-
-void cleanup_module(void)
+static void cleanup(void)
 {
 	int cnt;
 
@@ -4630,7 +4653,6 @@
 	proc_unregister(&proc_root, ixj_proc_entry.low_ino);
 }
 
-#endif
 
 // Typedefs
 typedef struct {
@@ -4724,13 +4746,22 @@
 static int dspio[IXJMAX + 1] = {0,};
 static int xio[IXJMAX + 1] = {0,};
 
+MODULE_DESCRIPTION("Internet PhoneJACK/Internet LineJACK module - www.quicknet.net");
+MODULE_AUTHOR("Ed Okerson <eokerson@quicknet.net>");
+
 MODULE_PARM(dspio, "1-" __MODULE_STRING(IXJMAX) "i");
 MODULE_PARM(xio, "1-" __MODULE_STRING(IXJMAX) "i");
 
 #ifdef MODULE
+
+void cleanup_module(void)
+{
+	cleanup();
+}
+
 int init_module(void)
 #else
-init __init ixj_init(void)
+int __init ixj_init(void)
 #endif
 {
 	int result;
@@ -4772,7 +4803,7 @@
 			result = check_region(ixj[cnt].DSPbase, 16);
 			if (result) {
 				printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase);
-				cleanup_module();
+				cleanup();
 				return result;
 			}
 			request_region(ixj[cnt].DSPbase, 16, "ixj DSP");
@@ -4814,7 +4845,7 @@
 			result = check_region(ixj[cnt].DSPbase, 16);
 			if (result) {
 				printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase);
-				cleanup_module();
+				cleanup();
 				return result;
 			}
 			request_region(ixj[cnt].DSPbase, 16, "ixj DSP");
@@ -4837,7 +4868,7 @@
 				result = check_region(ixj[cnt].DSPbase, 16);
 				if (result) {
 					printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[cnt].DSPbase);
-					cleanup_module();
+					cleanup();
 					return result;
 				}
 				request_region(ixj[cnt].DSPbase, 16, "ixj DSP");
@@ -4853,7 +4884,7 @@
 //  result = register_chrdev(ixj_major, "ixj", &ixj_fops);
 	//  if (result < 0) {
 	//    printk(KERN_INFO "ixj: can't get major number\n");
-	//    cleanup_module();
+	//    cleanup();
 	//    return result;
 	//  }
 	//  if (ixj_major == 0)
@@ -4862,7 +4893,7 @@
 	proc_register(&proc_root, &ixj_proc_entry);
 
 	ixj_init_timer();
-
+	ixj_add_timer();	
 	return probe;
 }
 
diff -urN linux-2.2.14-pre15/drivers/telephony/Makefile linux-2.2.14-pre16/drivers/telephony/Makefile
--- linux-2.2.14-pre15/drivers/telephony/Makefile	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/drivers/telephony/Makefile	2003-08-15 15:09:56.000000000 -0700
@@ -17,9 +17,9 @@
 M_OBJS  :=
 
 ifeq ($(CONFIG_PHONE),y)
-  L_OBJS += phonedev.o
+  LX_OBJS += phonedev.o
 else
-  ifeq ($(CONFIG_PARPORT),m)
+  ifeq ($(CONFIG_PHONE),m)
     MX_OBJS += phonedev.o
   endif
 endif
diff -urN linux-2.2.14-pre15/fs/fat/dir.c linux-2.2.14-pre16/fs/fat/dir.c
--- linux-2.2.14-pre15/fs/fat/dir.c	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.14-pre16/fs/fat/dir.c	2003-08-15 15:09:56.000000000 -0700
@@ -265,12 +265,12 @@
 	}
 
 Found:
-	fat_brelse(sb, bh);
 	res = long_slots + 1;
 	*spos = cpos - sizeof(struct msdos_dir_entry);
 	*lpos = cpos - res*sizeof(struct msdos_dir_entry);
 	MSDOS_I(inode)->i_last_pos = cpos;
 exit:
+	fat_brelse(sb, bh);
 	if (unicode) {
 		free_page((unsigned long) unicode);
 	}
diff -urN linux-2.2.14-pre15/fs/super.c linux-2.2.14-pre16/fs/super.c
--- linux-2.2.14-pre15/fs/super.c	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/fs/super.c	2003-08-15 15:09:56.000000000 -0700
@@ -983,6 +983,12 @@
 	if (!data)
 		return 0;
 
+	/* If this is the kernel, just trust the pointer. */
+	if (segment_eq(get_fs(), KERNEL_DS)) {
+		*where = (unsigned long) data;
+		return 0;
+	}
+
 	vma = find_vma(current->mm, (unsigned long) data);
 	if (!vma || (unsigned long) data < vma->vm_start)
 		return -EFAULT;
@@ -1002,6 +1008,13 @@
 	return 0;
 }
 
+static void free_mount_page(unsigned long page)
+{
+	if (segment_eq(get_fs(), KERNEL_DS))
+		return;
+	free_page(page);
+}
+
 /*
  * Flags is a 16-bit value that allows up to 16 non-fs dependent flags to
  * be given to the mount() call (ie: read-only, no-dev, no-suid etc).
@@ -1039,7 +1052,7 @@
 		retval = do_remount(dir_name,
 				    new_flags & ~MS_MGC_MSK & ~MS_REMOUNT,
 				    (char *) page);
-		free_page(page);
+		free_mount_page(page);
 		goto out;
 	}
 
@@ -1047,7 +1060,7 @@
 	if (retval < 0)
 		goto out;
 	fstype = get_fs_type((char *) page);
-	free_page(page);
+	free_mount_page(page);
 	retval = -ENODEV;
 	if (!fstype)		
 		goto out;
@@ -1101,7 +1114,7 @@
 	}
 	retval = do_mount(dev, dev_name, dir_name, fstype->name, flags,
 				(void *) page);
-	free_page(page);
+	free_mount_page(page);
 	if (retval)
 		goto clean_up;
 
diff -urN linux-2.2.14-pre15/fs/umsdos/inode.c linux-2.2.14-pre16/fs/umsdos/inode.c
--- linux-2.2.14-pre15/fs/umsdos/inode.c	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.14-pre16/fs/umsdos/inode.c	2003-08-15 15:09:56.000000000 -0700
@@ -168,7 +168,14 @@
 	} else if (S_ISBLK (inode->i_mode)) {
 		inode->i_op = &blkdev_inode_operations;
 	} else if (S_ISFIFO (inode->i_mode)) {
-		init_fifo (inode);
+	/* if someone is using FIFO, we must not reinitialize it, because
+	   we will destroy its locks, and sleep_on in fifo_open() will
+	   hardlock/oops our kernel! this started happening with
+	   patch-2.2.7. Why did it not happen before ? Maybe we were
+	   never called with i_count > 1 ?
+	*/
+		if (inode->i_count < 2)	
+			init_fifo (inode);
 	}
 }
 
@@ -349,7 +356,7 @@
 	if (!res)
 		goto out_fail;
 
-	printk (KERN_INFO "UMSDOS 0.85 "
+	printk (KERN_INFO "UMSDOS 0.85b "
 		"(compatibility level %d.%d, fast msdos)\n", 
 		UMSDOS_VERSION, UMSDOS_RELEASE);
 
diff -urN linux-2.2.14-pre15/fs/umsdos/ioctl.c linux-2.2.14-pre16/fs/umsdos/ioctl.c
--- linux-2.2.14-pre15/fs/umsdos/ioctl.c	1998-09-25 16:30:07.000000000 -0700
+++ linux-2.2.14-pre16/fs/umsdos/ioctl.c	2003-08-15 15:09:56.000000000 -0700
@@ -280,6 +280,8 @@
 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
 			ret = msdos_rename (dir, old_dentry, dir, new_dentry);
+			d_drop(new_dentry);
+			d_drop(old_dentry);
 			dput(new_dentry);
 		}
 		dput(old_dentry);
diff -urN linux-2.2.14-pre15/fs/umsdos/README-WIP.txt linux-2.2.14-pre16/fs/umsdos/README-WIP.txt
--- linux-2.2.14-pre15/fs/umsdos/README-WIP.txt	1999-08-09 12:04:41.000000000 -0700
+++ linux-2.2.14-pre16/fs/umsdos/README-WIP.txt	2003-08-15 15:09:56.000000000 -0700
@@ -11,6 +11,10 @@
 come and test it! See notes below for some more information, or if
 you are trying to use UMSDOS as root partition.
 
+Userland NOTE: new umsdos_progs (umssync, umssetup, udosctl & friends) that
+will compile and work on 2.2.x kernels and glibc based systems may be found
+at http://cvs.linux.hr/
+
 Legend: those lines marked with '+' on the beggining of line indicates it
 passed all of my tests, and performed perfect in all of them.
 
diff -urN linux-2.2.14-pre15/include/linux/joystick.h linux-2.2.14-pre16/include/linux/joystick.h
--- linux-2.2.14-pre15/include/linux/joystick.h	1999-05-11 10:36:43.000000000 -0700
+++ linux-2.2.14-pre16/include/linux/joystick.h	2003-08-15 15:09:56.000000000 -0700
@@ -4,7 +4,9 @@
 /*
  * /usr/include/linux/joystick.h  Version 1.2
  *
- * Copyright (C) 1996-1998 Vojtech Pavlik
+ * Copyright (C) 1996-1999 Vojtech Pavlik
+ *
+ * Sponsored by SuSE
  */
 
 /*
@@ -23,7 +25,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * 
  * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
  * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
  */
 
@@ -34,7 +36,7 @@
  * Version
  */
 
-#define JS_VERSION		0x01020d
+#define JS_VERSION		0x01020f
 
 /*
  * Types and constants for reading from /dev/js
@@ -128,75 +130,62 @@
 
 #include <linux/version.h>
 
-#ifndef KERNEL_VERSION
-#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
-#endif
-
-#ifndef LINUX_VERSION_CODE
-#error "You need to use at least 2.0 Linux kernel."
+#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,0,0)
-#error "You need to use at least 2.0 Linux kernel."
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#define JS_HAS_RDTSC (current_cpu_data.x86_capability & 0x10)
-#include <linux/init.h>
-#else
-#ifdef MODULE
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,35)
-#define JS_HAS_RDTSC (x86_capability & 0x10)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#include <asm/spinlock.h>
+typedef struct wait_queue *wait_queue_head_t;
+#define __setup(a,b)
+#define BASE_ADDRESS(x,i)	((x)->base_address[i])
+#define DECLARE_WAITQUEUE(x,y)	struct wait_queue x = { y, NULL }
+#define init_waitqueue_head(x)	do { *(x) = NULL; } while (0)
+#define __set_current_state(x)	current->state = x
+#define SETUP_PARAM		char *str, int *ints
+#define SETUP_PARSE(x)		do {} while (0)
 #else
-#define JS_HAS_RDTSC 0
-#endif
-#else
-#define JS_HAS_RDTSC (x86_capability & 0x10)
-#endif
-#define __initdata
-#define __init
-#define __cli cli
-#define __save_flags(flags) save_flags(flags)
-#define __restore_flags(flags)	restore_flags(flags)
-#define spin_lock_irqsave(x, flags) do { save_flags(flags); cli(); } while (0)
-#define spin_unlock_irqrestore(x, flags) restore_flags(flags)
-#define spin_lock_init(x) do { } while (0)
-typedef struct { int something; } spinlock_t;
-#define SPIN_LOCK_UNLOCKED { 0 }
-#define MODULE_AUTHOR(x)
-#define MODULE_PARM(x,y)
-#define MODULE_SUPPORTED_DEVICE(x)
-#define signal_pending(x) (((x)->signal) & ~((x)->blocked))
+#include <linux/spinlock.h>
+#define BASE_ADDRESS(x,i)	((x)->resource[i].start)
+#define SETUP_PARAM		char *str
+#define SETUP_PARSE(x)		int ints[x]; get_options(str, x, ints)
 #endif
 
+#define PCI_VENDOR_ID_AUREAL	0x12eb
+
 /*
  * Parport stuff
  */
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#define USE_PARPORT
-#endif
-
-#ifdef USE_PARPORT
 #include <linux/parport.h>
+
+#define JS_PAR_STATUS_INVERT	(0x80)
+#define JS_PAR_CTRL_INVERT	(0x04)
 #define JS_PAR_DATA_IN(y)	parport_read_data(y->port)
 #define JS_PAR_DATA_OUT(x,y)	parport_write_data(y->port, x)
 #define JS_PAR_STATUS(y)	parport_read_status(y->port)
+
+#ifndef PARPORT_NEED_GENERIC_OPS
 #define JS_PAR_CTRL_IN(y)	parport_read_control(y->port)
+#else
+#define JS_PAR_CTRL_IN(y)	inb(y->port->base+2) 
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
 #define JS_PAR_CTRL_OUT(x,y)	parport_write_control(y->port, x)
 #define JS_PAR_ECTRL_OUT(x,y)	parport_write_econtrol(y->port, x)
 #else
-#define JS_PAR_DATA_IN(y)	inb(y)
-#define JS_PAR_DATA_OUT(x,y)	outb(x,y)
-#define JS_PAR_STATUS(y)	inb(y+1)
-#define JS_PAR_CTRL_IN(y)	inb(y+2)
-#define JS_PAR_CTRL_OUT(x,y)	outb(x,y+2)
-#define JS_PAR_ECTRL_OUT(x,y)	outb(x,y+0x402)
+#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
 
-#define JS_PAR_STATUS_INVERT	(0x80)
-#define JS_PAR_CTRL_INVERT	(0x04)
-
 /*
  * Internal types
  */
@@ -204,8 +193,6 @@
 struct js_dev;
 
 typedef int (*js_read_func)(void *info, int **axes, int **buttons);
-typedef unsigned int (*js_time_func)(void);
-typedef int (*js_delta_func)(unsigned int x, unsigned int y);
 typedef int (*js_ops_func)(struct js_dev *dev);
 
 struct js_data {
@@ -217,7 +204,7 @@
 	struct js_dev *next;
 	struct js_list *list;
 	struct js_port *port;
-	struct wait_queue *wait;
+	wait_queue_head_t wait;
 	struct js_data cur;
 	struct js_data new;
 	struct js_corr *corr;
@@ -249,20 +236,14 @@
 	struct js_corr **corr;
 	void *info;
 	int ndevs;
+	int fail;
+	int total;
 };
 
 /*
  * Sub-module interface
  */
 
-extern unsigned int js_time_speed;
-extern js_time_func js_get_time;
-extern js_delta_func js_delta;
-
-extern unsigned int js_time_speed_a;
-extern js_time_func js_get_time_a;
-extern js_delta_func js_delta_a;
-
 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);
@@ -280,19 +261,19 @@
 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 void js_am_setup(char *str, int *ints);
-extern void js_an_setup(char *str, int *ints);
-extern void js_as_setup(char *str, int *ints);
-extern void js_console_setup(char *str, int *ints);
-extern void js_db9_setup(char *str, int *ints);
-extern void js_l4_setup(char *str, int *ints);
+extern int js_tg_init(void);
+extern int js_war_init(void);
 
 #endif /* __KERNEL__ */
 
diff -urN linux-2.2.14-pre15/init/main.c linux-2.2.14-pre16/init/main.c
--- linux-2.2.14-pre15/init/main.c	2003-08-15 15:09:55.000000000 -0700
+++ linux-2.2.14-pre16/init/main.c	2003-08-15 15:09:56.000000000 -0700
@@ -117,21 +117,30 @@
 #ifdef CONFIG_JOY_ANALOG
 extern void js_an_setup(char *str, int *ints);
 #endif
-#ifdef CONFIG_JOY_ASSASIN
+#ifdef CONFIG_JOY_ASSASSIN
 extern void js_as_setup(char *str, int *ints);
 #endif
 #ifdef CONFIG_JOY_CONSOLE
 extern void js_console_setup(char *str, int *ints);
+extern void js_console_setup_2(char *str, int *ints);
+extern void js_console_setup_3(char *str, int *ints);
 #endif
 #ifdef CONFIG_JOY_DB9
 extern void js_db9_setup(char *str, int *ints);
+extern void js_db9_setup_2(char *str, int *ints);
+extern void js_db9_setup_3(char *str, int *ints);
 #endif
 #ifdef CONFIG_JOY_TURBOGRAFX
 extern void js_tg_setup(char *str, int *ints);
+extern void js_tg_setup_2(char *str, int *ints);
+extern void js_tg_setup_3(char *str, int *ints);
 #endif
 #ifdef CONFIG_JOY_LIGHTNING
 extern void js_l4_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_JOY_PCI
+extern void js_pci_setup(char *str, int *ints);
+#endif
 extern void eth_setup(char *str, int *ints);
 #ifdef CONFIG_ARCNET_COM20020
 extern void com20020_setup(char *str, int *ints);
@@ -680,31 +689,34 @@
 #ifdef CONFIG_JOY_ANALOG
 	{ "js_an=", js_an_setup },
 #endif
-#ifdef CONFIG_JOY_ASSASIN
+#ifdef CONFIG_JOY_ASSASSIN
 	{ "js_as=", js_as_setup },
 #endif
 #ifdef CONFIG_JOY_CONSOLE
 	{ "js_console=", js_console_setup },
-	{ "js_console2=", js_console_setup },
-	{ "js_console3=", js_console_setup },
+	{ "js_console2=", js_console_setup_2 },
+	{ "js_console3=", js_console_setup_3 },
 #endif
 #ifdef CONFIG_JOY_DB9
 	{ "js_db9=", js_db9_setup },
-	{ "js_db9_2=", js_db9_setup },
-	{ "js_db9_3=", js_db9_setup },
+	{ "js_db9_2=", js_db9_setup_2 },
+	{ "js_db9_3=", js_db9_setup_3 },
 #endif
 #ifdef CONFIG_JOY_TURBOGRAFX
 	{ "js_tg=", js_tg_setup },
-	{ "js_tg_2=", js_tg_setup },
-	{ "js_tg_3=", js_tg_setup },
+	{ "js_tg_2=", js_tg_setup_2 },
+	{ "js_tg_3=", js_tg_setup_3 },
+#endif
+#ifdef CONFIG_JOY_LIGHTNING
+	{ "js_l4=", js_l4_setup },
+#endif
+#ifdef CONFIG_JOY_PCI
+	{ "js_pci=", js_pci_setup },
 #endif
 #ifdef CONFIG_SCSI
 	{ "max_scsi_luns=", scsi_luns_setup },
 	{ "scsi_logging=", scsi_logging_setup },
 #endif
-#ifdef CONFIG_JOY_LIGHTNING
-	{ "js_l4=", js_l4_setup },
-#endif
 #ifdef CONFIG_SCSI_ADVANSYS
 	{ "advansys=", advansys_setup },
 #endif
diff -urN linux-2.2.14-pre15/MAINTAINERS linux-2.2.14-pre16/MAINTAINERS
--- linux-2.2.14-pre15/MAINTAINERS	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/MAINTAINERS	2003-08-15 15:09:55.000000000 -0700
@@ -458,9 +458,9 @@
 
 JOYSTICK DRIVER
 P:	Vojtech Pavlik
-M:	vojtech@ucw.cz
+M:	vojtech@suse.cz
 L:	linux-joystick@atrey.karlin.mff.cuni.cz
-W:	http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/
+W:	http://www.suse.cz/development/joystick/
 S:	Maintained
 
 KERNEL AUTOMOUNTER (AUTOFS)
diff -urN linux-2.2.14-pre15/Makefile linux-2.2.14-pre16/Makefile
--- linux-2.2.14-pre15/Makefile	2003-08-15 15:09:54.000000000 -0700
+++ linux-2.2.14-pre16/Makefile	2003-08-15 15:09:55.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 14
-EXTRAVERSION = pre15
+EXTRAVERSION = pre16
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
