diff options
author | Colin Leitner <colin.leitner@googlemail.com> | 2013-05-27 17:41:05 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2013-05-28 04:16:49 -0400 |
commit | f04d51404f51947d3feabf2518495ba5aa3bb2c4 (patch) | |
tree | 5de567d8ef3cf1baf1ae0c314c55909b05e14afd /drivers/hid/hid-sony.c | |
parent | f755407dd19072b7d20719bc5454caed9ab41cc1 (diff) |
HID: driver for PS2/3 Buzz controllers
This patch adds support for PS2/3 Buzz controllers into hid-sony
It has been tested on Debian 7 with kernel version 3.10.0-rc2. Unfortunately
I can't test the patch with a regular six-axis controller myself.
Signed-off-by: Colin Leitner <colin.leitner@gmail.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-sony.c')
-rw-r--r-- | drivers/hid/hid-sony.c | 310 |
1 files changed, 301 insertions, 9 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 312098e4af4f..41e829c8bc48 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc | 6 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
7 | * Copyright (c) 2008 Jiri Slaby | 7 | * Copyright (c) 2008 Jiri Slaby |
8 | * Copyright (c) 2006-2008 Jiri Kosina | 8 | * Copyright (c) 2006-2008 Jiri Kosina |
9 | * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> | ||
9 | */ | 10 | */ |
10 | 11 | ||
11 | /* | 12 | /* |
@@ -26,6 +27,7 @@ | |||
26 | #define VAIO_RDESC_CONSTANT (1 << 0) | 27 | #define VAIO_RDESC_CONSTANT (1 << 0) |
27 | #define SIXAXIS_CONTROLLER_USB (1 << 1) | 28 | #define SIXAXIS_CONTROLLER_USB (1 << 1) |
28 | #define SIXAXIS_CONTROLLER_BT (1 << 2) | 29 | #define SIXAXIS_CONTROLLER_BT (1 << 2) |
30 | #define BUZZ_CONTROLLER (1 << 3) | ||
29 | 31 | ||
30 | static const u8 sixaxis_rdesc_fixup[] = { | 32 | static const u8 sixaxis_rdesc_fixup[] = { |
31 | 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, | 33 | 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, |
@@ -55,8 +57,56 @@ static const u8 sixaxis_rdesc_fixup2[] = { | |||
55 | 0xb1, 0x02, 0xc0, 0xc0, | 57 | 0xb1, 0x02, 0xc0, 0xc0, |
56 | }; | 58 | }; |
57 | 59 | ||
60 | static const unsigned int buzz_keymap[] = { | ||
61 | /* The controller has 4 remote buzzers, each with one LED and 5 | ||
62 | * buttons. | ||
63 | * | ||
64 | * We use the mapping chosen by the controller, which is: | ||
65 | * | ||
66 | * Key Offset | ||
67 | * ------------------- | ||
68 | * Buzz 1 | ||
69 | * Blue 5 | ||
70 | * Orange 4 | ||
71 | * Green 3 | ||
72 | * Yellow 2 | ||
73 | * | ||
74 | * So, for example, the orange button on the third buzzer is mapped to | ||
75 | * BTN_TRIGGER_HAPPY14 | ||
76 | */ | ||
77 | [ 1] = BTN_TRIGGER_HAPPY1, | ||
78 | [ 2] = BTN_TRIGGER_HAPPY2, | ||
79 | [ 3] = BTN_TRIGGER_HAPPY3, | ||
80 | [ 4] = BTN_TRIGGER_HAPPY4, | ||
81 | [ 5] = BTN_TRIGGER_HAPPY5, | ||
82 | [ 6] = BTN_TRIGGER_HAPPY6, | ||
83 | [ 7] = BTN_TRIGGER_HAPPY7, | ||
84 | [ 8] = BTN_TRIGGER_HAPPY8, | ||
85 | [ 9] = BTN_TRIGGER_HAPPY9, | ||
86 | [10] = BTN_TRIGGER_HAPPY10, | ||
87 | [11] = BTN_TRIGGER_HAPPY11, | ||
88 | [12] = BTN_TRIGGER_HAPPY12, | ||
89 | [13] = BTN_TRIGGER_HAPPY13, | ||
90 | [14] = BTN_TRIGGER_HAPPY14, | ||
91 | [15] = BTN_TRIGGER_HAPPY15, | ||
92 | [16] = BTN_TRIGGER_HAPPY16, | ||
93 | [17] = BTN_TRIGGER_HAPPY17, | ||
94 | [18] = BTN_TRIGGER_HAPPY18, | ||
95 | [19] = BTN_TRIGGER_HAPPY19, | ||
96 | [20] = BTN_TRIGGER_HAPPY20, | ||
97 | }; | ||
98 | |||
58 | struct sony_sc { | 99 | struct sony_sc { |
59 | unsigned long quirks; | 100 | unsigned long quirks; |
101 | |||
102 | void *extra; | ||
103 | }; | ||
104 | |||
105 | struct buzz_extra { | ||
106 | #ifdef CONFIG_LEDS_CLASS | ||
107 | int led_state; | ||
108 | struct led_classdev *leds[4]; | ||
109 | #endif | ||
60 | }; | 110 | }; |
61 | 111 | ||
62 | /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ | 112 | /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ |
@@ -117,6 +167,38 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
117 | return 0; | 167 | return 0; |
118 | } | 168 | } |
119 | 169 | ||
170 | static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
171 | struct hid_field *field, struct hid_usage *usage, | ||
172 | unsigned long **bit, int *max) | ||
173 | { | ||
174 | struct sony_sc *sc = hid_get_drvdata(hdev); | ||
175 | |||
176 | if (sc->quirks & BUZZ_CONTROLLER) { | ||
177 | unsigned int key = usage->hid & HID_USAGE; | ||
178 | |||
179 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) | ||
180 | return -1; | ||
181 | |||
182 | switch (usage->collection_index) { | ||
183 | case 1: | ||
184 | if (key >= ARRAY_SIZE(buzz_keymap)) | ||
185 | return -1; | ||
186 | |||
187 | key = buzz_keymap[key]; | ||
188 | if (!key) | ||
189 | return -1; | ||
190 | break; | ||
191 | default: | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); | ||
196 | return 1; | ||
197 | } | ||
198 | |||
199 | return -1; | ||
200 | } | ||
201 | |||
120 | /* | 202 | /* |
121 | * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP | 203 | * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP |
122 | * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() | 204 | * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() |
@@ -192,11 +274,201 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) | |||
192 | return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | 274 | return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); |
193 | } | 275 | } |
194 | 276 | ||
277 | #ifdef CONFIG_LEDS_CLASS | ||
278 | static void buzz_set_leds(struct hid_device *hdev, int leds) | ||
279 | { | ||
280 | struct list_head *report_list = | ||
281 | &hdev->report_enum[HID_OUTPUT_REPORT].report_list; | ||
282 | struct hid_report *report = list_entry(report_list->next, | ||
283 | struct hid_report, list); | ||
284 | __s32 *value = report->field[0]->value; | ||
285 | |||
286 | value[0] = 0x00; | ||
287 | value[1] = (leds & 1) ? 0xff : 0x00; | ||
288 | value[2] = (leds & 2) ? 0xff : 0x00; | ||
289 | value[3] = (leds & 4) ? 0xff : 0x00; | ||
290 | value[4] = (leds & 8) ? 0xff : 0x00; | ||
291 | value[5] = 0x00; | ||
292 | value[6] = 0x00; | ||
293 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
294 | } | ||
295 | |||
296 | static void buzz_led_set_brightness(struct led_classdev *led, | ||
297 | enum led_brightness value) | ||
298 | { | ||
299 | struct device *dev = led->dev->parent; | ||
300 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
301 | struct sony_sc *drv_data; | ||
302 | struct buzz_extra *buzz; | ||
303 | |||
304 | int n; | ||
305 | |||
306 | drv_data = hid_get_drvdata(hdev); | ||
307 | if (!drv_data || !drv_data->extra) { | ||
308 | hid_err(hdev, "No device data\n"); | ||
309 | return; | ||
310 | } | ||
311 | buzz = drv_data->extra; | ||
312 | |||
313 | for (n = 0; n < 4; n++) { | ||
314 | if (led == buzz->leds[n]) { | ||
315 | int on = !! (buzz->led_state & (1 << n)); | ||
316 | if (value == LED_OFF && on) { | ||
317 | buzz->led_state &= ~(1 << n); | ||
318 | buzz_set_leds(hdev, buzz->led_state); | ||
319 | } else if (value != LED_OFF && !on) { | ||
320 | buzz->led_state |= (1 << n); | ||
321 | buzz_set_leds(hdev, buzz->led_state); | ||
322 | } | ||
323 | break; | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | |||
328 | static enum led_brightness buzz_led_get_brightness(struct led_classdev *led) | ||
329 | { | ||
330 | struct device *dev = led->dev->parent; | ||
331 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
332 | struct sony_sc *drv_data; | ||
333 | struct buzz_extra *buzz; | ||
334 | |||
335 | int n; | ||
336 | int on = 0; | ||
337 | |||
338 | drv_data = hid_get_drvdata(hdev); | ||
339 | if (!drv_data || !drv_data->extra) { | ||
340 | hid_err(hdev, "No device data\n"); | ||
341 | return LED_OFF; | ||
342 | } | ||
343 | buzz = drv_data->extra; | ||
344 | |||
345 | for (n = 0; n < 4; n++) { | ||
346 | if (led == buzz->leds[n]) { | ||
347 | on = !! (buzz->led_state & (1 << n)); | ||
348 | break; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | return on ? LED_FULL : LED_OFF; | ||
353 | } | ||
354 | #endif | ||
355 | |||
356 | static int buzz_init(struct hid_device *hdev) | ||
357 | { | ||
358 | struct sony_sc *drv_data; | ||
359 | struct buzz_extra *buzz; | ||
360 | int ret = 0; | ||
361 | |||
362 | drv_data = hid_get_drvdata(hdev); | ||
363 | BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); | ||
364 | |||
365 | buzz = kzalloc(sizeof(*buzz), GFP_KERNEL); | ||
366 | if (!buzz) { | ||
367 | hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); | ||
368 | return -ENOMEM; | ||
369 | } | ||
370 | drv_data->extra = buzz; | ||
371 | |||
372 | /* Clear LEDs as we have no way of reading their initial state. This is | ||
373 | * only relevant if the driver is loaded after somebody actively set the | ||
374 | * LEDs to on */ | ||
375 | buzz_set_leds(hdev, 0x00); | ||
376 | |||
377 | #ifdef CONFIG_LEDS_CLASS | ||
378 | { | ||
379 | int n; | ||
380 | struct led_classdev *led; | ||
381 | size_t name_sz; | ||
382 | char *name; | ||
383 | |||
384 | name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1; | ||
385 | |||
386 | for (n = 0; n < 4; n++) { | ||
387 | led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); | ||
388 | if (!led) { | ||
389 | hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); | ||
390 | goto error_leds; | ||
391 | } | ||
392 | |||
393 | name = (void *)(&led[1]); | ||
394 | snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1); | ||
395 | led->name = name; | ||
396 | led->brightness = 0; | ||
397 | led->max_brightness = 1; | ||
398 | led->brightness_get = buzz_led_get_brightness; | ||
399 | led->brightness_set = buzz_led_set_brightness; | ||
400 | |||
401 | if (led_classdev_register(&hdev->dev, led)) { | ||
402 | hid_err(hdev, "Failed to register LED %d\n", n); | ||
403 | kfree(led); | ||
404 | goto error_leds; | ||
405 | } | ||
406 | |||
407 | buzz->leds[n] = led; | ||
408 | } | ||
409 | } | ||
410 | #endif | ||
411 | |||
412 | return ret; | ||
413 | |||
414 | #ifdef CONFIG_LEDS_CLASS | ||
415 | error_leds: | ||
416 | { | ||
417 | int n; | ||
418 | struct led_classdev *led; | ||
419 | |||
420 | for (n = 0; n < 4; n++) { | ||
421 | led = buzz->leds[n]; | ||
422 | buzz->leds[n] = NULL; | ||
423 | if (!led) | ||
424 | continue; | ||
425 | led_classdev_unregister(led); | ||
426 | kfree(led); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | kfree(drv_data->extra); | ||
431 | drv_data->extra = NULL; | ||
432 | return ret; | ||
433 | #endif | ||
434 | } | ||
435 | |||
436 | static void buzz_remove(struct hid_device *hdev) | ||
437 | { | ||
438 | struct sony_sc *drv_data; | ||
439 | struct buzz_extra *buzz; | ||
440 | |||
441 | drv_data = hid_get_drvdata(hdev); | ||
442 | BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); | ||
443 | |||
444 | buzz = drv_data->extra; | ||
445 | |||
446 | #ifdef CONFIG_LEDS_CLASS | ||
447 | { | ||
448 | int n; | ||
449 | struct led_classdev *led; | ||
450 | |||
451 | for (n = 0; n < 4; n++) { | ||
452 | led = buzz->leds[n]; | ||
453 | buzz->leds[n] = NULL; | ||
454 | if (!led) | ||
455 | continue; | ||
456 | led_classdev_unregister(led); | ||
457 | kfree(led); | ||
458 | } | ||
459 | } | ||
460 | #endif | ||
461 | |||
462 | kfree(drv_data->extra); | ||
463 | drv_data->extra = NULL; | ||
464 | } | ||
465 | |||
195 | static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | 466 | static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) |
196 | { | 467 | { |
197 | int ret; | 468 | int ret; |
198 | unsigned long quirks = id->driver_data; | 469 | unsigned long quirks = id->driver_data; |
199 | struct sony_sc *sc; | 470 | struct sony_sc *sc; |
471 | unsigned int connect_mask = HID_CONNECT_DEFAULT; | ||
200 | 472 | ||
201 | sc = kzalloc(sizeof(*sc), GFP_KERNEL); | 473 | sc = kzalloc(sizeof(*sc), GFP_KERNEL); |
202 | if (sc == NULL) { | 474 | if (sc == NULL) { |
@@ -213,8 +485,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
213 | goto err_free; | 485 | goto err_free; |
214 | } | 486 | } |
215 | 487 | ||
216 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | | 488 | if (sc->quirks & VAIO_RDESC_CONSTANT) |
217 | HID_CONNECT_HIDDEV_FORCE); | 489 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; |
490 | else if (sc->quirks & SIXAXIS_CONTROLLER_USB) | ||
491 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; | ||
492 | else if (sc->quirks & SIXAXIS_CONTROLLER_BT) | ||
493 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; | ||
494 | |||
495 | ret = hid_hw_start(hdev, connect_mask); | ||
218 | if (ret) { | 496 | if (ret) { |
219 | hid_err(hdev, "hw start failed\n"); | 497 | hid_err(hdev, "hw start failed\n"); |
220 | goto err_free; | 498 | goto err_free; |
@@ -226,6 +504,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
226 | } | 504 | } |
227 | else if (sc->quirks & SIXAXIS_CONTROLLER_BT) | 505 | else if (sc->quirks & SIXAXIS_CONTROLLER_BT) |
228 | ret = sixaxis_set_operational_bt(hdev); | 506 | ret = sixaxis_set_operational_bt(hdev); |
507 | else if (sc->quirks & BUZZ_CONTROLLER) | ||
508 | ret = buzz_init(hdev); | ||
229 | else | 509 | else |
230 | ret = 0; | 510 | ret = 0; |
231 | 511 | ||
@@ -242,8 +522,13 @@ err_free: | |||
242 | 522 | ||
243 | static void sony_remove(struct hid_device *hdev) | 523 | static void sony_remove(struct hid_device *hdev) |
244 | { | 524 | { |
525 | struct sony_sc *sc = hid_get_drvdata(hdev); | ||
526 | |||
527 | if (sc->quirks & BUZZ_CONTROLLER) | ||
528 | buzz_remove(hdev); | ||
529 | |||
245 | hid_hw_stop(hdev); | 530 | hid_hw_stop(hdev); |
246 | kfree(hid_get_drvdata(hdev)); | 531 | kfree(sc); |
247 | } | 532 | } |
248 | 533 | ||
249 | static const struct hid_device_id sony_devices[] = { | 534 | static const struct hid_device_id sony_devices[] = { |
@@ -257,17 +542,24 @@ static const struct hid_device_id sony_devices[] = { | |||
257 | .driver_data = VAIO_RDESC_CONSTANT }, | 542 | .driver_data = VAIO_RDESC_CONSTANT }, |
258 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), | 543 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), |
259 | .driver_data = VAIO_RDESC_CONSTANT }, | 544 | .driver_data = VAIO_RDESC_CONSTANT }, |
545 | /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as | ||
546 | * Logitech joystick from the device descriptor. */ | ||
547 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER), | ||
548 | .driver_data = BUZZ_CONTROLLER }, | ||
549 | { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER), | ||
550 | .driver_data = BUZZ_CONTROLLER }, | ||
260 | { } | 551 | { } |
261 | }; | 552 | }; |
262 | MODULE_DEVICE_TABLE(hid, sony_devices); | 553 | MODULE_DEVICE_TABLE(hid, sony_devices); |
263 | 554 | ||
264 | static struct hid_driver sony_driver = { | 555 | static struct hid_driver sony_driver = { |
265 | .name = "sony", | 556 | .name = "sony", |
266 | .id_table = sony_devices, | 557 | .id_table = sony_devices, |
267 | .probe = sony_probe, | 558 | .input_mapping = sony_mapping, |
268 | .remove = sony_remove, | 559 | .probe = sony_probe, |
269 | .report_fixup = sony_report_fixup, | 560 | .remove = sony_remove, |
270 | .raw_event = sony_raw_event | 561 | .report_fixup = sony_report_fixup, |
562 | .raw_event = sony_raw_event | ||
271 | }; | 563 | }; |
272 | module_hid_driver(sony_driver); | 564 | module_hid_driver(sony_driver); |
273 | 565 | ||