linux/drivers/hid/hid-holtek-kbd.c
<<
>>
Prefs
   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
  33static __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
 108static __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
 120static 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
 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);
 133
 134        return boot_hid_input->input->event(boot_hid_input->input, type, code,
 135                        value);
 136}
 137
 138static int holtek_kbd_probe(struct hid_device *hdev,
 139                const struct hid_device_id *id)
 140{
 141        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 142        int ret = hid_parse(hdev);
 143
 144        if (!ret)
 145                ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 146
 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;
 151                }
 152        }
 153
 154        return ret;
 155}
 156
 157static const struct hid_device_id holtek_kbd_devices[] = {
 158        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
 159                        USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
 160        { }
 161};
 162MODULE_DEVICE_TABLE(hid, holtek_kbd_devices);
 163
 164static 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
 169};
 170
 171static int __init holtek_kbd_init(void)
 172{
 173        return hid_register_driver(&holtek_kbd_driver);
 174}
 175
 176static void __exit holtek_kbd_exit(void)
 177{
 178        hid_unregister_driver(&holtek_kbd_driver);
 179}
 180
 181module_exit(holtek_kbd_exit);
 182module_init(holtek_kbd_init);
 183MODULE_LICENSE("GPL");
 184