diff options
author | Dominic Cerquetti <binary1230@yahoo.com> | 2006-10-10 17:42:48 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-10-17 17:46:33 -0400 |
commit | deb8ee43a23d48116cb23eb8dd1de2348efb1e80 (patch) | |
tree | c30cede8edd09cc7aff7c01ea6fbcb7b0d900e5d /drivers/usb/input/xpad.c | |
parent | 4550718f6c75c9abe8b987fa4c625fd041aa95a2 (diff) |
USB: xpad: dance pad support
Adds support for dance pads to the xpad driver. Dance pads require the
d-pad to be mapped to four buttons instead of two axes, so that
combinations of up/down and left/right can be hit simultaneously.
Known dance pads are detected, and there is a module parameter added
to default unknown xpad devices to map the d-pad to buttons if this is
desired. (dpad_to_buttons). Minor modifications were made to port the
changes in the original patch to a newer kernel version.
This patch was originally from Dominic Cerquetti originally written
for kernel 2.6.11.4, with minor modifications (API changes for USB,
spelling fixes to the documentation added in the original patch) made
to apply to the current kernel. I have modified Dominic's original
patch per some suggestions from Dmitry Torokhov. (There was nothing
in the patch format description about multiple From: lines, so I
haven't added myself.)
[akpm@osdl.org: cleanups]
Signed-off-by: Adam Buchbinder <adam.buchbinder@gmail.com>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/input/xpad.c')
-rw-r--r-- | drivers/usb/input/xpad.c | 139 |
1 files changed, 95 insertions, 44 deletions
diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index cebb6c463bfb..6a12a943b938 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c | |||
@@ -1,8 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * X-Box gamepad - v0.0.5 | 2 | * X-Box gamepad - v0.0.6 |
3 | * | 3 | * |
4 | * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> | 4 | * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> |
5 | * | 5 | * 2005 Dominic Cerquetti <binary1230@yahoo.com> |
6 | * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License as | 9 | * modify it under the terms of the GNU General Public License as |
@@ -30,9 +31,10 @@ | |||
30 | * - Greg Kroah-Hartman - usb-skeleton driver | 31 | * - Greg Kroah-Hartman - usb-skeleton driver |
31 | * | 32 | * |
32 | * TODO: | 33 | * TODO: |
33 | * - fine tune axes | 34 | * - fine tune axes (especially trigger axes) |
34 | * - fix "analog" buttons (reported as digital now) | 35 | * - fix "analog" buttons (reported as digital now) |
35 | * - get rumble working | 36 | * - get rumble working |
37 | * - need USB IDs for other dance pads | ||
36 | * | 38 | * |
37 | * History: | 39 | * History: |
38 | * | 40 | * |
@@ -57,25 +59,40 @@ | |||
57 | #include <linux/kernel.h> | 59 | #include <linux/kernel.h> |
58 | #include <linux/init.h> | 60 | #include <linux/init.h> |
59 | #include <linux/slab.h> | 61 | #include <linux/slab.h> |
62 | #include <linux/stat.h> | ||
60 | #include <linux/module.h> | 63 | #include <linux/module.h> |
64 | #include <linux/moduleparam.h> | ||
61 | #include <linux/smp_lock.h> | 65 | #include <linux/smp_lock.h> |
62 | #include <linux/usb/input.h> | 66 | #include <linux/usb/input.h> |
63 | 67 | ||
64 | #define DRIVER_VERSION "v0.0.5" | 68 | #define DRIVER_VERSION "v0.0.6" |
65 | #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" | 69 | #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" |
66 | #define DRIVER_DESC "X-Box pad driver" | 70 | #define DRIVER_DESC "X-Box pad driver" |
67 | 71 | ||
68 | #define XPAD_PKT_LEN 32 | 72 | #define XPAD_PKT_LEN 32 |
69 | 73 | ||
74 | /* xbox d-pads should map to buttons, as is required for DDR pads | ||
75 | but we map them to axes when possible to simplify things */ | ||
76 | #define MAP_DPAD_TO_BUTTONS 0 | ||
77 | #define MAP_DPAD_TO_AXES 1 | ||
78 | #define MAP_DPAD_UNKNOWN -1 | ||
79 | |||
80 | static int dpad_to_buttons; | ||
81 | module_param(dpad_to_buttons, bool, S_IRUGO); | ||
82 | MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads"); | ||
83 | |||
70 | static const struct xpad_device { | 84 | static const struct xpad_device { |
71 | u16 idVendor; | 85 | u16 idVendor; |
72 | u16 idProduct; | 86 | u16 idProduct; |
73 | char *name; | 87 | char *name; |
88 | u8 dpad_mapping; | ||
74 | } xpad_device[] = { | 89 | } xpad_device[] = { |
75 | { 0x045e, 0x0202, "Microsoft X-Box pad (US)" }, | 90 | { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES }, |
76 | { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)" }, | 91 | { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES }, |
77 | { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)" }, | 92 | { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES }, |
78 | { 0x0000, 0x0000, "X-Box pad" } | 93 | { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES }, |
94 | { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS }, | ||
95 | { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN } | ||
79 | }; | 96 | }; |
80 | 97 | ||
81 | static const signed short xpad_btn[] = { | 98 | static const signed short xpad_btn[] = { |
@@ -84,11 +101,23 @@ static const signed short xpad_btn[] = { | |||
84 | -1 /* terminating entry */ | 101 | -1 /* terminating entry */ |
85 | }; | 102 | }; |
86 | 103 | ||
104 | /* only used if MAP_DPAD_TO_BUTTONS */ | ||
105 | static const signed short xpad_btn_pad[] = { | ||
106 | BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ | ||
107 | BTN_0, BTN_1, /* d-pad up, down (XXX names??) */ | ||
108 | -1 /* terminating entry */ | ||
109 | }; | ||
110 | |||
87 | static const signed short xpad_abs[] = { | 111 | static const signed short xpad_abs[] = { |
88 | ABS_X, ABS_Y, /* left stick */ | 112 | ABS_X, ABS_Y, /* left stick */ |
89 | ABS_RX, ABS_RY, /* right stick */ | 113 | ABS_RX, ABS_RY, /* right stick */ |
90 | ABS_Z, ABS_RZ, /* triggers left/right */ | 114 | ABS_Z, ABS_RZ, /* triggers left/right */ |
91 | ABS_HAT0X, ABS_HAT0Y, /* digital pad */ | 115 | -1 /* terminating entry */ |
116 | }; | ||
117 | |||
118 | /* only used if MAP_DPAD_TO_AXES */ | ||
119 | static const signed short xpad_abs_pad[] = { | ||
120 | ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */ | ||
92 | -1 /* terminating entry */ | 121 | -1 /* terminating entry */ |
93 | }; | 122 | }; |
94 | 123 | ||
@@ -100,14 +129,16 @@ static struct usb_device_id xpad_table [] = { | |||
100 | MODULE_DEVICE_TABLE (usb, xpad_table); | 129 | MODULE_DEVICE_TABLE (usb, xpad_table); |
101 | 130 | ||
102 | struct usb_xpad { | 131 | struct usb_xpad { |
103 | struct input_dev *dev; /* input device interface */ | 132 | struct input_dev *dev; /* input device interface */ |
104 | struct usb_device *udev; /* usb device */ | 133 | struct usb_device *udev; /* usb device */ |
105 | 134 | ||
106 | struct urb *irq_in; /* urb for interrupt in report */ | 135 | struct urb *irq_in; /* urb for interrupt in report */ |
107 | unsigned char *idata; /* input data */ | 136 | unsigned char *idata; /* input data */ |
108 | dma_addr_t idata_dma; | 137 | dma_addr_t idata_dma; |
109 | 138 | ||
110 | char phys[65]; /* physical device path */ | 139 | char phys[65]; /* physical device path */ |
140 | |||
141 | int dpad_mapping; /* map d-pad to buttons or to axes */ | ||
111 | }; | 142 | }; |
112 | 143 | ||
113 | /* | 144 | /* |
@@ -137,14 +168,21 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d | |||
137 | input_report_abs(dev, ABS_RZ, data[11]); | 168 | input_report_abs(dev, ABS_RZ, data[11]); |
138 | 169 | ||
139 | /* digital pad */ | 170 | /* digital pad */ |
140 | input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); | 171 | if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { |
141 | input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); | 172 | input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); |
173 | input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); | ||
174 | } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ { | ||
175 | input_report_key(dev, BTN_LEFT, data[2] & 0x04); | ||
176 | input_report_key(dev, BTN_RIGHT, data[2] & 0x08); | ||
177 | input_report_key(dev, BTN_0, data[2] & 0x01); // up | ||
178 | input_report_key(dev, BTN_1, data[2] & 0x02); // down | ||
179 | } | ||
142 | 180 | ||
143 | /* start/back buttons and stick press left/right */ | 181 | /* start/back buttons and stick press left/right */ |
144 | input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4); | 182 | input_report_key(dev, BTN_START, data[2] & 0x10); |
145 | input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5); | 183 | input_report_key(dev, BTN_BACK, data[2] & 0x20); |
146 | input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6); | 184 | input_report_key(dev, BTN_THUMBL, data[2] & 0x40); |
147 | input_report_key(dev, BTN_THUMBR, data[2] >> 7); | 185 | input_report_key(dev, BTN_THUMBR, data[2] & 0x80); |
148 | 186 | ||
149 | /* "analog" buttons A, B, X, Y */ | 187 | /* "analog" buttons A, B, X, Y */ |
150 | input_report_key(dev, BTN_A, data[4]); | 188 | input_report_key(dev, BTN_A, data[4]); |
@@ -206,6 +244,28 @@ static void xpad_close (struct input_dev *dev) | |||
206 | usb_kill_urb(xpad->irq_in); | 244 | usb_kill_urb(xpad->irq_in); |
207 | } | 245 | } |
208 | 246 | ||
247 | static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) | ||
248 | { | ||
249 | set_bit(abs, input_dev->absbit); | ||
250 | |||
251 | switch (abs) { | ||
252 | case ABS_X: | ||
253 | case ABS_Y: | ||
254 | case ABS_RX: | ||
255 | case ABS_RY: /* the two sticks */ | ||
256 | input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128); | ||
257 | break; | ||
258 | case ABS_Z: | ||
259 | case ABS_RZ: /* the triggers */ | ||
260 | input_set_abs_params(input_dev, abs, 0, 255, 0, 0); | ||
261 | break; | ||
262 | case ABS_HAT0X: | ||
263 | case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */ | ||
264 | input_set_abs_params(input_dev, abs, -1, 1, 0, 0); | ||
265 | break; | ||
266 | } | ||
267 | } | ||
268 | |||
209 | static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) | 269 | static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) |
210 | { | 270 | { |
211 | struct usb_device *udev = interface_to_usbdev (intf); | 271 | struct usb_device *udev = interface_to_usbdev (intf); |
@@ -235,6 +295,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
235 | goto fail2; | 295 | goto fail2; |
236 | 296 | ||
237 | xpad->udev = udev; | 297 | xpad->udev = udev; |
298 | xpad->dpad_mapping = xpad_device[i].dpad_mapping; | ||
299 | if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) | ||
300 | xpad->dpad_mapping = dpad_to_buttons; | ||
238 | xpad->dev = input_dev; | 301 | xpad->dev = input_dev; |
239 | usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); | 302 | usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); |
240 | strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); | 303 | strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); |
@@ -249,32 +312,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
249 | 312 | ||
250 | input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); | 313 | input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); |
251 | 314 | ||
315 | /* set up buttons */ | ||
252 | for (i = 0; xpad_btn[i] >= 0; i++) | 316 | for (i = 0; xpad_btn[i] >= 0; i++) |
253 | set_bit(xpad_btn[i], input_dev->keybit); | 317 | set_bit(xpad_btn[i], input_dev->keybit); |
318 | if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) | ||
319 | for (i = 0; xpad_btn_pad[i] >= 0; i++) | ||
320 | set_bit(xpad_btn_pad[i], input_dev->keybit); | ||
254 | 321 | ||
255 | for (i = 0; xpad_abs[i] >= 0; i++) { | 322 | /* set up axes */ |
256 | 323 | for (i = 0; xpad_abs[i] >= 0; i++) | |
257 | signed short t = xpad_abs[i]; | 324 | xpad_set_up_abs(input_dev, xpad_abs[i]); |
258 | 325 | if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) | |
259 | set_bit(t, input_dev->absbit); | 326 | for (i = 0; xpad_abs_pad[i] >= 0; i++) |
260 | 327 | xpad_set_up_abs(input_dev, xpad_abs_pad[i]); | |
261 | switch (t) { | ||
262 | case ABS_X: | ||
263 | case ABS_Y: | ||
264 | case ABS_RX: | ||
265 | case ABS_RY: /* the two sticks */ | ||
266 | input_set_abs_params(input_dev, t, -32768, 32767, 16, 128); | ||
267 | break; | ||
268 | case ABS_Z: | ||
269 | case ABS_RZ: /* the triggers */ | ||
270 | input_set_abs_params(input_dev, t, 0, 255, 0, 0); | ||
271 | break; | ||
272 | case ABS_HAT0X: | ||
273 | case ABS_HAT0Y: /* the d-pad */ | ||
274 | input_set_abs_params(input_dev, t, -1, 1, 0, 0); | ||
275 | break; | ||
276 | } | ||
277 | } | ||
278 | 328 | ||
279 | ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; | 329 | ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; |
280 | usb_fill_int_urb(xpad->irq_in, udev, | 330 | usb_fill_int_urb(xpad->irq_in, udev, |
@@ -305,7 +355,8 @@ static void xpad_disconnect(struct usb_interface *intf) | |||
305 | usb_kill_urb(xpad->irq_in); | 355 | usb_kill_urb(xpad->irq_in); |
306 | input_unregister_device(xpad->dev); | 356 | input_unregister_device(xpad->dev); |
307 | usb_free_urb(xpad->irq_in); | 357 | usb_free_urb(xpad->irq_in); |
308 | usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); | 358 | usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, |
359 | xpad->idata, xpad->idata_dma); | ||
309 | kfree(xpad); | 360 | kfree(xpad); |
310 | } | 361 | } |
311 | } | 362 | } |