diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 15:38:14 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-25 15:38:14 -0400 |
commit | ce1d5b23a8d1e19866ab82bdec0dc41fde5273d8 (patch) | |
tree | 028c80655ee8853ebf607d435bc3d6ab223aef1f /drivers/input/joystick | |
parent | ad5e1b0f5d913d2c8bddfba81049cc07228da1a6 (diff) | |
parent | 308f0a5898033691d050374a949bbfe173987a16 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits)
Input: wacom - add support for Cintiq 20WSX
Input: ucb1400_ts - IRQ probe fix
Input: at32psif - update MODULE_AUTHOR with new email
Input: mac_hid - add lockdep annotation to emumousebtn
Input: i8042 - fix incorrect usage of strncpy and strncat
Input: bf54x-keys - add infrastructure for keypad wakeups
Input: add MODULE_ALIAS() to hotpluggable platform modules
Input: drivers/char/keyboard.c - use time_after
Input: fix ordering in joystick Makefile
Input: wm97xx-core - support use as a wakeup source
Input: wm97xx-core - use IRQF_SAMPLE_RANDOM
Input: wm97xx-core - only schedule interrupt handler if not already scheduled
Input: add Zhen Hua driver
Input: aiptek - add support for Genius G-PEN 560 tablet
Input: wacom - implement suspend and autosuspend
Input: xpad - set proper buffer length for outgoing requests
Input: omap-keypad - fix build warning
Input: gpio_keys - irq handling cleanup
Input: add PS/2 serio driver for AVR32 devices
Input: put ledstate in the keyboard notifier
...
Diffstat (limited to 'drivers/input/joystick')
-rw-r--r-- | drivers/input/joystick/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/joystick/Makefile | 3 | ||||
-rw-r--r-- | drivers/input/joystick/xpad.c | 213 | ||||
-rw-r--r-- | drivers/input/joystick/zhenhua.c | 243 |
4 files changed, 446 insertions, 25 deletions
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 7c662ee594a3..be5c14a5a0a4 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig | |||
@@ -193,6 +193,18 @@ config JOYSTICK_TWIDJOY | |||
193 | To compile this driver as a module, choose M here: the | 193 | To compile this driver as a module, choose M here: the |
194 | module will be called twidjoy. | 194 | module will be called twidjoy. |
195 | 195 | ||
196 | config JOYSTICK_ZHENHUA | ||
197 | tristate "5-byte Zhenhua RC transmitter" | ||
198 | select SERIO | ||
199 | help | ||
200 | Say Y here if you have a Zhen Hua PPM-4CH transmitter which is | ||
201 | supplied with a ready to fly micro electric indoor helicopters | ||
202 | such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want | ||
203 | to use it via serial cable as a joystick. | ||
204 | |||
205 | To compile this driver as a module, choose M here: the | ||
206 | module will be called zhenhua. | ||
207 | |||
196 | config JOYSTICK_DB9 | 208 | config JOYSTICK_DB9 |
197 | tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads" | 209 | tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads" |
198 | depends on PARPORT | 210 | depends on PARPORT |
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index e855abb0cc51..fdbf8c4c2876 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o | |||
15 | obj-$(CONFIG_JOYSTICK_GRIP) += grip.o | 15 | obj-$(CONFIG_JOYSTICK_GRIP) += grip.o |
16 | obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o | 16 | obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o |
17 | obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o | 17 | obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o |
18 | obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ | ||
18 | obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o | 19 | obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o |
19 | obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o | 20 | obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o |
20 | obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o | 21 | obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o |
@@ -27,5 +28,5 @@ obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o | |||
27 | obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o | 28 | obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o |
28 | obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o | 29 | obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o |
29 | obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o | 30 | obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o |
31 | obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o | ||
30 | 32 | ||
31 | obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ | ||
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 0380597249bb..52ddb04644ab 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * X-Box gamepad - v0.0.6 | 2 | * X-Box gamepad driver |
3 | * | 3 | * |
4 | * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> | 4 | * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de> |
5 | * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>, | 5 | * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>, |
@@ -68,6 +68,8 @@ | |||
68 | * - dance pads will map D-PAD to buttons, not axes | 68 | * - dance pads will map D-PAD to buttons, not axes |
69 | * - pass the module paramater 'dpad_to_buttons' to force | 69 | * - pass the module paramater 'dpad_to_buttons' to force |
70 | * the D-PAD to map to buttons if your pad is not detected | 70 | * the D-PAD to map to buttons if your pad is not detected |
71 | * | ||
72 | * Later changes can be tracked in SCM. | ||
71 | */ | 73 | */ |
72 | 74 | ||
73 | #include <linux/kernel.h> | 75 | #include <linux/kernel.h> |
@@ -77,7 +79,6 @@ | |||
77 | #include <linux/module.h> | 79 | #include <linux/module.h> |
78 | #include <linux/usb/input.h> | 80 | #include <linux/usb/input.h> |
79 | 81 | ||
80 | #define DRIVER_VERSION "v0.0.6" | ||
81 | #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" | 82 | #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>" |
82 | #define DRIVER_DESC "X-Box pad driver" | 83 | #define DRIVER_DESC "X-Box pad driver" |
83 | 84 | ||
@@ -87,10 +88,12 @@ | |||
87 | but we map them to axes when possible to simplify things */ | 88 | but we map them to axes when possible to simplify things */ |
88 | #define MAP_DPAD_TO_BUTTONS 0 | 89 | #define MAP_DPAD_TO_BUTTONS 0 |
89 | #define MAP_DPAD_TO_AXES 1 | 90 | #define MAP_DPAD_TO_AXES 1 |
90 | #define MAP_DPAD_UNKNOWN -1 | 91 | #define MAP_DPAD_UNKNOWN 2 |
91 | 92 | ||
92 | #define XTYPE_XBOX 0 | 93 | #define XTYPE_XBOX 0 |
93 | #define XTYPE_XBOX360 1 | 94 | #define XTYPE_XBOX360 1 |
95 | #define XTYPE_XBOX360W 2 | ||
96 | #define XTYPE_UNKNOWN 3 | ||
94 | 97 | ||
95 | static int dpad_to_buttons; | 98 | static int dpad_to_buttons; |
96 | module_param(dpad_to_buttons, bool, S_IRUGO); | 99 | module_param(dpad_to_buttons, bool, S_IRUGO); |
@@ -107,8 +110,10 @@ static const struct xpad_device { | |||
107 | { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 110 | { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
108 | { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 111 | { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
109 | { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 112 | { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
113 | { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, | ||
110 | { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 114 | { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
111 | { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 115 | { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
116 | { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, | ||
112 | { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 117 | { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
113 | { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 118 | { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
114 | { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 119 | { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
@@ -135,18 +140,26 @@ static const struct xpad_device { | |||
135 | { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 140 | { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
136 | { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 141 | { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
137 | { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 142 | { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
143 | { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, | ||
138 | { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 144 | { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
139 | { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, | 145 | { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, |
140 | { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, | 146 | { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, |
141 | { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_XBOX } | 147 | { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN } |
142 | }; | 148 | }; |
143 | 149 | ||
144 | static const signed short xpad_btn[] = { | 150 | /* buttons shared with xbox and xbox360 */ |
145 | BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */ | 151 | static const signed short xpad_common_btn[] = { |
152 | BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ | ||
146 | BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ | 153 | BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ |
147 | -1 /* terminating entry */ | 154 | -1 /* terminating entry */ |
148 | }; | 155 | }; |
149 | 156 | ||
157 | /* original xbox controllers only */ | ||
158 | static const signed short xpad_btn[] = { | ||
159 | BTN_C, BTN_Z, /* "analog" buttons */ | ||
160 | -1 /* terminating entry */ | ||
161 | }; | ||
162 | |||
150 | /* only used if MAP_DPAD_TO_BUTTONS */ | 163 | /* only used if MAP_DPAD_TO_BUTTONS */ |
151 | static const signed short xpad_btn_pad[] = { | 164 | static const signed short xpad_btn_pad[] = { |
152 | BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ | 165 | BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ |
@@ -173,12 +186,27 @@ static const signed short xpad_abs_pad[] = { | |||
173 | -1 /* terminating entry */ | 186 | -1 /* terminating entry */ |
174 | }; | 187 | }; |
175 | 188 | ||
176 | /* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only | 189 | /* Xbox 360 has a vendor-specific class, so we cannot match it with only |
177 | * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols, | 190 | * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we |
178 | * but we need only one of them. */ | 191 | * match against vendor id as well. Wired Xbox 360 devices have protocol 1, |
192 | * wireless controllers have protocol 129. */ | ||
193 | #define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ | ||
194 | .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ | ||
195 | .idVendor = (vend), \ | ||
196 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ | ||
197 | .bInterfaceSubClass = 93, \ | ||
198 | .bInterfaceProtocol = (pr) | ||
199 | #define XPAD_XBOX360_VENDOR(vend) \ | ||
200 | { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ | ||
201 | { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } | ||
202 | |||
179 | static struct usb_device_id xpad_table [] = { | 203 | static struct usb_device_id xpad_table [] = { |
180 | { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ | 204 | { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ |
181 | { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) }, /* X-Box 360 controller */ | 205 | XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ |
206 | XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ | ||
207 | XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ | ||
208 | XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ | ||
209 | XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ | ||
182 | { } | 210 | { } |
183 | }; | 211 | }; |
184 | 212 | ||
@@ -188,10 +216,15 @@ struct usb_xpad { | |||
188 | struct input_dev *dev; /* input device interface */ | 216 | struct input_dev *dev; /* input device interface */ |
189 | struct usb_device *udev; /* usb device */ | 217 | struct usb_device *udev; /* usb device */ |
190 | 218 | ||
219 | int pad_present; | ||
220 | |||
191 | struct urb *irq_in; /* urb for interrupt in report */ | 221 | struct urb *irq_in; /* urb for interrupt in report */ |
192 | unsigned char *idata; /* input data */ | 222 | unsigned char *idata; /* input data */ |
193 | dma_addr_t idata_dma; | 223 | dma_addr_t idata_dma; |
194 | 224 | ||
225 | struct urb *bulk_out; | ||
226 | unsigned char *bdata; | ||
227 | |||
195 | #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) | 228 | #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) |
196 | struct urb *irq_out; /* urb for interrupt out report */ | 229 | struct urb *irq_out; /* urb for interrupt out report */ |
197 | unsigned char *odata; /* output data */ | 230 | unsigned char *odata; /* output data */ |
@@ -227,13 +260,13 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d | |||
227 | input_report_abs(dev, ABS_X, | 260 | input_report_abs(dev, ABS_X, |
228 | (__s16) le16_to_cpup((__le16 *)(data + 12))); | 261 | (__s16) le16_to_cpup((__le16 *)(data + 12))); |
229 | input_report_abs(dev, ABS_Y, | 262 | input_report_abs(dev, ABS_Y, |
230 | (__s16) le16_to_cpup((__le16 *)(data + 14))); | 263 | ~(__s16) le16_to_cpup((__le16 *)(data + 14))); |
231 | 264 | ||
232 | /* right stick */ | 265 | /* right stick */ |
233 | input_report_abs(dev, ABS_RX, | 266 | input_report_abs(dev, ABS_RX, |
234 | (__s16) le16_to_cpup((__le16 *)(data + 16))); | 267 | (__s16) le16_to_cpup((__le16 *)(data + 16))); |
235 | input_report_abs(dev, ABS_RY, | 268 | input_report_abs(dev, ABS_RY, |
236 | (__s16) le16_to_cpup((__le16 *)(data + 18))); | 269 | ~(__s16) le16_to_cpup((__le16 *)(data + 18))); |
237 | 270 | ||
238 | /* triggers left/right */ | 271 | /* triggers left/right */ |
239 | input_report_abs(dev, ABS_Z, data[10]); | 272 | input_report_abs(dev, ABS_Z, data[10]); |
@@ -321,13 +354,13 @@ static void xpad360_process_packet(struct usb_xpad *xpad, | |||
321 | input_report_abs(dev, ABS_X, | 354 | input_report_abs(dev, ABS_X, |
322 | (__s16) le16_to_cpup((__le16 *)(data + 6))); | 355 | (__s16) le16_to_cpup((__le16 *)(data + 6))); |
323 | input_report_abs(dev, ABS_Y, | 356 | input_report_abs(dev, ABS_Y, |
324 | (__s16) le16_to_cpup((__le16 *)(data + 8))); | 357 | ~(__s16) le16_to_cpup((__le16 *)(data + 8))); |
325 | 358 | ||
326 | /* right stick */ | 359 | /* right stick */ |
327 | input_report_abs(dev, ABS_RX, | 360 | input_report_abs(dev, ABS_RX, |
328 | (__s16) le16_to_cpup((__le16 *)(data + 10))); | 361 | (__s16) le16_to_cpup((__le16 *)(data + 10))); |
329 | input_report_abs(dev, ABS_RY, | 362 | input_report_abs(dev, ABS_RY, |
330 | (__s16) le16_to_cpup((__le16 *)(data + 12))); | 363 | ~(__s16) le16_to_cpup((__le16 *)(data + 12))); |
331 | 364 | ||
332 | /* triggers left/right */ | 365 | /* triggers left/right */ |
333 | input_report_abs(dev, ABS_Z, data[4]); | 366 | input_report_abs(dev, ABS_Z, data[4]); |
@@ -336,6 +369,39 @@ static void xpad360_process_packet(struct usb_xpad *xpad, | |||
336 | input_sync(dev); | 369 | input_sync(dev); |
337 | } | 370 | } |
338 | 371 | ||
372 | /* | ||
373 | * xpad360w_process_packet | ||
374 | * | ||
375 | * Completes a request by converting the data into events for the | ||
376 | * input subsystem. It is version for xbox 360 wireless controller. | ||
377 | * | ||
378 | * Byte.Bit | ||
379 | * 00.1 - Status change: The controller or headset has connected/disconnected | ||
380 | * Bits 01.7 and 01.6 are valid | ||
381 | * 01.7 - Controller present | ||
382 | * 01.6 - Headset present | ||
383 | * 01.1 - Pad state (Bytes 4+) valid | ||
384 | * | ||
385 | */ | ||
386 | |||
387 | static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) | ||
388 | { | ||
389 | /* Presence change */ | ||
390 | if (data[0] & 0x08) { | ||
391 | if (data[1] & 0x80) { | ||
392 | xpad->pad_present = 1; | ||
393 | usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); | ||
394 | } else | ||
395 | xpad->pad_present = 0; | ||
396 | } | ||
397 | |||
398 | /* Valid pad data */ | ||
399 | if (!(data[1] & 0x1)) | ||
400 | return; | ||
401 | |||
402 | xpad360_process_packet(xpad, cmd, &data[4]); | ||
403 | } | ||
404 | |||
339 | static void xpad_irq_in(struct urb *urb) | 405 | static void xpad_irq_in(struct urb *urb) |
340 | { | 406 | { |
341 | struct usb_xpad *xpad = urb->context; | 407 | struct usb_xpad *xpad = urb->context; |
@@ -358,10 +424,16 @@ static void xpad_irq_in(struct urb *urb) | |||
358 | goto exit; | 424 | goto exit; |
359 | } | 425 | } |
360 | 426 | ||
361 | if (xpad->xtype == XTYPE_XBOX360) | 427 | switch (xpad->xtype) { |
428 | case XTYPE_XBOX360: | ||
362 | xpad360_process_packet(xpad, 0, xpad->idata); | 429 | xpad360_process_packet(xpad, 0, xpad->idata); |
363 | else | 430 | break; |
431 | case XTYPE_XBOX360W: | ||
432 | xpad360w_process_packet(xpad, 0, xpad->idata); | ||
433 | break; | ||
434 | default: | ||
364 | xpad_process_packet(xpad, 0, xpad->idata); | 435 | xpad_process_packet(xpad, 0, xpad->idata); |
436 | } | ||
365 | 437 | ||
366 | exit: | 438 | exit: |
367 | retval = usb_submit_urb (urb, GFP_ATOMIC); | 439 | retval = usb_submit_urb (urb, GFP_ATOMIC); |
@@ -399,6 +471,23 @@ exit: | |||
399 | __FUNCTION__, retval); | 471 | __FUNCTION__, retval); |
400 | } | 472 | } |
401 | 473 | ||
474 | static void xpad_bulk_out(struct urb *urb) | ||
475 | { | ||
476 | switch (urb->status) { | ||
477 | case 0: | ||
478 | /* success */ | ||
479 | break; | ||
480 | case -ECONNRESET: | ||
481 | case -ENOENT: | ||
482 | case -ESHUTDOWN: | ||
483 | /* this urb is terminated, clean up */ | ||
484 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | ||
485 | break; | ||
486 | default: | ||
487 | dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); | ||
488 | } | ||
489 | } | ||
490 | |||
402 | static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) | 491 | static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) |
403 | { | 492 | { |
404 | struct usb_endpoint_descriptor *ep_irq_out; | 493 | struct usb_endpoint_descriptor *ep_irq_out; |
@@ -408,7 +497,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) | |||
408 | return 0; | 497 | return 0; |
409 | 498 | ||
410 | xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN, | 499 | xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN, |
411 | GFP_ATOMIC, &xpad->odata_dma ); | 500 | GFP_KERNEL, &xpad->odata_dma); |
412 | if (!xpad->odata) | 501 | if (!xpad->odata) |
413 | goto fail1; | 502 | goto fail1; |
414 | 503 | ||
@@ -469,6 +558,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, | |||
469 | xpad->odata[5] = 0x00; | 558 | xpad->odata[5] = 0x00; |
470 | xpad->odata[6] = 0x00; | 559 | xpad->odata[6] = 0x00; |
471 | xpad->odata[7] = 0x00; | 560 | xpad->odata[7] = 0x00; |
561 | xpad->irq_out->transfer_buffer_length = 8; | ||
472 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | 562 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); |
473 | } | 563 | } |
474 | 564 | ||
@@ -477,6 +567,9 @@ static int xpad_play_effect(struct input_dev *dev, void *data, | |||
477 | 567 | ||
478 | static int xpad_init_ff(struct usb_xpad *xpad) | 568 | static int xpad_init_ff(struct usb_xpad *xpad) |
479 | { | 569 | { |
570 | if (xpad->xtype != XTYPE_XBOX360) | ||
571 | return 0; | ||
572 | |||
480 | input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); | 573 | input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); |
481 | 574 | ||
482 | return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); | 575 | return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); |
@@ -502,6 +595,7 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) | |||
502 | xpad->odata[0] = 0x01; | 595 | xpad->odata[0] = 0x01; |
503 | xpad->odata[1] = 0x03; | 596 | xpad->odata[1] = 0x03; |
504 | xpad->odata[2] = command; | 597 | xpad->odata[2] = command; |
598 | xpad->irq_out->transfer_buffer_length = 3; | ||
505 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | 599 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); |
506 | mutex_unlock(&xpad->odata_mutex); | 600 | mutex_unlock(&xpad->odata_mutex); |
507 | } | 601 | } |
@@ -574,6 +668,10 @@ static int xpad_open(struct input_dev *dev) | |||
574 | { | 668 | { |
575 | struct usb_xpad *xpad = input_get_drvdata(dev); | 669 | struct usb_xpad *xpad = input_get_drvdata(dev); |
576 | 670 | ||
671 | /* URB was submitted in probe */ | ||
672 | if(xpad->xtype == XTYPE_XBOX360W) | ||
673 | return 0; | ||
674 | |||
577 | xpad->irq_in->dev = xpad->udev; | 675 | xpad->irq_in->dev = xpad->udev; |
578 | if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) | 676 | if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) |
579 | return -EIO; | 677 | return -EIO; |
@@ -585,7 +683,8 @@ static void xpad_close(struct input_dev *dev) | |||
585 | { | 683 | { |
586 | struct usb_xpad *xpad = input_get_drvdata(dev); | 684 | struct usb_xpad *xpad = input_get_drvdata(dev); |
587 | 685 | ||
588 | usb_kill_urb(xpad->irq_in); | 686 | if(xpad->xtype != XTYPE_XBOX360W) |
687 | usb_kill_urb(xpad->irq_in); | ||
589 | xpad_stop_output(xpad); | 688 | xpad_stop_output(xpad); |
590 | } | 689 | } |
591 | 690 | ||
@@ -632,7 +731,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
632 | goto fail1; | 731 | goto fail1; |
633 | 732 | ||
634 | xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, | 733 | xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, |
635 | GFP_ATOMIC, &xpad->idata_dma); | 734 | GFP_KERNEL, &xpad->idata_dma); |
636 | if (!xpad->idata) | 735 | if (!xpad->idata) |
637 | goto fail1; | 736 | goto fail1; |
638 | 737 | ||
@@ -644,7 +743,16 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
644 | xpad->dpad_mapping = xpad_device[i].dpad_mapping; | 743 | xpad->dpad_mapping = xpad_device[i].dpad_mapping; |
645 | xpad->xtype = xpad_device[i].xtype; | 744 | xpad->xtype = xpad_device[i].xtype; |
646 | if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) | 745 | if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) |
647 | xpad->dpad_mapping = dpad_to_buttons; | 746 | xpad->dpad_mapping = !dpad_to_buttons; |
747 | if (xpad->xtype == XTYPE_UNKNOWN) { | ||
748 | if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { | ||
749 | if (intf->cur_altsetting->desc.bInterfaceProtocol == 129) | ||
750 | xpad->xtype = XTYPE_XBOX360W; | ||
751 | else | ||
752 | xpad->xtype = XTYPE_XBOX360; | ||
753 | } else | ||
754 | xpad->xtype = XTYPE_XBOX; | ||
755 | } | ||
648 | xpad->dev = input_dev; | 756 | xpad->dev = input_dev; |
649 | usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); | 757 | usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); |
650 | strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); | 758 | strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); |
@@ -662,11 +770,14 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
662 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | 770 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
663 | 771 | ||
664 | /* set up buttons */ | 772 | /* set up buttons */ |
665 | for (i = 0; xpad_btn[i] >= 0; i++) | 773 | for (i = 0; xpad_common_btn[i] >= 0; i++) |
666 | set_bit(xpad_btn[i], input_dev->keybit); | 774 | set_bit(xpad_common_btn[i], input_dev->keybit); |
667 | if (xpad->xtype == XTYPE_XBOX360) | 775 | if ((xpad->xtype == XTYPE_XBOX360) || (xpad->xtype == XTYPE_XBOX360W)) |
668 | for (i = 0; xpad360_btn[i] >= 0; i++) | 776 | for (i = 0; xpad360_btn[i] >= 0; i++) |
669 | set_bit(xpad360_btn[i], input_dev->keybit); | 777 | set_bit(xpad360_btn[i], input_dev->keybit); |
778 | else | ||
779 | for (i = 0; xpad_btn[i] >= 0; i++) | ||
780 | set_bit(xpad_btn[i], input_dev->keybit); | ||
670 | if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) | 781 | if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) |
671 | for (i = 0; xpad_btn_pad[i] >= 0; i++) | 782 | for (i = 0; xpad_btn_pad[i] >= 0; i++) |
672 | set_bit(xpad_btn_pad[i], input_dev->keybit); | 783 | set_bit(xpad_btn_pad[i], input_dev->keybit); |
@@ -703,8 +814,57 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
703 | goto fail4; | 814 | goto fail4; |
704 | 815 | ||
705 | usb_set_intfdata(intf, xpad); | 816 | usb_set_intfdata(intf, xpad); |
817 | |||
818 | /* | ||
819 | * Submit the int URB immediatly rather than waiting for open | ||
820 | * because we get status messages from the device whether | ||
821 | * or not any controllers are attached. In fact, it's | ||
822 | * exactly the message that a controller has arrived that | ||
823 | * we're waiting for. | ||
824 | */ | ||
825 | if (xpad->xtype == XTYPE_XBOX360W) { | ||
826 | xpad->irq_in->dev = xpad->udev; | ||
827 | error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); | ||
828 | if (error) | ||
829 | goto fail4; | ||
830 | |||
831 | /* | ||
832 | * Setup the message to set the LEDs on the | ||
833 | * controller when it shows up | ||
834 | */ | ||
835 | xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); | ||
836 | if(!xpad->bulk_out) | ||
837 | goto fail5; | ||
838 | |||
839 | xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); | ||
840 | if(!xpad->bdata) | ||
841 | goto fail6; | ||
842 | |||
843 | xpad->bdata[2] = 0x08; | ||
844 | switch (intf->cur_altsetting->desc.bInterfaceNumber) { | ||
845 | case 0: | ||
846 | xpad->bdata[3] = 0x42; | ||
847 | break; | ||
848 | case 2: | ||
849 | xpad->bdata[3] = 0x43; | ||
850 | break; | ||
851 | case 4: | ||
852 | xpad->bdata[3] = 0x44; | ||
853 | break; | ||
854 | case 6: | ||
855 | xpad->bdata[3] = 0x45; | ||
856 | } | ||
857 | |||
858 | ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; | ||
859 | usb_fill_bulk_urb(xpad->bulk_out, udev, | ||
860 | usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), | ||
861 | xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); | ||
862 | } | ||
863 | |||
706 | return 0; | 864 | return 0; |
707 | 865 | ||
866 | fail6: usb_free_urb(xpad->bulk_out); | ||
867 | fail5: usb_kill_urb(xpad->irq_in); | ||
708 | fail4: usb_free_urb(xpad->irq_in); | 868 | fail4: usb_free_urb(xpad->irq_in); |
709 | fail3: xpad_deinit_output(xpad); | 869 | fail3: xpad_deinit_output(xpad); |
710 | fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); | 870 | fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); |
@@ -723,6 +883,11 @@ static void xpad_disconnect(struct usb_interface *intf) | |||
723 | xpad_led_disconnect(xpad); | 883 | xpad_led_disconnect(xpad); |
724 | input_unregister_device(xpad->dev); | 884 | input_unregister_device(xpad->dev); |
725 | xpad_deinit_output(xpad); | 885 | xpad_deinit_output(xpad); |
886 | if (xpad->xtype == XTYPE_XBOX360W) { | ||
887 | usb_kill_urb(xpad->bulk_out); | ||
888 | usb_free_urb(xpad->bulk_out); | ||
889 | usb_kill_urb(xpad->irq_in); | ||
890 | } | ||
726 | usb_free_urb(xpad->irq_in); | 891 | usb_free_urb(xpad->irq_in); |
727 | usb_buffer_free(xpad->udev, XPAD_PKT_LEN, | 892 | usb_buffer_free(xpad->udev, XPAD_PKT_LEN, |
728 | xpad->idata, xpad->idata_dma); | 893 | xpad->idata, xpad->idata_dma); |
@@ -741,7 +906,7 @@ static int __init usb_xpad_init(void) | |||
741 | { | 906 | { |
742 | int result = usb_register(&xpad_driver); | 907 | int result = usb_register(&xpad_driver); |
743 | if (result == 0) | 908 | if (result == 0) |
744 | info(DRIVER_DESC ":" DRIVER_VERSION); | 909 | info(DRIVER_DESC); |
745 | return result; | 910 | return result; |
746 | } | 911 | } |
747 | 912 | ||
diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c new file mode 100644 index 000000000000..b5853125c898 --- /dev/null +++ b/drivers/input/joystick/zhenhua.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* | ||
2 | * derived from "twidjoy.c" | ||
3 | * | ||
4 | * Copyright (c) 2008 Martin Kebert | ||
5 | * Copyright (c) 2001 Arndt Schoenewald | ||
6 | * Copyright (c) 2000-2001 Vojtech Pavlik | ||
7 | * Copyright (c) 2000 Mark Fletcher | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama, | ||
13 | * EasyCopter etc.) as a joystick under Linux. | ||
14 | * | ||
15 | * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels | ||
16 | * transmitters for control a RC planes or RC helicopters with possibility to | ||
17 | * connect on a serial port. | ||
18 | * Data coming from transmitter is in this order: | ||
19 | * 1. byte = synchronisation byte | ||
20 | * 2. byte = X axis | ||
21 | * 3. byte = Y axis | ||
22 | * 4. byte = RZ axis | ||
23 | * 5. byte = Z axis | ||
24 | * (and this is repeated) | ||
25 | * | ||
26 | * For questions or feedback regarding this driver module please contact: | ||
27 | * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel | ||
28 | * coder :-( | ||
29 | */ | ||
30 | |||
31 | /* | ||
32 | * This program is free software; you can redistribute it and/or modify | ||
33 | * it under the terms of the GNU General Public License as published by | ||
34 | * the Free Software Foundation; either version 2 of the License, or | ||
35 | * (at your option) any later version. | ||
36 | * | ||
37 | * This program is distributed in the hope that it will be useful, | ||
38 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
39 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
40 | * GNU General Public License for more details. | ||
41 | * | ||
42 | * You should have received a copy of the GNU General Public License | ||
43 | * along with this program; if not, write to the Free Software | ||
44 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
45 | */ | ||
46 | |||
47 | #include <linux/kernel.h> | ||
48 | #include <linux/module.h> | ||
49 | #include <linux/slab.h> | ||
50 | #include <linux/input.h> | ||
51 | #include <linux/serio.h> | ||
52 | #include <linux/init.h> | ||
53 | |||
54 | #define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver" | ||
55 | |||
56 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
57 | MODULE_LICENSE("GPL"); | ||
58 | |||
59 | /* | ||
60 | * Constants. | ||
61 | */ | ||
62 | |||
63 | #define ZHENHUA_MAX_LENGTH 5 | ||
64 | |||
65 | /* | ||
66 | * Zhen Hua data. | ||
67 | */ | ||
68 | |||
69 | struct zhenhua { | ||
70 | struct input_dev *dev; | ||
71 | int idx; | ||
72 | unsigned char data[ZHENHUA_MAX_LENGTH]; | ||
73 | char phys[32]; | ||
74 | }; | ||
75 | |||
76 | |||
77 | /* bits in all incoming bytes needs to be "reversed" */ | ||
78 | static int zhenhua_bitreverse(int x) | ||
79 | { | ||
80 | x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); | ||
81 | x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); | ||
82 | x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); | ||
83 | return x; | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * zhenhua_process_packet() decodes packets the driver receives from the | ||
88 | * RC transmitter. It updates the data accordingly. | ||
89 | */ | ||
90 | |||
91 | static void zhenhua_process_packet(struct zhenhua *zhenhua) | ||
92 | { | ||
93 | struct input_dev *dev = zhenhua->dev; | ||
94 | unsigned char *data = zhenhua->data; | ||
95 | |||
96 | input_report_abs(dev, ABS_Y, data[1]); | ||
97 | input_report_abs(dev, ABS_X, data[2]); | ||
98 | input_report_abs(dev, ABS_RZ, data[3]); | ||
99 | input_report_abs(dev, ABS_Z, data[4]); | ||
100 | |||
101 | input_sync(dev); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * zhenhua_interrupt() is called by the low level driver when characters | ||
106 | * are ready for us. We then buffer them for further processing, or call the | ||
107 | * packet processing routine. | ||
108 | */ | ||
109 | |||
110 | static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags) | ||
111 | { | ||
112 | struct zhenhua *zhenhua = serio_get_drvdata(serio); | ||
113 | |||
114 | /* All Zhen Hua packets are 5 bytes. The fact that the first byte | ||
115 | * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200) | ||
116 | * can be used to check and regain sync. */ | ||
117 | |||
118 | if (data == 0xef) | ||
119 | zhenhua->idx = 0; /* this byte starts a new packet */ | ||
120 | else if (zhenhua->idx == 0) | ||
121 | return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ | ||
122 | |||
123 | if (zhenhua->idx < ZHENHUA_MAX_LENGTH) | ||
124 | zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data); | ||
125 | |||
126 | if (zhenhua->idx == ZHENHUA_MAX_LENGTH) { | ||
127 | zhenhua_process_packet(zhenhua); | ||
128 | zhenhua->idx = 0; | ||
129 | } | ||
130 | |||
131 | return IRQ_HANDLED; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * zhenhua_disconnect() is the opposite of zhenhua_connect() | ||
136 | */ | ||
137 | |||
138 | static void zhenhua_disconnect(struct serio *serio) | ||
139 | { | ||
140 | struct zhenhua *zhenhua = serio_get_drvdata(serio); | ||
141 | |||
142 | serio_close(serio); | ||
143 | serio_set_drvdata(serio, NULL); | ||
144 | input_unregister_device(zhenhua->dev); | ||
145 | kfree(zhenhua); | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * zhenhua_connect() is the routine that is called when someone adds a | ||
150 | * new serio device. It looks for the Twiddler, and if found, registers | ||
151 | * it as an input device. | ||
152 | */ | ||
153 | |||
154 | static int zhenhua_connect(struct serio *serio, struct serio_driver *drv) | ||
155 | { | ||
156 | struct zhenhua *zhenhua; | ||
157 | struct input_dev *input_dev; | ||
158 | int err = -ENOMEM; | ||
159 | |||
160 | zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL); | ||
161 | input_dev = input_allocate_device(); | ||
162 | if (!zhenhua || !input_dev) | ||
163 | goto fail1; | ||
164 | |||
165 | zhenhua->dev = input_dev; | ||
166 | snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys); | ||
167 | |||
168 | input_dev->name = "Zhen Hua 5-byte device"; | ||
169 | input_dev->phys = zhenhua->phys; | ||
170 | input_dev->id.bustype = BUS_RS232; | ||
171 | input_dev->id.vendor = SERIO_ZHENHUA; | ||
172 | input_dev->id.product = 0x0001; | ||
173 | input_dev->id.version = 0x0100; | ||
174 | input_dev->dev.parent = &serio->dev; | ||
175 | |||
176 | input_dev->evbit[0] = BIT(EV_ABS); | ||
177 | input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0); | ||
178 | input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0); | ||
179 | input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0); | ||
180 | input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0); | ||
181 | |||
182 | serio_set_drvdata(serio, zhenhua); | ||
183 | |||
184 | err = serio_open(serio, drv); | ||
185 | if (err) | ||
186 | goto fail2; | ||
187 | |||
188 | err = input_register_device(zhenhua->dev); | ||
189 | if (err) | ||
190 | goto fail3; | ||
191 | |||
192 | return 0; | ||
193 | |||
194 | fail3: serio_close(serio); | ||
195 | fail2: serio_set_drvdata(serio, NULL); | ||
196 | fail1: input_free_device(input_dev); | ||
197 | kfree(zhenhua); | ||
198 | return err; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * The serio driver structure. | ||
203 | */ | ||
204 | |||
205 | static struct serio_device_id zhenhua_serio_ids[] = { | ||
206 | { | ||
207 | .type = SERIO_RS232, | ||
208 | .proto = SERIO_ZHENHUA, | ||
209 | .id = SERIO_ANY, | ||
210 | .extra = SERIO_ANY, | ||
211 | }, | ||
212 | { 0 } | ||
213 | }; | ||
214 | |||
215 | MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids); | ||
216 | |||
217 | static struct serio_driver zhenhua_drv = { | ||
218 | .driver = { | ||
219 | .name = "zhenhua", | ||
220 | }, | ||
221 | .description = DRIVER_DESC, | ||
222 | .id_table = zhenhua_serio_ids, | ||
223 | .interrupt = zhenhua_interrupt, | ||
224 | .connect = zhenhua_connect, | ||
225 | .disconnect = zhenhua_disconnect, | ||
226 | }; | ||
227 | |||
228 | /* | ||
229 | * The functions for inserting/removing us as a module. | ||
230 | */ | ||
231 | |||
232 | static int __init zhenhua_init(void) | ||
233 | { | ||
234 | return serio_register_driver(&zhenhua_drv); | ||
235 | } | ||
236 | |||
237 | static void __exit zhenhua_exit(void) | ||
238 | { | ||
239 | serio_unregister_driver(&zhenhua_drv); | ||
240 | } | ||
241 | |||
242 | module_init(zhenhua_init); | ||
243 | module_exit(zhenhua_exit); | ||