aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/usbhid
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2007-06-19 08:09:14 -0400
committerJiri Kosina <jkosina@suse.cz>2007-07-09 08:13:34 -0400
commitea9a4a8b0e5a34eca6613e39d21be879d92ecff5 (patch)
treef9e816b5a7719e0528b2c0426aeac6d32d7b422f /drivers/hid/usbhid
parentb8e98f1c47b743e56f8ebe21006199cefd577ce5 (diff)
HID: separate quirks for report descriptor fixup
Lately there have been quite a lot of bug reports against broken devices which require us to fix their report descriptor in the runtime, before it is passed to the HID parser. Those devices have eaten quite an amount of our quirks space, which isn't particularly necessary - the quirks are not needed after the report descriptor is parsed, and they just consume bits. Therefore this patch separates the quirks for report descriptor fixup, and moves their handling into separate code. The quirks are then forgotten as soon as the report descriptor has been parsed. Module parameter 'rdesc_quirks' is introduced to be able to modify these quirks in runtime in a similar way to 'quirks' parameter for ordinary HID quirks. Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/usbhid')
-rw-r--r--drivers/hid/usbhid/hid-core.c89
-rw-r--r--drivers/hid/usbhid/hid-quirks.c155
2 files changed, 153 insertions, 91 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ef7b881aab3a..3efc3734cec2 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -60,6 +60,12 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
60 " quirks=vendorID:productID:quirks" 60 " quirks=vendorID:productID:quirks"
61 " where vendorID, productID, and quirks are all in" 61 " where vendorID, productID, and quirks are all in"
62 " 0x-prefixed hex"); 62 " 0x-prefixed hex");
63static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
64module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444);
65MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying "
66 " rdesc_quirks=vendorID:productID:rdesc_quirks"
67 " where vendorID, productID, and rdesc_quirks are all in"
68 " 0x-prefixed hex");
63/* 69/*
64 * Input submission and I/O error handler. 70 * Input submission and I/O error handler.
65 */ 71 */
@@ -633,20 +639,6 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
633} 639}
634 640
635/* 641/*
636 * Cherry Cymotion keyboard have an invalid HID report descriptor,
637 * that needs fixing before we can parse it.
638 */
639
640static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
641{
642 if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
643 info("Fixing up Cherry Cymotion report descriptor");
644 rdesc[11] = rdesc[16] = 0xff;
645 rdesc[12] = rdesc[17] = 0x03;
646 }
647}
648
649/*
650 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller 642 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
651 * to "operational". Without this, the ps3 controller will not report any 643 * to "operational". Without this, the ps3 controller will not report any
652 * events. 644 * events.
@@ -672,61 +664,6 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
672 kfree(buf); 664 kfree(buf);
673} 665}
674 666
675/*
676 * Certain Logitech keyboards send in report #3 keys which are far
677 * above the logical maximum described in descriptor. This extends
678 * the original value of 0x28c of logical maximum to 0x104d
679 */
680static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
681{
682 if (rsize >= 90 && rdesc[83] == 0x26
683 && rdesc[84] == 0x8c
684 && rdesc[85] == 0x02) {
685 info("Fixing up Logitech keyboard report descriptor");
686 rdesc[84] = rdesc[89] = 0x4d;
687 rdesc[85] = rdesc[90] = 0x10;
688 }
689}
690
691/* Petalynx Maxter Remote has maximum for consumer page set too low */
692static void hid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize)
693{
694 if (rsize >= 60 && rdesc[39] == 0x2a
695 && rdesc[40] == 0xf5
696 && rdesc[41] == 0x00
697 && rdesc[59] == 0x26
698 && rdesc[60] == 0xf9
699 && rdesc[61] == 0x00) {
700 info("Fixing up Petalynx Maxter Remote report descriptor");
701 rdesc[60] = 0xfa;
702 rdesc[40] = 0xfa;
703 }
704}
705
706/*
707 * Some USB barcode readers from cypress have usage min and usage max in
708 * the wrong order
709 */
710static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
711{
712 short fixed = 0;
713 int i;
714
715 for (i = 0; i < rsize - 4; i++) {
716 if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) {
717 unsigned char tmp;
718
719 rdesc[i] = 0x19; rdesc[i+2] = 0x29;
720 tmp = rdesc[i+3];
721 rdesc[i+3] = rdesc[i+1];
722 rdesc[i+1] = tmp;
723 }
724 }
725
726 if (fixed)
727 info("Fixing up Cypress report descriptor");
728}
729
730static struct hid_device *usb_hid_configure(struct usb_interface *intf) 667static struct hid_device *usb_hid_configure(struct usb_interface *intf)
731{ 668{
732 struct usb_host_interface *interface = intf->cur_altsetting; 669 struct usb_host_interface *interface = intf->cur_altsetting;
@@ -787,17 +724,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
787 return NULL; 724 return NULL;
788 } 725 }
789 726
790 if ((quirks & HID_QUIRK_CYMOTION)) 727 usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
791 hid_fixup_cymotion_descriptor(rdesc, rsize); 728 le16_to_cpu(dev->descriptor.idProduct), rdesc,
792 729 rsize, rdesc_quirks_param);
793 if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR)
794 hid_fixup_logitech_descriptor(rdesc, rsize);
795
796 if (quirks & HID_QUIRK_SWAPPED_MIN_MAX)
797 hid_fixup_cypress_descriptor(rdesc, rsize);
798
799 if (quirks & HID_QUIRK_PETALYNX_DESCRIPTOR)
800 hid_fixup_petalynx_descriptor(rdesc, rsize);
801 730
802#ifdef CONFIG_HID_DEBUG 731#ifdef CONFIG_HID_DEBUG
803 printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); 732 printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index a78f5187b6ef..a75c236fcafc 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -299,8 +299,6 @@ static const struct hid_blacklist {
299 { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, 299 { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
300 { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, 300 { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
301 301
302 { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
303
304 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, 302 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
305 303
306 { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, 304 { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
@@ -424,17 +422,11 @@ static const struct hid_blacklist {
424 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, 422 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
425 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, 423 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
426 424
427 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
428 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
429 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR },
430
431 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, 425 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
432 426
433 { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, 427 { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
434 { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, 428 { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
435 429
436 { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_PETALYNX_DESCRIPTOR | HID_QUIRK_NOGET },
437
438 { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, 430 { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER },
439 431
440 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, 432 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
@@ -443,6 +435,7 @@ static const struct hid_blacklist {
443 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, 435 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
444 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, 436 { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
445 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, 437 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET },
438 { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET },
446 { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, 439 { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
447 { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, 440 { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
448 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, 441 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
@@ -466,8 +459,26 @@ static const struct hid_blacklist {
466 459
467 { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, 460 { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
468 461
469 { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_SWAPPED_MIN_MAX }, 462 { 0, 0 }
470 { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_SWAPPED_MIN_MAX }, 463};
464
465/* Quirks for devices which require report descriptor fixup go here */
466static const struct hid_rdesc_blacklist {
467 __u16 idVendor;
468 __u16 idProduct;
469 __u32 quirks;
470} hid_rdesc_blacklist[] = {
471
472 { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION },
473
474 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
475 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
476 { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
477
478 { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX },
479
480 { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
481 { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
471 482
472 { 0, 0 } 483 { 0, 0 }
473}; 484};
@@ -576,7 +587,6 @@ int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
576 return 0; 587 return 0;
577} 588}
578 589
579
580/** 590/**
581 * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory 591 * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
582 * 592 *
@@ -709,3 +719,126 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
709 return quirks; 719 return quirks;
710} 720}
711 721
722/*
723 * Cherry Cymotion keyboard have an invalid HID report descriptor,
724 * that needs fixing before we can parse it.
725 */
726static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize)
727{
728 if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
729 printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n");
730 rdesc[11] = rdesc[16] = 0xff;
731 rdesc[12] = rdesc[17] = 0x03;
732 }
733}
734
735
736/*
737 * Certain Logitech keyboards send in report #3 keys which are far
738 * above the logical maximum described in descriptor. This extends
739 * the original value of 0x28c of logical maximum to 0x104d
740 */
741static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
742{
743 if (rsize >= 90 && rdesc[83] == 0x26
744 && rdesc[84] == 0x8c
745 && rdesc[85] == 0x02) {
746 printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n");
747 rdesc[84] = rdesc[89] = 0x4d;
748 rdesc[85] = rdesc[90] = 0x10;
749 }
750}
751
752/* Petalynx Maxter Remote has maximum for consumer page set too low */
753static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize)
754{
755 if (rsize >= 60 && rdesc[39] == 0x2a
756 && rdesc[40] == 0xf5
757 && rdesc[41] == 0x00
758 && rdesc[59] == 0x26
759 && rdesc[60] == 0xf9
760 && rdesc[61] == 0x00) {
761 printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n");
762 rdesc[60] = 0xfa;
763 rdesc[40] = 0xfa;
764 }
765}
766
767/*
768 * Some USB barcode readers from cypress have usage min and usage max in
769 * the wrong order
770 */
771static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
772{
773 short fixed = 0;
774 int i;
775
776 for (i = 0; i < rsize - 4; i++) {
777 if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) {
778 unsigned char tmp;
779
780 rdesc[i] = 0x19; rdesc[i+2] = 0x29;
781 tmp = rdesc[i+3];
782 rdesc[i+3] = rdesc[i+1];
783 rdesc[i+1] = tmp;
784 }
785 }
786
787 if (fixed)
788 printk(KERN_INFO "Fixing up Cypress report descriptor\n");
789}
790
791
792static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
793{
794 if ((quirks & HID_QUIRK_RDESC_CYMOTION))
795 usbhid_fixup_cymotion_descriptor(rdesc, rsize);
796
797 if (quirks & HID_QUIRK_RDESC_LOGITECH)
798 usbhid_fixup_logitech_descriptor(rdesc, rsize);
799
800 if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX)
801 usbhid_fixup_cypress_descriptor(rdesc, rsize);
802
803 if (quirks & HID_QUIRK_RDESC_PETALYNX)
804 usbhid_fixup_petalynx_descriptor(rdesc, rsize);
805}
806
807/**
808 * usbhid_fixup_report_descriptor: check if report descriptor needs fixup
809 *
810 * Description:
811 * Walks the hid_rdesc_blacklist[] array and checks whether the device
812 * is known to have broken report descriptor that needs to be fixed up
813 * prior to entering the HID parser
814 *
815 * Returns: nothing
816 */
817void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct,
818 char *rdesc, unsigned rsize, char **quirks_param)
819{
820 int n, m;
821 u16 paramVendor, paramProduct;
822 u32 quirks;
823
824 /* static rdesc quirk entries */
825 for (n = 0; hid_rdesc_blacklist[n].idVendor; n++)
826 if (hid_rdesc_blacklist[n].idVendor == idVendor &&
827 hid_rdesc_blacklist[n].idProduct == idProduct)
828 __usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks,
829 rdesc, rsize);
830
831 /* runtime rdesc quirk entries handling */
832 for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
833 m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
834 &paramVendor, &paramProduct, &quirks);
835
836 if (m != 3)
837 printk(KERN_WARNING
838 "Could not parse HID quirk module param %s\n",
839 quirks_param[n]);
840 else if (paramVendor == idVendor && paramProduct == idProduct)
841 __usbhid_fixup_report_descriptor(quirks, rdesc, rsize);
842 }
843
844}