linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20
@ 2002-12-06 14:22 yokotak
  2003-01-26 10:39 ` Vojtech Pavlik
  0 siblings, 1 reply; 8+ messages in thread
From: yokotak @ 2002-12-06 14:22 UTC (permalink / raw)
  To: vojtech; +Cc: linux-kernel

Dear Sirs,
This patch for char/joystick/gamecon.c will adds support for Sega
Saturn Pad on parallel port with DirectPad Pro interface. Please set
parameters "8" for Saturn, like "gc=0,8,8,8".
I tested it with several digital controller, multitap, mission stick,
shuttle-mouse(it returns Y-axis inverted value against joystick),
multi controller and racing controller on extra-5volt-powered 1st
connector. It not supports saturn keyboard, virtua gun, electric train
controller and pachinko controller. And I have not tested on 2nd
connector yet.

--- linux-2.4.20/drivers/char/joystick/gamecon.c	2001-09-13 07:34:06.000000000 +0900
+++ linux/drivers/char/joystick/gamecon.c	2002-12-04 09:56:13.000000000 +0900
@@ -11,7 +11,7 @@
  */
 
 /*
- * NES, SNES, N64, Multi1, Multi2, PSX gamepad driver for Linux
+ * NES, SNES, N64, Multi1, Multi2, PSX, SATURN gamepad driver for Linux
  */
 
 /*
@@ -41,11 +41,14 @@
 #include <linux/parport.h>
 #include <linux/input.h>
 
+#define GC_PADS_MAX 5
+#define GC_MAX_ARG "6"		/* GC_PADS_MAX + 1 */
+
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_LICENSE("GPL");
-MODULE_PARM(gc, "2-6i");
-MODULE_PARM(gc_2,"2-6i");
-MODULE_PARM(gc_3,"2-6i");
+MODULE_PARM(gc, "2-" GC_MAX_ARG "i");
+MODULE_PARM(gc_2, "2-" GC_MAX_ARG "i");
+MODULE_PARM(gc_3, "2-" GC_MAX_ARG "i");
 
 #define GC_SNES		1
 #define GC_NES		2
@@ -54,29 +57,31 @@
 #define GC_MULTI2	5
 #define GC_N64		6	
 #define GC_PSX		7
+#define GC_SATURN	8
 
-#define GC_MAX		7
+#define GC_MAX		8
 
 #define GC_REFRESH_TIME	HZ/100
  
 struct gc {
 	struct pardevice *pd;
-	struct input_dev dev[5];
+	struct input_dev dev[GC_PADS_MAX];
 	struct timer_list timer;
-	unsigned char pads[GC_MAX + 1];
+	unsigned pads[GC_MAX + 1];
 	int used;
 };
 
 static struct gc *gc_base[3];
 
-static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 };
-static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 };
-static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_2[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_3[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
 
-static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+static unsigned gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
+				    0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 };
 
 static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
-				"Multisystem 2-button joystick", "N64 controller", "PSX controller" };
+			    "Multisystem 2-button joystick", "N64 controller", "PSX controller", "SATURN controller" };
 /*
  * N64 support.
  */
@@ -287,17 +292,239 @@
 }
 
 /*
+ * SATURN support
+ *
+ * References
+ *
+ * Mr. Philhower's DirectPad Pro source code and circuit
+ *  joysrc.txt
+ *  saturn.gif
+ * http://www.ziplabel.com/
+ * http://www.arcadecontrols.com/Mirrors/www.ziplabel.com/dpadpro/dpadpr50.zip
+ *
+ * Mr. Kashima's SATURN pad analysis documentation
+ * http://Lillith.sk.tsukuba.ac.jp/~kashima/games/saturn-e.html
+ *
+ * Mr. Nagato's IF-SEGA documentation (Japanese)
+ * http://www.geocities.co.jp/Playtown-Dice/4434/ifsspad.htm
+ *
+ * Mr. Saka's PA (Windows 9x IF-SEGA driver) source code
+ *  PA.ASM
+ * http://blue.ribbon.to/~als4kmaniac/i2/pa.lzh
+ *
+ * Mr. Murata's linux IF-SEGA driver documentation (Japanese) and header file
+ *  DEVICE.txt
+ *  ifsega.h ("DEV TYPE" section)
+ * http://www1.tcnet.ne.jp/fmurata/linux/ifsega/ifsega-0.17.tar.gz
+ *
+ * Mr. Kimura's PSXPAD circuit
+ *  saturn_wiring.jpg
+ * http://speed.dynu.com/function/Wiring_e.html
+ */
+
+#define GC_SATURN_PADS_MAX 12
+#define GC_SATURN_LENGTH 60	/* multitap (1 (id) + 9 (known max)) byte x 6 connector */
+#define GC_SATURN_ANALOG_DELAY 400	/* micro-second (200-700) */
+#define GC_SATURN_DISABLE_EMPTY_CONNECTOR 1	/* when connector is empty, warn and disable device */
+
+/* output */
+#define GC_SATURN_P1_SEL1 0x01	/* db25 pin 2 */
+#define GC_SATURN_P1_SEL2 0x02	/* db25 pin 3 */
+#define GC_SATURN_P2_SEL1 0x10	/* db25 pin 6 */
+#define GC_SATURN_P2_SEL2 0x20	/* db25 pin 7 */
+#define GC_SATURN_POWER 0x08	/* db25 pin 5 */
+#define GC_SATURN_POWER_SUB 0x04	/* db25 pin 4 */
+#define GC_SATURN_P1_GND_HIGH 0x40	/* db25 pin 8 */
+#define GC_SATURN_P2_GND_HIGH 0x80	/* db25 pin 9 */
+/* input */
+#define GC_SATURN_DATA4 0x10	/* db25 pin 13 */
+#define GC_SATURN_DATA3 0x20	/* db25 pin 12 */
+#define GC_SATURN_DATA2 0x40	/* db25 pin 10 */
+#define GC_SATURN_DATA1 0x80	/* db25 pin 11 */
+
+static short gc_saturn_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z,
+				 ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static short gc_saturn_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,
+				 BTN_TL, BTN_TR, BTN_START };
+static int gc_saturn_btn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
+static unsigned char gc_saturn_btn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20,
+					      0x10, 0x08, 0x80, 0x08 };
+
+/*
+ * gc_saturn_read_sub() reads parallel port and returns formatted 4 bit data.
+ */
+
+static unsigned char gc_saturn_read_sub(struct gc *gc)
+{
+	unsigned char data;
+
+	/* read parallel port and invert bit 7 (pin 11) */
+	data = parport_read_status(gc->pd->port) ^ 0x80;
+
+	return (data & GC_SATURN_DATA1 ? 1 : 0) | (data & GC_SATURN_DATA2 ? 2 : 0)
+	    | (data & GC_SATURN_DATA3 ? 4 : 0) | (data & GC_SATURN_DATA4 ? 8 : 0);
+}
+
+/*
+ * gc_saturn_read_analog() sends clock and returns 8 bit data.
+ */
+
+static unsigned char gc_saturn_read_analog(struct gc *gc, unsigned char power, unsigned char sel2)
+{
+	unsigned char data;
+
+	/* sel2 low  - sel1 low */
+	parport_write_data(gc->pd->port, power);
+	udelay(GC_SATURN_ANALOG_DELAY);
+	data = gc_saturn_read_sub(gc) << 4;
+
+	/* sel2 high - sel1 low */
+	parport_write_data(gc->pd->port, power | sel2);
+	udelay(GC_SATURN_ANALOG_DELAY);
+	data |= gc_saturn_read_sub(gc);
+
+	return data;
+}
+
+/*
+ * gc_saturn_read_packet() reads a whole saturn packet at connector 
+ * and returns device identifier code.
+ */
+
+static unsigned gc_saturn_read_packet(struct gc *gc, unsigned char *data, int connector, int power_by_port)
+{
+	int i, j;
+	unsigned char power, sel1, sel2, tmp;
+
+	if (!connector) {
+		/* connector 1 */
+		power = GC_SATURN_P2_GND_HIGH + GC_SATURN_P2_SEL1 + GC_SATURN_P2_SEL2 
+		  + (power_by_port ? GC_SATURN_POWER : 0);
+		sel1 = GC_SATURN_P1_SEL1;
+		sel2 = GC_SATURN_P1_SEL2;
+	} else {
+		/* connector 2 */
+		power = GC_SATURN_P1_GND_HIGH + GC_SATURN_P1_SEL1 + GC_SATURN_P1_SEL2 
+		  + (power_by_port ? GC_SATURN_POWER : 0);
+		sel1 = GC_SATURN_P2_SEL1;
+		sel2 = GC_SATURN_P2_SEL2;
+	}
+
+	/* read digital id */
+	parport_write_data(gc->pd->port, power | sel2 | sel1);
+	data[0] = gc_saturn_read_sub(gc) & 0x0f;
+
+	switch (data[0]) {
+
+	case 0xf:
+		/* 1111  no pad */
+		return data[0] = 0xff;
+
+	case 0x4:
+	case 0x4 | 0x8:
+		/* ?100 : digital controller
+		 * data[ 0] data[ 1] data[ 2]
+		 * 00000010 rlduSACB RXYZL100
+		 */
+		power += (power_by_port ? GC_SATURN_POWER_SUB : 0);
+		/* pin 4 and pin 9 are connected in digital device. */
+
+		parport_write_data(gc->pd->port, power | sel2);
+		data[1] = gc_saturn_read_sub(gc) << 4;
+		parport_write_data(gc->pd->port, power | sel1);
+		data[1] |= gc_saturn_read_sub(gc);
+		parport_write_data(gc->pd->port, power);
+		data[2] = gc_saturn_read_sub(gc) << 4;
+		parport_write_data(gc->pd->port, power | sel2 | sel1);
+		data[2] |= gc_saturn_read_sub(gc);
+		return data[0] = 0x02; /* digital controller id in analog style */
+
+	case 0x1:
+		/* 0001 : analog controller */
+		parport_write_data(gc->pd->port, power | sel2);
+		udelay(GC_SATURN_ANALOG_DELAY);
+		/* read analog id */
+		data[0] = gc_saturn_read_analog(gc, power, sel2);
+		if (data[0] != 0x41) {
+			/* read controller
+			 * data[ 0] data[ 1] data[ 2] data[ 3] ..
+			 * deviceID rlduSACB RXYZL/// analog_1 ..   
+			 */
+			for (i = 0; i < (data[0] & 0x0f); i++)
+				data[i + 1] = gc_saturn_read_analog(gc, power, sel2);
+			parport_write_data(gc->pd->port, power | sel1 | sel2);
+			return data[0];
+		} else {
+			/* read multitap 
+			 * 0x41 : multitap id
+			 *
+			 * 0x60 : multitap id-2
+			 *
+			 * data[ 0] data[ 1] data[ 2] data[ 3] .. : connector 1
+			 * deviceID rlduSACB RXYZL/// analog_1 ..
+			 *
+			 * data[10] .. : connector 2
+			 * deviceID ..
+			 * .
+			 * .
+			 * data[n0] data[n1] data[n2]   
+			 * 00000010 rlduSACB RXYZL000 : digital pad
+			 *
+			 * data[m0]
+			 * 11111111 : no pad
+			 * .
+			 * .
+			 * data[50] data[51] data[52] data[53] .. data[59] : connector 6
+			 * deviceID rlduSACB RXYZL/// analog_1 .. analog_6   mission stick x2
+			 */ 
+			 
+			/* check multitap id-2 */
+			if (gc_saturn_read_analog(gc, power, sel2) != 0x60)
+				return data[0] = 0xff;
+
+			for (i = 0; i < 60; i += 10) {
+				data[i] = gc_saturn_read_analog(gc, power, sel2);
+				if (data[i] != 0xff) {
+					/* read pad */
+					for (j = 0; j < (data[i] & 0x0f); j++)
+						if (j < 9)
+							data[i + j + 1] = gc_saturn_read_analog(gc, power, sel2);
+				}
+			}
+			parport_write_data(gc->pd->port, power | sel2 | sel1);
+			return 0x41; /* multitap id */
+		}
+
+	case 0x0:
+		/* 0000 : mouse */
+		parport_write_data(gc->pd->port, power | sel2);
+		udelay(GC_SATURN_ANALOG_DELAY);
+		tmp = gc_saturn_read_analog(gc, power, sel2);
+		if (tmp == 0xff) {
+			for (i = 0; i < 3; i++)
+				data[i + 1] = gc_saturn_read_analog(gc, power, sel2);
+			parport_write_data(gc->pd->port, power | sel2 | sel1);
+			return data[0] = 0xe3;	/* mouse id */
+		}
+
+	default:
+		/* unknown */
+		return data[0];
+	}
+}
+
+/*
  * gc_timer() reads and analyzes console pads data.
  */
 
-#define GC_MAX_LENGTH GC_N64_LENGTH
+#define GC_MAX_LENGTH GC_SATURN_LENGTH
 
 static void gc_timer(unsigned long private)
 {
 	struct gc *gc = (void *) private;
 	struct input_dev *dev = gc->dev;
 	unsigned char data[GC_MAX_LENGTH];
-	int i, j, s;
+	int i, j, k, s, n;
 
 /*
  * N64 pads - must be read first, any read confuses them for 200 us
@@ -307,7 +534,7 @@
 
 		gc_n64_read_packet(gc, data);
 
-		for (i = 0; i < 5; i++) {
+		for (i = 0; i < GC_PADS_MAX; i++) {
 
 			s = gc_status_bit[i];
 
@@ -432,6 +659,83 @@
 		}
 	}
 
+/*
+ * SATURN controllers
+ */
+
+	if (gc->pads[GC_SATURN]) {
+		for (n = 0, i = 0; i < 2; i++) {
+			s = gc_saturn_read_packet(gc, data, i, 1) == 0x41 ? 60 : 10;
+			for (j = 0; j < s; j += 10, n++) {
+				if (gc_status_bit[n] & gc->pads[GC_SATURN]) {
+					switch (data[j]) {
+
+					case 0x16:
+						/* multi controller (analog 4 axis) */
+						input_report_abs(dev + n, ABS_Z, data[j + 6]);
+					case 0x15:
+						/* mission stick (analog 3 axis) */
+						input_report_abs(dev + n, ABS_RY, data[j + 4]);
+						input_report_abs(dev + n, ABS_RZ, data[j + 5]);
+					case 0x13:
+						/* racing controller (analog 1 axis) */
+						input_report_abs(dev + n, ABS_RX, data[j + 3]);
+					case 0x02:
+						/* digital pad (digital 2 axis + buttons) */
+						input_report_abs(dev + n, ABS_X,
+								 (data[j + 1] & 128 ? 0 : 1) - (data[j + 1] & 64 ? 0 : 1));
+						input_report_abs(dev + n, ABS_Y,
+								 (data[j + 1] & 32 ? 0 : 1) - (data[j + 1] & 16 ? 0 : 1));
+						for (k = 0; k < 9; k++)
+							input_report_key(dev + n, gc_saturn_btn[k],
+									 ~data[j + gc_saturn_btn_byte[k]] & gc_saturn_btn_mask[k]);
+						break;
+
+					case 0x19:
+						/* mission stick x2 (analog 6 axis + digital 4 axis + buttons) */
+						input_report_abs(dev + n, ABS_X,
+								 (data[j + 1] & 128 ? 0 : 1) - (data[j + 1] & 64 ? 0 : 1));
+						input_report_abs(dev + n, ABS_Y,
+								 (data[j + 1] & 32 ? 0 : 1) - (data[j + 1] & 16 ? 0 : 1));
+						for (k = 0; k < 9; k++)
+							input_report_key(dev + n, gc_saturn_btn[k],
+									 ~data[j + gc_saturn_btn_byte[k]] & gc_saturn_btn_mask[k]);
+						input_report_abs(dev + n, ABS_RX, data[j + 3]);
+						input_report_abs(dev + n, ABS_RY, data[j + 4]);
+						input_report_abs(dev + n, ABS_RZ, data[j + 5]);
+						input_report_abs(dev + n, ABS_HAT1X,
+								 (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+						input_report_abs(dev + n, ABS_HAT1Y,
+								 (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+						input_report_abs(dev + n, ABS_HAT0X, data[j + 7]);
+						input_report_abs(dev + n, ABS_HAT0Y, data[j + 8]);
+						input_report_abs(dev + n, ABS_Z, data[j + 9]);
+						break;
+
+					case 0xe3:
+						/* shuttle mouse (analog 2 axis + buttons. signed value) */
+						input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
+						input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
+						input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
+						input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
+						input_report_abs(dev + n, ABS_RX, data[j + 2] ^ 0x80);
+						input_report_abs(dev + n, ABS_RY, data[j + 3] ^ 0x80);
+						break;
+
+					case 0xff:
+					default:
+						/* no pad */
+						input_report_abs(dev + n, ABS_X, 0);
+						input_report_abs(dev + n, ABS_Y, 0);
+						for (k = 0; k < 9; k++)
+							input_report_key(dev + n, gc_saturn_btn[k], 0);
+						break;
+					}
+				}
+			}
+		}
+	}
+
 	mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
 }
 
@@ -460,8 +764,8 @@
 {
 	struct gc *gc;
 	struct parport *pp;
-	int i, j, psx;
-	unsigned char data[32];
+	int i, j, k, n, psx;
+	unsigned char data[GC_MAX_LENGTH];
 
 	if (config[0] < 0)
 		return NULL;
@@ -492,7 +796,7 @@
 	gc->timer.data = (long) gc;
 	gc->timer.function = gc_timer;
 
-	for (i = 0; i < 5; i++) {
+	for (i = 0; i < GC_PADS_MAX; i++) {
 
 		if (!config[i + 1])
 			continue;
@@ -588,6 +892,53 @@
 							" please report to <vojtech@suse.cz>.\n", psx);
 				}
 				break;
+
+		case GC_SATURN:
+			if (i >= GC_SATURN_PADS_MAX) {
+				gc->pads[GC_SATURN] &= ~gc_status_bit[i];
+				printk(KERN_ERR "gamecon.c: Max %d SATURN controllers supported.\n",GC_SATURN_PADS_MAX);
+				break;
+			}
+			for (n = 0, j = 0; j < 2; j++) {
+				gc_saturn_read_packet(gc, data, j, 1);	/* dummy read */
+				udelay(20000); /* enough long time */
+				psx = (gc_saturn_read_packet(gc, data, j, 1) == 0x41) ? 60 : 10;
+				for (k = 0; k < psx ; k += 10, n++) {
+					if (n == i) {
+						if (data[k] == 0xff && GC_SATURN_DISABLE_EMPTY_CONNECTOR) {
+							gc->pads[GC_SATURN] &= ~gc_status_bit[i];
+							printk(KERN_ERR "gamecon.c: No SATURN controller found.\n");
+						}
+						if (data[k] == 0x11) {
+							/* with external 5 volt powered single connector,
+							   analog device returns 0x11 when read connector 2. */
+							gc->pads[GC_SATURN] &= ~gc_status_bit[i];
+							printk(KERN_WARNING "gamecon.c: Single SATURN connector?\n");
+						}
+					}
+				}
+			}
+			if (i > n - 1) {
+				gc->pads[GC_SATURN] &= ~gc_status_bit[i];
+				printk(KERN_ERR "gamecon.c: No more connector.(%d exists)\n",n);
+				break;
+			}
+			if (gc->pads[GC_SATURN] & gc_status_bit[i]) {
+				for (j = 0; j < 9; j++)
+					set_bit(gc_saturn_btn[j], gc->dev[i].keybit);
+				for (j = 0; j < 10; j++) {
+					set_bit(gc_saturn_abs[j], gc->dev[i].absbit);
+					if (j < 2 || j > 7) {
+						gc->dev[i].absmin[gc_saturn_abs[j]] = -1;
+						gc->dev[i].absmax[gc_saturn_abs[j]] = 1;
+					} else {
+						gc->dev[i].absmin[gc_saturn_abs[j]] = 1;
+						gc->dev[i].absmax[gc_saturn_abs[j]] = 255;
+						gc->dev[i].absflat[gc_saturn_abs[j]] = 0;
+					}
+				}
+			}
+			break;
 		}
 
                 gc->dev[i].name = gc_names[config[i + 1]];
@@ -605,7 +956,7 @@
 		return NULL;
 	}
 
-	for (i = 0; i < 5; i++) 
+	for (i = 0; i < GC_PADS_MAX; i++)
 		if (gc->pads[0] & gc_status_bit[i]) {
 			input_register_device(gc->dev + i);
 			printk(KERN_INFO "input%d: %s on %s\n", gc->dev[i].number, gc->dev[i].name, gc->pd->port->name);
@@ -617,25 +968,26 @@
 #ifndef MODULE
 int __init gc_setup(char *str)
 {
-	int i, ints[7];
+	int i, ints[GC_PADS_MAX + 2];
 	get_options(str, ARRAY_SIZE(ints), ints);
-	for (i = 0; i <= ints[0] && i < 6; i++) gc[i] = ints[i + 1];
+	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc[i] = ints[i + 1];
 	return 1;
 }
 int __init gc_setup_2(char *str)
 {
-	int i, ints[7];
+	int i, ints[GC_PADS_MAX + 2];
 	get_options(str, ARRAY_SIZE(ints), ints);
-	for (i = 0; i <= ints[0] && i < 6; i++) gc_2[i] = ints[i + 1];
+	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc_2[i] = ints[i + 1];
 	return 1;
 }
 int __init gc_setup_3(char *str)
 {
-	int i, ints[7];
+	int i, ints[GC_PADS_MAX + 2];
 	get_options(str, ARRAY_SIZE(ints), ints);
-	for (i = 0; i <= ints[0] && i < 6; i++) gc_3[i] = ints[i + 1];
+	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc_3[i] = ints[i + 1];
 	return 1;
 }
+
 __setup("gc=", gc_setup);
 __setup("gc_2=", gc_setup_2);
 __setup("gc_3=", gc_setup_3);
@@ -659,7 +1011,7 @@
 
 	for (i = 0; i < 3; i++)
 		if (gc_base[i]) {
-			for (j = 0; j < 5; j++)
+			for (j = 0; j < GC_PADS_MAX; j++)
 				if (gc_base[i]->pads[0] & gc_status_bit[j])
 					input_unregister_device(gc_base[i]->dev + j); 
 			parport_unregister_device(gc_base[i]->pd);



---
YOKOTA, Ken-ichi


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20
  2002-12-06 14:22 [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20 yokotak
@ 2003-01-26 10:39 ` Vojtech Pavlik
  2003-01-27 10:34   ` yokotak
  0 siblings, 1 reply; 8+ messages in thread
From: Vojtech Pavlik @ 2003-01-26 10:39 UTC (permalink / raw)
  To: yokotak; +Cc: vojtech, linux-kernel

On Fri, Dec 06, 2002 at 11:22:09PM +0900, yokotak@rmail.plala.or.jp wrote:

> Dear Sirs,
> This patch for char/joystick/gamecon.c will adds support for Sega
> Saturn Pad on parallel port with DirectPad Pro interface. Please set
> parameters "8" for Saturn, like "gc=0,8,8,8".
> I tested it with several digital controller, multitap, mission stick,
> shuttle-mouse(it returns Y-axis inverted value against joystick),
> multi controller and racing controller on extra-5volt-powered 1st
> connector. It not supports saturn keyboard, virtua gun, electric train
> controller and pachinko controller. And I have not tested on 2nd
> connector yet.

Why are you adding it into gamecon.c and not fixing it in db9.c instead?

> --- linux-2.4.20/drivers/char/joystick/gamecon.c	2001-09-13 07:34:06.000000000 +0900
> +++ linux/drivers/char/joystick/gamecon.c	2002-12-04 09:56:13.000000000 +0900
> @@ -11,7 +11,7 @@
>   */
>  
>  /*
> - * NES, SNES, N64, Multi1, Multi2, PSX gamepad driver for Linux
> + * NES, SNES, N64, Multi1, Multi2, PSX, SATURN gamepad driver for Linux
>   */
>  
>  /*
> @@ -41,11 +41,14 @@
>  #include <linux/parport.h>
>  #include <linux/input.h>
>  
> +#define GC_PADS_MAX 5
> +#define GC_MAX_ARG "6"		/* GC_PADS_MAX + 1 */
> +
>  MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
>  MODULE_LICENSE("GPL");
> -MODULE_PARM(gc, "2-6i");
> -MODULE_PARM(gc_2,"2-6i");
> -MODULE_PARM(gc_3,"2-6i");
> +MODULE_PARM(gc, "2-" GC_MAX_ARG "i");
> +MODULE_PARM(gc_2, "2-" GC_MAX_ARG "i");
> +MODULE_PARM(gc_3, "2-" GC_MAX_ARG "i");
>  
>  #define GC_SNES		1
>  #define GC_NES		2
> @@ -54,29 +57,31 @@
>  #define GC_MULTI2	5
>  #define GC_N64		6	
>  #define GC_PSX		7
> +#define GC_SATURN	8
>  
> -#define GC_MAX		7
> +#define GC_MAX		8
>  
>  #define GC_REFRESH_TIME	HZ/100
>   
>  struct gc {
>  	struct pardevice *pd;
> -	struct input_dev dev[5];
> +	struct input_dev dev[GC_PADS_MAX];
>  	struct timer_list timer;
> -	unsigned char pads[GC_MAX + 1];
> +	unsigned pads[GC_MAX + 1];
>  	int used;
>  };
>  
>  static struct gc *gc_base[3];
>  
> -static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 };
> -static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 };
> -static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 };
> +static int gc[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
> +static int gc_2[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
> +static int gc_3[GC_PADS_MAX + 1] __initdata = { -1, 0, 0, 0, 0, 0 };
>  
> -static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
> +static unsigned gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
> +				    0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 };
>  
>  static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
> -				"Multisystem 2-button joystick", "N64 controller", "PSX controller" };
> +			    "Multisystem 2-button joystick", "N64 controller", "PSX controller", "SATURN controller" };
>  /*
>   * N64 support.
>   */
> @@ -287,17 +292,239 @@
>  }
>  
>  /*
> + * SATURN support
> + *
> + * References
> + *
> + * Mr. Philhower's DirectPad Pro source code and circuit
> + *  joysrc.txt
> + *  saturn.gif
> + * http://www.ziplabel.com/
> + * http://www.arcadecontrols.com/Mirrors/www.ziplabel.com/dpadpro/dpadpr50.zip
> + *
> + * Mr. Kashima's SATURN pad analysis documentation
> + * http://Lillith.sk.tsukuba.ac.jp/~kashima/games/saturn-e.html
> + *
> + * Mr. Nagato's IF-SEGA documentation (Japanese)
> + * http://www.geocities.co.jp/Playtown-Dice/4434/ifsspad.htm
> + *
> + * Mr. Saka's PA (Windows 9x IF-SEGA driver) source code
> + *  PA.ASM
> + * http://blue.ribbon.to/~als4kmaniac/i2/pa.lzh
> + *
> + * Mr. Murata's linux IF-SEGA driver documentation (Japanese) and header file
> + *  DEVICE.txt
> + *  ifsega.h ("DEV TYPE" section)
> + * http://www1.tcnet.ne.jp/fmurata/linux/ifsega/ifsega-0.17.tar.gz
> + *
> + * Mr. Kimura's PSXPAD circuit
> + *  saturn_wiring.jpg
> + * http://speed.dynu.com/function/Wiring_e.html
> + */
> +
> +#define GC_SATURN_PADS_MAX 12
> +#define GC_SATURN_LENGTH 60	/* multitap (1 (id) + 9 (known max)) byte x 6 connector */
> +#define GC_SATURN_ANALOG_DELAY 400	/* micro-second (200-700) */
> +#define GC_SATURN_DISABLE_EMPTY_CONNECTOR 1	/* when connector is empty, warn and disable device */
> +
> +/* output */
> +#define GC_SATURN_P1_SEL1 0x01	/* db25 pin 2 */
> +#define GC_SATURN_P1_SEL2 0x02	/* db25 pin 3 */
> +#define GC_SATURN_P2_SEL1 0x10	/* db25 pin 6 */
> +#define GC_SATURN_P2_SEL2 0x20	/* db25 pin 7 */
> +#define GC_SATURN_POWER 0x08	/* db25 pin 5 */
> +#define GC_SATURN_POWER_SUB 0x04	/* db25 pin 4 */
> +#define GC_SATURN_P1_GND_HIGH 0x40	/* db25 pin 8 */
> +#define GC_SATURN_P2_GND_HIGH 0x80	/* db25 pin 9 */
> +/* input */
> +#define GC_SATURN_DATA4 0x10	/* db25 pin 13 */
> +#define GC_SATURN_DATA3 0x20	/* db25 pin 12 */
> +#define GC_SATURN_DATA2 0x40	/* db25 pin 10 */
> +#define GC_SATURN_DATA1 0x80	/* db25 pin 11 */
> +
> +static short gc_saturn_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z,
> +				 ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
> +static short gc_saturn_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,
> +				 BTN_TL, BTN_TR, BTN_START };
> +static int gc_saturn_btn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
> +static unsigned char gc_saturn_btn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20,
> +					      0x10, 0x08, 0x80, 0x08 };
> +
> +/*
> + * gc_saturn_read_sub() reads parallel port and returns formatted 4 bit data.
> + */
> +
> +static unsigned char gc_saturn_read_sub(struct gc *gc)
> +{
> +	unsigned char data;
> +
> +	/* read parallel port and invert bit 7 (pin 11) */
> +	data = parport_read_status(gc->pd->port) ^ 0x80;
> +
> +	return (data & GC_SATURN_DATA1 ? 1 : 0) | (data & GC_SATURN_DATA2 ? 2 : 0)
> +	    | (data & GC_SATURN_DATA3 ? 4 : 0) | (data & GC_SATURN_DATA4 ? 8 : 0);
> +}
> +
> +/*
> + * gc_saturn_read_analog() sends clock and returns 8 bit data.
> + */
> +
> +static unsigned char gc_saturn_read_analog(struct gc *gc, unsigned char power, unsigned char sel2)
> +{
> +	unsigned char data;
> +
> +	/* sel2 low  - sel1 low */
> +	parport_write_data(gc->pd->port, power);
> +	udelay(GC_SATURN_ANALOG_DELAY);
> +	data = gc_saturn_read_sub(gc) << 4;
> +
> +	/* sel2 high - sel1 low */
> +	parport_write_data(gc->pd->port, power | sel2);
> +	udelay(GC_SATURN_ANALOG_DELAY);
> +	data |= gc_saturn_read_sub(gc);
> +
> +	return data;
> +}
> +
> +/*
> + * gc_saturn_read_packet() reads a whole saturn packet at connector 
> + * and returns device identifier code.
> + */
> +
> +static unsigned gc_saturn_read_packet(struct gc *gc, unsigned char *data, int connector, int power_by_port)
> +{
> +	int i, j;
> +	unsigned char power, sel1, sel2, tmp;
> +
> +	if (!connector) {
> +		/* connector 1 */
> +		power = GC_SATURN_P2_GND_HIGH + GC_SATURN_P2_SEL1 + GC_SATURN_P2_SEL2 
> +		  + (power_by_port ? GC_SATURN_POWER : 0);
> +		sel1 = GC_SATURN_P1_SEL1;
> +		sel2 = GC_SATURN_P1_SEL2;
> +	} else {
> +		/* connector 2 */
> +		power = GC_SATURN_P1_GND_HIGH + GC_SATURN_P1_SEL1 + GC_SATURN_P1_SEL2 
> +		  + (power_by_port ? GC_SATURN_POWER : 0);
> +		sel1 = GC_SATURN_P2_SEL1;
> +		sel2 = GC_SATURN_P2_SEL2;
> +	}
> +
> +	/* read digital id */
> +	parport_write_data(gc->pd->port, power | sel2 | sel1);
> +	data[0] = gc_saturn_read_sub(gc) & 0x0f;
> +
> +	switch (data[0]) {
> +
> +	case 0xf:
> +		/* 1111  no pad */
> +		return data[0] = 0xff;
> +
> +	case 0x4:
> +	case 0x4 | 0x8:
> +		/* ?100 : digital controller
> +		 * data[ 0] data[ 1] data[ 2]
> +		 * 00000010 rlduSACB RXYZL100
> +		 */
> +		power += (power_by_port ? GC_SATURN_POWER_SUB : 0);
> +		/* pin 4 and pin 9 are connected in digital device. */
> +
> +		parport_write_data(gc->pd->port, power | sel2);
> +		data[1] = gc_saturn_read_sub(gc) << 4;
> +		parport_write_data(gc->pd->port, power | sel1);
> +		data[1] |= gc_saturn_read_sub(gc);
> +		parport_write_data(gc->pd->port, power);
> +		data[2] = gc_saturn_read_sub(gc) << 4;
> +		parport_write_data(gc->pd->port, power | sel2 | sel1);
> +		data[2] |= gc_saturn_read_sub(gc);
> +		return data[0] = 0x02; /* digital controller id in analog style */
> +
> +	case 0x1:
> +		/* 0001 : analog controller */
> +		parport_write_data(gc->pd->port, power | sel2);
> +		udelay(GC_SATURN_ANALOG_DELAY);
> +		/* read analog id */
> +		data[0] = gc_saturn_read_analog(gc, power, sel2);
> +		if (data[0] != 0x41) {
> +			/* read controller
> +			 * data[ 0] data[ 1] data[ 2] data[ 3] ..
> +			 * deviceID rlduSACB RXYZL/// analog_1 ..   
> +			 */
> +			for (i = 0; i < (data[0] & 0x0f); i++)
> +				data[i + 1] = gc_saturn_read_analog(gc, power, sel2);
> +			parport_write_data(gc->pd->port, power | sel1 | sel2);
> +			return data[0];
> +		} else {
> +			/* read multitap 
> +			 * 0x41 : multitap id
> +			 *
> +			 * 0x60 : multitap id-2
> +			 *
> +			 * data[ 0] data[ 1] data[ 2] data[ 3] .. : connector 1
> +			 * deviceID rlduSACB RXYZL/// analog_1 ..
> +			 *
> +			 * data[10] .. : connector 2
> +			 * deviceID ..
> +			 * .
> +			 * .
> +			 * data[n0] data[n1] data[n2]   
> +			 * 00000010 rlduSACB RXYZL000 : digital pad
> +			 *
> +			 * data[m0]
> +			 * 11111111 : no pad
> +			 * .
> +			 * .
> +			 * data[50] data[51] data[52] data[53] .. data[59] : connector 6
> +			 * deviceID rlduSACB RXYZL/// analog_1 .. analog_6   mission stick x2
> +			 */ 
> +			 
> +			/* check multitap id-2 */
> +			if (gc_saturn_read_analog(gc, power, sel2) != 0x60)
> +				return data[0] = 0xff;
> +
> +			for (i = 0; i < 60; i += 10) {
> +				data[i] = gc_saturn_read_analog(gc, power, sel2);
> +				if (data[i] != 0xff) {
> +					/* read pad */
> +					for (j = 0; j < (data[i] & 0x0f); j++)
> +						if (j < 9)
> +							data[i + j + 1] = gc_saturn_read_analog(gc, power, sel2);
> +				}
> +			}
> +			parport_write_data(gc->pd->port, power | sel2 | sel1);
> +			return 0x41; /* multitap id */
> +		}
> +
> +	case 0x0:
> +		/* 0000 : mouse */
> +		parport_write_data(gc->pd->port, power | sel2);
> +		udelay(GC_SATURN_ANALOG_DELAY);
> +		tmp = gc_saturn_read_analog(gc, power, sel2);
> +		if (tmp == 0xff) {
> +			for (i = 0; i < 3; i++)
> +				data[i + 1] = gc_saturn_read_analog(gc, power, sel2);
> +			parport_write_data(gc->pd->port, power | sel2 | sel1);
> +			return data[0] = 0xe3;	/* mouse id */
> +		}
> +
> +	default:
> +		/* unknown */
> +		return data[0];
> +	}
> +}
> +
> +/*
>   * gc_timer() reads and analyzes console pads data.
>   */
>  
> -#define GC_MAX_LENGTH GC_N64_LENGTH
> +#define GC_MAX_LENGTH GC_SATURN_LENGTH
>  
>  static void gc_timer(unsigned long private)
>  {
>  	struct gc *gc = (void *) private;
>  	struct input_dev *dev = gc->dev;
>  	unsigned char data[GC_MAX_LENGTH];
> -	int i, j, s;
> +	int i, j, k, s, n;
>  
>  /*
>   * N64 pads - must be read first, any read confuses them for 200 us
> @@ -307,7 +534,7 @@
>  
>  		gc_n64_read_packet(gc, data);
>  
> -		for (i = 0; i < 5; i++) {
> +		for (i = 0; i < GC_PADS_MAX; i++) {
>  
>  			s = gc_status_bit[i];
>  
> @@ -432,6 +659,83 @@
>  		}
>  	}
>  
> +/*
> + * SATURN controllers
> + */
> +
> +	if (gc->pads[GC_SATURN]) {
> +		for (n = 0, i = 0; i < 2; i++) {
> +			s = gc_saturn_read_packet(gc, data, i, 1) == 0x41 ? 60 : 10;
> +			for (j = 0; j < s; j += 10, n++) {
> +				if (gc_status_bit[n] & gc->pads[GC_SATURN]) {
> +					switch (data[j]) {
> +
> +					case 0x16:
> +						/* multi controller (analog 4 axis) */
> +						input_report_abs(dev + n, ABS_Z, data[j + 6]);
> +					case 0x15:
> +						/* mission stick (analog 3 axis) */
> +						input_report_abs(dev + n, ABS_RY, data[j + 4]);
> +						input_report_abs(dev + n, ABS_RZ, data[j + 5]);
> +					case 0x13:
> +						/* racing controller (analog 1 axis) */
> +						input_report_abs(dev + n, ABS_RX, data[j + 3]);
> +					case 0x02:
> +						/* digital pad (digital 2 axis + buttons) */
> +						input_report_abs(dev + n, ABS_X,
> +								 (data[j + 1] & 128 ? 0 : 1) - (data[j + 1] & 64 ? 0 : 1));
> +						input_report_abs(dev + n, ABS_Y,
> +								 (data[j + 1] & 32 ? 0 : 1) - (data[j + 1] & 16 ? 0 : 1));
> +						for (k = 0; k < 9; k++)
> +							input_report_key(dev + n, gc_saturn_btn[k],
> +									 ~data[j + gc_saturn_btn_byte[k]] & gc_saturn_btn_mask[k]);
> +						break;
> +
> +					case 0x19:
> +						/* mission stick x2 (analog 6 axis + digital 4 axis + buttons) */
> +						input_report_abs(dev + n, ABS_X,
> +								 (data[j + 1] & 128 ? 0 : 1) - (data[j + 1] & 64 ? 0 : 1));
> +						input_report_abs(dev + n, ABS_Y,
> +								 (data[j + 1] & 32 ? 0 : 1) - (data[j + 1] & 16 ? 0 : 1));
> +						for (k = 0; k < 9; k++)
> +							input_report_key(dev + n, gc_saturn_btn[k],
> +									 ~data[j + gc_saturn_btn_byte[k]] & gc_saturn_btn_mask[k]);
> +						input_report_abs(dev + n, ABS_RX, data[j + 3]);
> +						input_report_abs(dev + n, ABS_RY, data[j + 4]);
> +						input_report_abs(dev + n, ABS_RZ, data[j + 5]);
> +						input_report_abs(dev + n, ABS_HAT1X,
> +								 (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
> +						input_report_abs(dev + n, ABS_HAT1Y,
> +								 (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
> +						input_report_abs(dev + n, ABS_HAT0X, data[j + 7]);
> +						input_report_abs(dev + n, ABS_HAT0Y, data[j + 8]);
> +						input_report_abs(dev + n, ABS_Z, data[j + 9]);
> +						break;
> +
> +					case 0xe3:
> +						/* shuttle mouse (analog 2 axis + buttons. signed value) */
> +						input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
> +						input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
> +						input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
> +						input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
> +						input_report_abs(dev + n, ABS_RX, data[j + 2] ^ 0x80);
> +						input_report_abs(dev + n, ABS_RY, data[j + 3] ^ 0x80);
> +						break;
> +
> +					case 0xff:
> +					default:
> +						/* no pad */
> +						input_report_abs(dev + n, ABS_X, 0);
> +						input_report_abs(dev + n, ABS_Y, 0);
> +						for (k = 0; k < 9; k++)
> +							input_report_key(dev + n, gc_saturn_btn[k], 0);
> +						break;
> +					}
> +				}
> +			}
> +		}
> +	}
> +
>  	mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
>  }
>  
> @@ -460,8 +764,8 @@
>  {
>  	struct gc *gc;
>  	struct parport *pp;
> -	int i, j, psx;
> -	unsigned char data[32];
> +	int i, j, k, n, psx;
> +	unsigned char data[GC_MAX_LENGTH];
>  
>  	if (config[0] < 0)
>  		return NULL;
> @@ -492,7 +796,7 @@
>  	gc->timer.data = (long) gc;
>  	gc->timer.function = gc_timer;
>  
> -	for (i = 0; i < 5; i++) {
> +	for (i = 0; i < GC_PADS_MAX; i++) {
>  
>  		if (!config[i + 1])
>  			continue;
> @@ -588,6 +892,53 @@
>  							" please report to <vojtech@suse.cz>.\n", psx);
>  				}
>  				break;
> +
> +		case GC_SATURN:
> +			if (i >= GC_SATURN_PADS_MAX) {
> +				gc->pads[GC_SATURN] &= ~gc_status_bit[i];
> +				printk(KERN_ERR "gamecon.c: Max %d SATURN controllers supported.\n",GC_SATURN_PADS_MAX);
> +				break;
> +			}
> +			for (n = 0, j = 0; j < 2; j++) {
> +				gc_saturn_read_packet(gc, data, j, 1);	/* dummy read */
> +				udelay(20000); /* enough long time */
> +				psx = (gc_saturn_read_packet(gc, data, j, 1) == 0x41) ? 60 : 10;
> +				for (k = 0; k < psx ; k += 10, n++) {
> +					if (n == i) {
> +						if (data[k] == 0xff && GC_SATURN_DISABLE_EMPTY_CONNECTOR) {
> +							gc->pads[GC_SATURN] &= ~gc_status_bit[i];
> +							printk(KERN_ERR "gamecon.c: No SATURN controller found.\n");
> +						}
> +						if (data[k] == 0x11) {
> +							/* with external 5 volt powered single connector,
> +							   analog device returns 0x11 when read connector 2. */
> +							gc->pads[GC_SATURN] &= ~gc_status_bit[i];
> +							printk(KERN_WARNING "gamecon.c: Single SATURN connector?\n");
> +						}
> +					}
> +				}
> +			}
> +			if (i > n - 1) {
> +				gc->pads[GC_SATURN] &= ~gc_status_bit[i];
> +				printk(KERN_ERR "gamecon.c: No more connector.(%d exists)\n",n);
> +				break;
> +			}
> +			if (gc->pads[GC_SATURN] & gc_status_bit[i]) {
> +				for (j = 0; j < 9; j++)
> +					set_bit(gc_saturn_btn[j], gc->dev[i].keybit);
> +				for (j = 0; j < 10; j++) {
> +					set_bit(gc_saturn_abs[j], gc->dev[i].absbit);
> +					if (j < 2 || j > 7) {
> +						gc->dev[i].absmin[gc_saturn_abs[j]] = -1;
> +						gc->dev[i].absmax[gc_saturn_abs[j]] = 1;
> +					} else {
> +						gc->dev[i].absmin[gc_saturn_abs[j]] = 1;
> +						gc->dev[i].absmax[gc_saturn_abs[j]] = 255;
> +						gc->dev[i].absflat[gc_saturn_abs[j]] = 0;
> +					}
> +				}
> +			}
> +			break;
>  		}
>  
>                  gc->dev[i].name = gc_names[config[i + 1]];
> @@ -605,7 +956,7 @@
>  		return NULL;
>  	}
>  
> -	for (i = 0; i < 5; i++) 
> +	for (i = 0; i < GC_PADS_MAX; i++)
>  		if (gc->pads[0] & gc_status_bit[i]) {
>  			input_register_device(gc->dev + i);
>  			printk(KERN_INFO "input%d: %s on %s\n", gc->dev[i].number, gc->dev[i].name, gc->pd->port->name);
> @@ -617,25 +968,26 @@
>  #ifndef MODULE
>  int __init gc_setup(char *str)
>  {
> -	int i, ints[7];
> +	int i, ints[GC_PADS_MAX + 2];
>  	get_options(str, ARRAY_SIZE(ints), ints);
> -	for (i = 0; i <= ints[0] && i < 6; i++) gc[i] = ints[i + 1];
> +	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc[i] = ints[i + 1];
>  	return 1;
>  }
>  int __init gc_setup_2(char *str)
>  {
> -	int i, ints[7];
> +	int i, ints[GC_PADS_MAX + 2];
>  	get_options(str, ARRAY_SIZE(ints), ints);
> -	for (i = 0; i <= ints[0] && i < 6; i++) gc_2[i] = ints[i + 1];
> +	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc_2[i] = ints[i + 1];
>  	return 1;
>  }
>  int __init gc_setup_3(char *str)
>  {
> -	int i, ints[7];
> +	int i, ints[GC_PADS_MAX + 2];
>  	get_options(str, ARRAY_SIZE(ints), ints);
> -	for (i = 0; i <= ints[0] && i < 6; i++) gc_3[i] = ints[i + 1];
> +	for (i = 0; i <= ints[0] && i < GC_PADS_MAX + 1; i++) gc_3[i] = ints[i + 1];
>  	return 1;
>  }
> +
>  __setup("gc=", gc_setup);
>  __setup("gc_2=", gc_setup_2);
>  __setup("gc_3=", gc_setup_3);
> @@ -659,7 +1011,7 @@
>  
>  	for (i = 0; i < 3; i++)
>  		if (gc_base[i]) {
> -			for (j = 0; j < 5; j++)
> +			for (j = 0; j < GC_PADS_MAX; j++)
>  				if (gc_base[i]->pads[0] & gc_status_bit[j])
>  					input_unregister_device(gc_base[i]->dev + j); 
>  			parport_unregister_device(gc_base[i]->pd);
> 
> 
> 
> ---
> YOKOTA, Ken-ichi

-- 
Vojtech Pavlik
SuSE Labs

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20
  2003-01-26 10:39 ` Vojtech Pavlik
@ 2003-01-27 10:34   ` yokotak
  2003-01-27 10:36     ` Vojtech Pavlik
  0 siblings, 1 reply; 8+ messages in thread
From: yokotak @ 2003-01-27 10:34 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: linux-kernel

Thank you very much for your reply.

Vojtech Pavlik <vojtech@suse.cz> wrote:
> Why are you adding it into gamecon.c and not fixing it in db9.c instead?

Db9.c supports ONE gamepad per port. Gamecon.c supports FIVE gamepads
per port.
Gamecon.c seems appropriate to support more than one sega saturn pad
on DPP compatible interface and multitap.

yokota


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20
  2003-01-27 10:34   ` yokotak
@ 2003-01-27 10:36     ` Vojtech Pavlik
  2003-01-28 12:34       ` yokotak
  2003-06-10 13:02       ` [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20) yokotak
  0 siblings, 2 replies; 8+ messages in thread
From: Vojtech Pavlik @ 2003-01-27 10:36 UTC (permalink / raw)
  To: yokotak; +Cc: linux-kernel

On Mon, Jan 27, 2003 at 07:34:10PM +0900, yokotak@rmail.plala.or.jp wrote:

> Thank you very much for your reply.
> 
> Vojtech Pavlik <vojtech@suse.cz> wrote:
> > Why are you adding it into gamecon.c and not fixing it in db9.c instead?
> 
> Db9.c supports ONE gamepad per port. Gamecon.c supports FIVE gamepads
> per port.

Two in some cases. And that's the number of Saturn pads supported by
your patch as well, right?

> Gamecon.c seems appropriate to support more than one sega saturn pad
> on DPP compatible interface and multitap.

Gamecon is based on that it uses one input line per device. The Saturn
needs more than one (four?).

-- 
Vojtech Pavlik
SuSE Labs

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20
  2003-01-27 10:36     ` Vojtech Pavlik
@ 2003-01-28 12:34       ` yokotak
  2003-06-10 13:02       ` [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20) yokotak
  1 sibling, 0 replies; 8+ messages in thread
From: yokotak @ 2003-01-28 12:34 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: linux-kernel

Thank you very much for your reply.

Vojtech Pavlik <vojtech@suse.cz> wrote:
> Two in some cases. And that's the number of Saturn pads supported by
> your patch as well, right?
I am sorry. Suggested by you, I find DB9_MULTI_0802_2 on db9.c now. I 
will try to hack db9.c. DPP compatible JOY-DB9.C patch previously exists.
 (http://www.self-core.org/~kaoru-k/pub/joy_db9_dpp_saturn_2.2.17.patch)
Below patch is preliminaly support for ninth button on db9.c compatible 
interface. I have not tested if it works or not. (I have no interface 
nor knowledge of 0x37a command register.)

> Gamecon is based on that it uses one input line per device. The Saturn
> needs more than one (four?).
But using gamecon.c might be the easiest way to support more (12 pads 
with two multitap on each connector, in maximum) pads, although satun 
pad can not live with other pads in my gamecon.c patch.

[Multitap and DirectPad Pro Interface]
      +-----------+
      |           |
      | Linux BOX |
      |           |(1 LPT port)
      +-----------+
               |
               |
               |
            +-----+
            | DPP |(2 connectors)
            +-----+
            /     \
           /       \
          /         \
    +-----+         +-----+
pad1|multi|pad6 pad7|multi|pad12 (12 gamepads)
pad2| tap |pad5 pad8| tap |pad11
    +-----+         +-----+
   pad3 pad4       pad9 pad10
(I have only tested with first 6 pad with a multitap.)


--- db9.c.orig	Thu Sep 13 07:34:06 2001
+++ db9.c	Tue Jan 28 20:18:22 2003
@@ -70,9 +70,9 @@
 #define DB9_NOSELECT		0x08
 
 #define DB9_SATURN0		0x00
-#define DB9_SATURN1		0x02
-#define DB9_SATURN2		0x04
-#define DB9_SATURN3		0x06
+#define DB9_SATURN1		(DB9_SATURN0 ^ 0x02)
+#define DB9_SATURN2		(DB9_SATURN0 ^ 0x04)
+#define DB9_SATURN3		(DB9_SATURN1 ^ 0x04)
 
 #define DB9_GENESIS6_DELAY	14
 #define DB9_REFRESH_TIME	HZ/100
@@ -95,7 +95,7 @@
 static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
 static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
 
-static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 };
+static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7 };
 static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
 					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn };
 static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
@@ -222,27 +222,32 @@
 
 		case DB9_SATURN_PAD:
 
-			parport_write_control(port, DB9_SATURN0);
+			parport_write_control(port, DB9_SATURN0);	/*  low -  low */
 			data = parport_read_data(port);
 
-			input_report_key(dev, BTN_Y,  ~data & DB9_LEFT);
-			input_report_key(dev, BTN_Z,  ~data & DB9_DOWN);
-			input_report_key(dev, BTN_TL, ~data & DB9_UP);
+			input_report_key(dev, BTN_X,  ~data & DB9_LEFT);
+			input_report_key(dev, BTN_Y,  ~data & DB9_DOWN);
+			input_report_key(dev, BTN_Z,  ~data & DB9_UP);
 			input_report_key(dev, BTN_TR, ~data & DB9_RIGHT);
 
-			parport_write_control(port, DB9_SATURN2);
+			parport_write_control(port, DB9_SATURN2);	/* high -  low */
 			data = parport_read_data(port);
 
 			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
 			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
 			
-			parport_write_control(port, DB9_NORMAL);
+			parport_write_control(port, DB9_SATURN1);	/*  low - high */
 			data = parport_read_data(port);
 
 			input_report_key(dev, BTN_A, ~data & DB9_LEFT);
 			input_report_key(dev, BTN_B, ~data & DB9_UP);
 			input_report_key(dev, BTN_C, ~data & DB9_DOWN);
-			input_report_key(dev, BTN_X, ~data & DB9_RIGHT);
+			input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
+
+			parport_write_control(port, DB9_SATURN3);	/* high - high */
+			data = parport_read_data(port);
+
+			input_report_key(dev, BTN_TL, ~data & DB9_RIGHT);
 			break;
 
 		case DB9_CD32_PAD:


yokota


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20)
  2003-01-27 10:36     ` Vojtech Pavlik
  2003-01-28 12:34       ` yokotak
@ 2003-06-10 13:02       ` yokotak
  2003-06-21 15:59         ` Vojtech Pavlik
  1 sibling, 1 reply; 8+ messages in thread
From: yokotak @ 2003-06-10 13:02 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: linux-kernel

Hello,

This is db9.c patch to support Saturn DPP.
I merged original interface section into DPP section to support
analog controllers. I gave priority to documentation of
joystick-parport.txt.

Set 11 or 12 in 'type' to use DPP (11 for a connector, 12 for two
connectors).

Tested with:
* DPP interface (one connector) and joystick-parport.txt interface.
* Digital controller, saturn keyboard (as joypad), racing controller,
  mission stick, multi controller, shuttle mouse, multi terminal 6,
  and sankyo ff.

NOT tested with:
* DPP interface (two connectors).
* Virtua Gun, mission stick x2, and other controllers.

Thank you for your reading,
  yokota


--- db9.c.orig	2001-09-13 07:34:06.000000000 +0900
+++ db9.c	2003-04-10 23:03:49.000000000 +0900
@@ -55,7 +55,9 @@
 #define DB9_MULTI_0802		0x08
 #define DB9_MULTI_0802_2	0x09
 #define DB9_CD32_PAD		0x0A
-#define DB9_MAX_PAD		0x0B
+#define DB9_SATURN_DPP		0x0B
+#define DB9_SATURN_DPP_2	0x0C
+#define DB9_MAX_PAD		0x0D
 
 #define DB9_UP			0x01
 #define DB9_DOWN		0x02
@@ -69,10 +71,7 @@
 #define DB9_NORMAL		0x0a
 #define DB9_NOSELECT		0x08
 
-#define DB9_SATURN0		0x00
-#define DB9_SATURN1		0x02
-#define DB9_SATURN2		0x04
-#define DB9_SATURN3		0x06
+#define DB9_MAX_DEVICES 2
 
 #define DB9_GENESIS6_DELAY	14
 #define DB9_REFRESH_TIME	HZ/100
@@ -82,7 +81,7 @@
 static int db9_3[] __initdata = { -1, 0 };
 
 struct db9 {
-	struct input_dev dev[2];
+	struct input_dev dev[DB9_MAX_DEVICES];
 	struct timer_list timer;
 	struct pardevice *pd;	
 	int mode;
@@ -95,12 +94,247 @@
 static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
 static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
 
-static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 };
+static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 };
 static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
-					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn };
+					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn,
+					db9_cd32_btn, db9_cd32_btn };
 static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
 				      NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick",
-				     "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad" };
+				     "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" };
+
+static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 };
+static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 };
+static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 };
+static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 };
+
+/*
+ * Saturn controllers
+ */
+#define DB9_SATURN_DELAY 300
+static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
+static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
+
+/*
+ * db9_saturn_write_sub() writes 2 bit data.
+ */
+static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
+{
+	unsigned char c;
+
+	switch (type) {
+	case 1: /* DPP1 */
+		c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
+		parport_write_data(port, c);
+		break;
+	case 2: /* DPP2 */
+		c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
+		parport_write_data(port, c);
+		break;
+	case 0:	/* DB9 */
+		c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
+		parport_write_control(port, c);
+		break;
+	}
+}
+
+/*
+ * gc_saturn_read_sub() reads 4 bit data.
+ */
+static unsigned char db9_saturn_read_sub(struct parport *port, int type)
+{
+	unsigned char data;
+
+	if (type) {
+		/* DPP */
+		data = parport_read_status(port) ^ 0x80;
+		return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
+		     | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
+	} else {
+		/* DB9 */
+		data = parport_read_data(port) & 0x0f;
+		return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
+		     | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
+	}
+}
+
+/*
+ * db9_saturn_read_analog() sends clock and reads 8 bit data.
+ */
+static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
+{
+	unsigned char data;
+
+	db9_saturn_write_sub(port, type, 0, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data = db9_saturn_read_sub(port, type) << 4;
+	db9_saturn_write_sub(port, type, 2, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data |= db9_saturn_read_sub(port, type);
+	return data;
+}
+
+/*
+ * db9_saturn_read_packet() reads whole saturn packet at connector 
+ * and returns device identifier code.
+ */
+static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
+{
+	int i, j;
+	unsigned char tmp;
+
+	db9_saturn_write_sub(port, type, 3, powered, 0);
+	data[0] = db9_saturn_read_sub(port, type);
+	switch (data[0] & 0x0f) {
+	case 0xf:
+		/* 1111  no pad */
+		return data[0] = 0xff;
+	case 0x4: case 0x4 | 0x8:
+		/* ?100 : digital controller */
+		db9_saturn_write_sub(port, type, 0, powered, 1);
+		data[2] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 2, powered, 1);
+		data[1] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 1, powered, 1);
+		data[1] |= db9_saturn_read_sub(port, type);
+		db9_saturn_write_sub(port, type, 3, powered, 1);
+		/* data[2] |= db9_saturn_read_sub(port, type); */
+		data[2] |= data[0];
+		return data[0] = 0x02;
+	case 0x1:
+		/* 0001 : analog controller or multitap */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		data[0] = db9_saturn_read_analog(port, type, powered);
+		if (data[0] != 0x41) {
+			/* read analog controller */
+			for (i = 0; i < (data[0] & 0x0f); i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0];
+		} else {
+			/* read multitap */
+			if (db9_saturn_read_analog(port, type, powered) != 0x60)
+				return data[0] = 0xff;
+			for (i = 0; i < 60; i += 10) {
+				data[i] = db9_saturn_read_analog(port, type, powered);
+				if (data[i] != 0xff)
+					/* read each pad */
+					for (j = 0; j < (data[i] & 0x0f); j++)
+						data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
+			}
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return 0x41;
+		}
+	case 0x0:
+		/* 0000 : mouse */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		tmp = db9_saturn_read_analog(port, type, powered);
+		if (tmp == 0xff) {
+			for (i = 0; i < 3; i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0] = 0xe3;
+		}
+	default:
+		return data[0];
+	}
+}
+
+/*
+ * db9_saturn_report() analyzes packet and reports.
+ */
+static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
+{
+	int tmp, i, j;
+
+	tmp = (id == 0x41) ? 60 : 10;
+	for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
+		switch (data[j]) {
+		case 0x16: /* multi controller (analog 4 axis) */
+			input_report_abs(dev + n, db9_abs[5], data[j + 6]);
+		case 0x15: /* mission stick (analog 3 axis) */
+			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+		case 0x13: /* racing controller (analog 1 axis) */
+			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+		case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
+		case 0x02: /* digital pad (digital 2 axis + buttons) */
+			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			break;
+		case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
+			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+			/*
+			input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+			input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+			*/
+			input_report_abs(dev + n, db9_abs[6], data[j + 7]);
+			input_report_abs(dev + n, db9_abs[7], data[j + 8]);
+			input_report_abs(dev + n, db9_abs[5], data[j + 9]);
+			break;
+		case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
+			input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
+			input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
+			break;
+		case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
+			input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
+			input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
+			input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
+			input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
+			input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
+			input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+			break;
+		case 0xff:
+		default: /* no pad */
+			input_report_abs(dev + n, db9_abs[0], 0);
+			input_report_abs(dev + n, db9_abs[1], 0);
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], 0);
+			break;
+		}
+	}
+	return n;
+}
+
+static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
+{
+	unsigned char id, data[60];
+	int type, n, max_pads;
+	int tmp, i;
+
+	switch (mode) {
+	case DB9_SATURN_PAD:
+		type = 0;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP:
+		type = 1;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP_2:
+		type = 1;
+		n = 2;
+		break;
+	default:
+		return -1;
+	}
+	max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES);
+	for (tmp = 0, i = 0; i < n; i++) {
+		id = db9_saturn_read_packet(port, data, type + i, 1);
+		tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
+	}
+	return 0;
+}
 
 static void db9_timer(unsigned long private)
 {
@@ -221,28 +455,10 @@
 			break;
 
 		case DB9_SATURN_PAD:
+		case DB9_SATURN_DPP:
+		case DB9_SATURN_DPP_2:
 
-			parport_write_control(port, DB9_SATURN0);
-			data = parport_read_data(port);
-
-			input_report_key(dev, BTN_Y,  ~data & DB9_LEFT);
-			input_report_key(dev, BTN_Z,  ~data & DB9_DOWN);
-			input_report_key(dev, BTN_TL, ~data & DB9_UP);
-			input_report_key(dev, BTN_TR, ~data & DB9_RIGHT);
-
-			parport_write_control(port, DB9_SATURN2);
-			data = parport_read_data(port);
-
-			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
-			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
-			
-			parport_write_control(port, DB9_NORMAL);
-			data = parport_read_data(port);
-
-			input_report_key(dev, BTN_A, ~data & DB9_LEFT);
-			input_report_key(dev, BTN_B, ~data & DB9_UP);
-			input_report_key(dev, BTN_C, ~data & DB9_DOWN);
-			input_report_key(dev, BTN_X, ~data & DB9_RIGHT);
+			db9_saturn(db9->mode, port, dev);
 			break;
 
 		case DB9_CD32_PAD:
@@ -276,8 +492,10 @@
 	if (!db9->used++) {
 		parport_claim(db9->pd);
 		parport_write_data(port, 0xff);
-		parport_data_reverse(port);
-		parport_write_control(port, DB9_NORMAL);
+		if (db9_reverse[db9->mode]) {
+			parport_data_reverse(port);
+			parport_write_control(port, DB9_NORMAL);
+		}
 		mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
 	}
 
@@ -318,11 +536,13 @@
 		return NULL;
 	}
 
-	if (!(pp->modes & PARPORT_MODE_TRISTATE) && config[1] != DB9_MULTI_0802) {
-		printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
-		return NULL;
+	if (db9_bidirectional[config[1]]) {
+		if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
+			printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
+			return NULL;
+		}
 	}
-	
+
 	if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL)))
 		return NULL;
 	memset(db9, 0, sizeof(struct db9));
@@ -340,7 +560,7 @@
 		return NULL;
 	}
 
-	for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) {
+	for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) {
 
 		db9->dev[i].private = db9;
 		db9->dev[i].open = db9_open;
@@ -353,14 +573,19 @@
 		db9->dev[i].idversion = 0x0100;
 
 		db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
-		db9->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
-
 		for (j = 0; j < db9_buttons[db9->mode]; j++)
 			set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit); 
-
-		db9->dev[i].absmin[ABS_X] = -1; db9->dev[i].absmax[ABS_X] = 1;
-		db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1;
-
+		for (j = 0; j < db9_num_axis[db9->mode]; j++) {
+			set_bit(db9_abs[j], db9->dev[i].absbit);
+			if (j < 2) {
+				db9->dev[i].absmin[db9_abs[j]] = -1;
+				db9->dev[i].absmax[db9_abs[j]] = 1;
+			} else {
+				db9->dev[i].absmin[db9_abs[j]] = 1;
+				db9->dev[i].absmax[db9_abs[j]] = 255;
+				db9->dev[i].absflat[db9_abs[j]] = 0;
+			}
+		}
 		input_register_device(db9->dev + i);
 		printk(KERN_INFO "input%d: %s on %s\n",
 			db9->dev[i].number, db9_name[db9->mode], db9->pd->port->name);
@@ -414,7 +639,7 @@
 
 	for (i = 0; i < 3; i++) 
 		if (db9_base[i]) {
-			for (j = 0; j < 1 + (db9_base[i]->mode == DB9_MULTI_0802_2); j++)
+			for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++)
 				input_unregister_device(db9_base[i]->dev + j);
 		parport_unregister_device(db9_base[i]->pd);
 	}





^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20)
  2003-06-10 13:02       ` [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20) yokotak
@ 2003-06-21 15:59         ` Vojtech Pavlik
  2003-06-23 13:17           ` yokotak
  0 siblings, 1 reply; 8+ messages in thread
From: Vojtech Pavlik @ 2003-06-21 15:59 UTC (permalink / raw)
  To: yokotak; +Cc: linux-kernel

On Tue, Jun 10, 2003 at 10:02:04PM +0900, yokotak@rmail.plala.or.jp wrote:
> Hello,
> 
> This is db9.c patch to support Saturn DPP.
> I merged original interface section into DPP section to support
> analog controllers. I gave priority to documentation of
> joystick-parport.txt.
> 
> Set 11 or 12 in 'type' to use DPP (11 for a connector, 12 for two
> connectors).
> 
> Tested with:
> * DPP interface (one connector) and joystick-parport.txt interface.
> * Digital controller, saturn keyboard (as joypad), racing controller,
>   mission stick, multi controller, shuttle mouse, multi terminal 6,
>   and sankyo ff.
> 
> NOT tested with:
> * DPP interface (two connectors).
> * Virtua Gun, mission stick x2, and other controllers.
> 
> Thank you for your reading,
>   yokota

Thanks for the patch! I applied it to my kernel tree. How about a
patch to update for the joystick-parport.txt documentation file?

> --- db9.c.orig	2001-09-13 07:34:06.000000000 +0900
> +++ db9.c	2003-04-10 23:03:49.000000000 +0900
> @@ -55,7 +55,9 @@
>  #define DB9_MULTI_0802		0x08
>  #define DB9_MULTI_0802_2	0x09
>  #define DB9_CD32_PAD		0x0A
> -#define DB9_MAX_PAD		0x0B
> +#define DB9_SATURN_DPP		0x0B
> +#define DB9_SATURN_DPP_2	0x0C
> +#define DB9_MAX_PAD		0x0D
>  
>  #define DB9_UP			0x01
>  #define DB9_DOWN		0x02
> @@ -69,10 +71,7 @@
>  #define DB9_NORMAL		0x0a
>  #define DB9_NOSELECT		0x08
>  
> -#define DB9_SATURN0		0x00
> -#define DB9_SATURN1		0x02
> -#define DB9_SATURN2		0x04
> -#define DB9_SATURN3		0x06
> +#define DB9_MAX_DEVICES 2
>  
>  #define DB9_GENESIS6_DELAY	14
>  #define DB9_REFRESH_TIME	HZ/100
> @@ -82,7 +81,7 @@
>  static int db9_3[] __initdata = { -1, 0 };
>  
>  struct db9 {
> -	struct input_dev dev[2];
> +	struct input_dev dev[DB9_MAX_DEVICES];
>  	struct timer_list timer;
>  	struct pardevice *pd;	
>  	int mode;
> @@ -95,12 +94,247 @@
>  static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
>  static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
>  
> -static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 };
> +static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 };
>  static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
> -					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn };
> +					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn,
> +					db9_cd32_btn, db9_cd32_btn };
>  static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
>  				      NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick",
> -				     "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad" };
> +				     "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" };
> +
> +static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 };
> +static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 };
> +static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
> +static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 };
> +static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 };
> +
> +/*
> + * Saturn controllers
> + */
> +#define DB9_SATURN_DELAY 300
> +static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
> +static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
> +
> +/*
> + * db9_saturn_write_sub() writes 2 bit data.
> + */
> +static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
> +{
> +	unsigned char c;
> +
> +	switch (type) {
> +	case 1: /* DPP1 */
> +		c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
> +		parport_write_data(port, c);
> +		break;
> +	case 2: /* DPP2 */
> +		c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
> +		parport_write_data(port, c);
> +		break;
> +	case 0:	/* DB9 */
> +		c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
> +		parport_write_control(port, c);
> +		break;
> +	}
> +}
> +
> +/*
> + * gc_saturn_read_sub() reads 4 bit data.
> + */
> +static unsigned char db9_saturn_read_sub(struct parport *port, int type)
> +{
> +	unsigned char data;
> +
> +	if (type) {
> +		/* DPP */
> +		data = parport_read_status(port) ^ 0x80;
> +		return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
> +		     | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
> +	} else {
> +		/* DB9 */
> +		data = parport_read_data(port) & 0x0f;
> +		return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
> +		     | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
> +	}
> +}
> +
> +/*
> + * db9_saturn_read_analog() sends clock and reads 8 bit data.
> + */
> +static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
> +{
> +	unsigned char data;
> +
> +	db9_saturn_write_sub(port, type, 0, powered, 0);
> +	udelay(DB9_SATURN_DELAY);
> +	data = db9_saturn_read_sub(port, type) << 4;
> +	db9_saturn_write_sub(port, type, 2, powered, 0);
> +	udelay(DB9_SATURN_DELAY);
> +	data |= db9_saturn_read_sub(port, type);
> +	return data;
> +}
> +
> +/*
> + * db9_saturn_read_packet() reads whole saturn packet at connector 
> + * and returns device identifier code.
> + */
> +static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
> +{
> +	int i, j;
> +	unsigned char tmp;
> +
> +	db9_saturn_write_sub(port, type, 3, powered, 0);
> +	data[0] = db9_saturn_read_sub(port, type);
> +	switch (data[0] & 0x0f) {
> +	case 0xf:
> +		/* 1111  no pad */
> +		return data[0] = 0xff;
> +	case 0x4: case 0x4 | 0x8:
> +		/* ?100 : digital controller */
> +		db9_saturn_write_sub(port, type, 0, powered, 1);
> +		data[2] = db9_saturn_read_sub(port, type) << 4;
> +		db9_saturn_write_sub(port, type, 2, powered, 1);
> +		data[1] = db9_saturn_read_sub(port, type) << 4;
> +		db9_saturn_write_sub(port, type, 1, powered, 1);
> +		data[1] |= db9_saturn_read_sub(port, type);
> +		db9_saturn_write_sub(port, type, 3, powered, 1);
> +		/* data[2] |= db9_saturn_read_sub(port, type); */
> +		data[2] |= data[0];
> +		return data[0] = 0x02;
> +	case 0x1:
> +		/* 0001 : analog controller or multitap */
> +		db9_saturn_write_sub(port, type, 2, powered, 0);
> +		udelay(DB9_SATURN_DELAY);
> +		data[0] = db9_saturn_read_analog(port, type, powered);
> +		if (data[0] != 0x41) {
> +			/* read analog controller */
> +			for (i = 0; i < (data[0] & 0x0f); i++)
> +				data[i + 1] = db9_saturn_read_analog(port, type, powered);
> +			db9_saturn_write_sub(port, type, 3, powered, 0);
> +			return data[0];
> +		} else {
> +			/* read multitap */
> +			if (db9_saturn_read_analog(port, type, powered) != 0x60)
> +				return data[0] = 0xff;
> +			for (i = 0; i < 60; i += 10) {
> +				data[i] = db9_saturn_read_analog(port, type, powered);
> +				if (data[i] != 0xff)
> +					/* read each pad */
> +					for (j = 0; j < (data[i] & 0x0f); j++)
> +						data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
> +			}
> +			db9_saturn_write_sub(port, type, 3, powered, 0);
> +			return 0x41;
> +		}
> +	case 0x0:
> +		/* 0000 : mouse */
> +		db9_saturn_write_sub(port, type, 2, powered, 0);
> +		udelay(DB9_SATURN_DELAY);
> +		tmp = db9_saturn_read_analog(port, type, powered);
> +		if (tmp == 0xff) {
> +			for (i = 0; i < 3; i++)
> +				data[i + 1] = db9_saturn_read_analog(port, type, powered);
> +			db9_saturn_write_sub(port, type, 3, powered, 0);
> +			return data[0] = 0xe3;
> +		}
> +	default:
> +		return data[0];
> +	}
> +}
> +
> +/*
> + * db9_saturn_report() analyzes packet and reports.
> + */
> +static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
> +{
> +	int tmp, i, j;
> +
> +	tmp = (id == 0x41) ? 60 : 10;
> +	for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
> +		switch (data[j]) {
> +		case 0x16: /* multi controller (analog 4 axis) */
> +			input_report_abs(dev + n, db9_abs[5], data[j + 6]);
> +		case 0x15: /* mission stick (analog 3 axis) */
> +			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
> +			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
> +		case 0x13: /* racing controller (analog 1 axis) */
> +			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
> +		case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
> +		case 0x02: /* digital pad (digital 2 axis + buttons) */
> +			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
> +			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
> +			for (i = 0; i < 9; i++)
> +				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
> +			break;
> +		case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
> +			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
> +			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
> +			for (i = 0; i < 9; i++)
> +				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
> +			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
> +			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
> +			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
> +			/*
> +			input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
> +			input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
> +			*/
> +			input_report_abs(dev + n, db9_abs[6], data[j + 7]);
> +			input_report_abs(dev + n, db9_abs[7], data[j + 8]);
> +			input_report_abs(dev + n, db9_abs[5], data[j + 9]);
> +			break;
> +		case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
> +			input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
> +			input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
> +			break;
> +		case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
> +			input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
> +			input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
> +			input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
> +			input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
> +			input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
> +			input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
> +			break;
> +		case 0xff:
> +		default: /* no pad */
> +			input_report_abs(dev + n, db9_abs[0], 0);
> +			input_report_abs(dev + n, db9_abs[1], 0);
> +			for (i = 0; i < 9; i++)
> +				input_report_key(dev + n, db9_cd32_btn[i], 0);
> +			break;
> +		}
> +	}
> +	return n;
> +}
> +
> +static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
> +{
> +	unsigned char id, data[60];
> +	int type, n, max_pads;
> +	int tmp, i;
> +
> +	switch (mode) {
> +	case DB9_SATURN_PAD:
> +		type = 0;
> +		n = 1;
> +		break;
> +	case DB9_SATURN_DPP:
> +		type = 1;
> +		n = 1;
> +		break;
> +	case DB9_SATURN_DPP_2:
> +		type = 1;
> +		n = 2;
> +		break;
> +	default:
> +		return -1;
> +	}
> +	max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES);
> +	for (tmp = 0, i = 0; i < n; i++) {
> +		id = db9_saturn_read_packet(port, data, type + i, 1);
> +		tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
> +	}
> +	return 0;
> +}
>  
>  static void db9_timer(unsigned long private)
>  {
> @@ -221,28 +455,10 @@
>  			break;
>  
>  		case DB9_SATURN_PAD:
> +		case DB9_SATURN_DPP:
> +		case DB9_SATURN_DPP_2:
>  
> -			parport_write_control(port, DB9_SATURN0);
> -			data = parport_read_data(port);
> -
> -			input_report_key(dev, BTN_Y,  ~data & DB9_LEFT);
> -			input_report_key(dev, BTN_Z,  ~data & DB9_DOWN);
> -			input_report_key(dev, BTN_TL, ~data & DB9_UP);
> -			input_report_key(dev, BTN_TR, ~data & DB9_RIGHT);
> -
> -			parport_write_control(port, DB9_SATURN2);
> -			data = parport_read_data(port);
> -
> -			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
> -			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
> -			
> -			parport_write_control(port, DB9_NORMAL);
> -			data = parport_read_data(port);
> -
> -			input_report_key(dev, BTN_A, ~data & DB9_LEFT);
> -			input_report_key(dev, BTN_B, ~data & DB9_UP);
> -			input_report_key(dev, BTN_C, ~data & DB9_DOWN);
> -			input_report_key(dev, BTN_X, ~data & DB9_RIGHT);
> +			db9_saturn(db9->mode, port, dev);
>  			break;
>  
>  		case DB9_CD32_PAD:
> @@ -276,8 +492,10 @@
>  	if (!db9->used++) {
>  		parport_claim(db9->pd);
>  		parport_write_data(port, 0xff);
> -		parport_data_reverse(port);
> -		parport_write_control(port, DB9_NORMAL);
> +		if (db9_reverse[db9->mode]) {
> +			parport_data_reverse(port);
> +			parport_write_control(port, DB9_NORMAL);
> +		}
>  		mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
>  	}
>  
> @@ -318,11 +536,13 @@
>  		return NULL;
>  	}
>  
> -	if (!(pp->modes & PARPORT_MODE_TRISTATE) && config[1] != DB9_MULTI_0802) {
> -		printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
> -		return NULL;
> +	if (db9_bidirectional[config[1]]) {
> +		if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
> +			printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
> +			return NULL;
> +		}
>  	}
> -	
> +
>  	if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL)))
>  		return NULL;
>  	memset(db9, 0, sizeof(struct db9));
> @@ -340,7 +560,7 @@
>  		return NULL;
>  	}
>  
> -	for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) {
> +	for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) {
>  
>  		db9->dev[i].private = db9;
>  		db9->dev[i].open = db9_open;
> @@ -353,14 +573,19 @@
>  		db9->dev[i].idversion = 0x0100;
>  
>  		db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
> -		db9->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
> -
>  		for (j = 0; j < db9_buttons[db9->mode]; j++)
>  			set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit); 
> -
> -		db9->dev[i].absmin[ABS_X] = -1; db9->dev[i].absmax[ABS_X] = 1;
> -		db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1;
> -
> +		for (j = 0; j < db9_num_axis[db9->mode]; j++) {
> +			set_bit(db9_abs[j], db9->dev[i].absbit);
> +			if (j < 2) {
> +				db9->dev[i].absmin[db9_abs[j]] = -1;
> +				db9->dev[i].absmax[db9_abs[j]] = 1;
> +			} else {
> +				db9->dev[i].absmin[db9_abs[j]] = 1;
> +				db9->dev[i].absmax[db9_abs[j]] = 255;
> +				db9->dev[i].absflat[db9_abs[j]] = 0;
> +			}
> +		}
>  		input_register_device(db9->dev + i);
>  		printk(KERN_INFO "input%d: %s on %s\n",
>  			db9->dev[i].number, db9_name[db9->mode], db9->pd->port->name);
> @@ -414,7 +639,7 @@
>  
>  	for (i = 0; i < 3; i++) 
>  		if (db9_base[i]) {
> -			for (j = 0; j < 1 + (db9_base[i]->mode == DB9_MULTI_0802_2); j++)
> +			for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++)
>  				input_unregister_device(db9_base[i]->dev + j);
>  		parport_unregister_device(db9_base[i]->pd);
>  	}
> 
> 
> 
> 

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20)
  2003-06-21 15:59         ` Vojtech Pavlik
@ 2003-06-23 13:17           ` yokotak
  0 siblings, 0 replies; 8+ messages in thread
From: yokotak @ 2003-06-23 13:17 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: linux-kernel

Hello,
Thank you very much for your reply and acceptance of my patch.

Vojtech Pavlik <vojtech@suse.cz> wrote:
> Thanks for the patch! I applied it to my kernel tree. How about a
> patch to update for the joystick-parport.txt documentation file?
I updated jostick-parport.txt file. New saturn figure is equivalent
to previous documentation. It might require a review, especially in
language.

Thank you for your reading,
  yokota


--- joystick-parport.txt.orig	Thu Sep 13 07:34:06 2001
+++ joystick-parport.txt	Mon Jun 23 21:53:14 2003
@@ -393,35 +393,66 @@
 
 2.4.3 Sega Saturn
 ~~~~~~~~~~~~~~~~~
-  Sega Saturn has eight buttons, and to transfer that, without hacks like
+  Sega Saturn has nine buttons, and to transfer that, without hacks like
 Genesis 6 pads use, it needs one more select pin. Anyway, it is still
 handled by the db9.c driver. Its pinout is very different from anything
 else.  Use this schematic:
 
-    +-----------> Select 1
-    | +---------> Power
-    | | +-------> Up
-    | | | +-----> Down
-    | | | | +---> Ground
-    | | | | |
-  _____________
-5 \ o o o o o / 1
-   \ o o o o /
-  9 `~~~~~~~' 6
-     | | | |
-     | | | +----> Select 2
-     | | +------> Right
-     | +--------> Left
-     +----------> Power
+  +-------------------> Ground (parallel port pin 18 - 25)
+  | +-----------------> Data 2 (pin 3)
+  | | +---------------> Data 3 (pin 2)
+  | | | +-------------> Power sub (pin 1)
+  | | | | +-----------> Select 1 (pin 14)
+  | | | | | +---------> Select 0 (pin 16)
+  | | | | | | +-------> Data 0 (pin 5)
+  | | | | | | | +-----> Data 1 (pin 4)
+  | | | | | | | | +---> Power (pin 1)
+  | | | | | | | | |
+ /= = = = = = = = =\
+| 1 2 3 4 5 6 7 8 9 |
++-------------------+
 
-  Select 1 is pin 14 on the parallel port, Select 2 is pin 16 on the
+  Select 1 is pin 14 on the parallel port, Select 0 is pin 16 on the
 parallel port.
 
 (pin 14) -----> Select 1
-(pin 16) -----> Select 2
+(pin 16) -----> Select 0
 
-  The other pins (Up, Down, Right, Left, Power, Ground) are the same as for
-Multi joysticks using db9.c
+  Data 2, 3, 0, 1 are connected to pins 3, 2, 5, 4 on the parallel port.
+
+(pin 3)  -----> Data 2
+(pin 2)  -----> Data 3
+(pin 5)  -----> Data 0
+(pin 4)  -----> Data 1
+
+  The other pins (Power, Ground) are the same as for Multi joysticks using
+db9.c
+
+  Power sub might require a resistor, when you use analog controller with
+external +5 volt power supply.
+
++5V supply ------+------------> Power (saturn 9)
+                 |  Resistor
+                 +--[10kOhm]--> Power sub (saturn 4)
+
+  On a side note, if you have already built a different adapter for use with
+DirectPadPro, this is also supported by the db9.c driver, as device type 11
+and 12. Use 11 for a connector, or 12 for two connectors. Two connectors are
+distinguished with its own Ground pins.
+
+  +-------------------> Ground (pin 8 or pin 9)
+  | +-----------------> Data 2 (pin 12)
+  | | +---------------> Data 3 (pin 13)
+  | | | +-------------> Power sub (pin 4)
+  | | | | +-----------> Select 1 (pin 3)
+  | | | | | +---------> Select 0 (pin 2)
+  | | | | | | +-------> Data 0 (pin 11)
+  | | | | | | | +-----> Data 1 (pin 10)
+  | | | | | | | | +---> Power (pin 5)
+  | | | | | | | | |
+ /= = = = = = = = =\
+| 1 2 3 4 5 6 7 8 9 |
++-------------------+
 
 3. The drivers
 ~~~~~~~~~~~~~~
@@ -483,10 +514,12 @@
 	  3  | Genesis pad (3+1 buttons)
 	  5  | Genesis pad (5+1 buttons)
 	  6  | Genesis pad (6+2 buttons)
-	  7  | Saturn pad (8 buttons)
+	  7  | Saturn pad (9 buttons)
 	  8  | Multisystem 1-button joystick (v0.8.0.2 pin-out)
 	  9  | Two Multisystem 1-button joysticks (v0.8.0.2 pin-out) 
 	 10  | Amiga CD32 pad
+	 11  | Saturn pad (DirectPadPro pin-out)
+	 12  | Two Saturn pads (DirectPadPro pin-out)
 
   Should you want to use more than one of these joysticks/pads at once, you
 can use db9_2 and db9_3 as additional command line parameters for two



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2003-06-23 13:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-06 14:22 [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20 yokotak
2003-01-26 10:39 ` Vojtech Pavlik
2003-01-27 10:34   ` yokotak
2003-01-27 10:36     ` Vojtech Pavlik
2003-01-28 12:34       ` yokotak
2003-06-10 13:02       ` [PATCH] db9.c (Re: [PATCH] gamecon (added support for Sega Saturn controller), kernel 2.4.20) yokotak
2003-06-21 15:59         ` Vojtech Pavlik
2003-06-23 13:17           ` yokotak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).