Input: gamecon - add SNES mouse support
Raphael Assenat [Sun, 2 Apr 2006 05:10:05 +0000 (00:10 -0500)]
SNES gamepads and mice share the same type of interface so they both can be
connected to the parallel port using a simple interface.  Adding mouse
support to a gamepad driver may sound funny at first, but doing so in this
case makes it possible to connect and SNES gamepads and mice at the same
time, on the same port.

Signed-off-by: Raphael Assenat <raph@raphnet.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

Documentation/input/joystick-parport.txt
drivers/input/joystick/gamecon.c

index 88a011c..d537c48 100644 (file)
@@ -36,12 +36,12 @@ with them.
 
   All NES and SNES use the same synchronous serial protocol, clocked from
 the computer's side (and thus timing insensitive). To allow up to 5 NES
-and/or SNES gamepads connected to the parallel port at once, the output
-lines of the parallel port are shared, while one of 5 available input lines
-is assigned to each gamepad.
+and/or SNES gamepads and/or SNES mice connected to the parallel port at once,
+the output lines of the parallel port are shared, while one of 5 available
+input lines is assigned to each gamepad.
 
   This protocol is handled by the gamecon.c driver, so that's the one
-you'll use for NES and SNES gamepads.
+you'll use for NES, SNES gamepads and SNES mice.
 
   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
@@ -106,7 +106,7 @@ A, Turbo B, Select and Start, and is connected through 5 wires, then it is
 either a NES or NES clone and will work with this connection. SNES gamepads
 also use 5 wires, but have more buttons. They will work as well, of course.
 
-Pinout for NES gamepads                 Pinout for SNES gamepads
+Pinout for NES gamepads                 Pinout for SNES gamepads and mice
 
           +----> Power                   +-----------------------\
           |                            7 | o  o  o  o |  x  x  o  | 1
@@ -454,6 +454,7 @@ uses the following kernel/module command line:
          6  | N64 pad
          7  | Sony PSX controller
          8  | Sony PSX DDR controller
+         9  | SNES mouse
 
   The exact type of the PSX controller type is autoprobed when used so
 hot swapping should work (but is not recomended).
index aa3ef89..ecbdb6b 100644 (file)
@@ -7,6 +7,7 @@
  *  Based on the work of:
  *     Andree Borrmann         John Dahlstrom
  *     David Kuder             Nathan Hand
+ *     Raphael Assenat
  */
 
 /*
@@ -73,8 +74,9 @@ __obsolete_setup("gc_3=");
 #define GC_N64         6
 #define GC_PSX         7
 #define GC_DDR         8
+#define GC_SNESMOUSE   9
 
-#define GC_MAX         8
+#define GC_MAX         9
 
 #define GC_REFRESH_TIME        HZ/100
 
@@ -94,7 +96,7 @@ static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
 
 static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
                                "Multisystem 2-button joystick", "N64 controller", "PSX controller",
-                               "PSX DDR controller" };
+                               "PSX DDR controller", "SNES mouse" };
 /*
  * N64 support.
  */
@@ -206,9 +208,12 @@ static void gc_n64_process_packet(struct gc *gc)
  * NES/SNES support.
  */
 
-#define GC_NES_DELAY   6       /* Delay between bits - 6us */
-#define GC_NES_LENGTH  8       /* The NES pads use 8 bits of data */
-#define GC_SNES_LENGTH 12      /* The SNES true length is 16, but the last 4 bits are unused */
+#define GC_NES_DELAY           6       /* Delay between bits - 6us */
+#define GC_NES_LENGTH          8       /* The NES pads use 8 bits of data */
+#define GC_SNES_LENGTH         12      /* The SNES true length is 16, but the
+                                          last 4 bits are unused */
+#define GC_SNESMOUSE_LENGTH    32      /* The SNES mouse uses 32 bits, the first
+                                          16 bits are equivalent to a gamepad */
 
 #define GC_NES_POWER   0xfc
 #define GC_NES_CLOCK   0x01
@@ -243,11 +248,15 @@ static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
 
 static void gc_nes_process_packet(struct gc *gc)
 {
-       unsigned char data[GC_SNES_LENGTH];
+       unsigned char data[GC_SNESMOUSE_LENGTH];
        struct input_dev *dev;
-       int i, j, s;
+       int i, j, s, len;
+       char x_rel, y_rel;
+
+       len = gc->pads[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH :
+                       (gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH);
 
-       gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data);
+       gc_nes_read_packet(gc, len, data);
 
        for (i = 0; i < GC_MAX_DEVICES; i++) {
 
@@ -270,6 +279,44 @@ static void gc_nes_process_packet(struct gc *gc)
                        for (j = 0; j < 8; j++)
                                input_report_key(dev, gc_snes_btn[j], s & data[gc_snes_bytes[j]]);
 
+               if (s & gc->pads[GC_SNESMOUSE]) {
+                       /*
+                        * The 4 unused bits from SNES controllers appear to be ID bits
+                        * so use them to make sure iwe are dealing with a mouse.
+                        * gamepad is connected. This is important since
+                        * my SNES gamepad sends 1's for bits 16-31, which
+                        * cause the mouse pointer to quickly move to the
+                        * upper left corner of the screen.
+                        */
+                       if (!(s & data[12]) && !(s & data[13]) &&
+                           !(s & data[14]) && (s & data[15])) {
+                               input_report_key(dev, BTN_LEFT, s & data[9]);
+                               input_report_key(dev, BTN_RIGHT, s & data[8]);
+
+                               x_rel = y_rel = 0;
+                               for (j = 0; j < 7; j++) {
+                                       x_rel <<= 1;
+                                       if (data[25 + j] & s)
+                                               x_rel |= 1;
+
+                                       y_rel <<= 1;
+                                       if (data[17 + j] & s)
+                                               y_rel |= 1;
+                               }
+
+                               if (x_rel) {
+                                       if (data[24] & s)
+                                               x_rel = -x_rel;
+                                       input_report_rel(dev, REL_X, x_rel);
+                               }
+
+                               if (y_rel) {
+                                       if (data[16] & s)
+                                               y_rel = -y_rel;
+                                       input_report_rel(dev, REL_Y, y_rel);
+                               }
+                       }
+               }
                input_sync(dev);
        }
 }
@@ -525,10 +572,10 @@ static void gc_timer(unsigned long private)
                gc_n64_process_packet(gc);
 
 /*
- * NES and SNES pads
+ * NES and SNES pads or mouse
  */
 
-       if (gc->pads[GC_NES] || gc->pads[GC_SNES])
+       if (gc->pads[GC_NES] || gc->pads[GC_SNES] || gc->pads[GC_SNESMOUSE])
                gc_nes_process_packet(gc);
 
 /*
@@ -610,10 +657,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
        input_dev->open = gc_open;
        input_dev->close = gc_close;
 
-       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       if (pad_type != GC_SNESMOUSE) {
+               input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
 
-       for (i = 0; i < 2; i++)
-               input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+               for (i = 0; i < 2; i++)
+                       input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+       } else
+               input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
 
        gc->pads[0] |= gc_status_bit[idx];
        gc->pads[pad_type] |= gc_status_bit[idx];
@@ -631,6 +681,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type)
 
                        break;
 
+               case GC_SNESMOUSE:
+                       set_bit(BTN_LEFT, input_dev->keybit);
+                       set_bit(BTN_RIGHT, input_dev->keybit);
+                       set_bit(REL_X, input_dev->relbit);
+                       set_bit(REL_Y, input_dev->relbit);
+                       break;
+
                case GC_SNES:
                        for (i = 4; i < 8; i++)
                                set_bit(gc_snes_btn[i], input_dev->keybit);