2 * HID driver for Holtek keyboard
3 * Copyright (c) 2012 Tom Harwood
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)
13 #include <linux/device.h>
14 #include <linux/hid.h>
15 #include <linux/module.h>
16 #include <linux/usb.h>
19 #include "usbhid/usbhid.h"
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.
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.
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 */
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 */
108 static __u8
*holtek_kbd_report_fixup(struct hid_device
*hdev
, __u8
*rdesc
,
111 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
113 if (intf
->cur_altsetting
->desc
.bInterfaceNumber
== 1) {
114 rdesc
= holtek_kbd_rdesc_fixed
;
115 *rsize
= sizeof(holtek_kbd_rdesc_fixed
);
120 static int holtek_kbd_input_event(struct input_dev
*dev
, unsigned int type
,
124 struct hid_device
*hid
= input_get_drvdata(dev
);
125 struct usb_device
*usb_dev
= hid_to_usb_dev(hid
);
127 /* Locate the boot interface, to receive the LED change events */
128 struct usb_interface
*boot_interface
= usb_ifnum_to_if(usb_dev
, 0);
130 struct hid_device
*boot_hid
= usb_get_intfdata(boot_interface
);
131 struct hid_input
*boot_hid_input
= list_first_entry(&boot_hid
->inputs
,
132 struct hid_input
, list
);
134 return boot_hid_input
->input
->event(boot_hid_input
->input
, type
, code
,
138 static int holtek_kbd_probe(struct hid_device
*hdev
,
139 const struct hid_device_id
*id
)
141 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
142 int ret
= hid_parse(hdev
);
145 ret
= hid_hw_start(hdev
, HID_CONNECT_DEFAULT
);
147 if (!ret
&& intf
->cur_altsetting
->desc
.bInterfaceNumber
== 1) {
148 struct hid_input
*hidinput
;
149 list_for_each_entry(hidinput
, &hdev
->inputs
, list
) {
150 hidinput
->input
->event
= holtek_kbd_input_event
;
157 static const struct hid_device_id holtek_kbd_devices
[] = {
158 { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT
,
159 USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD
) },
162 MODULE_DEVICE_TABLE(hid
, holtek_kbd_devices
);
164 static struct hid_driver holtek_kbd_driver
= {
165 .name
= "holtek_kbd",
166 .id_table
= holtek_kbd_devices
,
167 .report_fixup
= holtek_kbd_report_fixup
,
168 .probe
= holtek_kbd_probe
171 static int __init
holtek_kbd_init(void)
173 return hid_register_driver(&holtek_kbd_driver
);
176 static void __exit
holtek_kbd_exit(void)
178 hid_unregister_driver(&holtek_kbd_driver
);
181 module_exit(holtek_kbd_exit
);
182 module_init(holtek_kbd_init
);
183 MODULE_LICENSE("GPL");