| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * HID driver for Holtek keyboard | 
|  | 3 | * Copyright (c) 2012 Tom Harwood | 
|  | 4 | */ | 
|  | 5 |  | 
|  | 6 | /* | 
|  | 7 | * This program is free software; you can redistribute it and/or modify it | 
|  | 8 | * under the terms of the GNU General Public License as published by the Free | 
|  | 9 | * Software Foundation; either version 2 of the License, or (at your option) | 
|  | 10 | * any later version. | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <linux/device.h> | 
|  | 14 | #include <linux/hid.h> | 
|  | 15 | #include <linux/module.h> | 
|  | 16 | #include <linux/usb.h> | 
|  | 17 |  | 
|  | 18 | #include "hid-ids.h" | 
|  | 19 | #include "usbhid/usbhid.h" | 
|  | 20 |  | 
|  | 21 | /* Holtek based keyboards (USB ID 04d9:a055) have the following issues: | 
|  | 22 | * - The report descriptor specifies an excessively large number of consumer | 
|  | 23 | *   usages (2^15), which is more than HID_MAX_USAGES. This prevents proper | 
|  | 24 | *   parsing of the report descriptor. | 
|  | 25 | * - The report descriptor reports on caps/scroll/num lock key presses, but | 
|  | 26 | *   doesn't have an LED output usage block. | 
|  | 27 | * | 
|  | 28 | * The replacement descriptor below fixes the number of consumer usages, | 
|  | 29 | * and provides an LED output usage block. LED output events are redirected | 
|  | 30 | * to the boot interface. | 
|  | 31 | */ | 
|  | 32 |  | 
|  | 33 | static __u8 holtek_kbd_rdesc_fixed[] = { | 
|  | 34 | /* Original report descriptor, with reduced number of consumer usages */ | 
|  | 35 | 0x05, 0x01,         /*  Usage Page (Desktop),                         */ | 
|  | 36 | 0x09, 0x80,         /*  Usage (Sys Control),                          */ | 
|  | 37 | 0xA1, 0x01,         /*  Collection (Application),                     */ | 
|  | 38 | 0x85, 0x01,         /*      Report ID (1),                            */ | 
|  | 39 | 0x19, 0x81,         /*      Usage Minimum (Sys Power Down),           */ | 
|  | 40 | 0x29, 0x83,         /*      Usage Maximum (Sys Wake Up),              */ | 
|  | 41 | 0x15, 0x00,         /*      Logical Minimum (0),                      */ | 
|  | 42 | 0x25, 0x01,         /*      Logical Maximum (1),                      */ | 
|  | 43 | 0x95, 0x03,         /*      Report Count (3),                         */ | 
|  | 44 | 0x75, 0x01,         /*      Report Size (1),                          */ | 
|  | 45 | 0x81, 0x02,         /*      Input (Variable),                         */ | 
|  | 46 | 0x95, 0x01,         /*      Report Count (1),                         */ | 
|  | 47 | 0x75, 0x05,         /*      Report Size (5),                          */ | 
|  | 48 | 0x81, 0x01,         /*      Input (Constant),                         */ | 
|  | 49 | 0xC0,               /*  End Collection,                               */ | 
|  | 50 | 0x05, 0x0C,         /*  Usage Page (Consumer),                        */ | 
|  | 51 | 0x09, 0x01,         /*  Usage (Consumer Control),                     */ | 
|  | 52 | 0xA1, 0x01,         /*  Collection (Application),                     */ | 
|  | 53 | 0x85, 0x02,         /*      Report ID (2),                            */ | 
|  | 54 | 0x19, 0x00,         /*      Usage Minimum (00h),                      */ | 
|  | 55 | 0x2A, 0xFF, 0x2F,   /*      Usage Maximum (0x2FFF), previously 0x7FFF */ | 
|  | 56 | 0x15, 0x00,         /*      Logical Minimum (0),                      */ | 
|  | 57 | 0x26, 0xFF, 0x2F,   /*      Logical Maximum (0x2FFF),previously 0x7FFF*/ | 
|  | 58 | 0x95, 0x01,         /*      Report Count (1),                         */ | 
|  | 59 | 0x75, 0x10,         /*      Report Size (16),                         */ | 
|  | 60 | 0x81, 0x00,         /*      Input,                                    */ | 
|  | 61 | 0xC0,               /*  End Collection,                               */ | 
|  | 62 | 0x05, 0x01,         /*  Usage Page (Desktop),                         */ | 
|  | 63 | 0x09, 0x06,         /*  Usage (Keyboard),                             */ | 
|  | 64 | 0xA1, 0x01,         /*  Collection (Application),                     */ | 
|  | 65 | 0x85, 0x03,         /*      Report ID (3),                            */ | 
|  | 66 | 0x95, 0x38,         /*      Report Count (56),                        */ | 
|  | 67 | 0x75, 0x01,         /*      Report Size (1),                          */ | 
|  | 68 | 0x15, 0x00,         /*      Logical Minimum (0),                      */ | 
|  | 69 | 0x25, 0x01,         /*      Logical Maximum (1),                      */ | 
|  | 70 | 0x05, 0x07,         /*      Usage Page (Keyboard),                    */ | 
|  | 71 | 0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol),           */ | 
|  | 72 | 0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),             */ | 
|  | 73 | 0x19, 0x00,         /*      Usage Minimum (None),                     */ | 
|  | 74 | 0x29, 0x2F,         /*      Usage Maximum (KB Lboxbracket And Lbrace),*/ | 
|  | 75 | 0x81, 0x02,         /*      Input (Variable),                         */ | 
|  | 76 | 0xC0,               /*  End Collection,                               */ | 
|  | 77 | 0x05, 0x01,         /*  Usage Page (Desktop),                         */ | 
|  | 78 | 0x09, 0x06,         /*  Usage (Keyboard),                             */ | 
|  | 79 | 0xA1, 0x01,         /*  Collection (Application),                     */ | 
|  | 80 | 0x85, 0x04,         /*      Report ID (4),                            */ | 
|  | 81 | 0x95, 0x38,         /*      Report Count (56),                        */ | 
|  | 82 | 0x75, 0x01,         /*      Report Size (1),                          */ | 
|  | 83 | 0x15, 0x00,         /*      Logical Minimum (0),                      */ | 
|  | 84 | 0x25, 0x01,         /*      Logical Maximum (1),                      */ | 
|  | 85 | 0x05, 0x07,         /*      Usage Page (Keyboard),                    */ | 
|  | 86 | 0x19, 0x30,         /*      Usage Minimum (KB Rboxbracket And Rbrace),*/ | 
|  | 87 | 0x29, 0x67,         /*      Usage Maximum (KP Equals),                */ | 
|  | 88 | 0x81, 0x02,         /*      Input (Variable),                         */ | 
|  | 89 | 0xC0,               /*  End Collection                                */ | 
|  | 90 |  | 
|  | 91 | /* LED usage for the boot protocol interface */ | 
|  | 92 | 0x05, 0x01,         /*  Usage Page (Desktop),                         */ | 
|  | 93 | 0x09, 0x06,         /*  Usage (Keyboard),                             */ | 
|  | 94 | 0xA1, 0x01,         /*  Collection (Application),                     */ | 
|  | 95 | 0x05, 0x08,         /*      Usage Page (LED),                         */ | 
|  | 96 | 0x19, 0x01,         /*      Usage Minimum (01h),                      */ | 
|  | 97 | 0x29, 0x03,         /*      Usage Maximum (03h),                      */ | 
|  | 98 | 0x15, 0x00,         /*      Logical Minimum (0),                      */ | 
|  | 99 | 0x25, 0x01,         /*      Logical Maximum (1),                      */ | 
|  | 100 | 0x75, 0x01,         /*      Report Size (1),                          */ | 
|  | 101 | 0x95, 0x03,         /*      Report Count (3),                         */ | 
|  | 102 | 0x91, 0x02,         /*      Output (Variable),                        */ | 
|  | 103 | 0x95, 0x05,         /*      Report Count (5),                         */ | 
|  | 104 | 0x91, 0x01,         /*      Output (Constant),                        */ | 
|  | 105 | 0xC0,               /*  End Collection                                */ | 
|  | 106 | }; | 
|  | 107 |  | 
|  | 108 | static __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 
|  | 109 | unsigned int *rsize) | 
|  | 110 | { | 
|  | 111 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | 
|  | 112 |  | 
|  | 113 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { | 
|  | 114 | rdesc = holtek_kbd_rdesc_fixed; | 
|  | 115 | *rsize = sizeof(holtek_kbd_rdesc_fixed); | 
|  | 116 | } | 
|  | 117 | return rdesc; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type, | 
|  | 121 | unsigned int code, | 
|  | 122 | int value) | 
|  | 123 | { | 
|  | 124 | struct hid_device *hid = input_get_drvdata(dev); | 
|  | 125 | struct usb_device *usb_dev = hid_to_usb_dev(hid); | 
|  | 126 |  | 
|  | 127 | /* Locate the boot interface, to receive the LED change events */ | 
|  | 128 | struct usb_interface *boot_interface = usb_ifnum_to_if(usb_dev, 0); | 
|  | 129 | struct hid_device *boot_hid; | 
|  | 130 | struct hid_input *boot_hid_input; | 
|  | 131 |  | 
|  | 132 | if (unlikely(boot_interface == NULL)) | 
|  | 133 | return -ENODEV; | 
|  | 134 |  | 
|  | 135 | boot_hid = usb_get_intfdata(boot_interface); | 
|  | 136 | boot_hid_input = list_first_entry(&boot_hid->inputs, | 
|  | 137 | struct hid_input, list); | 
|  | 138 |  | 
|  | 139 | return boot_hid_input->input->event(boot_hid_input->input, type, code, | 
|  | 140 | value); | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | static int holtek_kbd_probe(struct hid_device *hdev, | 
|  | 144 | const struct hid_device_id *id) | 
|  | 145 | { | 
|  | 146 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | 
|  | 147 | int ret = hid_parse(hdev); | 
|  | 148 |  | 
|  | 149 | if (!ret) | 
|  | 150 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | 
|  | 151 |  | 
|  | 152 | if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) { | 
|  | 153 | struct hid_input *hidinput; | 
|  | 154 | list_for_each_entry(hidinput, &hdev->inputs, list) { | 
|  | 155 | hidinput->input->event = holtek_kbd_input_event; | 
|  | 156 | } | 
|  | 157 | } | 
|  | 158 |  | 
|  | 159 | return ret; | 
|  | 160 | } | 
|  | 161 |  | 
|  | 162 | static const struct hid_device_id holtek_kbd_devices[] = { | 
|  | 163 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, | 
|  | 164 | USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, | 
|  | 165 | { } | 
|  | 166 | }; | 
|  | 167 | MODULE_DEVICE_TABLE(hid, holtek_kbd_devices); | 
|  | 168 |  | 
|  | 169 | static struct hid_driver holtek_kbd_driver = { | 
|  | 170 | .name = "holtek_kbd", | 
|  | 171 | .id_table = holtek_kbd_devices, | 
|  | 172 | .report_fixup = holtek_kbd_report_fixup, | 
|  | 173 | .probe = holtek_kbd_probe | 
|  | 174 | }; | 
|  | 175 | module_hid_driver(holtek_kbd_driver); | 
|  | 176 |  | 
|  | 177 | MODULE_LICENSE("GPL"); |