|
17 | 17 | #include <linux/bitfield.h> |
18 | 18 | #include <linux/hid.h> |
19 | 19 | #include <linux/init.h> |
| 20 | +#include <linux/input.h> |
20 | 21 | #include <linux/math64.h> |
21 | 22 | #include <linux/slab.h> |
22 | 23 | #include <linux/usb.h> |
@@ -531,6 +532,263 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) |
531 | 532 | &snd_emu0204_control, NULL); |
532 | 533 | } |
533 | 534 |
|
| 535 | +/* |
| 536 | + * Sony DualSense controller (PS5) jack detection |
| 537 | + * |
| 538 | + * Since this is an UAC 1 device, it doesn't support jack detection. |
| 539 | + * However, the controller hid-playstation driver reports HP & MIC |
| 540 | + * insert events through a dedicated input device. |
| 541 | + */ |
| 542 | + |
| 543 | +#define SND_DUALSENSE_JACK_OUT_TERM_ID 3 |
| 544 | +#define SND_DUALSENSE_JACK_IN_TERM_ID 4 |
| 545 | + |
| 546 | +struct dualsense_mixer_elem_info { |
| 547 | + struct usb_mixer_elem_info info; |
| 548 | + struct input_handler ih; |
| 549 | + struct input_device_id id_table[2]; |
| 550 | + bool connected; |
| 551 | +}; |
| 552 | + |
| 553 | +static void snd_dualsense_ih_event(struct input_handle *handle, |
| 554 | + unsigned int type, unsigned int code, |
| 555 | + int value) |
| 556 | +{ |
| 557 | + struct dualsense_mixer_elem_info *mei; |
| 558 | + struct usb_mixer_elem_list *me; |
| 559 | + |
| 560 | + if (type != EV_SW) |
| 561 | + return; |
| 562 | + |
| 563 | + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); |
| 564 | + me = &mei->info.head; |
| 565 | + |
| 566 | + if ((me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && code == SW_HEADPHONE_INSERT) || |
| 567 | + (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && code == SW_MICROPHONE_INSERT)) { |
| 568 | + mei->connected = !!value; |
| 569 | + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
| 570 | + &me->kctl->id); |
| 571 | + } |
| 572 | +} |
| 573 | + |
| 574 | +static bool snd_dualsense_ih_match(struct input_handler *handler, |
| 575 | + struct input_dev *dev) |
| 576 | +{ |
| 577 | + struct dualsense_mixer_elem_info *mei; |
| 578 | + struct usb_device *snd_dev; |
| 579 | + char *input_dev_path, *usb_dev_path; |
| 580 | + size_t usb_dev_path_len; |
| 581 | + bool match = false; |
| 582 | + |
| 583 | + mei = container_of(handler, struct dualsense_mixer_elem_info, ih); |
| 584 | + snd_dev = mei->info.head.mixer->chip->dev; |
| 585 | + |
| 586 | + input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); |
| 587 | + if (!input_dev_path) { |
| 588 | + dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); |
| 589 | + return false; |
| 590 | + } |
| 591 | + |
| 592 | + usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); |
| 593 | + if (!usb_dev_path) { |
| 594 | + dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); |
| 595 | + goto free_paths; |
| 596 | + } |
| 597 | + |
| 598 | + /* |
| 599 | + * Ensure the VID:PID matched input device supposedly owned by the |
| 600 | + * hid-playstation driver belongs to the actual hardware handled by |
| 601 | + * the current USB audio device, which implies input_dev_path being |
| 602 | + * a subpath of usb_dev_path. |
| 603 | + * |
| 604 | + * This verification is necessary when there is more than one identical |
| 605 | + * controller attached to the host system. |
| 606 | + */ |
| 607 | + usb_dev_path_len = strlen(usb_dev_path); |
| 608 | + if (usb_dev_path_len >= strlen(input_dev_path)) |
| 609 | + goto free_paths; |
| 610 | + |
| 611 | + usb_dev_path[usb_dev_path_len] = '/'; |
| 612 | + match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); |
| 613 | + |
| 614 | +free_paths: |
| 615 | + kfree(input_dev_path); |
| 616 | + kfree(usb_dev_path); |
| 617 | + |
| 618 | + return match; |
| 619 | +} |
| 620 | + |
| 621 | +static int snd_dualsense_ih_connect(struct input_handler *handler, |
| 622 | + struct input_dev *dev, |
| 623 | + const struct input_device_id *id) |
| 624 | +{ |
| 625 | + struct input_handle *handle; |
| 626 | + int err; |
| 627 | + |
| 628 | + handle = kzalloc(sizeof(*handle), GFP_KERNEL); |
| 629 | + if (!handle) |
| 630 | + return -ENOMEM; |
| 631 | + |
| 632 | + handle->dev = dev; |
| 633 | + handle->handler = handler; |
| 634 | + handle->name = handler->name; |
| 635 | + |
| 636 | + err = input_register_handle(handle); |
| 637 | + if (err) |
| 638 | + goto err_free; |
| 639 | + |
| 640 | + err = input_open_device(handle); |
| 641 | + if (err) |
| 642 | + goto err_unregister; |
| 643 | + |
| 644 | + return 0; |
| 645 | + |
| 646 | +err_unregister: |
| 647 | + input_unregister_handle(handle); |
| 648 | +err_free: |
| 649 | + kfree(handle); |
| 650 | + return err; |
| 651 | +} |
| 652 | + |
| 653 | +static void snd_dualsense_ih_disconnect(struct input_handle *handle) |
| 654 | +{ |
| 655 | + input_close_device(handle); |
| 656 | + input_unregister_handle(handle); |
| 657 | + kfree(handle); |
| 658 | +} |
| 659 | + |
| 660 | +static void snd_dualsense_ih_start(struct input_handle *handle) |
| 661 | +{ |
| 662 | + struct dualsense_mixer_elem_info *mei; |
| 663 | + struct usb_mixer_elem_list *me; |
| 664 | + int status = -1; |
| 665 | + |
| 666 | + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); |
| 667 | + me = &mei->info.head; |
| 668 | + |
| 669 | + if (me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && |
| 670 | + test_bit(SW_HEADPHONE_INSERT, handle->dev->swbit)) |
| 671 | + status = test_bit(SW_HEADPHONE_INSERT, handle->dev->sw); |
| 672 | + else if (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && |
| 673 | + test_bit(SW_MICROPHONE_INSERT, handle->dev->swbit)) |
| 674 | + status = test_bit(SW_MICROPHONE_INSERT, handle->dev->sw); |
| 675 | + |
| 676 | + if (status >= 0) { |
| 677 | + mei->connected = !!status; |
| 678 | + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
| 679 | + &me->kctl->id); |
| 680 | + } |
| 681 | +} |
| 682 | + |
| 683 | +static int snd_dualsense_jack_get(struct snd_kcontrol *kctl, |
| 684 | + struct snd_ctl_elem_value *ucontrol) |
| 685 | +{ |
| 686 | + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); |
| 687 | + |
| 688 | + ucontrol->value.integer.value[0] = mei->connected; |
| 689 | + |
| 690 | + return 0; |
| 691 | +} |
| 692 | + |
| 693 | +static const struct snd_kcontrol_new snd_dualsense_jack_control = { |
| 694 | + .iface = SNDRV_CTL_ELEM_IFACE_CARD, |
| 695 | + .access = SNDRV_CTL_ELEM_ACCESS_READ, |
| 696 | + .info = snd_ctl_boolean_mono_info, |
| 697 | + .get = snd_dualsense_jack_get, |
| 698 | +}; |
| 699 | + |
| 700 | +static int snd_dualsense_resume_jack(struct usb_mixer_elem_list *list) |
| 701 | +{ |
| 702 | + snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, |
| 703 | + &list->kctl->id); |
| 704 | + return 0; |
| 705 | +} |
| 706 | + |
| 707 | +static void snd_dualsense_mixer_elem_free(struct snd_kcontrol *kctl) |
| 708 | +{ |
| 709 | + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); |
| 710 | + |
| 711 | + if (mei->ih.event) |
| 712 | + input_unregister_handler(&mei->ih); |
| 713 | + |
| 714 | + snd_usb_mixer_elem_free(kctl); |
| 715 | +} |
| 716 | + |
| 717 | +static int snd_dualsense_jack_create(struct usb_mixer_interface *mixer, |
| 718 | + const char *name, bool is_output) |
| 719 | +{ |
| 720 | + struct dualsense_mixer_elem_info *mei; |
| 721 | + struct input_device_id *idev_id; |
| 722 | + struct snd_kcontrol *kctl; |
| 723 | + int err; |
| 724 | + |
| 725 | + mei = kzalloc(sizeof(*mei), GFP_KERNEL); |
| 726 | + if (!mei) |
| 727 | + return -ENOMEM; |
| 728 | + |
| 729 | + snd_usb_mixer_elem_init_std(&mei->info.head, mixer, |
| 730 | + is_output ? SND_DUALSENSE_JACK_OUT_TERM_ID : |
| 731 | + SND_DUALSENSE_JACK_IN_TERM_ID); |
| 732 | + |
| 733 | + mei->info.head.resume = snd_dualsense_resume_jack; |
| 734 | + mei->info.val_type = USB_MIXER_BOOLEAN; |
| 735 | + mei->info.channels = 1; |
| 736 | + mei->info.min = 0; |
| 737 | + mei->info.max = 1; |
| 738 | + |
| 739 | + kctl = snd_ctl_new1(&snd_dualsense_jack_control, mei); |
| 740 | + if (!kctl) { |
| 741 | + kfree(mei); |
| 742 | + return -ENOMEM; |
| 743 | + } |
| 744 | + |
| 745 | + strscpy(kctl->id.name, name, sizeof(kctl->id.name)); |
| 746 | + kctl->private_free = snd_dualsense_mixer_elem_free; |
| 747 | + |
| 748 | + err = snd_usb_mixer_add_control(&mei->info.head, kctl); |
| 749 | + if (err) |
| 750 | + return err; |
| 751 | + |
| 752 | + idev_id = &mei->id_table[0]; |
| 753 | + idev_id->flags = INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT | |
| 754 | + INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT; |
| 755 | + idev_id->vendor = USB_ID_VENDOR(mixer->chip->usb_id); |
| 756 | + idev_id->product = USB_ID_PRODUCT(mixer->chip->usb_id); |
| 757 | + idev_id->evbit[BIT_WORD(EV_SW)] = BIT_MASK(EV_SW); |
| 758 | + if (is_output) |
| 759 | + idev_id->swbit[BIT_WORD(SW_HEADPHONE_INSERT)] = BIT_MASK(SW_HEADPHONE_INSERT); |
| 760 | + else |
| 761 | + idev_id->swbit[BIT_WORD(SW_MICROPHONE_INSERT)] = BIT_MASK(SW_MICROPHONE_INSERT); |
| 762 | + |
| 763 | + mei->ih.event = snd_dualsense_ih_event; |
| 764 | + mei->ih.match = snd_dualsense_ih_match; |
| 765 | + mei->ih.connect = snd_dualsense_ih_connect, |
| 766 | + mei->ih.disconnect = snd_dualsense_ih_disconnect, |
| 767 | + mei->ih.start = snd_dualsense_ih_start, |
| 768 | + mei->ih.name = name; |
| 769 | + mei->ih.id_table = mei->id_table; |
| 770 | + |
| 771 | + err = input_register_handler(&mei->ih); |
| 772 | + if (err) { |
| 773 | + dev_warn(&mixer->chip->dev->dev, |
| 774 | + "Could not register input handler: %d\n", err); |
| 775 | + mei->ih.event = NULL; |
| 776 | + } |
| 777 | + |
| 778 | + return 0; |
| 779 | +} |
| 780 | + |
| 781 | +static int snd_dualsense_controls_create(struct usb_mixer_interface *mixer) |
| 782 | +{ |
| 783 | + int err; |
| 784 | + |
| 785 | + err = snd_dualsense_jack_create(mixer, "Headphone Jack", true); |
| 786 | + if (err < 0) |
| 787 | + return err; |
| 788 | + |
| 789 | + return snd_dualsense_jack_create(mixer, "Headset Mic Jack", false); |
| 790 | +} |
| 791 | + |
534 | 792 | /* ASUS Xonar U1 / U3 controls */ |
535 | 793 |
|
536 | 794 | static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, |
@@ -4073,6 +4331,11 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) |
4073 | 4331 | err = snd_emu0204_controls_create(mixer); |
4074 | 4332 | break; |
4075 | 4333 |
|
| 4334 | + case USB_ID(0x054c, 0x0ce6): /* Sony DualSense controller (PS5) */ |
| 4335 | + case USB_ID(0x054c, 0x0df2): /* Sony DualSense Edge controller (PS5) */ |
| 4336 | + err = snd_dualsense_controls_create(mixer); |
| 4337 | + break; |
| 4338 | + |
4076 | 4339 | case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ |
4077 | 4340 | case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */ |
4078 | 4341 | err = snd_c400_create_mixer(mixer); |
|
0 commit comments