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 | |
| 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>
| -rw-r--r-- | drivers/input/joystick/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/input/joystick/xpad.c | 190 |
2 files changed, 155 insertions, 42 deletions
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 12db72d83ea0..e2abe18e575d 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig | |||
| @@ -275,4 +275,11 @@ config JOYSTICK_XPAD_FF | |||
| 275 | ---help--- | 275 | ---help--- |
| 276 | Say Y here if you want to take advantage of xbox 360 rumble features. | 276 | Say Y here if you want to take advantage of xbox 360 rumble features. |
| 277 | 277 | ||
| 278 | config JOYSTICK_XPAD_LEDS | ||
| 279 | bool "LED Support for Xbox360 controller 'BigX' LED" | ||
| 280 | depends on LEDS_CLASS && JOYSTICK_XPAD | ||
| 281 | ---help--- | ||
| 282 | This option enables support for the LED which surrounds the Big X on | ||
| 283 | XBox 360 controller. | ||
| 284 | |||
| 278 | endif | 285 | endif |
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); |
