X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Finput%2Fjoystick%2Fgamecon.c;h=ecbdb6b9bbd6dd64323a929a98a5b95d6d97dc52;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=e3f048b65bfc8820844b10a9ce1abbd54023976d;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index e3f048b65..ecbdb6b9b 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -1,12 +1,13 @@ /* * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * - * Copyright (c) 1999-2004 Vojtech Pavlik - * Copyright (c) 2004 Peter Nelson + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Peter Nelson * * Based on the work of: - * Andree Borrmann John Dahlstrom - * David Kuder Nathan Hand + * Andree Borrmann John Dahlstrom + * David Kuder Nathan Hand + * Raphael Assenat */ /* @@ -36,25 +37,28 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver"); MODULE_LICENSE("GPL"); -static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int gc_nargs __initdata = 0; -module_param_array_named(map, gc, int, &gc_nargs, 0); -MODULE_PARM_DESC(map, "Describers first set of devices (,,,..)"); +#define GC_MAX_PORTS 3 +#define GC_MAX_DEVICES 5 -static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int gc_nargs_2 __initdata = 0; -module_param_array_named(map2, gc_2, int, &gc_nargs_2, 0); -MODULE_PARM_DESC(map2, "Describers second set of devices"); +struct gc_config { + int args[GC_MAX_DEVICES + 1]; + int nargs; +}; + +static struct gc_config gc[GC_MAX_PORTS] __initdata; -static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int gc_nargs_3 __initdata = 0; -module_param_array_named(map3, gc_3, int, &gc_nargs_3, 0); -MODULE_PARM_DESC(map3, "Describers third set of devices"); +module_param_array_named(map, gc[0].args, int, &gc[0].nargs, 0); +MODULE_PARM_DESC(map, "Describes first set of devices (,,,..)"); +module_param_array_named(map2, gc[1].args, int, &gc[1].nargs, 0); +MODULE_PARM_DESC(map2, "Describes second set of devices"); +module_param_array_named(map3, gc[2].args, int, &gc[2].nargs, 0); +MODULE_PARM_DESC(map3, "Describes third set of devices"); __obsolete_setup("gc="); __obsolete_setup("gc_2="); @@ -70,18 +74,20 @@ __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 struct gc { struct pardevice *pd; - struct input_dev dev[5]; + struct input_dev *dev[GC_MAX_DEVICES]; struct timer_list timer; unsigned char pads[GC_MAX + 1]; int used; - char phys[5][32]; + struct mutex mutex; + char phys[GC_MAX_DEVICES][32]; }; static struct gc *gc_base[3]; @@ -90,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. */ @@ -156,13 +162,58 @@ static void gc_n64_read_packet(struct gc *gc, unsigned char *data) } +static void gc_n64_process_packet(struct gc *gc) +{ + unsigned char data[GC_N64_LENGTH]; + signed char axes[2]; + struct input_dev *dev; + int i, j, s; + + gc_n64_read_packet(gc, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + + dev = gc->dev[i]; + if (!dev) + continue; + + s = gc_status_bit[i]; + + if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) { + + axes[0] = axes[1] = 0; + + for (j = 0; j < 8; j++) { + if (data[23 - j] & s) + axes[0] |= 1 << j; + if (data[31 - j] & s) + axes[1] |= 1 << j; + } + + input_report_abs(dev, ABS_X, axes[0]); + input_report_abs(dev, ABS_Y, -axes[1]); + + input_report_abs(dev, ABS_HAT0X, !(s & data[6]) - !(s & data[7])); + input_report_abs(dev, ABS_HAT0Y, !(s & data[4]) - !(s & data[5])); + + for (j = 0; j < 10; j++) + input_report_key(dev, gc_n64_btn[j], s & data[gc_n64_bytes[j]]); + + input_sync(dev); + } + } +} + /* * 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 @@ -195,6 +246,81 @@ 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_SNESMOUSE_LENGTH]; + struct input_dev *dev; + 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, len, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + + dev = gc->dev[i]; + if (!dev) + continue; + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) { + input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7])); + input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5])); + } + + if (s & gc->pads[GC_NES]) + for (j = 0; j < 4; j++) + input_report_key(dev, gc_snes_btn[j], s & data[gc_nes_bytes[j]]); + + if (s & gc->pads[GC_SNES]) + for (j = 0; j < 8; j++) + input_report_key(dev, 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); + } +} + /* * Multisystem joystick support */ @@ -216,6 +342,35 @@ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) } } +static void gc_multi_process_packet(struct gc *gc) +{ + unsigned char data[GC_MULTI2_LENGTH]; + struct input_dev *dev; + int i, s; + + gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data); + + for (i = 0; i < GC_MAX_DEVICES; i++) { + + dev = gc->dev[i]; + if (!dev) + continue; + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) { + input_report_abs(dev, ABS_X, !(s & data[2]) - !(s & data[3])); + input_report_abs(dev, ABS_Y, !(s & data[0]) - !(s & data[1])); + input_report_key(dev, BTN_TRIGGER, s & data[4]); + } + + if (s & gc->pads[GC_MULTI2]) + input_report_key(dev, BTN_THUMB, s & data[5]); + + input_sync(dev); + } +} + /* * PSX support * @@ -227,7 +382,8 @@ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) */ #define GC_PSX_DELAY 25 /* 25 usec */ -#define GC_PSX_LENGTH 8 /* talk to the controller in bytes */ +#define GC_PSX_LENGTH 8 /* talk to the controller in bits */ +#define GC_PSX_BYTES 6 /* the maximum number of bytes to read off the controller */ #define GC_PSX_MOUSE 1 /* Mouse */ #define GC_PSX_NEGCON 2 /* NegCon */ @@ -241,7 +397,7 @@ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) #define GC_PSX_SELECT 0x02 /* Pin 3 */ #define GC_PSX_ID(x) ((x) >> 4) /* High nibble is device type */ -#define GC_PSX_LEN(x) ((x) & 0xf) /* Low nibble is length in words */ +#define GC_PSX_LEN(x) (((x) & 0xf) << 1) /* Low nibble is length in bytes/2 */ static int gc_psx_delay = GC_PSX_DELAY; module_param_named(psx_delay, gc_psx_delay, uint, 0); @@ -259,18 +415,19 @@ static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 }; * the psx pad. */ -static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGTH]) +static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_MAX_DEVICES]) { int i, j, cmd, read; - for (i = 0; i < 5; i++) + + for (i = 0; i < GC_MAX_DEVICES; i++) data[i] = 0; - for (i = 0; i < 8; i++, b >>= 1) { + for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) { cmd = (b & 1) ? GC_PSX_COMMAND : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_POWER); udelay(gc_psx_delay); read = parport_read_status(gc->pd->port) ^ 0x80; - for (j = 0; j < 5; j++) + for (j = 0; j < GC_MAX_DEVICES; j++) data[j] |= (read & gc_status_bit[j] & (gc->pads[GC_PSX] | gc->pads[GC_DDR])) ? (1 << i) : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); udelay(gc_psx_delay); @@ -282,11 +439,12 @@ static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGT * device identifier code. */ -static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGTH], unsigned char id[5]) +static void gc_psx_read_packet(struct gc *gc, unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES], + unsigned char id[GC_MAX_DEVICES]) { int i, j, max_len = 0; unsigned long flags; - unsigned char data2[5]; + unsigned char data2[GC_MAX_DEVICES]; parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */ udelay(gc_psx_delay); @@ -299,13 +457,15 @@ static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGT gc_psx_command(gc, 0x42, id); /* Get device ids */ gc_psx_command(gc, 0, data2); /* Dump status */ - for (i =0; i < 5; i++) /* Find the longest pad */ - if((gc_status_bit[i] & (gc->pads[GC_PSX] | gc->pads[GC_DDR])) && (GC_PSX_LEN(id[i]) > max_len)) + for (i =0; i < GC_MAX_DEVICES; i++) /* Find the longest pad */ + if((gc_status_bit[i] & (gc->pads[GC_PSX] | gc->pads[GC_DDR])) + && (GC_PSX_LEN(id[i]) > max_len) + && (GC_PSX_LEN(id[i]) <= GC_PSX_BYTES)) max_len = GC_PSX_LEN(id[i]); - for (i = 0; i < max_len * 2; i++) { /* Read in all the data */ + for (i = 0; i < max_len; i++) { /* Read in all the data */ gc_psx_command(gc, 0, data2); - for (j = 0; j < 5; j++) + for (j = 0; j < GC_MAX_DEVICES; j++) data[j][i] = data2[j]; } @@ -313,186 +473,124 @@ static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGT parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); - for(i = 0; i < 5; i++) /* Set id's to the real value */ + for(i = 0; i < GC_MAX_DEVICES; i++) /* Set id's to the real value */ id[i] = GC_PSX_ID(id[i]); } -/* - * gc_timer() reads and analyzes console pads data. - */ - -#define GC_MAX_LENGTH GC_N64_LENGTH - -static void gc_timer(unsigned long private) +static void gc_psx_process_packet(struct gc *gc) { - struct gc *gc = (void *) private; - struct input_dev *dev = gc->dev; - unsigned char data[GC_MAX_LENGTH]; - unsigned char data_psx[5][GC_PSX_LENGTH]; - int i, j, s; + unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES]; + unsigned char id[GC_MAX_DEVICES]; + struct input_dev *dev; + int i, j; -/* - * N64 pads - must be read first, any read confuses them for 200 us - */ + gc_psx_read_packet(gc, data, id); - if (gc->pads[GC_N64]) { + for (i = 0; i < GC_MAX_DEVICES; i++) { - gc_n64_read_packet(gc, data); + dev = gc->dev[i]; + if (!dev) + continue; - for (i = 0; i < 5; i++) { + switch (id[i]) { - s = gc_status_bit[i]; + case GC_PSX_RUMBLE: - if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) { + input_report_key(dev, BTN_THUMBL, ~data[i][0] & 0x04); + input_report_key(dev, BTN_THUMBR, ~data[i][0] & 0x02); - signed char axes[2]; - axes[0] = axes[1] = 0; + case GC_PSX_NEGCON: + case GC_PSX_ANALOG: - for (j = 0; j < 8; j++) { - if (data[23 - j] & s) axes[0] |= 1 << j; - if (data[31 - j] & s) axes[1] |= 1 << j; - } + if (gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev, gc_psx_ddr_btn[j], ~data[i][0] & (0x10 << j)); + } else { + for (j = 0; j < 4; j++) + input_report_abs(dev, gc_psx_abs[j + 2], data[i][j + 2]); - input_report_abs(dev + i, ABS_X, axes[0]); - input_report_abs(dev + i, ABS_Y, -axes[1]); + input_report_abs(dev, ABS_X, 128 + !(data[i][0] & 0x20) * 127 - !(data[i][0] & 0x80) * 128); + input_report_abs(dev, ABS_Y, 128 + !(data[i][0] & 0x40) * 127 - !(data[i][0] & 0x10) * 128); + } - input_report_abs(dev + i, ABS_HAT0X, !(s & data[6]) - !(s & data[7])); - input_report_abs(dev + i, ABS_HAT0Y, !(s & data[4]) - !(s & data[5])); + for (j = 0; j < 8; j++) + input_report_key(dev, gc_psx_btn[j], ~data[i][1] & (1 << j)); - for (j = 0; j < 10; j++) - input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]); + input_report_key(dev, BTN_START, ~data[i][0] & 0x08); + input_report_key(dev, BTN_SELECT, ~data[i][0] & 0x01); - input_sync(dev + i); - } - } - } + input_sync(dev); -/* - * NES and SNES pads - */ - - if (gc->pads[GC_NES] || gc->pads[GC_SNES]) { - - gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data); + break; - for (i = 0; i < 5; i++) { + case GC_PSX_NORMAL: + if (gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev, gc_psx_ddr_btn[j], ~data[i][0] & (0x10 << j)); + } else { + input_report_abs(dev, ABS_X, 128 + !(data[i][0] & 0x20) * 127 - !(data[i][0] & 0x80) * 128); + input_report_abs(dev, ABS_Y, 128 + !(data[i][0] & 0x40) * 127 - !(data[i][0] & 0x10) * 128); + + /* for some reason if the extra axes are left unset they drift */ + /* for (j = 0; j < 4; j++) + input_report_abs(dev, gc_psx_abs[j + 2], 128); + * This needs to be debugged properly, + * maybe fuzz processing needs to be done in input_sync() + * --vojtech + */ + } - s = gc_status_bit[i]; + for (j = 0; j < 8; j++) + input_report_key(dev, gc_psx_btn[j], ~data[i][1] & (1 << j)); - if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) { - input_report_abs(dev + i, ABS_X, !(s & data[6]) - !(s & data[7])); - input_report_abs(dev + i, ABS_Y, !(s & data[4]) - !(s & data[5])); - } + input_report_key(dev, BTN_START, ~data[i][0] & 0x08); + input_report_key(dev, BTN_SELECT, ~data[i][0] & 0x01); - if (s & gc->pads[GC_NES]) - for (j = 0; j < 4; j++) - input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]); + input_sync(dev); - if (s & gc->pads[GC_SNES]) - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]); + break; - input_sync(dev + i); + case 0: /* not a pad, ignore */ + break; } } +} /* - * Multi and Multi2 joysticks + * gc_timer() initiates reads of console pads data. */ - if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) { +static void gc_timer(unsigned long private) +{ + struct gc *gc = (void *) private; - gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data); +/* + * N64 pads - must be read first, any read confuses them for 200 us + */ - for (i = 0; i < 5; i++) { + if (gc->pads[GC_N64]) + gc_n64_process_packet(gc); - s = gc_status_bit[i]; +/* + * NES and SNES pads or mouse + */ - if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) { - input_report_abs(dev + i, ABS_X, !(s & data[2]) - !(s & data[3])); - input_report_abs(dev + i, ABS_Y, !(s & data[0]) - !(s & data[1])); - input_report_key(dev + i, BTN_TRIGGER, s & data[4]); - } + if (gc->pads[GC_NES] || gc->pads[GC_SNES] || gc->pads[GC_SNESMOUSE]) + gc_nes_process_packet(gc); - if (s & gc->pads[GC_MULTI2]) - input_report_key(dev + i, BTN_THUMB, s & data[5]); +/* + * Multi and Multi2 joysticks + */ - input_sync(dev + i); - } - } + if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) + gc_multi_process_packet(gc); /* * PSX controllers */ - if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) { - - gc_psx_read_packet(gc, data_psx, data); - - for (i = 0; i < 5; i++) { - switch (data[i]) { - - case GC_PSX_RUMBLE: - - input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04); - input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02); - - case GC_PSX_NEGCON: - case GC_PSX_ANALOG: - - if(gc->pads[GC_DDR] & gc_status_bit[i]) { - for(j = 0; j < 4; j++) - input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); - } else { - for (j = 0; j < 4; j++) - input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]); - - input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); - input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); - } - - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - - input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - - input_sync(dev + i); - - break; - - case GC_PSX_NORMAL: - if(gc->pads[GC_DDR] & gc_status_bit[i]) { - for(j = 0; j < 4; j++) - input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); - } else { - input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); - input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); - - /* for some reason if the extra axes are left unset they drift */ - /* for (j = 0; j < 4; j++) - input_report_abs(dev + i, gc_psx_abs[j+2], 128); - * This needs to be debugged properly, - * maybe fuzz processing needs to be done in input_sync() - * --vojtech - */ - } - - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - - input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - - input_sync(dev + i); - - break; - - case 0: /* not a pad, ignore */ - break; - } - } - } + if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) + gc_psx_process_packet(gc); mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); } @@ -500,194 +598,257 @@ static void gc_timer(unsigned long private) static int gc_open(struct input_dev *dev) { struct gc *gc = dev->private; + int err; + + err = mutex_lock_interruptible(&gc->mutex); + if (err) + return err; + if (!gc->used++) { parport_claim(gc->pd); parport_write_control(gc->pd->port, 0x04); mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); } + + mutex_unlock(&gc->mutex); return 0; } static void gc_close(struct input_dev *dev) { struct gc *gc = dev->private; + + mutex_lock(&gc->mutex); if (!--gc->used) { - del_timer(&gc->timer); + del_timer_sync(&gc->timer); parport_write_control(gc->pd->port, 0x00); parport_release(gc->pd); } + mutex_unlock(&gc->mutex); } -static struct gc __init *gc_probe(int *config, int nargs) +static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) { - struct gc *gc; - struct parport *pp; - int i, j; + struct input_dev *input_dev; + int i; - if (config[0] < 0) - return NULL; + if (!pad_type) + return 0; - if (nargs < 2) { - printk(KERN_ERR "gamecon.c: at least one device must be specified\n"); - return NULL; + if (pad_type < 1 || pad_type > GC_MAX) { + printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", pad_type); + return -EINVAL; } - pp = parport_find_number(config[0]); - - if (!pp) { - printk(KERN_ERR "gamecon.c: no such parport\n"); - return NULL; + gc->dev[idx] = input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "gamecon.c: Not enough memory for input device\n"); + return -ENOMEM; } - if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) { - parport_put_port(pp); - return NULL; - } - memset(gc, 0, sizeof(struct gc)); + input_dev->name = gc_names[pad_type]; + input_dev->phys = gc->phys[idx]; + input_dev->id.bustype = BUS_PARPORT; + input_dev->id.vendor = 0x0001; + input_dev->id.product = pad_type; + input_dev->id.version = 0x0100; + input_dev->private = gc; - gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + input_dev->open = gc_open; + input_dev->close = gc_close; - parport_put_port(pp); + if (pad_type != GC_SNESMOUSE) { + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - if (!gc->pd) { - printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n"); - kfree(gc); - return NULL; - } + 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); - parport_claim(gc->pd); + gc->pads[0] |= gc_status_bit[idx]; + gc->pads[pad_type] |= gc_status_bit[idx]; - init_timer(&gc->timer); - gc->timer.data = (long) gc; - gc->timer.function = gc_timer; - - for (i = 0; i < nargs - 1; i++) { + switch (pad_type) { - if (!config[i + 1]) - continue; + case GC_N64: + for (i = 0; i < 10; i++) + set_bit(gc_n64_btn[i], input_dev->keybit); - if (config[i + 1] < 1 || config[i + 1] > GC_MAX) { - printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]); - continue; - } + for (i = 0; i < 2; i++) { + input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2); + input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0); + } - gc->dev[i].private = gc; - gc->dev[i].open = gc_open; - gc->dev[i].close = gc_close; + 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); + case GC_NES: + for (i = 0; i < 4; i++) + set_bit(gc_snes_btn[i], input_dev->keybit); + break; + + case GC_MULTI2: + set_bit(BTN_THUMB, input_dev->keybit); + case GC_MULTI: + set_bit(BTN_TRIGGER, input_dev->keybit); + break; + + case GC_PSX: + for (i = 0; i < 6; i++) + input_set_abs_params(input_dev, gc_psx_abs[i], 4, 252, 0, 2); + for (i = 0; i < 12; i++) + set_bit(gc_psx_btn[i], input_dev->keybit); + + break; + + case GC_DDR: + for (i = 0; i < 4; i++) + set_bit(gc_psx_ddr_btn[i], input_dev->keybit); + for (i = 0; i < 12; i++) + set_bit(gc_psx_btn[i], input_dev->keybit); + + break; + } - gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + return 0; +} - for (j = 0; j < 2; j++) { - set_bit(ABS_X + j, gc->dev[i].absbit); - gc->dev[i].absmin[ABS_X + j] = -1; - gc->dev[i].absmax[ABS_X + j] = 1; - } +static struct gc __init *gc_probe(int parport, int *pads, int n_pads) +{ + struct gc *gc; + struct parport *pp; + struct pardevice *pd; + int i; + int err; - gc->pads[0] |= gc_status_bit[i]; - gc->pads[config[i + 1]] |= gc_status_bit[i]; + pp = parport_find_number(parport); + if (!pp) { + printk(KERN_ERR "gamecon.c: no such parport\n"); + err = -EINVAL; + goto err_out; + } - switch(config[i + 1]) { + pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!pd) { + printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n"); + err = -EBUSY; + goto err_put_pp; + } - case GC_N64: - for (j = 0; j < 10; j++) - set_bit(gc_n64_btn[j], gc->dev[i].keybit); + gc = kzalloc(sizeof(struct gc), GFP_KERNEL); + if (!gc) { + printk(KERN_ERR "gamecon.c: Not enough memory\n"); + err = -ENOMEM; + goto err_unreg_pardev; + } - for (j = 0; j < 2; j++) { - set_bit(ABS_X + j, gc->dev[i].absbit); - gc->dev[i].absmin[ABS_X + j] = -127; - gc->dev[i].absmax[ABS_X + j] = 126; - gc->dev[i].absflat[ABS_X + j] = 2; - set_bit(ABS_HAT0X + j, gc->dev[i].absbit); - gc->dev[i].absmin[ABS_HAT0X + j] = -1; - gc->dev[i].absmax[ABS_HAT0X + j] = 1; - } + mutex_init(&gc->mutex); + gc->pd = pd; + init_timer(&gc->timer); + gc->timer.data = (long) gc; + gc->timer.function = gc_timer; - break; + for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) { + if (!pads[i]) + continue; - case GC_SNES: - for (j = 4; j < 8; j++) - set_bit(gc_snes_btn[j], gc->dev[i].keybit); - case GC_NES: - for (j = 0; j < 4; j++) - set_bit(gc_snes_btn[j], gc->dev[i].keybit); - break; + sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i); + err = gc_setup_pad(gc, i, pads[i]); + if (err) + goto err_unreg_devs; - case GC_MULTI2: - set_bit(BTN_THUMB, gc->dev[i].keybit); - case GC_MULTI: - set_bit(BTN_TRIGGER, gc->dev[i].keybit); - break; + err = input_register_device(gc->dev[i]); + if (err) + goto err_free_dev; + } - case GC_PSX: - case GC_DDR: - if(config[i + 1] == GC_DDR) { - for (j = 0; j < 4; j++) - set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit); - } else { - for (j = 0; j < 6; j++) { - set_bit(gc_psx_abs[j], gc->dev[i].absbit); - gc->dev[i].absmin[gc_psx_abs[j]] = 4; - gc->dev[i].absmax[gc_psx_abs[j]] = 252; - gc->dev[i].absflat[gc_psx_abs[j]] = 2; - } - } + if (!gc->pads[0]) { + printk(KERN_ERR "gamecon.c: No valid devices specified\n"); + err = -EINVAL; + goto err_free_gc; + } - for (j = 0; j < 12; j++) - set_bit(gc_psx_btn[j], gc->dev[i].keybit); + parport_put_port(pp); + return gc; - break; - } + err_free_dev: + input_free_device(gc->dev[i]); + err_unreg_devs: + while (--i >= 0) + if (gc->dev[i]) + input_unregister_device(gc->dev[i]); + err_free_gc: + kfree(gc); + err_unreg_pardev: + parport_unregister_device(pd); + err_put_pp: + parport_put_port(pp); + err_out: + return ERR_PTR(err); +} - sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i); +static void gc_remove(struct gc *gc) +{ + int i; - gc->dev[i].name = gc_names[config[i + 1]]; - gc->dev[i].phys = gc->phys[i]; - gc->dev[i].id.bustype = BUS_PARPORT; - gc->dev[i].id.vendor = 0x0001; - gc->dev[i].id.product = config[i + 1]; - gc->dev[i].id.version = 0x0100; - } + for (i = 0; i < GC_MAX_DEVICES; i++) + if (gc->dev[i]) + input_unregister_device(gc->dev[i]); + parport_unregister_device(gc->pd); + kfree(gc); +} - parport_release(gc->pd); +static int __init gc_init(void) +{ + int i; + int have_dev = 0; + int err = 0; - if (!gc->pads[0]) { - parport_unregister_device(gc->pd); - kfree(gc); - return NULL; - } + for (i = 0; i < GC_MAX_PORTS; i++) { + if (gc[i].nargs == 0 || gc[i].args[0] < 0) + continue; - for (i = 0; i < 5; i++) - if (gc->pads[0] & gc_status_bit[i]) { - input_register_device(gc->dev + i); - printk(KERN_INFO "input: %s on %s\n", gc->dev[i].name, gc->pd->port->name); + if (gc[i].nargs < 2) { + printk(KERN_ERR "gamecon.c: at least one device must be specified\n"); + err = -EINVAL; + break; } - return gc; -} + gc_base[i] = gc_probe(gc[i].args[0], gc[i].args + 1, gc[i].nargs - 1); + if (IS_ERR(gc_base[i])) { + err = PTR_ERR(gc_base[i]); + break; + } -int __init gc_init(void) -{ - gc_base[0] = gc_probe(gc, gc_nargs); - gc_base[1] = gc_probe(gc_2, gc_nargs_2); - gc_base[2] = gc_probe(gc_3, gc_nargs_3); + have_dev = 1; + } - if (gc_base[0] || gc_base[1] || gc_base[2]) - return 0; + if (err) { + while (--i >= 0) + if (gc_base[i]) + gc_remove(gc_base[i]); + return err; + } - return -ENODEV; + return have_dev ? 0 : -ENODEV; } -void __exit gc_exit(void) +static void __exit gc_exit(void) { - int i, j; + int i; - for (i = 0; i < 3; i++) - if (gc_base[i]) { - for (j = 0; j < 5; j++) - if (gc_base[i]->pads[0] & gc_status_bit[j]) - input_unregister_device(gc_base[i]->dev + j); - parport_unregister_device(gc_base[i]->pd); - } + for (i = 0; i < GC_MAX_PORTS; i++) + if (gc_base[i]) + gc_remove(gc_base[i]); } module_init(gc_init);