From dacfecdbf3a0a5072984ff8b3c224a48c8461008 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 17 Jan 2011 13:08:45 +0300 Subject: HID: hid-roccat-koneplus: potential NULL dereference kmalloc() may fail, if so return -ENOMEM. Also koneplus_init_koneplus_device_struct() should check return code of koneplus_get_startup_profile() and propagate its return code. Signed-off-by: Vasiliy Kulikov Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 1608c8d..591a97c 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -220,6 +220,8 @@ static int koneplus_get_startup_profile(struct usb_device *usb_dev) int retval; buf = kmalloc(sizeof(struct koneplus_startup_profile), GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, buf, sizeof(struct koneplus_startup_profile)); @@ -614,6 +616,8 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, mutex_init(&koneplus->koneplus_lock); koneplus->startup_profile = koneplus_get_startup_profile(usb_dev); + if (koneplus->startup_profile < 0) + return koneplus->startup_profile; msleep(wait); retval = koneplus_get_info(usb_dev, &koneplus->info); -- cgit v0.10.2 From e68cc603b063416c85f3e408184219fb71d4a9ff Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 6 Jan 2011 09:00:34 +0100 Subject: HID: roccat: Add support for Roccat Arvo keyboard This patch add support for Roccat Arvo keyboard. Arvo has 5 additional configurable buttons and the ability to deactivate certain keys. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo b/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo new file mode 100644 index 0000000..d54827d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo @@ -0,0 +1,48 @@ +What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/actual_profile +Date: Januar 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-5. + When read, this attribute returns the number of the actual + profile which is also the profile that's active on device startup. + When written this attribute activates the selected profile + immediately. + +What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/button +Date: Januar 2011 +Contact: Stefan Achatz +Description: The keyboard can store short macros with consist of 1 button with + several modifier keys internally. + When written, this file lets one set the sequence for a specific + button for a specific profile. Button and profile numbers are + included in written data. The data has to be 24 bytes long. + This file is writeonly. + +What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/info +Date: Januar 2011 +Contact: Stefan Achatz +Description: When read, this file returns some info about the device like the + installed firmware version. + The size of the data is 8 bytes in size. + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/key_mask +Date: Januar 2011 +Contact: Stefan Achatz +Description: The keyboard lets the user deactivate 5 certain keys like the + windows and application keys, to protect the user from the outcome + of accidentally pressing them. + The integer value of this attribute has bits 0-4 set depending + on the state of the corresponding key. + When read, this file returns the current state of the buttons. + When written, the given buttons are activated/deactivated + immediately. + +What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/mode_key +Date: Januar 2011 +Contact: Stefan Achatz +Description: The keyboard has a condensed layout without num-lock key. + Instead it uses a mode-key which activates a gaming mode where + the assignment of the number block changes. + The integer value of this attribute ranges from 0 (OFF) to 1 (ON). + When read, this file returns the actual state of the key. + When written, the key is activated/deactivated immediately. \ No newline at end of file diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24cca2f..539865a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -417,6 +417,13 @@ config HID_ROCCAT Say Y here if you have a Roccat mouse or keyboard and want OSD or macro execution support. +config HID_ROCCAT_ARVO + tristate "Roccat Arvo keyboard support" + depends on USB_HID + select HID_ROCCAT + ---help--- + Support for Roccat Arvo keyboard. + config HID_ROCCAT_KONE tristate "Roccat Kone Mouse support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6efc2a0..fea4eb8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o +obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3..6178025 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1405,6 +1405,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61..92e0fa1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -496,6 +496,7 @@ #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 #define USB_VENDOR_ID_ROCCAT 0x1e7d +#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c new file mode 100644 index 0000000..8facbd3 --- /dev/null +++ b/drivers/hid/hid-roccat-arvo.c @@ -0,0 +1,516 @@ +/* + * Roccat Arvo driver for Linux + * + * Copyright (c) 2011 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 Arvo is a gamer keyboard with 5 macro keys that can be configured in + * 5 profiles. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat.h" +#include "hid-roccat-arvo.h" + +static struct class *arvo_class; + +static int arvo_receive(struct usb_device *usb_dev, uint usb_command, + void *buf, uint size) +{ + int len; + + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); + + return (len != size) ? -EIO : 0; +} + +static int arvo_send(struct usb_device *usb_dev, uint usb_command, + void const *buf, uint size) +{ + int len; + + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + usb_command, 0, (void *)buf, size, USB_CTRL_SET_TIMEOUT); + + return (len != size) ? -EIO : 0; +} + +static ssize_t arvo_sysfs_show_mode_key(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_mode_key *temp_buf; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + mutex_lock(&arvo->arvo_lock); + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + temp_buf, sizeof(struct arvo_mode_key)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + goto out; + + retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->state); +out: + kfree(temp_buf); + return retval; +} + +static ssize_t arvo_sysfs_set_mode_key(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_mode_key *temp_buf; + unsigned long state; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + retval = strict_strtoul(buf, 10, &state); + if (retval) + goto out; + + temp_buf->command = ARVO_COMMAND_MODE_KEY; + temp_buf->state = state; + + mutex_lock(&arvo->arvo_lock); + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + temp_buf, sizeof(struct arvo_mode_key)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + goto out; + + retval = size; +out: + kfree(temp_buf); + return retval; +} + +static ssize_t arvo_sysfs_show_key_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_key_mask *temp_buf; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + mutex_lock(&arvo->arvo_lock); + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + temp_buf, sizeof(struct arvo_key_mask)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + goto out; + + retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->key_mask); +out: + kfree(temp_buf); + return retval; +} + +static ssize_t arvo_sysfs_set_key_mask(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_key_mask *temp_buf; + unsigned long key_mask; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + retval = strict_strtoul(buf, 10, &key_mask); + if (retval) + goto out; + + temp_buf->command = ARVO_COMMAND_KEY_MASK; + temp_buf->key_mask = key_mask; + + mutex_lock(&arvo->arvo_lock); + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + temp_buf, sizeof(struct arvo_key_mask)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + goto out; + + retval = size; +out: + kfree(temp_buf); + return retval; +} + +/* retval is 1-5 on success, < 0 on error */ +static int arvo_get_actual_profile(struct usb_device *usb_dev) +{ + struct arvo_actual_profile *temp_buf; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + temp_buf, sizeof(struct arvo_actual_profile)); + + if (!retval) + retval = temp_buf->actual_profile; + + kfree(temp_buf); + return retval; +} + +static ssize_t arvo_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + + return snprintf(buf, PAGE_SIZE, "%d\n", arvo->actual_profile); +} + +static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_actual_profile *temp_buf; + unsigned long profile; + int retval; + + temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); + if (!temp_buf) + return -ENOMEM; + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + goto out; + + temp_buf->command = ARVO_COMMAND_ACTUAL_PROFILE; + temp_buf->actual_profile = profile; + + mutex_lock(&arvo->arvo_lock); + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + temp_buf, sizeof(struct arvo_actual_profile)); + if (!retval) { + arvo->actual_profile = profile; + retval = size; + } + mutex_unlock(&arvo->arvo_lock); + +out: + kfree(temp_buf); + return retval; +} + +static ssize_t arvo_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 arvo_device *arvo = 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(&arvo->arvo_lock); + retval = arvo_send(usb_dev, command, buf, real_size); + mutex_unlock(&arvo->arvo_lock); + + return (retval ? retval : real_size); +} + +static ssize_t arvo_sysfs_read(struct file *fp, + struct kobject *kobj, void *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 arvo_device *arvo = 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(&arvo->arvo_lock); + retval = arvo_receive(usb_dev, command, buf, real_size); + mutex_unlock(&arvo->arvo_lock); + + return (retval ? retval : real_size); +} + +static ssize_t arvo_sysfs_write_button(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return arvo_sysfs_write(fp, kobj, buf, off, count, + sizeof(struct arvo_button), ARVO_USB_COMMAND_BUTTON); +} + +static ssize_t arvo_sysfs_read_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return arvo_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct arvo_info), ARVO_USB_COMMAND_INFO); +} + + +static struct device_attribute arvo_attributes[] = { + __ATTR(mode_key, 0660, + arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key), + __ATTR(key_mask, 0660, + arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask), + __ATTR(actual_profile, 0660, + arvo_sysfs_show_actual_profile, + arvo_sysfs_set_actual_profile), + __ATTR_NULL +}; + +static struct bin_attribute arvo_bin_attributes[] = { + { + .attr = { .name = "button", .mode = 0220 }, + .size = sizeof(struct arvo_button), + .write = arvo_sysfs_write_button + }, + { + .attr = { .name = "info", .mode = 0440 }, + .size = sizeof(struct arvo_info), + .read = arvo_sysfs_read_info + }, + __ATTR_NULL +}; + +static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, + struct arvo_device *arvo) +{ + int retval; + + mutex_init(&arvo->arvo_lock); + + retval = arvo_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + arvo->actual_profile = retval; + + return 0; +} + +static int arvo_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 arvo_device *arvo; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_KEYBOARD) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + arvo = kzalloc(sizeof(*arvo), GFP_KERNEL); + if (!arvo) { + dev_err(&hdev->dev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, arvo); + + retval = arvo_init_arvo_device_struct(usb_dev, arvo); + if (retval) { + dev_err(&hdev->dev, + "couldn't init struct arvo_device\n"); + goto exit_free; + } + + retval = roccat_connect(arvo_class, hdev); + if (retval < 0) { + dev_err(&hdev->dev, "couldn't init char dev\n"); + } else { + arvo->chrdev_minor = retval; + arvo->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(arvo); + return retval; +} + +static void arvo_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct arvo_device *arvo; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_KEYBOARD) + return; + + arvo = hid_get_drvdata(hdev); + if (arvo->roccat_claimed) + roccat_disconnect(arvo->chrdev_minor); + kfree(arvo); +} + +static int arvo_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + dev_err(&hdev->dev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + dev_err(&hdev->dev, "hw start failed\n"); + goto exit; + } + + retval = arvo_init_specials(hdev); + if (retval) { + dev_err(&hdev->dev, "couldn't install keyboard\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void arvo_remove(struct hid_device *hdev) +{ + arvo_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void arvo_report_to_chrdev(struct arvo_device const *arvo, + u8 const *data) +{ + struct arvo_special_report const *special_report; + struct arvo_roccat_report roccat_report; + + special_report = (struct arvo_special_report const *)data; + + roccat_report.profile = arvo->actual_profile; + roccat_report.button = special_report->event & + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON; + if ((special_report->event & ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION) == + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS) + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_PRESS; + else + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; + + roccat_report_event(arvo->chrdev_minor, (uint8_t const *)&roccat_report, + sizeof(struct arvo_roccat_report)); +} + +static int arvo_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct arvo_device *arvo = hid_get_drvdata(hdev); + + if (size != 3) + return 0; + + if (arvo->roccat_claimed) + arvo_report_to_chrdev(arvo, data); + + return 0; +} + +static const struct hid_device_id arvo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, arvo_devices); + +static struct hid_driver arvo_driver = { + .name = "arvo", + .id_table = arvo_devices, + .probe = arvo_probe, + .remove = arvo_remove, + .raw_event = arvo_raw_event +}; + +static int __init arvo_init(void) +{ + int retval; + + arvo_class = class_create(THIS_MODULE, "arvo"); + if (IS_ERR(arvo_class)) + return PTR_ERR(arvo_class); + arvo_class->dev_attrs = arvo_attributes; + arvo_class->dev_bin_attrs = arvo_bin_attributes; + + retval = hid_register_driver(&arvo_driver); + if (retval) + class_destroy(arvo_class); + return retval; +} + +static void __exit arvo_exit(void) +{ + class_destroy(arvo_class); + hid_unregister_driver(&arvo_driver); +} + +module_init(arvo_init); +module_exit(arvo_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Arvo driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-arvo.h b/drivers/hid/hid-roccat-arvo.h new file mode 100644 index 0000000..d284a78 --- /dev/null +++ b/drivers/hid/hid-roccat-arvo.h @@ -0,0 +1,98 @@ +#ifndef __HID_ROCCAT_ARVO_H +#define __HID_ROCCAT_ARVO_H + +/* + * Copyright (c) 2011 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 + +struct arvo_mode_key { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_MODE_KEY */ + uint8_t state; +} __packed; + +struct arvo_button { + uint8_t unknown[24]; +} __packed; + +struct arvo_info { + uint8_t unknown[8]; +} __packed; + +struct arvo_key_mask { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_KEY_MASK */ + uint8_t key_mask; +} __packed; + +/* selected profile is persistent */ +struct arvo_actual_profile { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_ACTUAL_PROFILE */ + uint8_t actual_profile; +} __packed; + +enum arvo_commands { + ARVO_COMMAND_MODE_KEY = 0x3, + ARVO_COMMAND_BUTTON = 0x4, + ARVO_COMMAND_INFO = 0x5, + ARVO_COMMAND_KEY_MASK = 0x6, + ARVO_COMMAND_ACTUAL_PROFILE = 0x7, +}; + +enum arvo_usb_commands { + ARVO_USB_COMMAND_MODE_KEY = 0x303, + /* + * read/write + * Read uses both index bytes as profile/key indexes + * Write has index 0, profile/key is determined by payload + */ + ARVO_USB_COMMAND_BUTTON = 0x304, + ARVO_USB_COMMAND_INFO = 0x305, + ARVO_USB_COMMAND_KEY_MASK = 0x306, + ARVO_USB_COMMAND_ACTUAL_PROFILE = 0x307, +}; + +struct arvo_special_report { + uint8_t unknown1; /* always 0x01 */ + uint8_t event; + uint8_t unknown2; /* always 0x70 */ +} __packed; + +enum arvo_special_report_events { + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS = 0x10, + ARVO_SPECIAL_REPORT_EVENT_ACTION_RELEASE = 0x0, +}; + +enum arvo_special_report_event_masks { + ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION = 0xf0, + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON = 0x0f, +}; + +struct arvo_roccat_report { + uint8_t profile; + uint8_t button; + uint8_t action; +} __packed; + +enum arvo_roccat_report_action { + ARVO_ROCCAT_REPORT_ACTION_RELEASE = 0, + ARVO_ROCCAT_REPORT_ACTION_PRESS = 1, +}; + +struct arvo_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex arvo_lock; + + int actual_profile; +}; + +#endif -- cgit v0.10.2 From fd82be6c0a359fc706a805a49b1f167af21e63ea Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 6 Jan 2011 09:00:41 +0100 Subject: HID: roccat: Fix an error message when reading more data from bin attributes Using cat or something to read from binary attributes would try to do an additional read with offset equal to filesize. This resulted in an invalid attribute error. This is fixed by giving the right answer instead of EINVAL. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 591a97c..65d7cde 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -258,6 +258,9 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, 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; -- cgit v0.10.2 From b79b36be56da0ba61fa10a3fa96939c3a3eb499a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 31 Jan 2011 11:28:19 +0100 Subject: HID: hid-multitouch: change default mt_class The safest quirk for a device (the one that works out of the box for most of them) is MT_QUIRK_NOT_SEEN_MEANS_UP. Indeed, it does not make any assumption on the device. When adding a new device, we can easily test it against MT_CLS_DEFAULT, and then optimize it with other quirks: that's why no device use MT_CLS_DEFAULT right now. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183..86863a3 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -104,7 +104,7 @@ static int find_slot_from_contactid(struct mt_device *td) struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, - .quirks = MT_QUIRK_VALID_IS_INRANGE, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, .maxcontacts = 10 }, { .name = MT_CLS_DUAL1, .quirks = MT_QUIRK_VALID_IS_INRANGE | -- cgit v0.10.2 From 1e9cf35b995610e7ba2934d3dc92e3a03fa361a1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 31 Jan 2011 11:28:20 +0100 Subject: HID: hid-multitouch: Rename MT_CLS_DUAL1 and MT_CLS_DUAL2 This patch renames MT_CLS_DUAL1 to MT_CLS_DUAL_INRANGE_CONTACTID and MT_CLS_DUAL2 to MT_CLS_DUAL_INRANGE_CONTACTNUMBER for better readability. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 86863a3..b976d10 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -65,10 +65,10 @@ struct mt_class { }; /* classes of device behavior */ -#define MT_CLS_DEFAULT 1 -#define MT_CLS_DUAL1 2 -#define MT_CLS_DUAL2 3 -#define MT_CLS_CYPRESS 4 +#define MT_CLS_DEFAULT 1 +#define MT_CLS_DUAL_INRANGE_CONTACTID 2 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 3 +#define MT_CLS_CYPRESS 4 /* * these device-dependent functions determine what slot corresponds @@ -106,11 +106,11 @@ struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, .maxcontacts = 10 }, - { .name = MT_CLS_DUAL1, + { .name = MT_CLS_DUAL_INRANGE_CONTACTID, .quirks = MT_QUIRK_VALID_IS_INRANGE | MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, - { .name = MT_CLS_DUAL2, + { .name = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, .quirks = MT_QUIRK_VALID_IS_INRANGE | MT_QUIRK_SLOT_IS_CONTACTNUMBER, .maxcontacts = 2 }, @@ -466,15 +466,15 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, /* GeneralTouch panel */ - { .driver_data = MT_CLS_DUAL2, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, /* PixCir-based panels */ - { .driver_data = MT_CLS_DUAL1, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, - { .driver_data = MT_CLS_DUAL1, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, -- cgit v0.10.2 From ef2fafb3e233ca9cb390fc3c1461b7f3859998fc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 31 Jan 2011 11:28:21 +0100 Subject: HID: hid-multitouch: add Benjamin Tissoires as module_author Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index b976d10..4f63409 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -24,6 +24,7 @@ MODULE_AUTHOR("Stephane Chatty "); +MODULE_AUTHOR("Benjamin Tissoires "); MODULE_DESCRIPTION("HID multitouch panels"); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 4dfcced8a1f42248f9e7a461485f6aa5f66d2105 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 31 Jan 2011 11:28:22 +0100 Subject: HID: hid-multitouch: introduce IrTouch Infrared USB device This patch adds support for IrTouch 42 inches. Tested-by: Victor Zhuk Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24cca2f..a0b117d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -304,6 +304,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - Cypress TrueTouch panels - Hanvon dual touch panels + - IrTouch Infrared USB panels - Pixcir dual touch panels - 'Sensing Win7-TwoFinger' panel by GeneralTouch diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61..b1dd7ad 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -333,6 +333,9 @@ #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 +#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 +#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 + #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 4f63409..69f8744 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -471,6 +471,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, + /* IRTOUCH panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, + USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, + /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, -- cgit v0.10.2 From cf28a673666fba5770b40fb084584391f451c07d Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Wed, 26 Jan 2011 22:25:18 -0500 Subject: HID: hidraw: Replace Confusing += Operator with = Setting of the return value of hidraw_read() uses the += operator when = is more appropriate. There is no case where ret can be anything other than zero when the assignment is made, making = equivalent to += and much more clear. Signed-off-by: Alan Ott Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b..66fbcba 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -91,7 +91,7 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ret = -EFAULT; goto out; } - ret += len; + ret = len; kfree(list->buffer[list->tail].value); list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); -- cgit v0.10.2 From 3a22ebe9cc76acac2511b1d3979a35609924ce42 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sat, 29 Jan 2011 02:17:30 +0100 Subject: HID: hidraw: fix hidraw_disconnect() hidraw_disconnect() first sets an entry in hidraw_table to NULL and calls device_destroy() afterwards. The thereby called hidraw_release() tries to read this already cleared value resulting in never removing any device from the list. This got fixed by changing the order of events. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b..5516ea4 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -428,12 +428,12 @@ void hidraw_disconnect(struct hid_device *hid) hidraw->exist = 0; + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + mutex_lock(&minors_lock); hidraw_table[hidraw->minor] = NULL; mutex_unlock(&minors_lock); - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - if (hidraw->open) { hid_hw_close(hid); wake_up_interruptible(&hidraw->wait); -- cgit v0.10.2 From cb38cd87862aa515cd0559473e94d4495372a590 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:20 +0100 Subject: HID: roccat: Remove empty function definitions for roccat chardev Roccat chardev is a dependency of all device specific drivers, so the empty definitions are not needed anymore. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat.h b/drivers/hid/hid-roccat.h index 5784281..c739337 100644 --- a/drivers/hid/hid-roccat.h +++ b/drivers/hid/hid-roccat.h @@ -15,18 +15,8 @@ #include #include -#if defined(CONFIG_HID_ROCCAT) || defined(CONFIG_HID_ROCCAT_MODULE) int roccat_connect(struct class *klass, struct hid_device *hid); void roccat_disconnect(int minor); int roccat_report_event(int minor, u8 const *data, int len); -#else -static inline int roccat_connect(struct class *klass, - struct hid_device *hid) { return -1; } -static inline void roccat_disconnect(int minor) {} -static inline int roccat_report_event(int minor, u8 const *data, int len) -{ - return 0; -} -#endif #endif -- cgit v0.10.2 From a28764ef80dd5aef657f810a9c295ccda421c823 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:21 +0100 Subject: HID: roccat: Use new hid_err macros Using the new hid_err macros instead of dev_err. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 8facbd3..ebf3c15 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -358,21 +358,20 @@ static int arvo_init_specials(struct hid_device *hdev) arvo = kzalloc(sizeof(*arvo), GFP_KERNEL); if (!arvo) { - dev_err(&hdev->dev, "can't alloc device descriptor\n"); + hid_err(hdev, "can't alloc device descriptor\n"); return -ENOMEM; } hid_set_drvdata(hdev, arvo); retval = arvo_init_arvo_device_struct(usb_dev, arvo); if (retval) { - dev_err(&hdev->dev, - "couldn't init struct arvo_device\n"); + hid_err(hdev, "couldn't init struct arvo_device\n"); goto exit_free; } retval = roccat_connect(arvo_class, hdev); if (retval < 0) { - dev_err(&hdev->dev, "couldn't init char dev\n"); + hid_err(hdev, "couldn't init char dev\n"); } else { arvo->chrdev_minor = retval; arvo->roccat_claimed = 1; @@ -406,19 +405,19 @@ static int arvo_probe(struct hid_device *hdev, retval = hid_parse(hdev); if (retval) { - dev_err(&hdev->dev, "parse failed\n"); + hid_err(hdev, "parse failed\n"); goto exit; } retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (retval) { - dev_err(&hdev->dev, "hw start failed\n"); + hid_err(hdev, "hw start failed\n"); goto exit; } retval = arvo_init_specials(hdev); if (retval) { - dev_err(&hdev->dev, "couldn't install keyboard\n"); + hid_err(hdev, "couldn't install keyboard\n"); goto exit_stop; } diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 65d7cde..c826c0d 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -116,7 +116,7 @@ static int koneplus_receive_control_status(struct usb_device *usb_dev) goto out; } - dev_err(&usb_dev->dev, "koneplus_receive_control_status: " + hid_err(usb_dev, "koneplus_receive_control_status: " "unknown response value 0x%x\n", control->value); retval = -EINVAL; goto out; @@ -658,21 +658,20 @@ static int koneplus_init_specials(struct hid_device *hdev) koneplus = kzalloc(sizeof(*koneplus), GFP_KERNEL); if (!koneplus) { - dev_err(&hdev->dev, "can't alloc device descriptor\n"); + hid_err(hdev, "can't alloc device descriptor\n"); return -ENOMEM; } hid_set_drvdata(hdev, koneplus); retval = koneplus_init_koneplus_device_struct(usb_dev, koneplus); if (retval) { - dev_err(&hdev->dev, - "couldn't init struct koneplus_device\n"); + hid_err(hdev, "couldn't init struct koneplus_device\n"); goto exit_free; } retval = roccat_connect(koneplus_class, hdev); if (retval < 0) { - dev_err(&hdev->dev, "couldn't init char dev\n"); + hid_err(hdev, "couldn't init char dev\n"); } else { koneplus->chrdev_minor = retval; koneplus->roccat_claimed = 1; @@ -708,19 +707,19 @@ static int koneplus_probe(struct hid_device *hdev, retval = hid_parse(hdev); if (retval) { - dev_err(&hdev->dev, "parse failed\n"); + hid_err(hdev, "parse failed\n"); goto exit; } retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (retval) { - dev_err(&hdev->dev, "hw start failed\n"); + hid_err(hdev, "hw start failed\n"); goto exit; } retval = koneplus_init_specials(hdev); if (retval) { - dev_err(&hdev->dev, "couldn't install mouse\n"); + hid_err(hdev, "couldn't install mouse\n"); goto exit_stop; } -- cgit v0.10.2 From 5772f63613ce0a6777e82a7e8fb553e49da27719 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:23 +0100 Subject: HID: roccat: Introduce module hid-roccat-common Module hid-roccat-common contains functions used by roccat device driver modules to reduce code duplication. At the moment it contains just two wrapper methods for usb_control_msg that ensure that the buffer used for transfer is dma capable which wasn't the case before. The kconfig option is not visible to the user but will be selected by the device specific drivers. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 539865a..7952369 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -417,10 +417,14 @@ config HID_ROCCAT Say Y here if you have a Roccat mouse or keyboard and want OSD or macro execution support. +config HID_ROCCAT_COMMON + tristate + config HID_ROCCAT_ARVO tristate "Roccat Arvo keyboard support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Arvo keyboard. @@ -428,6 +432,7 @@ config HID_ROCCAT_KONE tristate "Roccat Kone Mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Kone mouse. @@ -435,6 +440,7 @@ config HID_ROCCAT_KONEPLUS tristate "Roccat Kone[+] mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Kone[+] mouse. @@ -442,6 +448,7 @@ config HID_ROCCAT_PYRA tristate "Roccat Pyra mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Pyra mouse. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fea4eb8..086cf62 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o +obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index ebf3c15..d72ee41 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -19,41 +19,15 @@ #include #include #include -#include #include #include #include "hid-ids.h" #include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-arvo.h" static struct class *arvo_class; -static int arvo_receive(struct usb_device *usb_dev, uint usb_command, - void *buf, uint size) -{ - int len; - - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); - - return (len != size) ? -EIO : 0; -} - -static int arvo_send(struct usb_device *usb_dev, uint usb_command, - void const *buf, uint size) -{ - int len; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - usb_command, 0, (void *)buf, size, USB_CTRL_SET_TIMEOUT); - - return (len != size) ? -EIO : 0; -} - static ssize_t arvo_sysfs_show_mode_key(struct device *dev, struct device_attribute *attr, char *buf) { @@ -61,24 +35,17 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev->parent->parent)); - struct arvo_mode_key *temp_buf; + struct arvo_mode_key temp_buf; int retval; - temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - mutex_lock(&arvo->arvo_lock); - retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY, - temp_buf, sizeof(struct arvo_mode_key)); + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + &temp_buf, sizeof(struct arvo_mode_key)); mutex_unlock(&arvo->arvo_lock); if (retval) - goto out; + return retval; - retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->state); -out: - kfree(temp_buf); - return retval; + return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.state); } static ssize_t arvo_sysfs_set_mode_key(struct device *dev, @@ -88,32 +55,25 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev->parent->parent)); - struct arvo_mode_key *temp_buf; + struct arvo_mode_key temp_buf; unsigned long state; int retval; - temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - retval = strict_strtoul(buf, 10, &state); if (retval) - goto out; + return retval; - temp_buf->command = ARVO_COMMAND_MODE_KEY; - temp_buf->state = state; + temp_buf.command = ARVO_COMMAND_MODE_KEY; + temp_buf.state = state; mutex_lock(&arvo->arvo_lock); - retval = arvo_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY, - temp_buf, sizeof(struct arvo_mode_key)); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + &temp_buf, sizeof(struct arvo_mode_key)); mutex_unlock(&arvo->arvo_lock); if (retval) - goto out; + return retval; - retval = size; -out: - kfree(temp_buf); - return retval; + return size; } static ssize_t arvo_sysfs_show_key_mask(struct device *dev, @@ -123,24 +83,17 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev->parent->parent)); - struct arvo_key_mask *temp_buf; + struct arvo_key_mask temp_buf; int retval; - temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - mutex_lock(&arvo->arvo_lock); - retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK, - temp_buf, sizeof(struct arvo_key_mask)); + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + &temp_buf, sizeof(struct arvo_key_mask)); mutex_unlock(&arvo->arvo_lock); if (retval) - goto out; + return retval; - retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->key_mask); -out: - kfree(temp_buf); - return retval; + return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.key_mask); } static ssize_t arvo_sysfs_set_key_mask(struct device *dev, @@ -150,52 +103,40 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev->parent->parent)); - struct arvo_key_mask *temp_buf; + struct arvo_key_mask temp_buf; unsigned long key_mask; int retval; - temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - retval = strict_strtoul(buf, 10, &key_mask); if (retval) - goto out; + return retval; - temp_buf->command = ARVO_COMMAND_KEY_MASK; - temp_buf->key_mask = key_mask; + temp_buf.command = ARVO_COMMAND_KEY_MASK; + temp_buf.key_mask = key_mask; mutex_lock(&arvo->arvo_lock); - retval = arvo_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK, - temp_buf, sizeof(struct arvo_key_mask)); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + &temp_buf, sizeof(struct arvo_key_mask)); mutex_unlock(&arvo->arvo_lock); if (retval) - goto out; + return retval; - retval = size; -out: - kfree(temp_buf); - return retval; + return size; } /* retval is 1-5 on success, < 0 on error */ static int arvo_get_actual_profile(struct usb_device *usb_dev) { - struct arvo_actual_profile *temp_buf; + struct arvo_actual_profile temp_buf; int retval; - temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - - retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, - temp_buf, sizeof(struct arvo_actual_profile)); + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + &temp_buf, sizeof(struct arvo_actual_profile)); - if (!retval) - retval = temp_buf->actual_profile; + if (retval) + return retval; - kfree(temp_buf); - return retval; + return temp_buf.actual_profile; } static ssize_t arvo_sysfs_show_actual_profile(struct device *dev, @@ -214,32 +155,25 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev->parent->parent)); - struct arvo_actual_profile *temp_buf; + struct arvo_actual_profile temp_buf; unsigned long profile; int retval; - temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); - if (!temp_buf) - return -ENOMEM; - retval = strict_strtoul(buf, 10, &profile); if (retval) - goto out; + return retval; - temp_buf->command = ARVO_COMMAND_ACTUAL_PROFILE; - temp_buf->actual_profile = profile; + temp_buf.command = ARVO_COMMAND_ACTUAL_PROFILE; + temp_buf.actual_profile = profile; mutex_lock(&arvo->arvo_lock); - retval = arvo_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, - temp_buf, sizeof(struct arvo_actual_profile)); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + &temp_buf, sizeof(struct arvo_actual_profile)); if (!retval) { arvo->actual_profile = profile; retval = size; } mutex_unlock(&arvo->arvo_lock); - -out: - kfree(temp_buf); return retval; } @@ -257,7 +191,7 @@ static ssize_t arvo_sysfs_write(struct file *fp, return -EINVAL; mutex_lock(&arvo->arvo_lock); - retval = arvo_send(usb_dev, command, buf, real_size); + retval = roccat_common_send(usb_dev, command, buf, real_size); mutex_unlock(&arvo->arvo_lock); return (retval ? retval : real_size); @@ -280,7 +214,7 @@ static ssize_t arvo_sysfs_read(struct file *fp, return -EINVAL; mutex_lock(&arvo->arvo_lock); - retval = arvo_receive(usb_dev, command, buf, real_size); + retval = roccat_common_receive(usb_dev, command, buf, real_size); mutex_unlock(&arvo->arvo_lock); return (retval ? retval : real_size); diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c new file mode 100644 index 0000000..13b1eb0 --- /dev/null +++ b/drivers/hid/hid-roccat-common.c @@ -0,0 +1,62 @@ +/* + * Roccat common functions for device specific drivers + * + * Copyright (c) 2011 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 +#include "hid-roccat-common.h" + +int roccat_common_receive(struct usb_device *usb_dev, uint usb_command, + void *data, uint size) +{ + char *buf; + int len; + + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); + + memcpy(data, buf, size); + kfree(buf); + return ((len < 0) ? len : ((len != size) ? -EIO : 0)); +} +EXPORT_SYMBOL_GPL(roccat_common_receive); + +int roccat_common_send(struct usb_device *usb_dev, uint usb_command, + void const *data, uint size) +{ + char *buf; + int len; + + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, data, size); + + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); + + kfree(buf); + return ((len < 0) ? len : ((len != size) ? -EIO : 0)); +} +EXPORT_SYMBOL_GPL(roccat_common_send); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h new file mode 100644 index 0000000..fe45fae --- /dev/null +++ b/drivers/hid/hid-roccat-common.h @@ -0,0 +1,23 @@ +#ifndef __HID_ROCCAT_COMMON_H +#define __HID_ROCCAT_COMMON_H + +/* + * Copyright (c) 2011 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 +#include + +int roccat_common_receive(struct usb_device *usb_dev, uint usb_command, + void *data, uint size); +int roccat_common_send(struct usb_device *usb_dev, uint usb_command, + void const *data, uint size); + +#endif diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index cbd8cc4..5516653 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -28,11 +28,11 @@ #include #include #include -#include #include #include #include "hid-ids.h" #include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-kone.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -58,12 +58,8 @@ static void kone_set_settings_checksum(struct kone_settings *settings) */ static int kone_check_write(struct usb_device *usb_dev) { - int len; - unsigned char *data; - - data = kmalloc(1, GFP_KERNEL); - if (!data) - return -ENOMEM; + int retval; + uint8_t data; do { /* @@ -72,56 +68,36 @@ static int kone_check_write(struct usb_device *usb_dev) */ msleep(80); - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | - USB_DIR_IN, - kone_command_confirm_write, 0, data, 1, - USB_CTRL_SET_TIMEOUT); - - if (len != 1) { - kfree(data); - return -EIO; - } + retval = roccat_common_receive(usb_dev, + kone_command_confirm_write, &data, 1); + if (retval) + return retval; /* * value of 3 seems to mean something like * "not finished yet, but it looks good" * So check again after a moment. */ - } while (*data == 3); + } while (data == 3); - if (*data == 1) { /* everything alright */ - kfree(data); + if (data == 1) /* everything alright */ return 0; - } else { /* unknown answer */ - hid_err(usb_dev, "got retval %d when checking write\n", *data); - kfree(data); - return -EIO; - } + + /* unknown answer */ + hid_err(usb_dev, "got retval %d when checking write\n", data); + return -EIO; } /* * Reads settings from mouse and stores it in @buf - * @buf has to be alloced with GFP_KERNEL * On success returns 0 * On failure returns errno */ static int kone_get_settings(struct usb_device *usb_dev, struct kone_settings *buf) { - int len; - - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - kone_command_settings, 0, buf, - sizeof(struct kone_settings), USB_CTRL_SET_TIMEOUT); - - if (len != sizeof(struct kone_settings)) - return -EIO; - - return 0; + return roccat_common_receive(usb_dev, kone_command_settings, buf, + sizeof(struct kone_settings)); } /* @@ -132,22 +108,12 @@ static int kone_get_settings(struct usb_device *usb_dev, static int kone_set_settings(struct usb_device *usb_dev, struct kone_settings const *settings) { - int len; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - kone_command_settings, 0, (char *)settings, - sizeof(struct kone_settings), - USB_CTRL_SET_TIMEOUT); - - if (len != sizeof(struct kone_settings)) - return -EIO; - - if (kone_check_write(usb_dev)) - return -EIO; - - return 0; + int retval; + retval = roccat_common_send(usb_dev, kone_command_settings, + settings, sizeof(struct kone_settings)); + if (retval) + return retval; + return kone_check_write(usb_dev); } /* @@ -193,7 +159,7 @@ static int kone_set_profile(struct usb_device *usb_dev, len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), USB_REQ_SET_CONFIGURATION, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - kone_command_profile, number, (char *)profile, + kone_command_profile, number, (void *)profile, sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT); @@ -213,24 +179,15 @@ static int kone_set_profile(struct usb_device *usb_dev, */ static int kone_get_weight(struct usb_device *usb_dev, int *result) { - int len; - uint8_t *data; + int retval; + uint8_t data; - data = kmalloc(1, GFP_KERNEL); - if (!data) - return -ENOMEM; + retval = roccat_common_receive(usb_dev, kone_command_weight, &data, 1); - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - kone_command_weight, 0, data, 1, USB_CTRL_SET_TIMEOUT); + if (retval) + return retval; - if (len != 1) { - kfree(data); - return -EIO; - } - *result = (int)*data; - kfree(data); + *result = (int)data; return 0; } @@ -241,25 +198,15 @@ static int kone_get_weight(struct usb_device *usb_dev, int *result) */ static int kone_get_firmware_version(struct usb_device *usb_dev, int *result) { - int len; - unsigned char *data; - - data = kmalloc(2, GFP_KERNEL); - if (!data) - return -ENOMEM; + int retval; + uint16_t data; - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - kone_command_firmware_version, 0, data, 2, - USB_CTRL_SET_TIMEOUT); + retval = roccat_common_receive(usb_dev, kone_command_firmware_version, + &data, 2); + if (retval) + return retval; - if (len != 2) { - kfree(data); - return -EIO; - } - *result = le16_to_cpu(*data); - kfree(data); + *result = le16_to_cpu(data); return 0; } @@ -435,23 +382,9 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev, static int kone_tcu_command(struct usb_device *usb_dev, int number) { - int len; - char *value; - - value = kmalloc(1, GFP_KERNEL); - if (!value) - return -ENOMEM; - - *value = number; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - kone_command_calibrate, 0, value, 1, - USB_CTRL_SET_TIMEOUT); - - kfree(value); - return ((len != 1) ? -EIO : 0); + unsigned char value; + value = number; + return roccat_common_send(usb_dev, kone_command_calibrate, &value, 1); } /* diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index c826c0d..d1c3a02 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -19,11 +19,11 @@ #include #include #include -#include #include #include #include "hid-ids.h" #include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -39,110 +39,63 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus, static int koneplus_send_control(struct usb_device *usb_dev, uint value, enum koneplus_control_requests request) { - int len; - struct koneplus_control *control; + struct koneplus_control control; if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && value > 4) return -EINVAL; - control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL); - if (!control) - return -ENOMEM; + control.command = KONEPLUS_COMMAND_CONTROL; + control.value = value; + control.request = request; - control->command = KONEPLUS_COMMAND_CONTROL; - control->value = value; - control->request = request; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - KONEPLUS_USB_COMMAND_CONTROL, 0, control, - sizeof(struct koneplus_control), - USB_CTRL_SET_TIMEOUT); - - kfree(control); - - if (len != sizeof(struct koneplus_control)) - return len; - - return 0; -} - -static int koneplus_receive(struct usb_device *usb_dev, uint usb_command, - void *buf, uint size) { - int len; - - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); - - return (len != size) ? -EIO : 0; + return roccat_common_send(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct koneplus_control)); } static int koneplus_receive_control_status(struct usb_device *usb_dev) { int retval; - struct koneplus_control *control; - - control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL); - if (!control) - return -ENOMEM; + struct koneplus_control control; do { - retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, - control, sizeof(struct koneplus_control)); + retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct koneplus_control)); /* check if we get a completely wrong answer */ if (retval) - goto out; + return retval; - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) { - retval = 0; - goto out; - } + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) + return 0; /* indicates that hardware needs some more time to complete action */ - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) { + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) { msleep(500); /* windows driver uses 1000 */ continue; } /* seems to be critical - replug necessary */ - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) { - retval = -EINVAL; - goto out; - } + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) + return -EINVAL; hid_err(usb_dev, "koneplus_receive_control_status: " - "unknown response value 0x%x\n", control->value); - retval = -EINVAL; - goto out; - + "unknown response value 0x%x\n", control.value); + return -EINVAL; } while (1); -out: - kfree(control); - return retval; } static int koneplus_send(struct usb_device *usb_dev, uint command, - void *buf, uint size) { - int len; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - command, 0, buf, size, USB_CTRL_SET_TIMEOUT); - - if (len != size) - return -EIO; + void const *buf, uint size) +{ + int retval; - if (koneplus_receive_control_status(usb_dev)) - return -EIO; + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; - return 0; + return koneplus_receive_control_status(usb_dev); } static int koneplus_select_profile(struct usb_device *usb_dev, uint number, @@ -167,7 +120,7 @@ static int koneplus_select_profile(struct usb_device *usb_dev, uint number, static int koneplus_get_info(struct usb_device *usb_dev, struct koneplus_info *buf) { - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO, buf, sizeof(struct koneplus_info)); } @@ -181,7 +134,7 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, buf, sizeof(struct koneplus_profile_settings)); } @@ -189,7 +142,7 @@ static int koneplus_set_profile_settings(struct usb_device *usb_dev, struct koneplus_profile_settings const *settings) { return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, - (void *)settings, sizeof(struct koneplus_profile_settings)); + settings, sizeof(struct koneplus_profile_settings)); } static int koneplus_get_profile_buttons(struct usb_device *usb_dev, @@ -202,7 +155,7 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev, if (retval) return retval; - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, buf, sizeof(struct koneplus_profile_buttons)); } @@ -210,29 +163,19 @@ static int koneplus_set_profile_buttons(struct usb_device *usb_dev, struct koneplus_profile_buttons const *buttons) { return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, - (void *)buttons, sizeof(struct koneplus_profile_buttons)); + buttons, sizeof(struct koneplus_profile_buttons)); } /* retval is 0-4 on success, < 0 on error */ static int koneplus_get_startup_profile(struct usb_device *usb_dev) { - struct koneplus_startup_profile *buf; + struct koneplus_startup_profile buf; int retval; - buf = kmalloc(sizeof(struct koneplus_startup_profile), GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, - buf, sizeof(struct koneplus_startup_profile)); + retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, + &buf, sizeof(struct koneplus_startup_profile)); - if (retval) - goto out; - - retval = buf->startup_profile; -out: - kfree(buf); - return retval; + return retval ? retval : buf.startup_profile; } static int koneplus_set_startup_profile(struct usb_device *usb_dev, @@ -245,7 +188,7 @@ static int koneplus_set_startup_profile(struct usb_device *usb_dev, buf.startup_profile = startup_profile; return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, - (char *)&buf, sizeof(struct koneplus_profile_buttons)); + &buf, sizeof(struct koneplus_profile_buttons)); } static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, @@ -265,7 +208,7 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = koneplus_receive(usb_dev, command, buf, real_size); + retval = roccat_common_receive(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) @@ -288,7 +231,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = koneplus_send(usb_dev, command, (void *)buf, real_size); + retval = koneplus_send(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) @@ -352,7 +295,7 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, count = sizeof(struct koneplus_profile_settings) - off; mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((void const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off, + memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off, count); mutex_unlock(&koneplus->koneplus_lock); @@ -411,7 +354,7 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, count = sizeof(struct koneplus_profile_buttons) - off; mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((void const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off, + memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off, count); mutex_unlock(&koneplus->koneplus_lock); diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 02c58e0..abe77d3 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -20,11 +20,11 @@ #include #include #include -#include #include #include #include "hid-ids.h" #include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-pyra.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -42,7 +42,6 @@ static void profile_activated(struct pyra_device *pyra, static int pyra_send_control(struct usb_device *usb_dev, int value, enum pyra_control_requests request) { - int len; struct pyra_control control; if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || @@ -54,47 +53,31 @@ static int pyra_send_control(struct usb_device *usb_dev, int value, control.value = value; control.request = request; - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, - sizeof(struct pyra_control), - USB_CTRL_SET_TIMEOUT); - - if (len != sizeof(struct pyra_control)) - return len; - - return 0; + return roccat_common_send(usb_dev, PYRA_USB_COMMAND_CONTROL, + &control, sizeof(struct pyra_control)); } static int pyra_receive_control_status(struct usb_device *usb_dev) { - int len; + int retval; struct pyra_control control; do { msleep(10); - - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | - USB_DIR_IN, - PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, - sizeof(struct pyra_control), - USB_CTRL_SET_TIMEOUT); + retval = roccat_common_receive(usb_dev, PYRA_USB_COMMAND_CONTROL, + &control, sizeof(struct pyra_control)); /* requested too early, try again */ - } while (len == -EPROTO); + } while (retval == -EPROTO); - if (len == sizeof(struct pyra_control) && - control.command == PYRA_COMMAND_CONTROL && + if (!retval && control.command == PYRA_COMMAND_CONTROL && control.request == PYRA_CONTROL_REQUEST_STATUS && control.value == 1) - return 0; + return 0; else { hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n", control.request, control.value); - return -EINVAL; + return retval ? retval : -EINVAL; } } @@ -102,125 +85,72 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev, struct pyra_profile_settings *buf, int number) { int retval; - retval = pyra_send_control(usb_dev, number, PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); - if (retval) return retval; - - retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf, - sizeof(struct pyra_profile_settings), - USB_CTRL_SET_TIMEOUT); - - if (retval != sizeof(struct pyra_profile_settings)) - return retval; - - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, + buf, sizeof(struct pyra_profile_settings)); } static int pyra_get_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons *buf, int number) { int retval; - retval = pyra_send_control(usb_dev, number, PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); - if (retval) return retval; - - retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf, - sizeof(struct pyra_profile_buttons), - USB_CTRL_SET_TIMEOUT); - - if (retval != sizeof(struct pyra_profile_buttons)) - return retval; - - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, + buf, sizeof(struct pyra_profile_buttons)); } static int pyra_get_settings(struct usb_device *usb_dev, struct pyra_settings *buf) { - int len; - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_SETTINGS, 0, buf, - sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_settings)) - return -EIO; - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_SETTINGS, + buf, sizeof(struct pyra_settings)); } static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) { - int len; - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_INFO, 0, buf, - sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_info)) - return -EIO; - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_INFO, + buf, sizeof(struct pyra_info)); +} + +static int pyra_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 pyra_receive_control_status(usb_dev); } static int pyra_set_profile_settings(struct usb_device *usb_dev, struct pyra_profile_settings const *settings) { - int len; - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings, - sizeof(struct pyra_profile_settings), - USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_profile_settings)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, settings, + sizeof(struct pyra_profile_settings)); } static int pyra_set_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons const *buttons) { - int len; - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons, - sizeof(struct pyra_profile_buttons), - USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_profile_buttons)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, buttons, + sizeof(struct pyra_profile_buttons)); } static int pyra_set_settings(struct usb_device *usb_dev, struct pyra_settings const *settings) { - int len; - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings, - sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_settings)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + int retval; + retval = roccat_common_send(usb_dev, PYRA_USB_COMMAND_SETTINGS, settings, + sizeof(struct pyra_settings)); + if (retval) + return retval; + return pyra_receive_control_status(usb_dev); } static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, @@ -521,21 +451,16 @@ static struct bin_attribute pyra_bin_attributes[] = { static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, struct pyra_device *pyra) { - struct pyra_info *info; + struct pyra_info info; int retval, i; mutex_init(&pyra->pyra_lock); - info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - retval = pyra_get_info(usb_dev, info); - if (retval) { - kfree(info); + retval = pyra_get_info(usb_dev, &info); + if (retval) return retval; - } - pyra->firmware_version = info->firmware_version; - kfree(info); + + pyra->firmware_version = info.firmware_version; retval = pyra_get_settings(usb_dev, &pyra->settings); if (retval) -- cgit v0.10.2 From 0e70f97f257edcef4daa92ab9371a9aac0c851ed Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:24 +0100 Subject: HID: roccat: Add support for Kova[+] mouse This patch adds support for Roccat Kova[+] mouse. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus new file mode 100644 index 0000000..9e54af4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -0,0 +1,91 @@ +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_cpi +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-4. + When read, this attribute returns the number of the active + cpi level. + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 0-4. + When read, this attribute returns the number of the active + profile. + When written, the mouse activates this profile immediately. + The profile that's active when powered down is the same that's + active when the mouse is powered on. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_x +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-10. + When read, this attribute returns the number of the actual + sensitivity in x direction. + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_y +Date: January 2011 +Contact: Stefan Achatz +Description: The integer value of this attribute ranges from 1-10. + When read, this attribute returns the number of the actual + sensitivity in y direction. + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/firmware_version +Date: January 2011 +Contact: Stefan Achatz +Description: When read, this file returns the raw integer version number of the + firmware reported by the mouse. Using the integer value eases + further usage in other programs. To receive the real version + number the decimal point has to be shifted 2 positions to the + left. E.g. a returned value of 121 means 1.21 + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds informations about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 23 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. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_buttons +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds informations about button layout. + When read, these files return the respective profile buttons. + The returned data is 23 bytes in size. + This file is readonly. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings 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 16 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. + +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile[1-5]_settings +Date: January 2011 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds informations like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 16 bytes in size. + This file is readonly. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7952369..022515f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -444,6 +444,14 @@ config HID_ROCCAT_KONEPLUS ---help--- Support for Roccat Kone[+] mouse. +config HID_ROCCAT_KOVAPLUS + tristate "Roccat Kova[+] mouse support" + depends on USB_HID + select HID_ROCCAT + select HID_ROCCAT_COMMON + ---help--- + Support for Roccat Kova[+] mouse. + config HID_ROCCAT_PYRA tristate "Roccat Pyra mouse support" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 086cf62..cb80181 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o +obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.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 6178025..0180f20 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1407,6 +1407,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, + { 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_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 92e0fa1..75f2e14 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -499,6 +499,7 @@ #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c new file mode 100644 index 0000000..1f547a2 --- /dev/null +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -0,0 +1,715 @@ +/* + * Roccat Kova[+] driver for Linux + * + * Copyright (c) 2011 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 Kova[+] is a bigger version of the Pyra with two more side buttons. + */ + +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat.h" +#include "hid-roccat-common.h" +#include "hid-roccat-kovaplus.h" + +static uint profile_numbers[5] = {0, 1, 2, 3, 4}; + +static struct class *kovaplus_class; + +static uint kovaplus_convert_event_cpi(uint value) +{ + return (value == 7 ? 4 : (value == 4 ? 3 : value)); +} + +static void kovaplus_profile_activated(struct kovaplus_device *kovaplus, + uint new_profile_index) +{ + kovaplus->actual_profile = new_profile_index; + kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level; + kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x; + kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y; +} + +static int kovaplus_send_control(struct usb_device *usb_dev, uint value, + enum kovaplus_control_requests request) +{ + int retval; + struct kovaplus_control control; + + if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || + request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && + value > 4) + return -EINVAL; + + control.command = KOVAPLUS_COMMAND_CONTROL; + control.value = value; + control.request = request; + + retval = roccat_common_send(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct kovaplus_control)); + + return retval; +} + +static int kovaplus_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct kovaplus_control control; + + do { + retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct kovaplus_control)); + + /* check if we get a completely wrong answer */ + if (retval) + return retval; + + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK) + return 0; + + /* indicates that hardware needs some more time to complete action */ + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) { + msleep(500); /* windows driver uses 1000 */ + continue; + } + + /* seems to be critical - replug necessary */ + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) + return -EINVAL; + + hid_err(usb_dev, "kovaplus_receive_control_status: " + "unknown response value 0x%x\n", control.value); + return -EINVAL; + } while (1); +} + +static int kovaplus_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; + + msleep(100); + + return kovaplus_receive_control_status(usb_dev); +} + +static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, + enum kovaplus_control_requests request) +{ + return kovaplus_send_control(usb_dev, number, request); +} + +static int kovaplus_get_info(struct usb_device *usb_dev, + struct kovaplus_info *buf) +{ + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_INFO, + buf, sizeof(struct kovaplus_info)); +} + +static int kovaplus_get_profile_settings(struct usb_device *usb_dev, + struct kovaplus_profile_settings *buf, uint number) +{ + int retval; + + retval = kovaplus_select_profile(usb_dev, number, + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); + if (retval) + return retval; + + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS, + buf, sizeof(struct kovaplus_profile_settings)); +} + +static int kovaplus_set_profile_settings(struct usb_device *usb_dev, + struct kovaplus_profile_settings const *settings) +{ + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS, + settings, sizeof(struct kovaplus_profile_settings)); +} + +static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, + struct kovaplus_profile_buttons *buf, int number) +{ + int retval; + + retval = kovaplus_select_profile(usb_dev, number, + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); + if (retval) + return retval; + + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS, + buf, sizeof(struct kovaplus_profile_buttons)); +} + +static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, + struct kovaplus_profile_buttons const *buttons) +{ + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS, + buttons, sizeof(struct kovaplus_profile_buttons)); +} + +/* retval is 0-4 on success, < 0 on error */ +static int kovaplus_get_actual_profile(struct usb_device *usb_dev) +{ + struct kovaplus_actual_profile buf; + int retval; + + retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct kovaplus_actual_profile)); + + return retval ? retval : buf.actual_profile; +} + +static int kovaplus_set_actual_profile(struct usb_device *usb_dev, + int new_profile) +{ + struct kovaplus_actual_profile buf; + + buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE; + buf.size = sizeof(struct kovaplus_actual_profile); + buf.actual_profile = new_profile; + + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct kovaplus_actual_profile)); +} + +static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct kovaplus_profile_settings)) + return 0; + + if (off + count > sizeof(struct kovaplus_profile_settings)) + count = sizeof(struct kovaplus_profile_settings) - off; + + mutex_lock(&kovaplus->kovaplus_lock); + memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off, + count); + mutex_unlock(&kovaplus->kovaplus_lock); + + return count; +} + +static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_index; + struct kovaplus_profile_settings *profile_settings; + + if (off != 0 || count != sizeof(struct kovaplus_profile_settings)) + return -EINVAL; + + profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index; + profile_settings = &kovaplus->profile_settings[profile_index]; + + mutex_lock(&kovaplus->kovaplus_lock); + difference = memcmp(buf, profile_settings, + sizeof(struct kovaplus_profile_settings)); + if (difference) { + retval = kovaplus_set_profile_settings(usb_dev, + (struct kovaplus_profile_settings const *)buf); + if (!retval) + memcpy(profile_settings, buf, + sizeof(struct kovaplus_profile_settings)); + } + mutex_unlock(&kovaplus->kovaplus_lock); + + if (retval) + return retval; + + return sizeof(struct kovaplus_profile_settings); +} + +static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct kovaplus_profile_buttons)) + return 0; + + if (off + count > sizeof(struct kovaplus_profile_buttons)) + count = sizeof(struct kovaplus_profile_buttons) - off; + + mutex_lock(&kovaplus->kovaplus_lock); + memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off, + count); + mutex_unlock(&kovaplus->kovaplus_lock); + + return count; +} + +static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + uint profile_index; + struct kovaplus_profile_buttons *profile_buttons; + + if (off != 0 || count != sizeof(struct kovaplus_profile_buttons)) + return -EINVAL; + + profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index; + profile_buttons = &kovaplus->profile_buttons[profile_index]; + + mutex_lock(&kovaplus->kovaplus_lock); + difference = memcmp(buf, profile_buttons, + sizeof(struct kovaplus_profile_buttons)); + if (difference) { + retval = kovaplus_set_profile_buttons(usb_dev, + (struct kovaplus_profile_buttons const *)buf); + if (!retval) + memcpy(profile_buttons, buf, + sizeof(struct kovaplus_profile_buttons)); + } + mutex_unlock(&kovaplus->kovaplus_lock); + + if (retval) + return retval; + + return sizeof(struct kovaplus_profile_buttons); +} + +static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile); +} + +static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct kovaplus_device *kovaplus; + struct usb_device *usb_dev; + unsigned long profile; + int retval; + + dev = dev->parent->parent; + kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + return retval; + + if (profile >= 5) + return -EINVAL; + + mutex_lock(&kovaplus->kovaplus_lock); + retval = kovaplus_set_actual_profile(usb_dev, profile); + kovaplus->actual_profile = profile; + mutex_unlock(&kovaplus->kovaplus_lock); + if (retval) + return retval; + + return size; +} + +static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi); +} + +static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity); +} + +static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity); +} + +static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version); +} + +static struct device_attribute kovaplus_attributes[] = { + __ATTR(actual_cpi, 0440, + kovaplus_sysfs_show_actual_cpi, NULL), + __ATTR(firmware_version, 0440, + kovaplus_sysfs_show_firmware_version, NULL), + __ATTR(actual_profile, 0660, + kovaplus_sysfs_show_actual_profile, + kovaplus_sysfs_set_actual_profile), + __ATTR(actual_sensitivity_x, 0440, + kovaplus_sysfs_show_actual_sensitivity_x, NULL), + __ATTR(actual_sensitivity_y, 0440, + kovaplus_sysfs_show_actual_sensitivity_y, NULL), + __ATTR_NULL +}; + +static struct bin_attribute kovaplus_bin_attributes[] = { + { + .attr = { .name = "profile_settings", .mode = 0220 }, + .size = sizeof(struct kovaplus_profile_settings), + .write = kovaplus_sysfs_write_profile_settings + }, + { + .attr = { .name = "profile1_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[0] + }, + { + .attr = { .name = "profile2_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[1] + }, + { + .attr = { .name = "profile3_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[2] + }, + { + .attr = { .name = "profile4_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[3] + }, + { + .attr = { .name = "profile5_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[4] + }, + { + .attr = { .name = "profile_buttons", .mode = 0220 }, + .size = sizeof(struct kovaplus_profile_buttons), + .write = kovaplus_sysfs_write_profile_buttons + }, + { + .attr = { .name = "profile1_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[0] + }, + { + .attr = { .name = "profile2_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[1] + }, + { + .attr = { .name = "profile3_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[2] + }, + { + .attr = { .name = "profile4_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[3] + }, + { + .attr = { .name = "profile5_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[4] + }, + __ATTR_NULL +}; + +static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, + struct kovaplus_device *kovaplus) +{ + int retval, i; + static uint wait = 70; /* device will freeze with just 60 */ + + mutex_init(&kovaplus->kovaplus_lock); + + retval = kovaplus_get_info(usb_dev, &kovaplus->info); + if (retval) + return retval; + + for (i = 0; i < 5; ++i) { + msleep(wait); + retval = kovaplus_get_profile_settings(usb_dev, + &kovaplus->profile_settings[i], i); + if (retval) + return retval; + + msleep(wait); + retval = kovaplus_get_profile_buttons(usb_dev, + &kovaplus->profile_buttons[i], i); + if (retval) + return retval; + } + + msleep(wait); + retval = kovaplus_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + kovaplus_profile_activated(kovaplus, retval); + + return 0; +} + +static int kovaplus_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 kovaplus_device *kovaplus; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + + kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL); + if (!kovaplus) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, kovaplus); + + retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus); + if (retval) { + hid_err(hdev, "couldn't init struct kovaplus_device\n"); + goto exit_free; + } + + retval = roccat_connect(kovaplus_class, hdev); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + kovaplus->chrdev_minor = retval; + kovaplus->roccat_claimed = 1; + } + + } else { + hid_set_drvdata(hdev, NULL); + } + + return 0; +exit_free: + kfree(kovaplus); + return retval; +} + +static void kovaplus_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct kovaplus_device *kovaplus; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + kovaplus = hid_get_drvdata(hdev); + if (kovaplus->roccat_claimed) + roccat_disconnect(kovaplus->chrdev_minor); + kfree(kovaplus); + } +} + +static int kovaplus_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 = kovaplus_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 kovaplus_remove(struct hid_device *hdev) +{ + kovaplus_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus, + u8 const *data) +{ + struct kovaplus_mouse_report_button const *button_report; + + if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct kovaplus_mouse_report_button const *)data; + + switch (button_report->type) { + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1: + kovaplus_profile_activated(kovaplus, button_report->data1 - 1); + break; + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI: + kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1); + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY: + kovaplus->actual_x_sensitivity = button_report->data1; + kovaplus->actual_y_sensitivity = button_report->data2; + } +} + +static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus, + u8 const *data) +{ + struct kovaplus_roccat_report roccat_report; + struct kovaplus_mouse_report_button const *button_report; + + if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct kovaplus_mouse_report_button const *)data; + + if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2) + return; + + roccat_report.type = button_report->type; + roccat_report.profile = kovaplus->actual_profile + 1; + + if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER) + roccat_report.button = button_report->data1; + else + roccat_report.button = 0; + + if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI) + roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1); + else + roccat_report.data1 = button_report->data1; + + roccat_report.data2 = button_report->data2; + + roccat_report_event(kovaplus->chrdev_minor, + (uint8_t const *)&roccat_report, + sizeof(struct kovaplus_roccat_report)); +} + +static int kovaplus_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 kovaplus_device *kovaplus = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + kovaplus_keep_values_up_to_date(kovaplus, data); + + if (kovaplus->roccat_claimed) + kovaplus_report_to_chrdev(kovaplus, data); + + return 0; +} + +static const struct hid_device_id kovaplus_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, kovaplus_devices); + +static struct hid_driver kovaplus_driver = { + .name = "kovaplus", + .id_table = kovaplus_devices, + .probe = kovaplus_probe, + .remove = kovaplus_remove, + .raw_event = kovaplus_raw_event +}; + +static int __init kovaplus_init(void) +{ + int retval; + + kovaplus_class = class_create(THIS_MODULE, "kovaplus"); + if (IS_ERR(kovaplus_class)) + return PTR_ERR(kovaplus_class); + kovaplus_class->dev_attrs = kovaplus_attributes; + kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes; + + retval = hid_register_driver(&kovaplus_driver); + if (retval) + class_destroy(kovaplus_class); + return retval; +} + +static void __exit kovaplus_exit(void) +{ + class_destroy(kovaplus_class); + hid_unregister_driver(&kovaplus_driver); +} + +module_init(kovaplus_init); +module_exit(kovaplus_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Kova[+] driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h new file mode 100644 index 0000000..ce40607 --- /dev/null +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -0,0 +1,157 @@ +#ifndef __HID_ROCCAT_KOVAPLUS_H +#define __HID_ROCCAT_KOVAPLUS_H + +/* + * Copyright (c) 2010 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 + +struct kovaplus_control { + uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */ + uint8_t value; + uint8_t request; +} __packed; + +enum kovaplus_control_requests { + /* read after write; value = 1 */ + KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0, + /* write; value = profile number range 0-4 */ + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, + /* write; value = profile number range 0-4 */ + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20, +}; + +enum kovaplus_control_values { + KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */ + KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1, + KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */ +}; + +struct kovaplus_actual_profile { + uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */ + uint8_t size; /* always 3 */ + uint8_t actual_profile; /* Range 0-4! */ +} __packed; + +struct kovaplus_profile_settings { + uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_SETTINGS */ + uint8_t size; /* 16 */ + uint8_t profile_index; /* range 0-4 */ + uint8_t unknown1; + uint8_t sensitivity_x; /* range 1-10 */ + uint8_t sensitivity_y; /* range 1-10 */ + uint8_t cpi_levels_enabled; + uint8_t cpi_startup_level; /* range 1-4 */ + uint8_t data[8]; +} __packed; + +struct kovaplus_profile_buttons { + uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_BUTTONS */ + uint8_t size; /* 23 */ + uint8_t profile_index; /* range 0-4 */ + uint8_t data[20]; +} __packed; + +struct kovaplus_info { + uint8_t command; /* KOVAPLUS_COMMAND_INFO */ + uint8_t size; /* 6 */ + uint8_t firmware_version; + uint8_t unknown[3]; +} __packed; + +/* writes 1 on plugin */ +struct kovaplus_a { + uint8_t command; /* KOVAPLUS_COMMAND_A */ + uint8_t size; /* 3 */ + uint8_t unknown; +} __packed; + +enum kovaplus_commands { + KOVAPLUS_COMMAND_CONTROL = 0x4, + KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, + KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7, + KOVAPLUS_COMMAND_INFO = 0x9, + KOVAPLUS_COMMAND_A = 0xa, +}; + +enum kovaplus_usb_commands { + KOVAPLUS_USB_COMMAND_CONTROL = 0x304, + KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305, + KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306, + KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307, + KOVAPLUS_USB_COMMAND_INFO = 0x309, + KOVAPLUS_USB_COMMAND_A = 0x30a, +}; + +enum kovaplus_mouse_report_numbers { + KOVAPLUS_MOUSE_REPORT_NUMBER_MOUSE = 1, + KOVAPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2, + KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON = 3, + KOVAPLUS_MOUSE_REPORT_NUMBER_KBD = 4, +}; + +struct kovaplus_mouse_report_button { + uint8_t report_number; /* KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON */ + uint8_t unknown1; + uint8_t type; + uint8_t data1; + uint8_t data2; +} __packed; + +enum kovaplus_mouse_report_button_types { + /* data1 = profile_number range 1-5; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1 = 0x20, + /* data1 = profile_number range 1-5; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2 = 0x30, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO = 0x40, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT = 0x50, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80, + /* data1 = 1 = 400, 2 = 800, 4 = 1600, 7 = 3200; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0, + /* data1 + data2 = sense range 1-10; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0, + /* data1 = type as in profile_buttons; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +enum kovaplus_mouse_report_button_actions { + KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_PRESS = 0, + KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_RELEASE = 1, +}; + +struct kovaplus_roccat_report { + uint8_t type; + uint8_t profile; + uint8_t button; + uint8_t data1; + uint8_t data2; +} __packed; + +struct kovaplus_device { + int actual_profile; + int actual_cpi; + int actual_x_sensitivity; + int actual_y_sensitivity; + int roccat_claimed; + int chrdev_minor; + struct mutex kovaplus_lock; + struct kovaplus_info info; + struct kovaplus_profile_settings profile_settings[5]; + struct kovaplus_profile_buttons profile_buttons[5]; +}; + +#endif -- cgit v0.10.2 From 432762e28b8146d0feff61cc8063b26c517acf26 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:26 +0100 Subject: HID: roccat: Increase waiting time for Kone[+] to prevent occasional freezes On newer kernels the device freezes occasionally on initialization with just 70msec between reads. Increased this value to safe 100msec. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index d1c3a02..df85ed8 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -557,7 +557,7 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, struct koneplus_device *koneplus) { int retval, i; - static uint wait = 70; /* device will freeze with just 60 */ + static uint wait = 100; /* device will freeze with just 60 */ mutex_init(&koneplus->koneplus_lock); -- cgit v0.10.2 From 8211e46004518c977f70f2661da961d5ba617399 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:25 +0100 Subject: HID: roccat: Add ioctl command to retreive report size from chardev Roccat chardev was reworked to support only a defined report size per device and this can be retreived by an ioctl now to enable future changes in report definitions. Header was moved/renamed from drivers/hid to include/linux for accessibility. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index ac293e9..b0f1fa0 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -133,6 +133,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! +'H' F1 linux/roccat.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index d72ee41..7b9a992 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -21,8 +21,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" @@ -303,7 +303,8 @@ static int arvo_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(arvo_class, hdev); + retval = roccat_connect(arvo_class, hdev, + sizeof(struct arvo_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -386,8 +387,8 @@ static void arvo_report_to_chrdev(struct arvo_device const *arvo, else roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; - roccat_report_event(arvo->chrdev_minor, (uint8_t const *)&roccat_report, - sizeof(struct arvo_roccat_report)); + roccat_report_event(arvo->chrdev_minor, + (uint8_t const *)&roccat_report); } static int arvo_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 5516653..5cdb282 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -30,8 +30,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-kone.h" @@ -660,7 +660,8 @@ static int kone_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kone_class, hdev); + retval = roccat_connect(kone_class, hdev, + sizeof(struct kone_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); /* be tolerant about not getting chrdev */ @@ -760,8 +761,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = event->value; roccat_report.key = 0; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); break; case kone_mouse_event_call_overlong_macro: if (event->value == kone_keystroke_action_press) { @@ -769,8 +769,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = kone->actual_profile; roccat_report.key = event->macro_key; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); } break; } diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index df85ed8..7367e4e 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -21,8 +21,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" @@ -612,7 +612,8 @@ static int koneplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(koneplus_class, hdev); + retval = roccat_connect(koneplus_class, hdev, + sizeof(struct koneplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -718,8 +719,7 @@ static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus, roccat_report.data2 = button_report->data2; roccat_report.profile = koneplus->actual_profile + 1; roccat_report_event(koneplus->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct koneplus_roccat_report)); + (uint8_t const *)&roccat_report); } static int koneplus_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1f547a2..7664e2c 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -20,8 +20,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-kovaplus.h" @@ -524,7 +524,8 @@ static int kovaplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kovaplus_class, hdev); + retval = roccat_connect(kovaplus_class, hdev, + sizeof(struct kovaplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -648,8 +649,7 @@ static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus, roccat_report.data2 = button_report->data2; roccat_report_event(kovaplus->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct kovaplus_roccat_report)); + (uint8_t const *)&roccat_report); } static int kovaplus_raw_event(struct hid_device *hdev, diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index abe77d3..be4daa9 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -22,8 +22,8 @@ #include #include #include +#include #include "hid-ids.h" -#include "hid-roccat.h" #include "hid-roccat-common.h" #include "hid-roccat-pyra.h" @@ -506,7 +506,8 @@ static int pyra_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(pyra_class, hdev); + retval = roccat_connect(pyra_class, hdev, + sizeof(struct pyra_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -610,8 +611,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, roccat_report.value = button_event->data1; roccat_report.key = 0; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); break; case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: @@ -625,8 +625,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, */ roccat_report.value = pyra->actual_profile + 1; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); } break; } diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index a14c579..0fa23de 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,8 +26,7 @@ #include #include #include - -#include "hid-roccat.h" +#include #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 @@ -37,11 +36,11 @@ struct roccat_report { uint8_t *value; - int len; }; struct roccat_device { unsigned int minor; + int report_size; int open; int exist; wait_queue_head_t wait; @@ -123,7 +122,7 @@ static ssize_t roccat_read(struct file *file, char __user *buffer, * If report is larger than requested amount of data, rest of report * is lost! */ - len = report->len > count ? count : report->len; + len = device->report_size > count ? count : device->report_size; if (copy_to_user(buffer, report->value, len)) { retval = -EFAULT; @@ -248,26 +247,25 @@ static int roccat_release(struct inode *inode, struct file *file) * * This is called from interrupt handler. */ -int roccat_report_event(int minor, u8 const *data, int len) +int roccat_report_event(int minor, u8 const *data) { struct roccat_device *device; struct roccat_reader *reader; struct roccat_report *report; uint8_t *new_value; - new_value = kmemdup(data, len, GFP_ATOMIC); + device = devices[minor]; + + new_value = kmemdup(data, device->report_size, GFP_ATOMIC); if (!new_value) return -ENOMEM; - device = devices[minor]; - report = &device->cbuf[device->cbuf_end]; /* passing NULL is safe */ kfree(report->value); report->value = new_value; - report->len = len; device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE; list_for_each_entry(reader, &device->readers, node) { @@ -295,7 +293,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. */ -int roccat_connect(struct class *klass, struct hid_device *hid) +int roccat_connect(struct class *klass, struct hid_device *hid, int report_size) { unsigned int minor; struct roccat_device *device; @@ -343,6 +341,7 @@ int roccat_connect(struct class *klass, struct hid_device *hid) device->hid = hid; device->exist = 1; device->cbuf_end = 0; + device->report_size = report_size; return minor; } @@ -373,6 +372,34 @@ void roccat_disconnect(int minor) } EXPORT_SYMBOL_GPL(roccat_disconnect); +static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct roccat_device *device; + unsigned int minor = iminor(inode); + long retval = 0; + + mutex_lock(&devices_lock); + + device = devices[minor]; + if (!device) { + retval = -ENODEV; + goto out; + } + + switch (cmd) { + case ROCCATIOCGREPSIZE: + if (put_user(device->report_size, (int __user *)arg)) + retval = -EFAULT; + break; + default: + retval = -ENOTTY; + } +out: + mutex_unlock(&devices_lock); + return retval; +} + static const struct file_operations roccat_ops = { .owner = THIS_MODULE, .read = roccat_read, @@ -380,6 +407,7 @@ static const struct file_operations roccat_ops = { .open = roccat_open, .release = roccat_release, .llseek = noop_llseek, + .unlocked_ioctl = roccat_ioctl, }; static int __init roccat_init(void) diff --git a/drivers/hid/hid-roccat.h b/drivers/hid/hid-roccat.h deleted file mode 100644 index c739337..0000000 --- a/drivers/hid/hid-roccat.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_H - -/* - * Copyright (c) 2010 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 -#include - -int roccat_connect(struct class *klass, struct hid_device *hid); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data, int len); - -#endif diff --git a/include/linux/roccat.h b/include/linux/roccat.h new file mode 100644 index 0000000..24e1ca0 --- /dev/null +++ b/include/linux/roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 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 +#include + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif -- cgit v0.10.2 From 74b643dac475e29f53f4132d2349ec1dba3c9e44 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:27 +0100 Subject: HID: roccat: Fix NULL pointer dereference when unloading module Class was destroyed before starting the unregistering driver chain. Disconnecting a device from roccat chardev in this process then raised a NULL pointer dereference. Fixed this by destroying class after unregistering driver. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 7b9a992..75f532f 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -438,8 +438,8 @@ static int __init arvo_init(void) static void __exit arvo_exit(void) { - class_destroy(arvo_class); hid_unregister_driver(&arvo_driver); + class_destroy(arvo_class); } module_init(arvo_init); diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 5cdb282..d749796 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -844,8 +844,8 @@ static int __init kone_init(void) static void __exit kone_exit(void) { - class_destroy(kone_class); hid_unregister_driver(&kone_driver); + class_destroy(kone_class); } module_init(kone_init); diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 7367e4e..ac20107 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -774,8 +774,8 @@ static int __init koneplus_init(void) static void __exit koneplus_exit(void) { - class_destroy(koneplus_class); hid_unregister_driver(&koneplus_driver); + class_destroy(koneplus_class); } module_init(koneplus_init); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 7664e2c..4eeb62f 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -703,8 +703,8 @@ static int __init kovaplus_init(void) static void __exit kovaplus_exit(void) { - class_destroy(kovaplus_class); hid_unregister_driver(&kovaplus_driver); + class_destroy(kovaplus_class); } module_init(kovaplus_init); diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index be4daa9..02a7243 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -685,8 +685,8 @@ static int __init pyra_init(void) static void __exit pyra_exit(void) { - class_destroy(pyra_class); hid_unregister_driver(&pyra_driver); + class_destroy(pyra_class); } module_init(pyra_init); -- cgit v0.10.2 From e6fa47af5542df3383a78d7cc78f5d5ef63c0d42 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 30 Jan 2011 13:38:30 +0100 Subject: HID: roccat: Fix bug that prevented roccat chardev from removing devices pointer to device was cleared too early. This is fixed now. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 0fa23de..bbe294c 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -356,13 +356,16 @@ void roccat_disconnect(int minor) mutex_lock(&devices_lock); device = devices[minor]; - devices[minor] = NULL; mutex_unlock(&devices_lock); device->exist = 0; /* TODO exist maybe not needed */ device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + mutex_lock(&devices_lock); + devices[minor] = NULL; + mutex_unlock(&devices_lock); + if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); -- cgit v0.10.2 From 5dc0c9835fb96c75c8dbf657393764bd0abbac04 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 3 Feb 2011 16:14:43 +0100 Subject: HID: roccat: Rename header roccat.h -> hid-roccat.h It was desired that the header roccat.h should be named hid-roccat.h Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index b0f1fa0..e68543f 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -133,7 +133,7 @@ Code Seq#(hex) Include File Comments 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! 'H' C0-DF net/bluetooth/bnep/bnep.h conflict! -'H' F1 linux/roccat.h +'H' F1 linux/hid-roccat.h 'I' all linux/isdn.h conflict! 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 75f532f..2307471 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index d749796..a57838d 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kone.h" diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index ac20107..a8e2117 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 4eeb62f..984be2f 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-kovaplus.h" diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 02a7243..160f481 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "hid-ids.h" #include "hid-roccat-common.h" #include "hid-roccat-pyra.h" diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index bbe294c..5666e75 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 diff --git a/include/linux/hid-roccat.h b/include/linux/hid-roccat.h new file mode 100644 index 0000000..24e1ca0 --- /dev/null +++ b/include/linux/hid-roccat.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_H +#define __HID_ROCCAT_H + +/* + * Copyright (c) 2010 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 +#include + +#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) + +#ifdef __KERNEL__ + +int roccat_connect(struct class *klass, struct hid_device *hid, + int report_size); +void roccat_disconnect(int minor); +int roccat_report_event(int minor, u8 const *data); + +#endif + +#endif diff --git a/include/linux/roccat.h b/include/linux/roccat.h deleted file mode 100644 index 24e1ca0..0000000 --- a/include/linux/roccat.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_H - -/* - * Copyright (c) 2010 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 -#include - -#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int) - -#ifdef __KERNEL__ - -int roccat_connect(struct class *klass, struct hid_device *hid, - int report_size); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data); - -#endif - -#endif -- cgit v0.10.2 From 75b07022462fc33d6be15669ad604118653210a9 Mon Sep 17 00:00:00 2001 From: Chris Schlund Date: Thu, 3 Feb 2011 16:41:47 +0100 Subject: HID: add support for wireless remote LC Power model RC1000MCE Signed-off-by: Chris Schlund Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24cca2f..94e73db 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -218,6 +218,12 @@ config HID_KENSINGTON ---help--- Support for Kensington Slimblade Trackball. +config HID_LCPOWER + tristate "LC-Power" + depends on USB_HID + ---help--- + Support for LC-Power RC1000MCE RF remote control. + config HID_LOGITECH tristate "Logitech devices" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6efc2a0..2e1b1e3 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KYE) += hid-kye.o +obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3..2dcdf9f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1348,6 +1348,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61..15aca15 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -352,6 +352,9 @@ #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 +#define USB_VENDOR_ID_LCPOWER 0x1241 +#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767 + #define USB_VENDOR_ID_LD 0x0f11 #define USB_DEVICE_ID_LD_CASSY 0x1000 #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c new file mode 100644 index 0000000..4bdbcf8 --- /dev/null +++ b/drivers/hid/hid-lcpower.c @@ -0,0 +1,70 @@ +/* + * HID driver for LC Power Model RC1000MCE + * + * Copyright (c) 2011 Chris Schlund + * based on hid-topseed module + */ + +/* + * 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 "hid-ids.h" + +#define ts_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x046: ts_map_key_clear(KEY_YELLOW); break; + case 0x047: ts_map_key_clear(KEY_GREEN); break; + case 0x049: ts_map_key_clear(KEY_RED); break; + case 0x04a: ts_map_key_clear(KEY_BLUE); break; + case 0x00d: ts_map_key_clear(KEY_HOME); break; + case 0x025: ts_map_key_clear(KEY_TV); break; + case 0x048: ts_map_key_clear(KEY_VCR); break; + case 0x024: ts_map_key_clear(KEY_MENU); break; + default: + return 0; + } + + return 1; +} + +static const struct hid_device_id ts_devices[] = { + { HID_USB_DEVICE( USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ts_devices); + +static struct hid_driver ts_driver = { + .name = "LC RC1000MCE", + .id_table = ts_devices, + .input_mapping = ts_input_mapping, +}; + +static int __init ts_init(void) +{ + return hid_register_driver(&ts_driver); +} + +static void __exit ts_exit(void) +{ + hid_unregister_driver(&ts_driver); +} + +module_init(ts_init); +module_exit(ts_exit); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 759ae89a6e29472cde17b80778a690430252049a Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Thu, 3 Feb 2011 17:10:30 +0100 Subject: HID: roccat: Add 'Users:' tag to sysfs documentation Added 'Users:' tag to sysfs documentation of roccat device drivers pointing to project website at sourceforge. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo b/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo index d54827d..55e281b 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo @@ -6,6 +6,7 @@ Description: The integer value of this attribute ranges from 1-5. profile which is also the profile that's active on device startup. When written this attribute activates the selected profile immediately. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/button Date: Januar 2011 @@ -16,6 +17,7 @@ Description: The keyboard can store short macros with consist of 1 button with button for a specific profile. Button and profile numbers are included in written data. The data has to be 24 bytes long. This file is writeonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/info Date: Januar 2011 @@ -24,6 +26,7 @@ Description: When read, this file returns some info about the device like the installed firmware version. The size of the data is 8 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/key_mask Date: Januar 2011 @@ -36,6 +39,7 @@ Description: The keyboard lets the user deactivate 5 certain keys like the When read, this file returns the current state of the buttons. When written, the given buttons are activated/deactivated immediately. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./arvo/roccatarvo/mode_key Date: Januar 2011 @@ -45,4 +49,5 @@ Description: The keyboard has a condensed layout without num-lock key. the assignment of the number block changes. The integer value of this attribute ranges from 0 (OFF) to 1 (ON). When read, this file returns the actual state of the key. - When written, the key is activated/deactivated immediately. \ No newline at end of file + When written, the key is activated/deactivated immediately. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone index 698b808..b4c4f15 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone @@ -16,12 +16,14 @@ Description: It is possible to switch the dpi setting of the mouse with the 6 3200 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/actual_profile Date: March 2010 Contact: Stefan Achatz Description: When read, this file returns the number of the actual profile. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/firmware_version Date: March 2010 @@ -32,6 +34,7 @@ Description: When read, this file returns the raw integer version number of the number the decimal point has to be shifted 2 positions to the left. E.g. a returned value of 138 means 1.38 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/profile[1-5] Date: March 2010 @@ -47,6 +50,7 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data, whereas the profile number stored in the profile doesn't need to fit the number of the store. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/settings Date: March 2010 @@ -57,6 +61,7 @@ Description: When read, this file returns the settings stored in the mouse. When written, this file lets write settings back to the mouse. The data has to be 36 bytes long. The mouse will reject invalid data. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/startup_profile Date: March 2010 @@ -66,6 +71,7 @@ Description: The integer value of this attribute ranges from 1 to 5. that's active when the mouse is powered on. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/tcu Date: March 2010 @@ -77,6 +83,7 @@ Description: The mouse has a "Tracking Control Unit" which lets the user Writing 0 in this file will switch the TCU off. Writing 1 in this file will start the calibration which takes around 6 seconds to complete and activates the TCU. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kone/roccatkone/weight Date: March 2010 @@ -96,3 +103,4 @@ Description: The mouse can be equipped with one of four supplied weights 4 20g This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index 0f9f30e..00efced 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -4,6 +4,7 @@ Contact: Stefan Achatz Description: When read, this file returns the number of the actual profile in range 0-4. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version Date: October 2010 @@ -14,6 +15,7 @@ Description: When read, this file returns the raw integer version number of the number the decimal point has to be shifted 2 positions to the left. E.g. a returned value of 121 means 1.21 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/macro Date: October 2010 @@ -24,6 +26,7 @@ Description: The mouse can store a macro with max 500 key/button strokes button for a specific profile. Button and profile numbers are included in written data. The data has to be 2082 bytes long. This file is writeonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_buttons Date: August 2010 @@ -37,6 +40,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons Date: August 2010 @@ -47,6 +51,7 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile buttons. The returned data is 77 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_settings Date: October 2010 @@ -61,6 +66,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings Date: August 2010 @@ -72,6 +78,7 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile settings. The returned data is 43 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/sensor Date: October 2010 @@ -80,6 +87,7 @@ Description: The mouse has a tracking- and a distance-control-unit. These can be activated/deactivated and the lift-off distance can be set. The data has to be 6 bytes long. This file is writeonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/startup_profile Date: October 2010 @@ -89,6 +97,7 @@ Description: The integer value of this attribute ranges from 0-4. that's active when the mouse is powered on. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu Date: October 2010 @@ -97,6 +106,7 @@ Description: When written a calibration process for the tracking control unit can be initiated/cancelled. The data has to be 3 bytes long. This file is writeonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu_image Date: October 2010 @@ -106,3 +116,4 @@ Description: When read the mouse returns a 30x30 pixel image of the calibration process initiated with tcu. The returned data is 1028 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus index 9e54af4..fdfa16f 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -5,6 +5,7 @@ Description: The integer value of this attribute ranges from 1-4. When read, this attribute returns the number of the active cpi level. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile Date: January 2011 @@ -15,6 +16,7 @@ Description: The integer value of this attribute ranges from 0-4. When written, the mouse activates this profile immediately. The profile that's active when powered down is the same that's active when the mouse is powered on. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_x Date: January 2011 @@ -23,6 +25,7 @@ Description: The integer value of this attribute ranges from 1-10. When read, this attribute returns the number of the actual sensitivity in x direction. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_y Date: January 2011 @@ -31,6 +34,7 @@ Description: The integer value of this attribute ranges from 1-10. When read, this attribute returns the number of the actual sensitivity in y direction. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/firmware_version Date: January 2011 @@ -41,6 +45,7 @@ Description: When read, this file returns the raw integer version number of the number the decimal point has to be shifted 2 positions to the left. E.g. a returned value of 121 means 1.21 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons Date: January 2011 @@ -54,6 +59,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./kovaplus/roccatkovaplus/profile[1-5]_buttons Date: January 2011 @@ -64,6 +70,7 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile buttons. The returned data is 23 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings Date: January 2011 @@ -78,6 +85,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./kovaplus/roccatkovaplus/profile[1-5]_settings Date: January 2011 @@ -89,3 +97,4 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile settings. The returned data is 16 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra index 1c37b82..5fab71a 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -13,6 +13,7 @@ Description: It is possible to switch the cpi setting of the mouse with the 4 1600 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_profile Date: August 2010 @@ -20,6 +21,7 @@ Contact: Stefan Achatz Description: When read, this file returns the number of the actual profile in range 0-4. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/firmware_version Date: August 2010 @@ -30,6 +32,7 @@ Description: When read, this file returns the raw integer version number of the number the decimal point has to be shifted 2 positions to the left. E.g. a returned value of 138 means 1.38 This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings Date: August 2010 @@ -44,6 +47,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./pyra/roccatpyra/profile[1-5]_settings Date: August 2010 @@ -55,6 +59,7 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile settings. The returned data is 13 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_buttons Date: August 2010 @@ -68,6 +73,7 @@ Description: The mouse can store 5 profiles which can be switched by the 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/-:./::./pyra/roccatpyra/profile[1-5]_buttons Date: August 2010 @@ -78,6 +84,7 @@ Description: The mouse can store 5 profiles which can be switched by the When read, these files return the respective profile buttons. The returned data is 19 bytes in size. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile Date: August 2010 @@ -86,6 +93,7 @@ Description: The integer value of this attribute ranges from 0-4. When read, this attribute returns the number of the profile that's active when the mouse is powered on. This file is readonly. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings Date: August 2010 @@ -96,3 +104,4 @@ Description: When read, this file returns the settings stored in the mouse. When written, this file lets write settings back to the mouse. The data has to be 3 bytes long. The mouse will reject invalid data. +Users: http://roccat.sourceforge.net -- cgit v0.10.2 From 0f69dca20f77dc374b67e17e10b30cec37e778c4 Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:37 -0500 Subject: HID: bt: Move hid_add_device() call to after hidp_session() has started. Move the call to hid_add_device() (which calls a device's probe() function) to after the kernel_thread() call which starts the hidp_session() thread. This ensures the Bluetooth receive socket is fully running by the time a device's probe() function is called. This way, a device can communicate (send and receive) with the Bluetooth device from its probe() function. Signed-off-by: Alan Ott Acked-by: Gustavo F. Padovan Signed-off-by: Jiri Kosina diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 29544c2..67cc4bc 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -563,6 +563,8 @@ static int hidp_session(void *arg) init_waitqueue_entry(&intr_wait, current); add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); add_wait_queue(sk_sleep(intr_sk), &intr_wait); + session->waiting_for_startup = 0; + wake_up_interruptible(&session->startup_queue); while (!atomic_read(&session->terminate)) { set_current_state(TASK_INTERRUPTIBLE); @@ -754,6 +756,8 @@ static struct hid_ll_driver hidp_hid_driver = { .hidinput_input_event = hidp_hidinput_event, }; +/* This function sets up the hid device. It does not add it + to the HID system. That is done in hidp_add_connection(). */ static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { @@ -795,16 +799,8 @@ static int hidp_setup_hid(struct hidp_session *session, hid->hid_output_raw_report = hidp_output_raw_report; - err = hid_add_device(hid); - if (err < 0) - goto failed; - return 0; -failed: - hid_destroy_device(hid); - session->hid = NULL; - fault: kfree(session->rd_data); session->rd_data = NULL; @@ -853,6 +849,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, skb_queue_head_init(&session->ctrl_transmit); skb_queue_head_init(&session->intr_transmit); + init_waitqueue_head(&session->startup_queue); + session->waiting_for_startup = 1; session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; @@ -875,6 +873,14 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, err = kernel_thread(hidp_session, session, CLONE_KERNEL); if (err < 0) goto unlink; + while (session->waiting_for_startup) { + wait_event_interruptible(session->startup_queue, + !session->waiting_for_startup); + } + + err = hid_add_device(session->hid); + if (err < 0) + goto err_add_device; if (session->input) { hidp_send_ctrl_message(session, @@ -888,6 +894,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, up_write(&hidp_session_sem); return 0; +err_add_device: + hid_destroy_device(session->hid); + session->hid = NULL; + atomic_inc(&session->terminate); + hidp_schedule(session); + unlink: hidp_del_timer(session); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 8d934a1..2cc35dc 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -157,6 +157,9 @@ struct hidp_session { /* Report descriptor */ __u8 *rd_data; uint rd_size; + + wait_queue_head_t startup_queue; + int waiting_for_startup; }; static inline void hidp_schedule(struct hidp_session *session) -- cgit v0.10.2 From 0825411ade21a39ac63b3e011d092b1f95b5f3f5 Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:38 -0500 Subject: HID: bt: Wait for ACK on Sent Reports Wait for an ACK from the device before returning from hidp_output_raw_report(). This way, failures can be returned to the user application. Also, it prevents ACK/NAK packets from an output packet from being confused with ACK/NAK packets from an input request packet. Signed-off-by: Alan Ott Acked-by: Gustavo F. Padovan Signed-off-by: Jiri Kosina diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 67cc4bc..5383e6c 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -316,6 +316,9 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, unsigned char report_type) { + struct hidp_session *session = hid->driver_data; + int ret; + switch (report_type) { case HID_FEATURE_REPORT: report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; @@ -327,10 +330,47 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s return -EINVAL; } + if (mutex_lock_interruptible(&session->report_mutex)) + return -ERESTARTSYS; + + /* Set up our wait, and send the report request to the device. */ + set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); if (hidp_send_ctrl_message(hid->driver_data, report_type, - data, count)) - return -ENOMEM; - return count; + data, count)) { + ret = -ENOMEM; + goto err; + } + + /* Wait for the ACK from the device. */ + while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { + int res; + + res = wait_event_interruptible_timeout(session->report_queue, + !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), + 10*HZ); + if (res == 0) { + /* timeout */ + ret = -EIO; + goto err; + } + if (res < 0) { + /* signal */ + ret = -ERESTARTSYS; + goto err; + } + } + + if (!session->output_report_success) { + ret = -EIO; + goto err; + } + + ret = count; + +err: + clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + mutex_unlock(&session->report_mutex); + return ret; } static void hidp_idle_timeout(unsigned long arg) @@ -357,10 +397,12 @@ static void hidp_process_handshake(struct hidp_session *session, unsigned char param) { BT_DBG("session %p param 0x%02x", session, param); + session->output_report_success = 0; /* default condition */ switch (param) { case HIDP_HSHK_SUCCESSFUL: /* FIXME: Call into SET_ GET_ handlers here */ + session->output_report_success = 1; break; case HIDP_HSHK_NOT_READY: @@ -385,6 +427,12 @@ static void hidp_process_handshake(struct hidp_session *session, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); break; } + + /* Wake up the waiting thread. */ + if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { + clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + wake_up_interruptible(&session->report_queue); + } } static void hidp_process_hid_control(struct hidp_session *session, diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 2cc35dc..92e093e 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -80,6 +80,7 @@ #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 +#define HIDP_WAITING_FOR_SEND_ACK 11 struct hidp_connadd_req { int ctrl_sock; // Connected control socket @@ -154,6 +155,9 @@ struct hidp_session { struct sk_buff_head ctrl_transmit; struct sk_buff_head intr_transmit; + /* Used in hidp_output_raw_report() */ + int output_report_success; /* boolean */ + /* Report descriptor */ __u8 *rd_data; uint rd_size; -- cgit v0.10.2 From b4dbde9da8ece42bbe4c70c26bac3b28dd6a3ddb Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:39 -0500 Subject: HID: Add Support for Setting and Getting Feature Reports from hidraw Per the HID Specification, Feature reports must be sent and received on the Configuration endpoint (EP 0) through the Set_Report/Get_Report interfaces. This patch adds two ioctls to hidraw to set and get feature reports to and from the device. Modifications were made to hidraw and usbhid. New hidraw ioctls: HIDIOCSFEATURE - Perform a Set_Report transfer of a Feature report. HIDIOCGFEATURE - Perform a Get_Report transfer of a Feature report. Signed-off-by: Alan Ott Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b..8f06044 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -102,15 +102,14 @@ out: } /* the first byte is expected to be a report number */ -static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +/* This function is to be called with the minors_lock mutex held */ +static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file->f_path.dentry->d_inode); struct hid_device *dev; __u8 *buf; int ret = 0; - mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { ret = -ENODEV; goto out; @@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); + ret = dev->hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + ssize_t ret; + mutex_lock(&minors_lock); + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); mutex_unlock(&minors_lock); return ret; } + +/* This function performs a Get_Report transfer over the control endpoint + per section 7.2.1 of the HID specification, version 1.1. The first byte + of buffer is the report number to request, or 0x0 if the defice does not + use numbered reports. The report_type parameter can be HID_FEATURE_REPORT + or HID_INPUT_REPORT. This function is to be called with the minors_lock + mutex held. */ +static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = hidraw_table[minor]->hid; + + if (!dev->hid_get_raw_report) { + ret = -ENODEV; + goto out; + } + + if (count > HID_MAX_BUFFER_SIZE) { + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Read the first byte from the user. This is the report number, + which is passed to dev->hid_get_raw_report(). */ + if (copy_from_user(&report_number, buffer, 1)) { + ret = -EFAULT; + goto out_free; + } + + ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + + if (ret < 0) + goto out_free; + + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + static unsigned int hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; @@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { ret = -EINVAL; break; } @@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, -EFAULT : len; break; } - } + } ret = -ENOTTY; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b336dd8..38c261a 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } +static int usbhid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int skipped_report_id = 0; + int ret; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = report_number; + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_number, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; + hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e..e8ee0a9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -504,6 +504,9 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); + /* handler for raw input (Get_Report) data, used by hidraw */ + int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); + /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index dd8d692..4b88e69 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -35,6 +35,9 @@ struct hidraw_devinfo { #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) #define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) +/* The first byte of SFEATURE and GFEATURE is the report number */ +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #define HIDRAW_FIRST_MINOR 0 #define HIDRAW_MAX_DEVICES 64 -- cgit v0.10.2 From 0ff1731a1ae51e8e48cd559d70db536281c47f8e Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:40 -0500 Subject: HID: bt: Add support for hidraw HIDIOCGFEATURE and HIDIOCSFEATURE This patch adds support or getting and setting feature reports for bluetooth HID devices from HIDRAW. Signed-off-by: Alan Ott Acked-by: Gustavo F. Padovan Signed-off-by: Jiri Kosina diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 5383e6c..6df8ea1 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -313,6 +314,86 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep return hidp_queue_report(session, buf, rsize); } +static int hidp_get_raw_report(struct hid_device *hid, + unsigned char report_number, + unsigned char *data, size_t count, + unsigned char report_type) +{ + struct hidp_session *session = hid->driver_data; + struct sk_buff *skb; + size_t len; + int numbered_reports = hid->report_enum[report_type].numbered; + + switch (report_type) { + case HID_FEATURE_REPORT: + report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; + break; + case HID_INPUT_REPORT: + report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; + break; + case HID_OUTPUT_REPORT: + report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; + break; + default: + return -EINVAL; + } + + if (mutex_lock_interruptible(&session->report_mutex)) + return -ERESTARTSYS; + + /* Set up our wait, and send the report request to the device. */ + session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; + session->waiting_report_number = numbered_reports ? report_number : -1; + set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + data[0] = report_number; + if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) + goto err_eio; + + /* Wait for the return of the report. The returned report + gets put in session->report_return. */ + while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { + int res; + + res = wait_event_interruptible_timeout(session->report_queue, + !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), + 5*HZ); + if (res == 0) { + /* timeout */ + goto err_eio; + } + if (res < 0) { + /* signal */ + goto err_restartsys; + } + } + + skb = session->report_return; + if (skb) { + len = skb->len < count ? skb->len : count; + memcpy(data, skb->data, len); + + kfree_skb(skb); + session->report_return = NULL; + } else { + /* Device returned a HANDSHAKE, indicating protocol error. */ + len = -EIO; + } + + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + mutex_unlock(&session->report_mutex); + + return len; + +err_restartsys: + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + mutex_unlock(&session->report_mutex); + return -ERESTARTSYS; +err_eio: + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + mutex_unlock(&session->report_mutex); + return -EIO; +} + static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, unsigned char report_type) { @@ -409,6 +490,10 @@ static void hidp_process_handshake(struct hidp_session *session, case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_INVALID_PARAMETER: + if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + wake_up_interruptible(&session->report_queue); + } /* FIXME: Call into SET_ GET_ handlers here */ break; @@ -451,9 +536,11 @@ static void hidp_process_hid_control(struct hidp_session *session, } } -static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, +/* Returns true if the passed-in skb should be freed by the caller. */ +static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, unsigned char param) { + int done_with_skb = 1; BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); switch (param) { @@ -465,7 +552,6 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, if (session->hid) hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); - break; case HIDP_DATA_RTYPE_OTHER: @@ -477,12 +563,27 @@ static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, __hidp_send_ctrl_message(session, HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); } + + if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && + param == session->waiting_report_type) { + if (session->waiting_report_number < 0 || + session->waiting_report_number == skb->data[0]) { + /* hidp_get_raw_report() is waiting on this report. */ + session->report_return = skb; + done_with_skb = 0; + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + wake_up_interruptible(&session->report_queue); + } + } + + return done_with_skb; } static void hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) { unsigned char hdr, type, param; + int free_skb = 1; BT_DBG("session %p skb %p len %d", session, skb, skb->len); @@ -502,7 +603,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, break; case HIDP_TRANS_DATA: - hidp_process_data(session, skb, param); + free_skb = hidp_process_data(session, skb, param); break; default: @@ -511,7 +612,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session, break; } - kfree_skb(skb); + if (free_skb) + kfree_skb(skb); } static void hidp_recv_intr_frame(struct hidp_session *session, @@ -845,6 +947,7 @@ static int hidp_setup_hid(struct hidp_session *session, hid->dev.parent = hidp_get_device(session); hid->ll_driver = &hidp_hid_driver; + hid->hid_get_raw_report = hidp_get_raw_report; hid->hid_output_raw_report = hidp_output_raw_report; return 0; @@ -897,6 +1000,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, skb_queue_head_init(&session->ctrl_transmit); skb_queue_head_init(&session->intr_transmit); + mutex_init(&session->report_mutex); + init_waitqueue_head(&session->report_queue); init_waitqueue_head(&session->startup_queue); session->waiting_for_startup = 1; session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 92e093e..13de5fa 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -80,6 +80,7 @@ #define HIDP_VIRTUAL_CABLE_UNPLUG 0 #define HIDP_BOOT_PROTOCOL_MODE 1 #define HIDP_BLUETOOTH_VENDOR_ID 9 +#define HIDP_WAITING_FOR_RETURN 10 #define HIDP_WAITING_FOR_SEND_ACK 11 struct hidp_connadd_req { @@ -155,6 +156,13 @@ struct hidp_session { struct sk_buff_head ctrl_transmit; struct sk_buff_head intr_transmit; + /* Used in hidp_get_raw_report() */ + int waiting_report_type; /* HIDP_DATA_RTYPE_* */ + int waiting_report_number; /* -1 for not numbered */ + struct mutex report_mutex; + struct sk_buff *report_return; + wait_queue_head_t report_queue; + /* Used in hidp_output_raw_report() */ int output_report_success; /* boolean */ -- cgit v0.10.2 From 8ef395310852710bb4237d918bbac53b22622ef0 Mon Sep 17 00:00:00 2001 From: Chris Schlund Date: Mon, 7 Feb 2011 17:53:10 +0100 Subject: HID: hid-lcpower: fix key mapping I already got some feedback for this module, telling me red and blue keys are exchanged. I checked it, and they are right. Due to my incorrect userspace settings I missed this during my testing. Signed-off-by: Chris Schlund Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c index 4bdbcf8..c4fe9bd0 100644 --- a/drivers/hid/hid-lcpower.c +++ b/drivers/hid/hid-lcpower.c @@ -30,8 +30,8 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, switch (usage->hid & HID_USAGE) { case 0x046: ts_map_key_clear(KEY_YELLOW); break; case 0x047: ts_map_key_clear(KEY_GREEN); break; - case 0x049: ts_map_key_clear(KEY_RED); break; - case 0x04a: ts_map_key_clear(KEY_BLUE); break; + case 0x049: ts_map_key_clear(KEY_BLUE); break; + case 0x04a: ts_map_key_clear(KEY_RED); break; case 0x00d: ts_map_key_clear(KEY_HOME); break; case 0x025: ts_map_key_clear(KEY_TV); break; case 0x048: ts_map_key_clear(KEY_VCR); break; -- cgit v0.10.2 From 6497dc3a5473a0a7948765c45c3dad9ef0395b28 Mon Sep 17 00:00:00 2001 From: Steffen Barszus Date: Mon, 14 Feb 2011 19:53:48 +0100 Subject: HID: Add teletext/color keys - gyration remote - EU version (GYAR3101CKDE) This patch adds support for 5 keys which can be found only on the EU version of the gyration remote, which has been not mapped before. Signed-off-by: Steffen Barszus Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index 3975e03..e88b951 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -43,6 +43,11 @@ static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi, case 0x048: gy_map_key_clear(KEY_MEDIA); break; case 0x049: gy_map_key_clear(KEY_CAMERA); break; case 0x04a: gy_map_key_clear(KEY_VIDEO); break; + case 0x05a: gy_map_key_clear(KEY_TEXT); break; + case 0x05b: gy_map_key_clear(KEY_RED); break; + case 0x05c: gy_map_key_clear(KEY_GREEN); break; + case 0x05d: gy_map_key_clear(KEY_YELLOW); break; + case 0x05e: gy_map_key_clear(KEY_BLUE); break; default: return 0; -- cgit v0.10.2 From d9236303d0b7ba8bbaeb6adbbf088c3fe2a9ab9e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 15 Feb 2011 15:41:10 +0100 Subject: HID: kernel oops in out_cleanup in function hidinput_connect Goto out_cleanup infers a kernel oops: hidinput_disconnect calls input_unregister_driver to all members of hid->inputs. However, hidinput already has been added to hid->inputs even though input_register_device was not called. Signed-off-by: Benjamin Tissoires Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bf..f53911d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -928,6 +928,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return 0; out_cleanup: + list_del(&hidinput->list); input_free_device(hidinput->input); kfree(hidinput); out_unwind: -- cgit v0.10.2 From b2e7617e09a8e774d7819c629a0989cbddb5d1ab Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 15 Feb 2011 15:33:48 +0100 Subject: HID: add IRTOUCH infrared USB to hid_have_special_driver Without this patch, the device is handled by hidinput and does not have the right behavior. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3..a5f6439 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1345,6 +1345,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_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, -- cgit v0.10.2 From 6d3bfb74341c2c1454fcf5ec9c4dd707011f78e5 Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Sun, 23 Jan 2011 22:50:18 -0500 Subject: HID: Add HID Report Descriptor to sysfs Add a new binary sysfs entry called report_descriptor which contains the HID report descriptor. Signed-off-by: Alan Ott Signed-off-by: Jiri Kosina diff --git a/Documentation/ABI/testing/sysfs-driver-hid b/Documentation/ABI/testing/sysfs-driver-hid new file mode 100644 index 0000000..b6490e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid @@ -0,0 +1,10 @@ +What: For USB devices : /sys/bus/usb/devices/-:./::./report_descriptor + For BT devices : /sys/class/bluetooth/hci/::./report_descriptor + Symlink : /sys/class/hidraw/hidraw/device/report_descriptor +Date: Jan 2011 +KernelVersion: 2.0.39 +Contact: Alan Ott +Description: When read, this file returns the device's raw binary HID + report descriptor. + This file cannot be written. +Users: HIDAPI library (http://www.signal11.us/oss/hidapi) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2dcdf9f..5e0e4ee 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1159,6 +1159,32 @@ static bool hid_hiddev(struct hid_device *hdev) return !!hid_match_id(hdev, hid_hiddev_list); } + +static ssize_t +read_report_descriptor(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (off >= hdev->rsize) + return 0; + + if (off + count > hdev->rsize) + count = hdev->rsize - off; + + memcpy(buf, hdev->rdesc + off, count); + + return count; +} + +static struct bin_attribute dev_bin_attr_report_desc = { + .attr = { .name = "report_descriptor", .mode = 0444 }, + .read = read_report_descriptor, + .size = HID_MAX_DESCRIPTOR_SIZE, +}; + int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { static const char *types[] = { "Device", "Pointer", "Mouse", "Device", @@ -1169,6 +1195,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) char buf[64]; unsigned int i; int len; + int ret; if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); @@ -1230,6 +1257,11 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) bus = ""; } + ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc); + if (ret) + hid_warn(hdev, + "can't create sysfs report descriptor attribute err: %d\n", ret); + hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n", buf, bus, hdev->version >> 8, hdev->version & 0xff, type, hdev->name, hdev->phys); @@ -1240,6 +1272,7 @@ EXPORT_SYMBOL_GPL(hid_connect); void hid_disconnect(struct hid_device *hdev) { + device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc); if (hdev->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hdev); if (hdev->claimed & HID_CLAIMED_HIDDEV) -- cgit v0.10.2 From 177900e8c9ab28cdf097314fe7dd3877774df97d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 17 Feb 2011 15:12:45 +0100 Subject: HID: add support for Keytouch IEC 60945 The keyboard has several bugs in its report descriptor, most importantly the Logical Min/Max are completely off. Replace it with simplified descriptor which describes it properly. Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 94e73db..5fa0b95 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -180,6 +180,14 @@ config HID_EZKEY ---help--- Support for Ezkey BTC 8193 keyboard. +config HID_KEYTOUCH + tristate "Keyoutch HID devices" + depends on USB_HID + ---help--- + Support for Keytouch HID devices not fully compliant with + the specification. Currently supported: + - Keytouch IEC 60945 + config HID_KYE tristate "Kye/Genius Ergo Mouse" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2e1b1e3..5696477 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o 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_LOGITECH) += hid-logitech.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5e0e4ee..570db37 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1379,6 +1379,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, { 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) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 15aca15..8fc82a7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -345,6 +345,9 @@ #define USB_VENDOR_ID_KWORLD 0x1b80 #define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 +#define USB_VENDOR_ID_KEYTOUCH 0x0926 +#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333 + #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 diff --git a/drivers/hid/hid-keytouch.c b/drivers/hid/hid-keytouch.c new file mode 100644 index 0000000..07cd825 --- /dev/null +++ b/drivers/hid/hid-keytouch.c @@ -0,0 +1,66 @@ +/* + * HID driver for Keytouch devices not fully compliant with HID standard + * + * Copyright (c) 2011 Jiri Kosina + */ + +/* + * 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 "hid-ids.h" + +/* Replace the broken report descriptor of this device with rather + * a default one */ +static __u8 keytouch_fixed_rdesc[] = { +0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, +0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, +0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, +0x02, 0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, +0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0 +}; + +static __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + hid_info(hdev, "fixing up Keytouch IEC report descriptor\n"); + + rdesc = keytouch_fixed_rdesc; + *rsize = sizeof(keytouch_fixed_rdesc); + + return rdesc; +} + +static const struct hid_device_id keytouch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, + { } +}; +MODULE_DEVICE_TABLE(hid, keytouch_devices); + +static struct hid_driver keytouch_driver = { + .name = "keytouch", + .id_table = keytouch_devices, + .report_fixup = keytouch_report_fixup, +}; + +static int __init keytouch_init(void) +{ + return hid_register_driver(&keytouch_driver); +} + +static void __exit keytouch_exit(void) +{ + hid_unregister_driver(&keytouch_driver); +} + +module_init(keytouch_init); +module_exit(keytouch_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiri Kosina"); -- cgit v0.10.2 From 5710fabf315efcd53c54ad4ecc6158f2964745e3 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Sun, 20 Feb 2011 18:26:45 +0100 Subject: HID: hid-sony.c: Fix sending Output reports to the Sixaxis The Sixaxis does not want the report_id as part of the data packet in Output reports, so we have to discard buf[0] when sending the actual control message. Add also some documentation about that and about why hdev->hid_output_raw_report needs to be overridden. Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 68d7b36..93819a0 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -46,6 +46,16 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +/* + * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP + * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() + * so we need to override that forcing HID Output Reports on the Control EP. + * + * There is also another issue about HID Output Reports via USB, the Sixaxis + * does not want the report_id as part of the data packet, so we have to + * discard buf[0] when sending the actual control message, even for numbered + * reports, humpf! + */ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -55,6 +65,12 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, int report_id = buf[0]; int ret; + if (report_type == HID_OUTPUT_REPORT) { + /* Don't send the Report ID */ + buf++; + count--; + } + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, @@ -62,6 +78,10 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, interface->desc.bInterfaceNumber, buf, count, USB_CTRL_SET_TIMEOUT); + /* Count also the Report ID, in case of an Output report. */ + if (ret > 0 && report_type == HID_OUTPUT_REPORT) + ret++; + return ret; } -- cgit v0.10.2 From 97e1efbbe1c8492ea4f804618e26b19325c879fe Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Sun, 20 Feb 2011 18:26:46 +0100 Subject: HID: bt hidp: send Output reports using SET_REPORT on the Control channel The current implementation of hidp_output_raw_report() relies only on the Control channel even for Output reports, and the BT HID specification [1] does not mention using the DATA message for Output reports on the Control channel (see section 7.9.1 and also Figure 11: SET_ Flow Chart), so let us just use SET_REPORT. This also fixes sending Output reports to some devices (like Sony Sixaxis) which are not able to handle DATA messages on the Control channel. Ideally hidp_output_raw_report() could be improved to use this scheme: Feature Report -- SET_REPORT on the Control channel Output Report -- DATA on the Interrupt channel for more efficiency, but as said above, right now only the Control channel is used. [1] http://www.bluetooth.com/Specification%20Documents/HID_SPEC_V10.pdf Signed-off-by: Antonio Ospite Acked-by: Gustavo F. Padovan Signed-off-by: Jiri Kosina diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 29544c2..e286e70 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -321,7 +321,7 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; break; case HID_OUTPUT_REPORT: - report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; + report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; break; default: return -EINVAL; -- cgit v0.10.2 From f635bd11c8d332d917fb9a4cad3071b2357d5b2a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 19:30:59 +0100 Subject: HID: Do not create input devices for feature reports When the multi input quirk is set, there is a new input device created for every feature report. Since the idea is to present features per hid device, not per input device, revert back to the original report loop and change the feature_mapping() callback to not take the input device as argument. Signed-off-by: Henrik Rydberg Tested-by: Benjamin Tissoires Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bf..ebcc02a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183..2bbc954 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -122,7 +122,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e..fc5faf6 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -638,7 +638,7 @@ struct hid_driver { struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); void (*feature_mapping)(struct hid_device *hdev, - struct hid_input *hidinput, struct hid_field *field, + struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); -- cgit v0.10.2 From f41a52d3010579949a3b9fd76783120d9643b60b Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Tue, 8 Mar 2011 00:24:29 -0500 Subject: HID: ntrig don't dereference unclaimed hidinput Check before dereferencing field->hidinput to fix a reported invalid deference bug. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index beb4034..a93e58c 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -539,8 +539,19 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int ntrig_event (struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { - struct input_dev *input = field->hidinput->input; struct ntrig_data *nd = hid_get_drvdata(hid); + struct input_dev *input; + + /* Skip processing if not a claimed input */ + if (!(hid->claimed & HID_CLAIMED_INPUT)) + goto not_claimed_input; + + /* This function is being called before the structures are fully + * initialized */ + if(!(field->hidinput && field->hidinput->input)) + return -EINVAL; + + input = field->hidinput->input; /* No special handling needed for the pen */ if (field->application == HID_DG_PEN) @@ -810,6 +821,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, } } +not_claimed_input: + /* we have handled the hidinput part, now remains hiddev */ if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) hid->hiddev_hid_event(hid, field, usage, value); -- cgit v0.10.2 From 2d9ca4e9f393d81d8f37ed37505aecbf3a5e1bd6 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 9 Mar 2011 18:38:57 +0100 Subject: HID: hid-magicmouse: Correct touch orientation direction The magic trackpad and mouse both report touch orientation in opposite direction to the bcm5974 driver and what is written in Documents/input/multi-touch-protocol.txt. This patch reverts the direction, so that all in-kernel devices with this feature behave the same way. Since no known application has been utilizing this information yet, it seems appropriate also for stable. Cc: stable@kernel.org Cc: Michael Poole Signed-off-by: Henrik Rydberg Acked-by: Chase Douglas Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 698e645..318cc40 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -258,7 +258,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); - input_report_abs(input, ABS_MT_ORIENTATION, orientation); + input_report_abs(input, ABS_MT_ORIENTATION, -orientation); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -397,7 +397,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB -- cgit v0.10.2 From 6638dedaec6e3d32654f0ac5786f5d32963e208b Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Wed, 9 Mar 2011 23:33:51 -0500 Subject: HID: ntrig: apply NO_INIT_REPORTS quirk Probing reports does bad things with some ntrig firmwares, better to just leave them alone. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index a93e58c..f152b51 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -839,7 +839,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) struct hid_report *report; if (id->driver_data) - hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_MULTI_INPUT + | HID_QUIRK_NO_INIT_REPORTS; nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); if (!nd) { -- cgit v0.10.2 From 0ae43810976bc969ee158510c4acbe70ed136e61 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 11 Mar 2011 00:27:34 -0800 Subject: HID: ACRUX - activate the device immediately after binding This device does not tolerate delayed opening and goes into a coma if we try to that. Ubuntu even has a crutch for udev that opened the device upon seeing it for the first time, but it did not work if we happened to boot with the device attached, since by the time userspace got around opening the device it was too late. Let's start the device immediately to deal with this issue. Reported-by: Sergei Kolzun Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5fa0b95..d942d42 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -68,9 +68,15 @@ config HID_A4TECH ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. -config HID_ACRUX_FF - tristate "ACRUX force feedback" +config HID_ACRUX + tristate "ACRUX game controller support" depends on USB_HID + ---help--- + Say Y here if you want to enable support for ACRUX game controllers. + +config HID_ACRUX_FF + tristate "ACRUX force feedback support" + depends on HID_ACRUX select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for ACRUX diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 5696477..125ba10 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -27,7 +27,7 @@ endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o -obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o +obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CANDO) += hid-cando.o diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index e5b961d..b455428 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -33,6 +33,8 @@ #include #include "hid-ids.h" + +#ifdef CONFIG_HID_ACRUX_FF #include "usbhid/usbhid.h" struct axff_device { @@ -109,6 +111,12 @@ err_free_mem: kfree(axff); return error; } +#else +static inline int axff_init(struct hid_device *hid) +{ + return 0; +} +#endif static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -139,9 +147,25 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) error); } + /* + * We need to start polling device right away, otherwise + * it will go into a coma. + */ + error = hid_hw_open(hdev); + if (error) { + dev_err(&hdev->dev, "hw open failed\n"); + return error; + } + return 0; } +static void ax_remove(struct hid_device *hdev) +{ + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + static const struct hid_device_id ax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, { } @@ -149,9 +173,10 @@ static const struct hid_device_id ax_devices[] = { MODULE_DEVICE_TABLE(hid, ax_devices); static struct hid_driver ax_driver = { - .name = "acrux", - .id_table = ax_devices, - .probe = ax_probe, + .name = "acrux", + .id_table = ax_devices, + .probe = ax_probe, + .remove = ax_remove, }; static int __init ax_init(void) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 570db37..c650efb 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1289,9 +1289,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, -#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, -#endif { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, -- cgit v0.10.2 From ff4046294ba215abeaf45c10ab6cbfae41978b14 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Mon, 7 Mar 2011 21:13:28 -0500 Subject: HID: ntrig remove if and drop an indent Signed-off-by: Rafi Rubin Tested-by: Peter Hutterer Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index f152b51..dffffa7 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -557,268 +557,266 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, if (field->application == HID_DG_PEN) return 0; - if (hid->claimed & HID_CLAIMED_INPUT) { - switch (usage->hid) { - case 0xff000001: - /* Tag indicating the start of a multitouch group */ - nd->reading_mt = 1; - nd->first_contact_touch = 0; - break; - case HID_DG_TIPSWITCH: - nd->tipswitch = value; - /* Prevent emission of touch until validated */ - return 1; - case HID_DG_CONFIDENCE: - nd->confidence = value; - break; - case HID_GD_X: - nd->x = value; - /* Clear the contact footer */ - nd->mt_foot_count = 0; - break; - case HID_GD_Y: - nd->y = value; - break; - case HID_DG_CONTACTID: - nd->id = value; - break; - case HID_DG_WIDTH: - nd->w = value; - break; - case HID_DG_HEIGHT: - nd->h = value; + switch (usage->hid) { + case 0xff000001: + /* Tag indicating the start of a multitouch group */ + nd->reading_mt = 1; + nd->first_contact_touch = 0; + break; + case HID_DG_TIPSWITCH: + nd->tipswitch = value; + /* Prevent emission of touch until validated */ + return 1; + case HID_DG_CONFIDENCE: + nd->confidence = value; + break; + case HID_GD_X: + nd->x = value; + /* Clear the contact footer */ + nd->mt_foot_count = 0; + break; + case HID_GD_Y: + nd->y = value; + break; + case HID_DG_CONTACTID: + nd->id = value; + break; + case HID_DG_WIDTH: + nd->w = value; + break; + case HID_DG_HEIGHT: + nd->h = value; + /* + * when in single touch mode, this is the last + * report received in a finger event. We want + * to emit a normal (X, Y) position + */ + if (!nd->reading_mt) { /* - * when in single touch mode, this is the last - * report received in a finger event. We want - * to emit a normal (X, Y) position + * TipSwitch indicates the presence of a + * finger in single touch mode. */ - if (!nd->reading_mt) { - /* - * TipSwitch indicates the presence of a - * finger in single touch mode. - */ - input_report_key(input, BTN_TOUCH, - nd->tipswitch); - input_report_key(input, BTN_TOOL_DOUBLETAP, - nd->tipswitch); - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + input_report_key(input, BTN_TOUCH, + nd->tipswitch); + input_report_key(input, BTN_TOOL_DOUBLETAP, + nd->tipswitch); + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } + break; + case 0xff000002: + /* + * we receive this when the device is in multitouch + * mode. The first of the three values tagged with + * this usage tells if the contact point is real + * or a placeholder + */ + + /* Shouldn't get more than 4 footer packets, so skip */ + if (nd->mt_foot_count >= 4) break; - case 0xff000002: - /* - * we receive this when the device is in multitouch - * mode. The first of the three values tagged with - * this usage tells if the contact point is real - * or a placeholder - */ - /* Shouldn't get more than 4 footer packets, so skip */ - if (nd->mt_foot_count >= 4) - break; + nd->mt_footer[nd->mt_foot_count++] = value; - nd->mt_footer[nd->mt_foot_count++] = value; + /* if the footer isn't complete break */ + if (nd->mt_foot_count != 4) + break; - /* if the footer isn't complete break */ - if (nd->mt_foot_count != 4) - break; + /* Pen activity signal. */ + if (nd->mt_footer[2]) { + /* + * When the pen deactivates touch, we see a + * bogus frame with ContactCount > 0. + * We can + * save a bit of work by ensuring act_state < 0 + * even if deactivation slack is turned off. + */ + nd->act_state = deactivate_slack - 1; + nd->confidence = 0; + break; + } - /* Pen activity signal. */ - if (nd->mt_footer[2]) { - /* - * When the pen deactivates touch, we see a - * bogus frame with ContactCount > 0. - * We can - * save a bit of work by ensuring act_state < 0 - * even if deactivation slack is turned off. - */ - nd->act_state = deactivate_slack - 1; + /* + * The first footer value indicates the presence of a + * finger. + */ + if (nd->mt_footer[0]) { + /* + * We do not want to process contacts under + * the size threshold, but do not want to + * ignore them for activation state + */ + if (nd->w < nd->min_width || + nd->h < nd->min_height) nd->confidence = 0; - break; - } + } else + break; + if (nd->act_state > 0) { /* - * The first footer value indicates the presence of a - * finger. + * Contact meets the activation size threshold */ - if (nd->mt_footer[0]) { - /* - * We do not want to process contacts under - * the size threshold, but do not want to - * ignore them for activation state - */ - if (nd->w < nd->min_width || - nd->h < nd->min_height) - nd->confidence = 0; - } else - break; - - if (nd->act_state > 0) { - /* - * Contact meets the activation size threshold - */ - if (nd->w >= nd->activation_width && - nd->h >= nd->activation_height) { - if (nd->id) - /* - * first contact, activate now - */ - nd->act_state = 0; - else { - /* - * avoid corrupting this frame - * but ensure next frame will - * be active - */ - nd->act_state = 1; - break; - } - } else + if (nd->w >= nd->activation_width && + nd->h >= nd->activation_height) { + if (nd->id) /* - * Defer adjusting the activation state - * until the end of the frame. + * first contact, activate now */ + nd->act_state = 0; + else { + /* + * avoid corrupting this frame + * but ensure next frame will + * be active + */ + nd->act_state = 1; break; - } - - /* Discarding this contact */ - if (!nd->confidence) - break; - - /* emit a normal (X, Y) for the first point only */ - if (nd->id == 0) { + } + } else /* - * TipSwitch is superfluous in multitouch - * mode. The footer events tell us - * if there is a finger on the screen or - * not. + * Defer adjusting the activation state + * until the end of the frame. */ - nd->first_contact_touch = nd->confidence; - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + break; + } - /* Emit MT events */ - input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); + /* Discarding this contact */ + if (!nd->confidence) + break; + /* emit a normal (X, Y) for the first point only */ + if (nd->id == 0) { /* - * Translate from height and width to size - * and orientation. + * TipSwitch is superfluous in multitouch + * mode. The footer events tell us + * if there is a finger on the screen or + * not. */ - if (nd->w > nd->h) { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 1); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->w); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->h); - } else { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 0); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->h); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->w); - } - input_mt_sync(field->hidinput->input); - break; + nd->first_contact_touch = nd->confidence; + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } - case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ - if (!nd->reading_mt) /* Just to be sure */ - break; + /* Emit MT events */ + input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); + + /* + * Translate from height and width to size + * and orientation. + */ + if (nd->w > nd->h) { + input_event(input, EV_ABS, + ABS_MT_ORIENTATION, 1); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->w); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->h); + } else { + input_event(input, EV_ABS, + ABS_MT_ORIENTATION, 0); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->h); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->w); + } + input_mt_sync(field->hidinput->input); + break; - nd->reading_mt = 0; + case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ + if (!nd->reading_mt) /* Just to be sure */ + break; + nd->reading_mt = 0; + + + /* + * Activation state machine logic: + * + * Fundamental states: + * state > 0: Inactive + * state <= 0: Active + * state < -deactivate_slack: + * Pen termination of touch + * + * Specific values of interest + * state == activate_slack + * no valid input since the last reset + * + * state == 0 + * general operational state + * + * state == -deactivate_slack + * read sufficient empty frames to accept + * the end of input and reset + */ + + if (nd->act_state > 0) { /* Currently inactive */ + if (value) + /* + * Consider each live contact as + * evidence of intentional activity. + */ + nd->act_state = (nd->act_state > value) + ? nd->act_state - value + : 0; + else + /* + * Empty frame before we hit the + * activity threshold, reset. + */ + nd->act_state = nd->activate_slack; /* - * Activation state machine logic: - * - * Fundamental states: - * state > 0: Inactive - * state <= 0: Active - * state < -deactivate_slack: - * Pen termination of touch - * - * Specific values of interest - * state == activate_slack - * no valid input since the last reset - * - * state == 0 - * general operational state - * - * state == -deactivate_slack - * read sufficient empty frames to accept - * the end of input and reset + * Entered this block inactive and no + * coordinates sent this frame, so hold off + * on button state. */ - - if (nd->act_state > 0) { /* Currently inactive */ - if (value) - /* - * Consider each live contact as - * evidence of intentional activity. - */ - nd->act_state = (nd->act_state > value) - ? nd->act_state - value - : 0; - else - /* - * Empty frame before we hit the - * activity threshold, reset. - */ - nd->act_state = nd->activate_slack; - + break; + } else { /* Currently active */ + if (value && nd->act_state >= + nd->deactivate_slack) /* - * Entered this block inactive and no - * coordinates sent this frame, so hold off - * on button state. + * Live point: clear accumulated + * deactivation count. */ - break; - } else { /* Currently active */ - if (value && nd->act_state >= - nd->deactivate_slack) - /* - * Live point: clear accumulated - * deactivation count. - */ - nd->act_state = 0; - else if (nd->act_state <= nd->deactivate_slack) - /* - * We've consumed the deactivation - * slack, time to deactivate and reset. - */ - nd->act_state = - nd->activate_slack; - else { /* Move towards deactivation */ - nd->act_state--; - break; - } - } - - if (nd->first_contact_touch && nd->act_state <= 0) { + nd->act_state = 0; + else if (nd->act_state <= nd->deactivate_slack) /* - * Check to see if we're ready to start - * emitting touch events. - * - * Note: activation slack will decrease over - * the course of the frame, and it will be - * inconsistent from the start to the end of - * the frame. However if the frame starts - * with slack, first_contact_touch will still - * be 0 and we will not get to this point. + * We've consumed the deactivation + * slack, time to deactivate and reset. */ - input_report_key(input, BTN_TOOL_DOUBLETAP, 1); - input_report_key(input, BTN_TOUCH, 1); - } else { - input_report_key(input, BTN_TOOL_DOUBLETAP, 0); - input_report_key(input, BTN_TOUCH, 0); + nd->act_state = + nd->activate_slack; + else { /* Move towards deactivation */ + nd->act_state--; + break; } - break; + } - default: - /* fall-back to the generic hidinput handling */ - return 0; + if (nd->first_contact_touch && nd->act_state <= 0) { + /* + * Check to see if we're ready to start + * emitting touch events. + * + * Note: activation slack will decrease over + * the course of the frame, and it will be + * inconsistent from the start to the end of + * the frame. However if the frame starts + * with slack, first_contact_touch will still + * be 0 and we will not get to this point. + */ + input_report_key(input, BTN_TOOL_DOUBLETAP, 1); + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, BTN_TOOL_DOUBLETAP, 0); + input_report_key(input, BTN_TOUCH, 0); } + break; + + default: + /* fall-back to the generic hidinput handling */ + return 0; } not_claimed_input: -- cgit v0.10.2 From f153fc3990d4ad2709a52d7150e2c04363afb1fb Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 9 Mar 2011 06:35:25 +0100 Subject: HID: hid-multitouch: Send events per slot if CONTACTCOUNT is missing The recent capacitive DWAV firmwares do not use the CONTACTCOUNT field, and the touch frame boundary can therefore not be determined. This patch makes the driver report the touch frame at each completed slot instead. Signed-off-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 69f8744..4518006 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -364,8 +364,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, return 0; } - if (usage->hid == td->last_slot_field) + if (usage->hid == td->last_slot_field) { mt_complete_slot(td); + if (!td->last_field_index) + mt_emit_event(td, field->hidinput->input); + } if (field->index == td->last_field_index && td->num_received >= td->num_expected) -- cgit v0.10.2 From 4875ac114d8bce99838a9b0ee7c3f5469cc6352e Mon Sep 17 00:00:00 2001 From: Richard Nauber Date: Wed, 9 Mar 2011 06:20:57 +0100 Subject: HID: merge hid-egalax into hid-multitouch This patch merges the hid-egalax driver into hid-multitouch. There are two types of devices support by the hid-egalax driver: resistive and capacitive. Here, they are implicitly distinguished by the absence of a HID_DG_CONTACTCOUNT field in the latter, so no special code path needs to be introduced. As a side effect, this patch fixes the broken suspend/resume behavior in the old driver. [rydberg@euromail.se: minor fixups] Signed-off-by: Richard Nauber Signed-off-by: Henrik Rydberg Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a0b117d..b4b8b21 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -160,13 +160,6 @@ config HID_EMS_FF Currently the following devices are known to be supported: - Trio Linker Plus II -config HID_EGALAX - tristate "eGalax multi-touch panel" - depends on USB_HID - ---help--- - Support for the eGalax dual-touch panels, including the - Joojoo and Wetab tablets. - config HID_ELECOM tristate "ELECOM BM084 bluetooth mouse" depends on BT_HIDP @@ -307,6 +300,8 @@ config HID_MULTITOUCH - IrTouch Infrared USB panels - Pixcir dual touch panels - 'Sensing Win7-TwoFinger' panel by GeneralTouch + - eGalax dual-touch panels, including the + Joojoo and Wetab tablets If unsure, say N. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6efc2a0..29e9898 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-drff.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o -obj-$(CONFIG_HID_EGALAX) += hid-egalax.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c deleted file mode 100644 index 03bee19..0000000 --- a/drivers/hid/hid-egalax.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * HID driver for eGalax dual-touch panels - * - * Copyright (c) 2010 Stephane Chatty - * Copyright (c) 2010 Henrik Rydberg - * Copyright (c) 2010 Canonical, Ltd. - * - */ - -/* - * 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 -#include -#include "usbhid/usbhid.h" - -MODULE_AUTHOR("Stephane Chatty "); -MODULE_DESCRIPTION("eGalax dual-touch panel"); -MODULE_LICENSE("GPL"); - -#include "hid-ids.h" - -#define MAX_SLOTS 2 - -/* estimated signal-to-noise ratios */ -#define SN_MOVE 4096 -#define SN_PRESSURE 32 - -struct egalax_data { - int valid; - int slot; - int touch; - int x, y, z; -}; - -static void set_abs(struct input_dev *input, unsigned int code, - struct hid_field *field, int snratio) -{ - int fmin = field->logical_minimum; - int fmax = field->logical_maximum; - int fuzz = snratio ? (fmax - fmin) / snratio : 0; - input_set_abs_params(input, code, fmin, fmax, fuzz, 0); -} - -static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - struct input_dev *input = hi->input; - - switch (usage->hid & HID_USAGE_PAGE) { - - case HID_UP_GENDESK: - switch (usage->hid) { - case HID_GD_X: - field->logical_maximum = 32760; - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_X); - set_abs(input, ABS_MT_POSITION_X, field, SN_MOVE); - /* touchscreen emulation */ - set_abs(input, ABS_X, field, SN_MOVE); - return 1; - case HID_GD_Y: - field->logical_maximum = 32760; - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_Y); - set_abs(input, ABS_MT_POSITION_Y, field, SN_MOVE); - /* touchscreen emulation */ - set_abs(input, ABS_Y, field, SN_MOVE); - return 1; - } - return 0; - - case HID_UP_DIGITIZER: - switch (usage->hid) { - case HID_DG_TIPSWITCH: - /* touchscreen emulation */ - hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); - input_set_capability(input, EV_KEY, BTN_TOUCH); - return 1; - case HID_DG_INRANGE: - case HID_DG_CONFIDENCE: - case HID_DG_CONTACTCOUNT: - case HID_DG_CONTACTMAX: - return -1; - case HID_DG_CONTACTID: - input_mt_init_slots(input, MAX_SLOTS); - return 1; - case HID_DG_TIPPRESSURE: - field->logical_minimum = 0; - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_PRESSURE); - set_abs(input, ABS_MT_PRESSURE, field, SN_PRESSURE); - /* touchscreen emulation */ - set_abs(input, ABS_PRESSURE, field, SN_PRESSURE); - return 1; - } - return 0; - } - - /* ignore others (from other reports we won't get anyway) */ - return -1; -} - -static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - /* tell hid-input to skip setup of these event types */ - if (usage->type == EV_KEY || usage->type == EV_ABS) - set_bit(usage->type, hi->input->evbit); - return -1; -} - -/* - * this function is called when a whole finger has been parsed, - * so that it can decide what to send to the input layer. - */ -static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) -{ - input_mt_slot(input, td->slot); - input_mt_report_slot_state(input, MT_TOOL_FINGER, td->touch); - if (td->touch) { - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); - input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); - } - input_mt_report_pointer_emulation(input, true); -} - -static int egalax_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - struct egalax_data *td = hid_get_drvdata(hid); - - /* Note, eGalax has two product lines: the first is resistive and - * uses a standard parallel multitouch protocol (product ID == - * 48xx). The second is capacitive and uses an unusual "serial" - * protocol with a different message for each multitouch finger - * (product ID == 72xx). - */ - if (hid->claimed & HID_CLAIMED_INPUT) { - struct input_dev *input = field->hidinput->input; - - switch (usage->hid) { - case HID_DG_INRANGE: - td->valid = value; - break; - case HID_DG_CONFIDENCE: - /* avoid interference from generic hidinput handling */ - break; - case HID_DG_TIPSWITCH: - td->touch = value; - break; - case HID_DG_TIPPRESSURE: - td->z = value; - break; - case HID_DG_CONTACTID: - td->slot = clamp_val(value, 0, MAX_SLOTS - 1); - break; - case HID_GD_X: - td->x = value; - break; - case HID_GD_Y: - td->y = value; - /* this is the last field in a finger */ - if (td->valid) - egalax_filter_event(td, input); - break; - case HID_DG_CONTACTCOUNT: - /* touch emulation: this is the last field in a frame */ - break; - - default: - /* fallback to the generic hidinput handling */ - return 0; - } - } - - /* we have handled the hidinput part, now remains hiddev */ - if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); - - return 1; -} - -static int egalax_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - struct egalax_data *td; - struct hid_report *report; - - td = kzalloc(sizeof(struct egalax_data), GFP_KERNEL); - if (!td) { - hid_err(hdev, "cannot allocate eGalax data\n"); - return -ENOMEM; - } - hid_set_drvdata(hdev, td); - - ret = hid_parse(hdev); - if (ret) - goto end; - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) - goto end; - - report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[5]; - if (report) { - report->field[0]->value[0] = 2; - usbhid_submit_report(hdev, report, USB_DIR_OUT); - } - -end: - if (ret) - kfree(td); - - return ret; -} - -static void egalax_remove(struct hid_device *hdev) -{ - hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); - hid_set_drvdata(hdev, NULL); -} - -static const struct hid_device_id egalax_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, - { } -}; -MODULE_DEVICE_TABLE(hid, egalax_devices); - -static const struct hid_usage_id egalax_grabbed_usages[] = { - { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} -}; - -static struct hid_driver egalax_driver = { - .name = "egalax-touch", - .id_table = egalax_devices, - .probe = egalax_probe, - .remove = egalax_remove, - .input_mapping = egalax_input_mapping, - .input_mapped = egalax_input_mapped, - .usage_table = egalax_grabbed_usages, - .event = egalax_event, -}; - -static int __init egalax_init(void) -{ - return hid_register_driver(&egalax_driver); -} - -static void __exit egalax_exit(void) -{ - hid_unregister_driver(&egalax_driver); -} - -module_init(egalax_init); -module_exit(egalax_exit); - diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 4518006..af5a709 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -5,6 +5,12 @@ * Copyright (c) 2010-2011 Benjamin Tissoires * Copyright (c) 2010-2011 Ecole Nationale de l'Aviation Civile, France * + * This code is partly based on hid-egalax.c: + * + * Copyright (c) 2010 Stephane Chatty + * Copyright (c) 2010 Henrik Rydberg + * Copyright (c) 2010 Canonical, Ltd. + * */ /* @@ -37,6 +43,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) #define MT_QUIRK_VALID_IS_INRANGE (1 << 4) #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5) +#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6) struct mt_slot { __s32 x, y, p, w, h; @@ -70,6 +77,7 @@ struct mt_class { #define MT_CLS_DUAL_INRANGE_CONTACTID 2 #define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 3 #define MT_CLS_CYPRESS 4 +#define MT_CLS_EGALAX 5 /* * these device-dependent functions determine what slot corresponds @@ -120,6 +128,14 @@ struct mt_class mt_classes[] = { MT_QUIRK_CYPRESS, .maxcontacts = 10 }, + { .name = MT_CLS_EGALAX, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_EGALAX_XYZ_FIXUP, + .maxcontacts = 2, + .sn_move = 4096, + .sn_pressure = 32, + }, { } }; @@ -147,11 +163,15 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = td->mtclass; + __s32 quirks = cls->quirks; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); set_abs(hi->input, ABS_MT_POSITION_X, field, @@ -161,6 +181,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, td->last_slot_field = usage->hid; return 1; case HID_GD_Y: + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); set_abs(hi->input, ABS_MT_POSITION_Y, field, @@ -204,6 +226,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, td->last_slot_field = usage->hid; return 1; case HID_DG_TIPPRESSURE: + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); set_abs(hi->input, ABS_MT_PRESSURE, field, @@ -487,6 +511,25 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, + /* Resistive eGalax devices */ + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, + + /* Capacitive eGalax devices */ + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + { } }; MODULE_DEVICE_TABLE(hid, mt_devices); -- cgit v0.10.2 From 7b2a64c96ad53c4299f7e6ddf8c2f99cb48940a9 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Wed, 9 Mar 2011 23:33:52 -0500 Subject: HID: hid-ntrig: init settle and mode check Adding a wait before the wakeup signal. As a precautionary measure sanity check the current sensor mode. If needed reset it to "dual". When the device is responding poorly and needs the wakeup call, it was missing it. Giving it a chance to settle first improves the chances that signal gets through. Signed-off-by: Rafi Rubin Tested-by: Peter Hutterer Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index dffffa7..9fae2eb 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -110,6 +110,36 @@ static int ntrig_version_string(unsigned char *raw, char *buf) return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); } +static inline int ntrig_get_mode(struct hid_device *hdev) +{ + struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[0x0d]; + + if (!report) + return -EINVAL; + + usbhid_submit_report(hdev, report, USB_DIR_IN); + usbhid_wait_io(hdev); + return (int)report->field[0]->value[0]; +} + +static inline void ntrig_set_mode(struct hid_device *hdev, const int mode) +{ + struct hid_report *report; + __u8 mode_commands[4] = { 0xe, 0xf, 0x1b, 0x10 }; + + if (mode < 0 || mode > 3) + return; + + report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[mode_commands[mode]]; + + if (!report) + return; + + usbhid_submit_report(hdev, report, USB_DIR_IN); +} + static void ntrig_report_version(struct hid_device *hdev) { int ret; @@ -905,8 +935,19 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) /* This is needed for devices with more recent firmware versions */ report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; - if (report) - usbhid_submit_report(hdev, report, USB_DIR_OUT); + if (report) { + /* Let the device settle to ensure the wakeup message gets + * through */ + usbhid_wait_io(hdev); + usbhid_submit_report(hdev, report, USB_DIR_IN); + + /* + * Sanity check: if the current mode is invalid reset it to + * something reasonable. + */ + if (ntrig_get_mode(hdev) >= 4) + ntrig_set_mode(hdev, 3); + } ntrig_report_version(hdev); -- cgit v0.10.2 From 61c29f5b1856f90bf0a87d83cfa035a11986d2c5 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Mon, 14 Mar 2011 21:43:07 +0100 Subject: HID: roccat-koneplus: vorrect mode of sysfs attr 'sensor' Mode of sysfs attribute 'sensor' was wrongly set to writeonly. Corrected this to readwrite. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index a8e2117..33eec74 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -460,7 +460,7 @@ static struct device_attribute koneplus_attributes[] = { static struct bin_attribute koneplus_bin_attributes[] = { { - .attr = { .name = "sensor", .mode = 0220 }, + .attr = { .name = "sensor", .mode = 0660 }, .size = sizeof(struct koneplus_sensor), .read = koneplus_sysfs_read_sensor, .write = koneplus_sysfs_write_sensor -- cgit v0.10.2 From 270fdc0748bd3f7b625caff985f2fcf8e2185ec7 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 16 Mar 2011 14:13:53 -0300 Subject: HID: add support for Ortek PKB-1700 As reported on http://ubuntuforums.org/showthread.php?t=1594007 the PKB-1700 needs same special handling as WKB-2000. This change is originally based on patch posted by user asmoore82 on the Ubuntu forums. Cc: stable@kernel.org Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Jiri Kosina diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24cca2f..51a598b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -319,10 +319,10 @@ config HID_NTRIG Support for N-Trig touch screen. config HID_ORTEK - tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad" + tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad" depends on USB_HID ---help--- - Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. + Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad. config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3..295e248 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1400,6 +1400,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61..5f5cc91 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -466,6 +466,7 @@ #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 #define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 #define USB_VENDOR_ID_PANJIT 0x134c diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index e90edfc..ad6faa6 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -1,5 +1,5 @@ /* - * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad). + * HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad). * Fixes LogicalMaximum error in USB report description, see * http://bugzilla.kernel.org/show_bug.cgi?id=14787 * @@ -30,6 +30,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, } static const struct hid_device_id ortek_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { } }; -- cgit v0.10.2 From e96838df559ed33df5e05ea013e37741669953cc Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 17 Mar 2011 00:39:33 +0100 Subject: HID: hid-ortek: remove spurious reference Remove spurious bugzilla reference from the source comment header. Also fix the comment to be more correct (it's HID report descriptor rather than USB). Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index ad6faa6..f9b7dd4 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -1,7 +1,6 @@ /* * HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad). - * Fixes LogicalMaximum error in USB report description, see - * http://bugzilla.kernel.org/show_bug.cgi?id=14787 + * Fixes LogicalMaximum error in HID report description. * * Copyright (c) 2010 Johnathon Harris */ -- cgit v0.10.2 From 5623a24a80814fe471e777f12b9dbbb0f77e002e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 17 Mar 2011 00:43:23 +0100 Subject: HID: add support for Logitech Driving Force Pro wheel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add force feedback support for Logitech Driving Force Pro wheel. Device IDs reported by Michal Malý. Reported-by: Michal Malý Signed-off-by: Jiri Kosina diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 295e248..f99413b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1368,6 +1368,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5f5cc91..cb21c41 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -383,6 +383,7 @@ #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 +#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 #define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index aef4104..3da9040 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -377,6 +377,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL), + .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), -- cgit v0.10.2