diff options
author | Jan Kratochvil <honza@jikos.cz> | 2007-07-18 00:35:40 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor@insightbb.com> | 2007-07-18 00:35:40 -0400 |
commit | 4994cd8dadcf9d484ab3ec19f3c7c7a4e5353c1c (patch) | |
tree | 787afb858825b3e747bf8cbe04419fae80302d0d /drivers/input/joystick/xpad.c | |
parent | cb32da0416b823b7f4b65e7e85d6cba16ca4d1e1 (diff) |
Input: xpad - add support for leds on xbox 360 pad
Export LEDs on Xbox360 pad via led subsystem as a single device in
/sys/class/leds/xpad[0-9]+.
Xbox360 pad has four leds, which form a circle. Unfortunately the leds
can't be controlled independently and can only display a predefined
set of patterns (for example one is turned on wile others are off or
a rotating pattern - 1-2-3-4). To activate a pattern one needs to send
a specific command to the device (see http://www.free60.org/wiki/Gamepad).
Led subsystem allows us to set brightness, but there is nothing like
brightness on this device. So brightness is actually interpreted as
the command (only values between 0 and 14 are accepted).
Signed-off-by: Jan Kratochvil <honza@jikos.cz>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/joystick/xpad.c')
-rw-r--r-- | drivers/input/joystick/xpad.c | 190 |
1 files changed, 148 insertions, 42 deletions
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 244089c52650..28080395899c 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c | |||
@@ -191,13 +191,18 @@ struct usb_xpad { | |||
191 | unsigned char *idata; /* input data */ | 191 | unsigned char *idata; /* input data */ |
192 | dma_addr_t idata_dma; | 192 | dma_addr_t idata_dma; |
193 | 193 | ||
194 | #ifdef CONFIG_JOYSTICK_XPAD_FF | 194 | #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) |
195 | struct urb *irq_out; /* urb for interrupt out report */ | 195 | struct urb *irq_out; /* urb for interrupt out report */ |
196 | unsigned char *odata; /* output data */ | 196 | unsigned char *odata; /* output data */ |
197 | dma_addr_t odata_dma; | 197 | dma_addr_t odata_dma; |
198 | struct mutex odata_mutex; | ||
199 | #endif | ||
200 | |||
201 | #if defined(CONFIG_JOYSTICK_XPAD_LEDS) | ||
202 | struct xpad_led *led; | ||
198 | #endif | 203 | #endif |
199 | 204 | ||
200 | char phys[65]; /* physical device path */ | 205 | char phys[64]; /* physical device path */ |
201 | 206 | ||
202 | int dpad_mapping; /* map d-pad to buttons or to axes */ | 207 | int dpad_mapping; /* map d-pad to buttons or to axes */ |
203 | int xtype; /* type of xbox device */ | 208 | int xtype; /* type of xbox device */ |
@@ -349,7 +354,7 @@ exit: | |||
349 | __FUNCTION__, retval); | 354 | __FUNCTION__, retval); |
350 | } | 355 | } |
351 | 356 | ||
352 | #ifdef CONFIG_JOYSTICK_XPAD_FF | 357 | #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) |
353 | static void xpad_irq_out(struct urb *urb) | 358 | static void xpad_irq_out(struct urb *urb) |
354 | { | 359 | { |
355 | int retval; | 360 | int retval; |
@@ -376,29 +381,7 @@ exit: | |||
376 | __FUNCTION__, retval); | 381 | __FUNCTION__, retval); |
377 | } | 382 | } |
378 | 383 | ||
379 | static int xpad_play_effect(struct input_dev *dev, void *data, | 384 | static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) |
380 | struct ff_effect *effect) | ||
381 | { | ||
382 | struct usb_xpad *xpad = input_get_drvdata(dev); | ||
383 | |||
384 | if (effect->type == FF_RUMBLE) { | ||
385 | __u16 strong = effect->u.rumble.strong_magnitude; | ||
386 | __u16 weak = effect->u.rumble.weak_magnitude; | ||
387 | xpad->odata[0] = 0x00; | ||
388 | xpad->odata[1] = 0x08; | ||
389 | xpad->odata[2] = 0x00; | ||
390 | xpad->odata[3] = strong / 256; | ||
391 | xpad->odata[4] = weak / 256; | ||
392 | xpad->odata[5] = 0x00; | ||
393 | xpad->odata[6] = 0x00; | ||
394 | xpad->odata[7] = 0x00; | ||
395 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) | ||
402 | { | 385 | { |
403 | struct usb_endpoint_descriptor *ep_irq_out; | 386 | struct usb_endpoint_descriptor *ep_irq_out; |
404 | int error = -ENOMEM; | 387 | int error = -ENOMEM; |
@@ -411,6 +394,8 @@ static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) | |||
411 | if (!xpad->odata) | 394 | if (!xpad->odata) |
412 | goto fail1; | 395 | goto fail1; |
413 | 396 | ||
397 | mutex_init(&xpad->odata_mutex); | ||
398 | |||
414 | xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); | 399 | xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); |
415 | if (!xpad->irq_out) | 400 | if (!xpad->irq_out) |
416 | goto fail2; | 401 | goto fail2; |
@@ -423,25 +408,19 @@ static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) | |||
423 | xpad->irq_out->transfer_dma = xpad->odata_dma; | 408 | xpad->irq_out->transfer_dma = xpad->odata_dma; |
424 | xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 409 | xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
425 | 410 | ||
426 | input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); | ||
427 | |||
428 | error = input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); | ||
429 | if (error) | ||
430 | goto fail2; | ||
431 | |||
432 | return 0; | 411 | return 0; |
433 | 412 | ||
434 | fail2: usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); | 413 | fail2: usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); |
435 | fail1: return error; | 414 | fail1: return error; |
436 | } | 415 | } |
437 | 416 | ||
438 | static void xpad_stop_ff(struct usb_xpad *xpad) | 417 | static void xpad_stop_output(struct usb_xpad *xpad) |
439 | { | 418 | { |
440 | if (xpad->xtype == XTYPE_XBOX360) | 419 | if (xpad->xtype == XTYPE_XBOX360) |
441 | usb_kill_urb(xpad->irq_out); | 420 | usb_kill_urb(xpad->irq_out); |
442 | } | 421 | } |
443 | 422 | ||
444 | static void xpad_deinit_ff(struct usb_xpad *xpad) | 423 | static void xpad_deinit_output(struct usb_xpad *xpad) |
445 | { | 424 | { |
446 | if (xpad->xtype == XTYPE_XBOX360) { | 425 | if (xpad->xtype == XTYPE_XBOX360) { |
447 | usb_free_urb(xpad->irq_out); | 426 | usb_free_urb(xpad->irq_out); |
@@ -449,13 +428,130 @@ static void xpad_deinit_ff(struct usb_xpad *xpad) | |||
449 | xpad->odata, xpad->odata_dma); | 428 | xpad->odata, xpad->odata_dma); |
450 | } | 429 | } |
451 | } | 430 | } |
431 | #else | ||
432 | static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } | ||
433 | static void xpad_deinit_output(struct usb_xpad *xpad) {} | ||
434 | static void xpad_stop_output(struct usb_xpad *xpad) {} | ||
435 | #endif | ||
436 | |||
437 | #ifdef CONFIG_JOYSTICK_XPAD_FF | ||
438 | static int xpad_play_effect(struct input_dev *dev, void *data, | ||
439 | struct ff_effect *effect) | ||
440 | { | ||
441 | struct usb_xpad *xpad = input_get_drvdata(dev); | ||
452 | 442 | ||
443 | if (effect->type == FF_RUMBLE) { | ||
444 | __u16 strong = effect->u.rumble.strong_magnitude; | ||
445 | __u16 weak = effect->u.rumble.weak_magnitude; | ||
446 | xpad->odata[0] = 0x00; | ||
447 | xpad->odata[1] = 0x08; | ||
448 | xpad->odata[2] = 0x00; | ||
449 | xpad->odata[3] = strong / 256; | ||
450 | xpad->odata[4] = weak / 256; | ||
451 | xpad->odata[5] = 0x00; | ||
452 | xpad->odata[6] = 0x00; | ||
453 | xpad->odata[7] = 0x00; | ||
454 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | ||
455 | } | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | static int xpad_init_ff(struct usb_xpad *xpad) | ||
461 | { | ||
462 | input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); | ||
463 | |||
464 | return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); | ||
465 | } | ||
466 | |||
467 | #else | ||
468 | static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } | ||
469 | #endif | ||
470 | |||
471 | #if defined(CONFIG_JOYSTICK_XPAD_LEDS) | ||
472 | #include <linux/leds.h> | ||
473 | |||
474 | struct xpad_led { | ||
475 | char name[16]; | ||
476 | struct led_classdev led_cdev; | ||
477 | struct usb_xpad *xpad; | ||
478 | }; | ||
479 | |||
480 | static void xpad_send_led_command(struct usb_xpad *xpad, int command) | ||
481 | { | ||
482 | if (command >= 0 && command < 14) { | ||
483 | mutex_lock(&xpad->odata_mutex); | ||
484 | xpad->odata[0] = 0x01; | ||
485 | xpad->odata[1] = 0x03; | ||
486 | xpad->odata[2] = command; | ||
487 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | ||
488 | mutex_unlock(&xpad->odata_mutex); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | static void xpad_led_set(struct led_classdev *led_cdev, | ||
493 | enum led_brightness value) | ||
494 | { | ||
495 | struct xpad_led *xpad_led = container_of(led_cdev, | ||
496 | struct xpad_led, led_cdev); | ||
497 | |||
498 | xpad_send_led_command(xpad_led->xpad, value); | ||
499 | } | ||
500 | |||
501 | static int xpad_led_probe(struct usb_xpad *xpad) | ||
502 | { | ||
503 | static atomic_t led_seq = ATOMIC_INIT(0); | ||
504 | long led_no; | ||
505 | struct xpad_led *led; | ||
506 | struct led_classdev *led_cdev; | ||
507 | int error; | ||
508 | |||
509 | if (xpad->xtype != XTYPE_XBOX360) | ||
510 | return 0; | ||
511 | |||
512 | xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); | ||
513 | if (!led) | ||
514 | return -ENOMEM; | ||
515 | |||
516 | led_no = (long)atomic_inc_return(&led_seq) - 1; | ||
517 | |||
518 | snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); | ||
519 | led->xpad = xpad; | ||
520 | |||
521 | led_cdev = &led->led_cdev; | ||
522 | led_cdev->name = led->name; | ||
523 | led_cdev->brightness_set = xpad_led_set; | ||
524 | |||
525 | error = led_classdev_register(&xpad->udev->dev, led_cdev); | ||
526 | if (error) { | ||
527 | kfree(led); | ||
528 | xpad->led = NULL; | ||
529 | return error; | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * Light up the segment corresponding to controller number | ||
534 | */ | ||
535 | xpad_send_led_command(xpad, (led_no % 4) + 2); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static void xpad_led_disconnect(struct usb_xpad *xpad) | ||
541 | { | ||
542 | struct xpad_led *xpad_led = xpad->led; | ||
543 | |||
544 | if (xpad_led) { | ||
545 | led_classdev_unregister(&xpad_led->led_cdev); | ||
546 | kfree(xpad_led->name); | ||
547 | } | ||
548 | } | ||
453 | #else | 549 | #else |
454 | static int xpad_init_ff(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } | 550 | static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } |
455 | static void xpad_stop_ff(struct usb_xpad *xpad) { } | 551 | static void xpad_led_disconnect(struct usb_xpad *xpad) { } |
456 | static void xpad_deinit_ff(struct usb_xpad *xpad) { } | ||
457 | #endif | 552 | #endif |
458 | 553 | ||
554 | |||
459 | static int xpad_open(struct input_dev *dev) | 555 | static int xpad_open(struct input_dev *dev) |
460 | { | 556 | { |
461 | struct usb_xpad *xpad = input_get_drvdata(dev); | 557 | struct usb_xpad *xpad = input_get_drvdata(dev); |
@@ -472,7 +568,7 @@ static void xpad_close(struct input_dev *dev) | |||
472 | struct usb_xpad *xpad = input_get_drvdata(dev); | 568 | struct usb_xpad *xpad = input_get_drvdata(dev); |
473 | 569 | ||
474 | usb_kill_urb(xpad->irq_in); | 570 | usb_kill_urb(xpad->irq_in); |
475 | xpad_stop_ff(xpad); | 571 | xpad_stop_output(xpad); |
476 | } | 572 | } |
477 | 573 | ||
478 | static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) | 574 | static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) |
@@ -564,10 +660,18 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
564 | for (i = 0; xpad_abs_pad[i] >= 0; i++) | 660 | for (i = 0; xpad_abs_pad[i] >= 0; i++) |
565 | xpad_set_up_abs(input_dev, xpad_abs_pad[i]); | 661 | xpad_set_up_abs(input_dev, xpad_abs_pad[i]); |
566 | 662 | ||
567 | error = xpad_init_ff(intf, xpad); | 663 | error = xpad_init_output(intf, xpad); |
568 | if (error) | 664 | if (error) |
569 | goto fail2; | 665 | goto fail2; |
570 | 666 | ||
667 | error = xpad_init_ff(xpad); | ||
668 | if (error) | ||
669 | goto fail3; | ||
670 | |||
671 | error = xpad_led_probe(xpad); | ||
672 | if (error) | ||
673 | goto fail3; | ||
674 | |||
571 | ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; | 675 | ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; |
572 | usb_fill_int_urb(xpad->irq_in, udev, | 676 | usb_fill_int_urb(xpad->irq_in, udev, |
573 | usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), | 677 | usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), |
@@ -578,12 +682,13 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id | |||
578 | 682 | ||
579 | error = input_register_device(xpad->dev); | 683 | error = input_register_device(xpad->dev); |
580 | if (error) | 684 | if (error) |
581 | goto fail3; | 685 | goto fail4; |
582 | 686 | ||
583 | usb_set_intfdata(intf, xpad); | 687 | usb_set_intfdata(intf, xpad); |
584 | return 0; | 688 | return 0; |
585 | 689 | ||
586 | fail3: usb_free_urb(xpad->irq_in); | 690 | fail4: usb_free_urb(xpad->irq_in); |
691 | fail3: xpad_deinit_output(xpad); | ||
587 | fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); | 692 | fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); |
588 | fail1: input_free_device(input_dev); | 693 | fail1: input_free_device(input_dev); |
589 | kfree(xpad); | 694 | kfree(xpad); |
@@ -597,8 +702,9 @@ static void xpad_disconnect(struct usb_interface *intf) | |||
597 | 702 | ||
598 | usb_set_intfdata(intf, NULL); | 703 | usb_set_intfdata(intf, NULL); |
599 | if (xpad) { | 704 | if (xpad) { |
705 | xpad_led_disconnect(xpad); | ||
600 | input_unregister_device(xpad->dev); | 706 | input_unregister_device(xpad->dev); |
601 | xpad_deinit_ff(xpad); | 707 | xpad_deinit_output(xpad); |
602 | usb_free_urb(xpad->irq_in); | 708 | usb_free_urb(xpad->irq_in); |
603 | usb_buffer_free(xpad->udev, XPAD_PKT_LEN, | 709 | usb_buffer_free(xpad->udev, XPAD_PKT_LEN, |
604 | xpad->idata, xpad->idata_dma); | 710 | xpad->idata, xpad->idata_dma); |