From c1dcad2d32d0252e8a3023d20311b52a187ecda3 Mon Sep 17 00:00:00 2001 From: Bernhard Seibold Date: Wed, 15 Feb 2012 13:40:43 +0100 Subject: HID: Driver for Lenovo Keyboard with Trackpoint This driver for the "Lenovo ThinkPad USB Keyboard with Trackpoint" supports setting various device attributes, controlling mute and microphone mute LEDs and enables use of the microphone mute key. Signed-off-by: Bernhard Seibold Signed-off-by: Jiri Kosina --- drivers/hid/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid/Makefile') diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485..05913c533ec 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o +obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o -- cgit v1.2.3-70-g09d2 From 6a2a6390cf098b899a30146ef5c1fb85c9fefb3c Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 20 May 2012 22:44:54 +0200 Subject: HID: roccat: add support for Roccat Savu This patch adds rupport for Roccat Savu gaming mouse. In comparison to the other Roccat modules I tried to move even more functionality to userland. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-roccat-savu | 68 ++++ drivers/hid/Makefile | 3 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-savu.c | 357 +++++++++++++++++++++ drivers/hid/hid-roccat-savu.h | 103 ++++++ 6 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-savu create mode 100644 drivers/hid/hid-roccat-savu.c create mode 100644 drivers/hid/hid-roccat-savu.h (limited to 'drivers/hid/Makefile') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu new file mode 100644 index 00000000000..e2334900128 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu @@ -0,0 +1,68 @@ +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/buttons +Date: Mai 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split into general settings and + button settings. buttons holds informations about button layout. + When written, this file lets one write the respective profile + buttons to the mouse. The data has to be 47 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/control +Date: Mai 2012 +Contact: Stefan Achatz +Description: When written, this file lets one select which data from which + profile will be read next. The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/general +Date: Mai 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split into general settings and + button settings. profile holds informations like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 43 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/info +Date: Mai 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + The data is 8 bytes long. + This file is readonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/macro +Date: Mai 2012 +Contact: Stefan Achatz +Description: When written, this file lets one store macros with max 500 + keystrokes for a specific button for a specific profile. + Button and profile numbers are included in written data. + The data has to be 2083 bytes long. + Before reading this file, control has to be written to select + which profile and key to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/profile +Date: Mai 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. profile holds number of actual profile. + This value is persistent, so its value determines the profile + that's active when the mouse is powered on next time. + When written, the mouse activates the set profile immediately. + The data has to be 3 bytes long. + The mouse will reject invalid data. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485..348b9040459 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -69,7 +69,8 @@ obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o + hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ + hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6ac0286b537..fd95df8d1e0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1617,6 +1617,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index d1cdd2d2840..ddc293d827f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -644,6 +644,7 @@ #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 +#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c new file mode 100644 index 00000000000..d6c82d57408 --- /dev/null +++ b/drivers/hid/hid-roccat-savu.c @@ -0,0 +1,357 @@ +/* + * Roccat Savu driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* Roccat Savu is a gamer mouse with macro keys that can be configured in + * 5 profiles. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-savu.h" + +static struct class *savu_class; + +static int savu_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct savu_control control; + + do { + msleep(50); + retval = roccat_common_receive(usb_dev, SAVU_COMMAND_CONTROL, + &control, sizeof(struct savu_control)); + + if (retval) + return retval; + + switch (control.value) { + case SAVU_CONTROL_REQUEST_WRITE_CHECK_OK: + return 0; + case SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT: + continue; + case SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID: + /* seems to be critical - replug necessary */ + case SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD: + return -EINVAL; + default: + hid_err(usb_dev, "savu_receive_control_status: " + "unknown response value 0x%x\n", + control.value); + return -EINVAL; + } + + } while (1); +} + +static int savu_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + return savu_receive_control_status(usb_dev); +} + +static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&savu->savu_lock); + retval = roccat_common_receive(usb_dev, command, buf, real_size); + mutex_unlock(&savu->savu_lock); + + return retval ? retval : real_size; +} + +static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&savu->savu_lock); + retval = savu_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&savu->savu_lock); + + return retval ? retval : real_size; +} + +#define SAVU_SYSFS_W(thingy, THINGY) \ +static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return savu_sysfs_write(fp, kobj, buf, off, count, \ + SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ +} + +#define SAVU_SYSFS_R(thingy, THINGY) \ +static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return savu_sysfs_read(fp, kobj, buf, off, count, \ + SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ +} + +#define SAVU_SYSFS_RW(thingy, THINGY) \ +SAVU_SYSFS_W(thingy, THINGY) \ +SAVU_SYSFS_R(thingy, THINGY) + +#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .read = savu_sysfs_read_ ## thingy, \ + .write = savu_sysfs_write_ ## thingy \ +} + +#define SAVU_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .read = savu_sysfs_read_ ## thingy, \ +} + +#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .write = savu_sysfs_write_ ## thingy \ +} + +SAVU_SYSFS_W(control, CONTROL) +SAVU_SYSFS_RW(profile, PROFILE) +SAVU_SYSFS_RW(general, GENERAL) +SAVU_SYSFS_RW(buttons, BUTTONS) +SAVU_SYSFS_RW(macro, MACRO) +SAVU_SYSFS_R(info, INFO) + +static struct bin_attribute savu_bin_attributes[] = { + SAVU_BIN_ATTRIBUTE_W(control, CONTROL), + SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE), + SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), + SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), + SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), + SAVU_BIN_ATTRIBUTE_R(info, INFO), + __ATTR_NULL +}; + +static int savu_init_savu_device_struct(struct usb_device *usb_dev, + struct savu_device *savu) +{ + mutex_init(&savu->savu_lock); + + return 0; +} + +static int savu_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct savu_device *savu; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + savu = kzalloc(sizeof(*savu), GFP_KERNEL); + if (!savu) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, savu); + + retval = savu_init_savu_device_struct(usb_dev, savu); + if (retval) { + hid_err(hdev, "couldn't init struct savu_device\n"); + goto exit_free; + } + + retval = roccat_connect(savu_class, hdev, + sizeof(struct savu_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + savu->chrdev_minor = retval; + savu->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(savu); + return retval; +} + +static void savu_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct savu_device *savu; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return; + + savu = hid_get_drvdata(hdev); + if (savu->roccat_claimed) + roccat_disconnect(savu->chrdev_minor); + kfree(savu); +} + +static int savu_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = savu_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void savu_remove(struct hid_device *hdev) +{ + savu_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void savu_report_to_chrdev(struct savu_device const *savu, + u8 const *data) +{ + struct savu_roccat_report roccat_report; + struct savu_mouse_report_special const *special_report; + + if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL) + return; + + special_report = (struct savu_mouse_report_special const *)data; + + roccat_report.type = special_report->type; + roccat_report.data[0] = special_report->data[0]; + roccat_report.data[1] = special_report->data[1]; + roccat_report_event(savu->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int savu_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct savu_device *savu = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + if (savu == NULL) + return 0; + + if (savu->roccat_claimed) + savu_report_to_chrdev(savu, data); + + return 0; +} + +static const struct hid_device_id savu_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, savu_devices); + +static struct hid_driver savu_driver = { + .name = "savu", + .id_table = savu_devices, + .probe = savu_probe, + .remove = savu_remove, + .raw_event = savu_raw_event +}; + +static int __init savu_init(void) +{ + int retval; + + savu_class = class_create(THIS_MODULE, "savu"); + if (IS_ERR(savu_class)) + return PTR_ERR(savu_class); + savu_class->dev_bin_attrs = savu_bin_attributes; + + retval = hid_register_driver(&savu_driver); + if (retval) + class_destroy(savu_class); + return retval; +} + +static void __exit savu_exit(void) +{ + hid_unregister_driver(&savu_driver); + class_destroy(savu_class); +} + +module_init(savu_init); +module_exit(savu_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Savu driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h new file mode 100644 index 00000000000..97b43d5b047 --- /dev/null +++ b/drivers/hid/hid-roccat-savu.h @@ -0,0 +1,103 @@ +#ifndef __HID_ROCCAT_SAVU_H +#define __HID_ROCCAT_SAVU_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +enum { + SAVU_SIZE_CONTROL = 0x03, + SAVU_SIZE_PROFILE = 0x03, + SAVU_SIZE_GENERAL = 0x10, + SAVU_SIZE_BUTTONS = 0x2f, + SAVU_SIZE_MACRO = 0x0823, + SAVU_SIZE_INFO = 0x08, +}; + +struct savu_control { + uint8_t command; /* SAVU_COMMAND_CONTROL */ + /* + * value is profile number in range 0-4 for requesting settings and buttons + * 1 if status ok for requesting status + */ + uint8_t value; + uint8_t request; +} __packed; + +enum savu_control_requests { + SAVU_CONTROL_REQUEST_WRITE_CHECK = 0x00, + SAVU_CONTROL_REQUEST_GENERAL = 0x80, + SAVU_CONTROL_REQUEST_BUTTONS = 0x90, +}; + +enum savu_control_values { + SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD = 0, + SAVU_CONTROL_REQUEST_WRITE_CHECK_OK = 1, + SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID = 2, + SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT = 3, +}; + +enum savu_commands { + SAVU_COMMAND_CONTROL = 0x4, + SAVU_COMMAND_PROFILE = 0x5, + SAVU_COMMAND_GENERAL = 0x6, + SAVU_COMMAND_BUTTONS = 0x7, + SAVU_COMMAND_MACRO = 0x8, + SAVU_COMMAND_INFO = 0x9, +}; + +struct savu_mouse_report_special { + uint8_t report_number; /* always 3 */ + uint8_t zero; + uint8_t type; + uint8_t data[2]; +} __packed; + +enum { + SAVU_MOUSE_REPORT_NUMBER_SPECIAL = 3, +}; + +enum savu_mouse_report_button_types { + /* data1 = new profile range 1-5 */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_PROFILE = 0x20, + + /* data1 = button number range 1-24; data2 = action */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + + /* data1 = button number range 1-24; data2 = action */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80, + + /* data1 = setting number range 1-5 */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0, + + /* data1 and data2 = range 0x1-0xb */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0, + + /* data1 = 22 = next track... + * data2 = action + */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +struct savu_roccat_report { + uint8_t type; + uint8_t data[2]; +} __packed; + +struct savu_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex savu_lock; +}; + +#endif -- cgit v1.2.3-70-g09d2 From ff9bf5a2eff6e726406bcc097e8a578822d38859 Mon Sep 17 00:00:00 2001 From: Tom Harwood Date: Fri, 6 Jul 2012 08:05:04 -0700 Subject: HID: Add driver for Holtek based keyboards with broken HID Corrects two HID descriptor issues, which prevent some Holtek based (USB ID 04d9:a055) keyboards from working. The error when not using the driver is: generic-usb: probe ... failed with error -22 . Signed-off-by: Tom Harwood Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 +- drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-holtek-kbd.c | 183 +++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 3 + 5 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 drivers/hid/hid-holtek-kbd.c (limited to 'drivers/hid/Makefile') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cbf0e0344e3..8e439e290fc 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -200,10 +200,12 @@ config HID_EZKEY Support for Ezkey BTC 8193 keyboard. config HID_HOLTEK - tristate "Holtek On Line Grip based game controller support" + tristate "Holtek HID devices" depends on USB_HID ---help--- - Say Y here if you have a Holtek On Line Grip based game controller. + Support for Holtek based devices: + - Holtek On Line Grip based game controller + - Trust GXT 18 Gaming Keyboard config HOLTEK_FF bool "Holtek On Line Grip force feedback support" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 05913c533ec..2429ae7c58c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o +obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f69568032c0..ba1c3644e6a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1536,6 +1536,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c new file mode 100644 index 00000000000..e0a5d1739fc --- /dev/null +++ b/drivers/hid/hid-holtek-kbd.c @@ -0,0 +1,183 @@ +/* + * HID driver for Holtek keyboard + * Copyright (c) 2012 Tom Harwood +*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" +#include "usbhid/usbhid.h" + +/* Holtek based keyboards (USB ID 04d9:a055) have the following issues: + * - The report descriptor specifies an excessively large number of consumer + * usages (2^15), which is more than HID_MAX_USAGES. This prevents proper + * parsing of the report descriptor. + * - The report descriptor reports on caps/scroll/num lock key presses, but + * doesn't have an LED output usage block. + * + * The replacement descriptor below fixes the number of consumer usages, + * and provides an LED output usage block. LED output events are redirected + * to the boot interface. + */ + +static __u8 holtek_kbd_rdesc_fixed[] = { + /* Original report descriptor, with reduced number of consumer usages */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x80, /* Usage (Sys Control), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x19, 0x81, /* Usage Minimum (Sys Power Down), */ + 0x29, 0x83, /* Usage Maximum (Sys Wake Up), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x75, 0x01, /* Report Size (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x05, /* Report Size (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0x05, 0x0C, /* Usage Page (Consumer), */ + 0x09, 0x01, /* Usage (Consumer Control), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x19, 0x00, /* Usage Minimum (00h), */ + 0x2A, 0xFF, 0x2F, /* Usage Maximum (0x2FFF), previously 0x7FFF */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x2F, /* Logical Maximum (0x2FFF),previously 0x7FFF*/ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x10, /* Report Size (16), */ + 0x81, 0x00, /* Input, */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x03, /* Report ID (3), */ + 0x95, 0x38, /* Report Count (56), */ + 0x75, 0x01, /* Report Size (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ + 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ + 0x19, 0x00, /* Usage Minimum (None), */ + 0x29, 0x2F, /* Usage Maximum (KB Lboxbracket And Lbrace),*/ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x04, /* Report ID (4), */ + 0x95, 0x38, /* Report Count (56), */ + 0x75, 0x01, /* Report Size (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x19, 0x30, /* Usage Minimum (KB Rboxbracket And Rbrace),*/ + 0x29, 0x67, /* Usage Maximum (KP Equals), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection */ + + /* LED usage for the boot protocol interface */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x05, 0x08, /* Usage Page (LED), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x91, 0x02, /* Output (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x91, 0x01, /* Output (Constant), */ + 0xC0, /* End Collection */ +}; + +static __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + rdesc = holtek_kbd_rdesc_fixed; + *rsize = sizeof(holtek_kbd_rdesc_fixed); + } + return rdesc; +} + +static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, + int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct usb_device *usb_dev = hid_to_usb_dev(hid); + + /* Locate the boot interface, to receive the LED change events */ + struct usb_interface *boot_interface = usb_ifnum_to_if(usb_dev, 0); + + struct hid_device *boot_hid = usb_get_intfdata(boot_interface); + struct hid_input *boot_hid_input = list_first_entry(&boot_hid->inputs, + struct hid_input, list); + + return boot_hid_input->input->event(boot_hid_input->input, type, code, + value); +} + +static int holtek_kbd_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + int ret = hid_parse(hdev); + + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) { + struct hid_input *hidinput; + list_for_each_entry(hidinput, &hdev->inputs, list) { + hidinput->input->event = holtek_kbd_input_event; + } + } + + return ret; +} + +static const struct hid_device_id holtek_kbd_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { } +}; +MODULE_DEVICE_TABLE(hid, holtek_kbd_devices); + +static struct hid_driver holtek_kbd_driver = { + .name = "holtek_kbd", + .id_table = holtek_kbd_devices, + .report_fixup = holtek_kbd_report_fixup, + .probe = holtek_kbd_probe +}; + +static int __init holtek_kbd_init(void) +{ + return hid_register_driver(&holtek_kbd_driver); +} + +static void __exit holtek_kbd_exit(void) +{ + hid_unregister_driver(&holtek_kbd_driver); +} + +module_exit(holtek_kbd_exit); +module_init(holtek_kbd_init); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c607e6f3395..a139d9619ad 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -404,6 +404,9 @@ #define USB_VENDOR_ID_HOLTEK 0x1241 #define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015 +#define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 +#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 + #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 -- cgit v1.2.3-70-g09d2