From 4424f616e48e0a48ac31de8d223ba1bdb46a84f1 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Tue, 16 Oct 2012 17:40:09 +0200 Subject: HID: roccat: add support for Roccat Lua This patch adds support for Roccat Lua gaming mouse. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/Makefile | 4 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-lua.c | 227 +++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-roccat-lua.h | 29 ++++++ 5 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 drivers/hid/hid-roccat-lua.c create mode 100644 drivers/hid/hid-roccat-lua.h (limited to 'drivers') diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cef68ca859d3..ffe848fd83a4 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -93,8 +93,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ - hid-roccat-savu.o + hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \ + hid-roccat-pyra.o hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bd3971bf31bf..9904776a7065 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1655,6 +1655,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { 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_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 269b50912a4a..52b27b12a4a7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -675,6 +675,7 @@ #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_LUA 0x2c2e #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 #define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c new file mode 100644 index 000000000000..5084fb4b7e91 --- /dev/null +++ b/drivers/hid/hid-roccat-lua.c @@ -0,0 +1,227 @@ +/* + * Roccat Lua driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Roccat Lua is a gamer mouse which cpi, button and light settings can be + * configured. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-lua.h" + +static ssize_t lua_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lua_device *lua = 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(&lua->lua_lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +static ssize_t lua_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); + struct lua_device *lua = 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(&lua->lua_lock); + retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&lua->lua_lock); + + return retval ? retval : real_size; +} + +#define LUA_SYSFS_W(thingy, THINGY) \ +static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_write(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_SYSFS_R(thingy, THINGY) \ +static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + return lua_sysfs_read(fp, kobj, buf, off, count, \ + LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \ +} + +#define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +LUA_SYSFS_W(thingy, THINGY) \ +LUA_SYSFS_R(thingy, THINGY) \ +static struct bin_attribute lua_ ## thingy ## _attr = { \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = LUA_SIZE_ ## THINGY, \ + .read = lua_sysfs_read_ ## thingy, \ + .write = lua_sysfs_write_ ## thingy \ +}; + +LUA_BIN_ATTRIBUTE_RW(control, CONTROL) + +static int lua_create_sysfs_attributes(struct usb_interface *intf) +{ + return sysfs_create_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static void lua_remove_sysfs_attributes(struct usb_interface *intf) +{ + sysfs_remove_bin_file(&intf->dev.kobj, &lua_control_attr); +} + +static int lua_init_lua_device_struct(struct usb_device *usb_dev, + struct lua_device *lua) +{ + mutex_init(&lua->lua_lock); + + return 0; +} + +static int lua_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 lua_device *lua; + int retval; + + lua = kzalloc(sizeof(*lua), GFP_KERNEL); + if (!lua) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, lua); + + retval = lua_init_lua_device_struct(usb_dev, lua); + if (retval) { + hid_err(hdev, "couldn't init struct lua_device\n"); + goto exit; + } + + retval = lua_create_sysfs_attributes(intf); + if (retval) { + hid_err(hdev, "cannot create sysfs files\n"); + goto exit; + } + + return 0; +exit: + kfree(lua); + return retval; +} + +static void lua_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct lua_device *lua; + + lua_remove_sysfs_attributes(intf); + + lua = hid_get_drvdata(hdev); + kfree(lua); +} + +static int lua_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 = lua_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 lua_remove(struct hid_device *hdev) +{ + lua_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id lua_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, lua_devices); + +static struct hid_driver lua_driver = { + .name = "lua", + .id_table = lua_devices, + .probe = lua_probe, + .remove = lua_remove +}; + +static int __init lua_init(void) +{ + return hid_register_driver(&lua_driver); +} + +static void __exit lua_exit(void) +{ + hid_unregister_driver(&lua_driver); +} + +module_init(lua_init); +module_exit(lua_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Lua driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-lua.h b/drivers/hid/hid-roccat-lua.h new file mode 100644 index 000000000000..547d77a375d1 --- /dev/null +++ b/drivers/hid/hid-roccat-lua.h @@ -0,0 +1,29 @@ +#ifndef __HID_ROCCAT_LUA_H +#define __HID_ROCCAT_LUA_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +enum { + LUA_SIZE_CONTROL = 8, +}; + +enum lua_commands { + LUA_COMMAND_CONTROL = 3, +}; + +struct lua_device { + struct mutex lua_lock; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 5277e97c2474a85eb0325ac6fff71910a4e6c134 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Wed, 17 Oct 2012 16:35:42 +0200 Subject: HID: roccat: allow readout of koneplus sensor register data tcu sysfs attr was used to only control calibration process so far. Direct sensor register access possibility has been revealed. Allowing readout of tcu permits usage of this feature. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus | 6 +++--- drivers/hid/hid-roccat-koneplus.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index 65e6e5dd67e8..f9e2a61900e5 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -104,9 +104,9 @@ What: /sys/bus/usb/devices/-:./ 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. + can be initiated/cancelled. Also lets one read/write sensor + registers. + The data has to be 4 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/tcu_image diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index f5602fec4865..c47540a5d4f3 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -222,6 +222,14 @@ static ssize_t koneplus_sysfs_write_tcu(struct file *fp, sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); } +static ssize_t koneplus_sysfs_read_tcu(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); +} + static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -426,8 +434,9 @@ static struct bin_attribute koneplus_bin_attributes[] = { .write = koneplus_sysfs_write_sensor }, { - .attr = { .name = "tcu", .mode = 0220 }, + .attr = { .name = "tcu", .mode = 0660 }, .size = sizeof(struct koneplus_tcu), + .read = koneplus_sysfs_read_tcu, .write = koneplus_sysfs_write_tcu }, { -- cgit v1.2.3-59-g8ed1b From 4ddfe0289bd4622a2a84747ab90c0614da4673e0 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 31 Oct 2012 12:10:10 +0100 Subject: HID: Add driver for ION iCade Add a driver for the ION iCade mini arcade cabinet [1]. The device generates a key press and release for each joystick movement or button press or release. For example, moving the stick to the left will generate the "A" key being pressed and then released. A list of all the combinations is available in the iCade developer guide [2]. This driver hides all this and makes the device work as a generic joystick. [1]: http://www.ionaudio.com/products/details/icade [2]: http://www.ionaudio.com/downloads/iCade_Dev_Resource_v1.3.pdf Signed-off-by: Bastien Nocera Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 9 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-icade.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 3 + 5 files changed, 273 insertions(+) create mode 100644 drivers/hid/hid-icade.c (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1630150ad2b1..813f0fe7711d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -265,6 +265,15 @@ config HID_GYRATION ---help--- Support for Gyration remote control. +config HID_ICADE + tristate "ION iCade arcade controller" + depends on (BT_HIDP) + ---help--- + Support for the ION iCade arcade controller to work as a joystick. + + To compile this driver as a module, choose M here: the + module will be called hid-icade. + config HID_TWINHAN tristate "Twinhan IR remote control" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cef68ca859d3..85c910c6d9d2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o +obj-$(CONFIG_HID_ICADE) += hid-icade.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bd3971bf31bf..0a6b36fbb4ce 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1572,6 +1572,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) }, { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) }, { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c new file mode 100644 index 000000000000..1d6565e37ba3 --- /dev/null +++ b/drivers/hid/hid-icade.c @@ -0,0 +1,259 @@ +/* + * ION iCade input driver + * + * Copyright (c) 2012 Bastien Nocera + * Copyright (c) 2012 Benjamin Tissoires + */ + +/* + * 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" + +/* + * ↑ A C Y L + * ← → + * ↓ B X Z R + * + * + * UP ON,OFF = w,e + * RT ON,OFF = d,c + * DN ON,OFF = x,z + * LT ON,OFF = a,q + * A ON,OFF = y,t + * B ON,OFF = h,r + * C ON,OFF = u,f + * X ON,OFF = j,n + * Y ON,OFF = i,m + * Z ON,OFF = k,p + * L ON,OFF = o,g + * R ON,OFF = l,v + */ + +/* The translation code uses HID usage instead of input layer + * keys. This code generates a lookup table that makes + * translation quick. + * + * #include + * #include + * #include + * + * #define unk KEY_UNKNOWN + * + * < copy of hid_keyboard[] from hid-input.c > + * + * struct icade_key_translation { + * int from; + * const char *to; + * int press; + * }; + * + * static const struct icade_key_translation icade_keys[] = { + * { KEY_W, "KEY_UP", 1 }, + * { KEY_E, "KEY_UP", 0 }, + * { KEY_D, "KEY_RIGHT", 1 }, + * { KEY_C, "KEY_RIGHT", 0 }, + * { KEY_X, "KEY_DOWN", 1 }, + * { KEY_Z, "KEY_DOWN", 0 }, + * { KEY_A, "KEY_LEFT", 1 }, + * { KEY_Q, "KEY_LEFT", 0 }, + * { KEY_Y, "BTN_A", 1 }, + * { KEY_T, "BTN_A", 0 }, + * { KEY_H, "BTN_B", 1 }, + * { KEY_R, "BTN_B", 0 }, + * { KEY_U, "BTN_C", 1 }, + * { KEY_F, "BTN_C", 0 }, + * { KEY_J, "BTN_X", 1 }, + * { KEY_N, "BTN_X", 0 }, + * { KEY_I, "BTN_Y", 1 }, + * { KEY_M, "BTN_Y", 0 }, + * { KEY_K, "BTN_Z", 1 }, + * { KEY_P, "BTN_Z", 0 }, + * { KEY_O, "BTN_THUMBL", 1 }, + * { KEY_G, "BTN_THUMBL", 0 }, + * { KEY_L, "BTN_THUMBR", 1 }, + * { KEY_V, "BTN_THUMBR", 0 }, + * + * { } + * }; + * + * static int + * usage_for_key (int key) + * { + * int i; + * for (i = 0; i < 256; i++) { + * if (hid_keyboard[i] == key) + * return i; + * } + * assert(0); + * } + * + * int main (int argc, char **argv) + * { + * const struct icade_key_translation *trans; + * int max_usage = 0; + * + * for (trans = icade_keys; trans->from; trans++) { + * int usage = usage_for_key (trans->from); + * max_usage = usage > max_usage ? usage : max_usage; + * } + * + * printf ("#define ICADE_MAX_USAGE %d\n\n", max_usage); + * printf ("struct icade_key {\n"); + * printf ("\tu16 to;\n"); + * printf ("\tu8 press:1;\n"); + * printf ("};\n\n"); + * printf ("static const struct icade_key " + * "icade_usage_table[%d] = {\n", max_usage + 1); + * for (trans = icade_keys; trans->from; trans++) { + * printf ("\t[%d] = { %s, %d },\n", + * usage_for_key (trans->from), trans->to, trans->press); + * } + * printf ("};\n"); + * + * return 0; + * } + */ + +#define ICADE_MAX_USAGE 29 + +struct icade_key { + u16 to; + u8 press:1; +}; + +static const struct icade_key icade_usage_table[30] = { + [26] = { KEY_UP, 1 }, + [8] = { KEY_UP, 0 }, + [7] = { KEY_RIGHT, 1 }, + [6] = { KEY_RIGHT, 0 }, + [27] = { KEY_DOWN, 1 }, + [29] = { KEY_DOWN, 0 }, + [4] = { KEY_LEFT, 1 }, + [20] = { KEY_LEFT, 0 }, + [28] = { BTN_A, 1 }, + [23] = { BTN_A, 0 }, + [11] = { BTN_B, 1 }, + [21] = { BTN_B, 0 }, + [24] = { BTN_C, 1 }, + [9] = { BTN_C, 0 }, + [13] = { BTN_X, 1 }, + [17] = { BTN_X, 0 }, + [12] = { BTN_Y, 1 }, + [16] = { BTN_Y, 0 }, + [14] = { BTN_Z, 1 }, + [19] = { BTN_Z, 0 }, + [18] = { BTN_THUMBL, 1 }, + [10] = { BTN_THUMBL, 0 }, + [15] = { BTN_THUMBR, 1 }, + [25] = { BTN_THUMBR, 0 }, +}; + +static const struct icade_key *icade_find_translation(u16 from) +{ + if (from < 0 || from > ICADE_MAX_USAGE) + return NULL; + return &icade_usage_table[from]; +} + +static int icade_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + const struct icade_key *trans; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* We ignore the fake key up, and act only on key down */ + if (!value) + return 1; + + trans = icade_find_translation(usage->hid & HID_USAGE); + + if (!trans) + return 1; + + input_event(field->hidinput->input, usage->type, + trans->to, trans->press); + + return 1; +} + +static int icade_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + const struct icade_key *trans; + + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) { + trans = icade_find_translation(usage->hid & HID_USAGE); + + if (!trans) + return -1; + + hid_map_usage(hi, usage, bit, max, EV_KEY, trans->to); + set_bit(trans->to, hi->input->keybit); + + return 1; + } + + /* ignore others */ + return -1; + +} + +static int icade_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY) + set_bit(usage->type, hi->input->evbit); + + return -1; +} + +static const struct hid_device_id icade_devices[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, + + { } +}; +MODULE_DEVICE_TABLE(hid, icade_devices); + +static struct hid_driver icade_driver = { + .name = "icade", + .id_table = icade_devices, + .event = icade_event, + .input_mapped = icade_input_mapped, + .input_mapping = icade_input_mapping, +}; + +static int __init icade_init(void) +{ + int ret; + + ret = hid_register_driver(&icade_driver); + if (ret) + pr_err("can't register icade driver\n"); + + return ret; +} + +static void __exit icade_exit(void) +{ + hid_unregister_driver(&icade_driver); +} + +module_init(icade_init); +module_exit(icade_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bastien Nocera "); +MODULE_DESCRIPTION("ION iCade input driver"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 269b50912a4a..9bc8d570777a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -420,6 +420,9 @@ #define USB_VENDOR_ID_ILITEK 0x222a #define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 +#define USB_VENDOR_ID_ION 0x15e4 +#define USB_DEVICE_ID_ICADE 0x0132 + #define USB_VENDOR_ID_HOLTEK 0x1241 #define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015 -- cgit v1.2.3-59-g8ed1b From c10354d4a6c9256c3393feafcbd030cb392a9baf Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 31 Oct 2012 15:54:34 +0100 Subject: HID: remove spurious brackets for iCade Remove spurious brackets in Kconfig for hid-icade. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 813f0fe7711d..f0ac277c4248 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -267,7 +267,7 @@ config HID_GYRATION config HID_ICADE tristate "ION iCade arcade controller" - depends on (BT_HIDP) + depends on BT_HIDP ---help--- Support for the ION iCade arcade controller to work as a joystick. -- cgit v1.2.3-59-g8ed1b From f8e86d054686d9090549476ea2394aa04c614a93 Mon Sep 17 00:00:00 2001 From: Ingo Ruhnke Date: Mon, 29 Oct 2012 14:29:30 +0100 Subject: HID: fix incorrect handling of devices with high button count Button names for USB gamepads are currently assigned incorrectly, as the evdev code assigned to buttons is "BTN_GAMEPAD + code", which on devices with more then 16 buttons bleeds over into button names reserved for graphic tablets (BTN_TOOL_PEN, etc.). This causes problems further down the line as the device are now no longer detected as joystick. This patch fixes that by assigning buttons outside the range to BTN_TRIGGER_HAPPY (as is already the case for USB joysticks). Furthermore this patch corrects the assignment to BTN_TRIGGER_HAPPY, as currently the first button over 16 is assigned to BTN_TRIGGER_HAPPY17 (i.e. BTN_TRIGGER_HAPPY+0x10) not BTN_TRIGGER_HAPPY. Signed-off-by: Ingo Ruhnke Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d917c0d53685..10248cf548fc 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -502,9 +502,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (code <= 0xf) code += BTN_JOYSTICK; else - code += BTN_TRIGGER_HAPPY; + code += BTN_TRIGGER_HAPPY - 0x10; + break; + case HID_GD_GAMEPAD: + if (code <= 0xf) + code += BTN_GAMEPAD; + else + code += BTN_TRIGGER_HAPPY - 0x10; break; - case HID_GD_GAMEPAD: code += BTN_GAMEPAD; break; default: switch (field->physical) { case HID_GD_MOUSE: -- cgit v1.2.3-59-g8ed1b From 537b6643dcb0ff7f47b21debd7bc3346b1456a44 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Sat, 3 Nov 2012 10:43:31 +0100 Subject: HID: apple: Set APPLE_ISO_KEYBOARD at USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO Set APPLE_ISO_KEYBOARD flag on USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO so we get the ISO translation on German keyboards for the MacBook4,1 as well. Signed-off-by: Johannes Thumshirn Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 06ebdbb6ea02..5df5e20178a6 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -439,7 +439,8 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO), - .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, -- cgit v1.2.3-59-g8ed1b From 8e74a2d31b980c2b2dc43641e5bb232c7cc3df4c Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:38:52 +0100 Subject: HID: roccat: add support for KoneXTD KoneXTD is just a Kone[+] with updated sensor, so it's handled by koneplus driver. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-koneplus.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 52b27b12a4a7..71806edaad2e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -674,6 +674,7 @@ #define USB_DEVICE_ID_ROCCAT_ISKU 0x319c #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index c47540a5d4f3..0df408362ef1 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -14,6 +14,7 @@ /* * Roccat Kone[+] is an updated/improved version of the Kone with more memory * and functionality and without the non-standard behaviours the Kone had. + * KoneXTD has same capabilities but updated sensor. */ #include @@ -718,6 +719,7 @@ static int koneplus_raw_event(struct hid_device *hdev, static const struct hid_device_id koneplus_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) }, { } }; @@ -758,5 +760,5 @@ module_init(koneplus_init); module_exit(koneplus_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat Kone[+] driver"); +MODULE_DESCRIPTION("USB Roccat Kone[+]/XTD driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 9e78eb8fbba5b00678d3d20e8e435cd0500716f3 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:38:58 +0100 Subject: HID: roccat: enable Savu device reset Device can be reset to factory state by sending a command via info sysfs attr. Changed from ro to rw to enable this feature. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-savu | 3 +-- drivers/hid/hid-roccat-savu.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu index b42922cf6b1f..f1e02a98bd9d 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-savu @@ -40,8 +40,8 @@ What: /sys/bus/usb/devices/-:./ Description: When read, this file returns general data like firmware version. + When written, the device can be reset. The data is 8 bytes long. - This file is readonly. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./savu/roccatsavu/macro @@ -74,4 +74,3 @@ Description: The mouse has a Avago ADNS-3090 sensor. This file allows reading and writing of the mouse sensors registers. The data has to be 4 bytes long. Users: http://roccat.sourceforge.net - diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 014afba407e0..31747a29c093 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -120,7 +120,7 @@ SAVU_SYSFS_RW(profile, PROFILE) SAVU_SYSFS_RW(general, GENERAL) SAVU_SYSFS_RW(buttons, BUTTONS) SAVU_SYSFS_RW(macro, MACRO) -SAVU_SYSFS_R(info, INFO) +SAVU_SYSFS_RW(info, INFO) SAVU_SYSFS_RW(sensor, SENSOR) static struct bin_attribute savu_bin_attributes[] = { @@ -129,7 +129,7 @@ static struct bin_attribute savu_bin_attributes[] = { SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), - SAVU_BIN_ATTRIBUTE_R(info, INFO), + SAVU_BIN_ATTRIBUTE_RW(info, INFO), SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR), __ATTR_NULL }; -- cgit v1.2.3-59-g8ed1b From fabe51eb68d450d25b18882988b29626f51592fd Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:39:04 +0100 Subject: HID: roccat: enable Koneplus device reset Device can be reset to factory state by sending a command via info sysfs attr. Added binary attribute info for this purpose, which obsoletes firmware_version attribute. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-koneplus | 11 +++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-koneplus | 13 +++++-------- drivers/hid/hid-roccat-koneplus.c | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus index c2a270b45b03..22568b45973e 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus @@ -8,3 +8,14 @@ Description: The integer value of this attribute ranges from 0-4. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. Please use actual_profile, it does the same thing. + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version +Date: October 2010 +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. + Please read binary attribute info which contains firmware version. diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index f9e2a61900e5..ed1213defb81 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -9,15 +9,12 @@ Description: The integer value of this attribute ranges from 0-4. and the mouse activates this profile immediately. Users: http://roccat.sourceforge.net -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version -Date: October 2010 +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/info +Date: November 2012 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. +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 8 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/macro diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 0df408362ef1..69592f427579 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -183,6 +183,22 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return real_size; } +static ssize_t koneplus_sysfs_read_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +} + +static ssize_t koneplus_sysfs_write_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return koneplus_sysfs_write(fp, kobj, buf, off, count, + sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +} + static ssize_t koneplus_sysfs_write_talk(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -428,6 +444,12 @@ static struct device_attribute koneplus_attributes[] = { }; static struct bin_attribute koneplus_bin_attributes[] = { + { + .attr = { .name = "info", .mode = 0660 }, + .size = sizeof(struct koneplus_info), + .read = koneplus_sysfs_read_info, + .write = koneplus_sysfs_write_info + }, { .attr = { .name = "sensor", .mode = 0660 }, .size = sizeof(struct koneplus_sensor), -- cgit v1.2.3-59-g8ed1b From f114fec54c30ba9c6842ac1121d3e16d3d3c6bdd Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 4 Nov 2012 09:39:09 +0100 Subject: HID: roccat: Cleanup of koneplus module Removed caching of profile data which lowers memory footprint, speeds up init and fixes problems with device reset. Removed unnecessary infos in header. Code moved nearer to format of newer drivers. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-koneplus.c | 371 +++++++++----------------------------- drivers/hid/hid-roccat-koneplus.h | 99 ++-------- 2 files changed, 102 insertions(+), 368 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 69592f427579..9fe445082308 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -56,56 +56,6 @@ static int koneplus_send_control(struct usb_device *usb_dev, uint value, &control, sizeof(struct roccat_common2_control)); } -static int koneplus_get_info(struct usb_device *usb_dev, - struct koneplus_info *buf) -{ - return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO, - buf, sizeof(struct koneplus_info)); -} - -static int koneplus_get_profile_settings(struct usb_device *usb_dev, - struct koneplus_profile_settings *buf, uint number) -{ - int retval; - - retval = koneplus_send_control(usb_dev, number, - KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); - if (retval) - return retval; - - return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS, - buf, sizeof(struct koneplus_profile_settings)); -} - -static int koneplus_set_profile_settings(struct usb_device *usb_dev, - struct koneplus_profile_settings const *settings) -{ - return roccat_common2_send_with_status(usb_dev, - KONEPLUS_COMMAND_PROFILE_SETTINGS, - settings, sizeof(struct koneplus_profile_settings)); -} - -static int koneplus_get_profile_buttons(struct usb_device *usb_dev, - struct koneplus_profile_buttons *buf, int number) -{ - int retval; - - retval = koneplus_send_control(usb_dev, number, - KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); - if (retval) - return retval; - - return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS, - buf, sizeof(struct koneplus_profile_buttons)); -} - -static int koneplus_set_profile_buttons(struct usb_device *usb_dev, - struct koneplus_profile_buttons const *buttons) -{ - return roccat_common2_send_with_status(usb_dev, - KONEPLUS_COMMAND_PROFILE_BUTTONS, - buttons, sizeof(struct koneplus_profile_buttons)); -} /* retval is 0-4 on success, < 0 on error */ static int koneplus_get_actual_profile(struct usb_device *usb_dev) @@ -114,7 +64,7 @@ static int koneplus_get_actual_profile(struct usb_device *usb_dev) int retval; retval = roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, - &buf, sizeof(struct koneplus_actual_profile)); + &buf, KONEPLUS_SIZE_ACTUAL_PROFILE); return retval ? retval : buf.actual_profile; } @@ -125,12 +75,12 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev, struct koneplus_actual_profile buf; buf.command = KONEPLUS_COMMAND_ACTUAL_PROFILE; - buf.size = sizeof(struct koneplus_actual_profile); + buf.size = KONEPLUS_SIZE_ACTUAL_PROFILE; buf.actual_profile = new_profile; return roccat_common2_send_with_status(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, - &buf, sizeof(struct koneplus_actual_profile)); + &buf, KONEPLUS_SIZE_ACTUAL_PROFILE); } static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, @@ -183,77 +133,58 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return real_size; } -static ssize_t koneplus_sysfs_read_info(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_read(fp, kobj, buf, off, count, - sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +#define KONEPLUS_SYSFS_W(thingy, THINGY) \ +static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return koneplus_sysfs_write(fp, kobj, buf, off, count, \ + KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \ } -static ssize_t koneplus_sysfs_write_info(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_write(fp, kobj, buf, off, count, - sizeof(struct koneplus_info), KONEPLUS_COMMAND_INFO); +#define KONEPLUS_SYSFS_R(thingy, THINGY) \ +static ssize_t koneplus_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return koneplus_sysfs_read(fp, kobj, buf, off, count, \ + KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \ } -static ssize_t koneplus_sysfs_write_talk(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_write(fp, kobj, buf, off, count, - sizeof(struct koneplus_talk), KONEPLUS_COMMAND_TALK); -} +#define KONEPLUS_SYSFS_RW(thingy, THINGY) \ +KONEPLUS_SYSFS_W(thingy, THINGY) \ +KONEPLUS_SYSFS_R(thingy, THINGY) -static ssize_t koneplus_sysfs_write_macro(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_write(fp, kobj, buf, off, count, - sizeof(struct koneplus_macro), KONEPLUS_COMMAND_MACRO); +#define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = KONEPLUS_SIZE_ ## THINGY, \ + .read = koneplus_sysfs_read_ ## thingy, \ + .write = koneplus_sysfs_write_ ## thingy \ } -static ssize_t koneplus_sysfs_read_sensor(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_read(fp, kobj, buf, off, count, - sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR); +#define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = KONEPLUS_SIZE_ ## THINGY, \ + .read = koneplus_sysfs_read_ ## thingy, \ } -static ssize_t koneplus_sysfs_write_sensor(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_write(fp, kobj, buf, off, count, - sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR); +#define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = KONEPLUS_SIZE_ ## THINGY, \ + .write = koneplus_sysfs_write_ ## thingy \ } -static ssize_t koneplus_sysfs_write_tcu(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_write(fp, kobj, buf, off, count, - sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); -} - -static ssize_t koneplus_sysfs_read_tcu(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_read(fp, kobj, buf, off, count, - sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU); -} - -static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) -{ - return koneplus_sysfs_read(fp, kobj, buf, off, count, - sizeof(struct koneplus_tcu_image), KONEPLUS_COMMAND_TCU); -} +KONEPLUS_SYSFS_RW(info, INFO) +KONEPLUS_SYSFS_W(talk, TALK) +KONEPLUS_SYSFS_W(macro, MACRO) +KONEPLUS_SYSFS_RW(sensor, SENSOR) +KONEPLUS_SYSFS_RW(tcu, TCU) +KONEPLUS_SYSFS_R(tcu_image, TCU_IMAGE) +KONEPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) +KONEPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -261,57 +192,17 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; - struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev)); - - if (off >= sizeof(struct koneplus_profile_settings)) - return 0; - - if (off + count > sizeof(struct koneplus_profile_settings)) - count = sizeof(struct koneplus_profile_settings) - off; - - mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off, - count); - mutex_unlock(&koneplus->koneplus_lock); - - return count; -} - -static ssize_t koneplus_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 koneplus_device *koneplus = 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_number; - struct koneplus_profile_settings *profile_settings; - - if (off != 0 || count != sizeof(struct koneplus_profile_settings)) - return -EINVAL; - - profile_number = ((struct koneplus_profile_settings const *)buf)->number; - profile_settings = &koneplus->profile_settings[profile_number]; - - mutex_lock(&koneplus->koneplus_lock); - difference = memcmp(buf, profile_settings, - sizeof(struct koneplus_profile_settings)); - if (difference) { - retval = koneplus_set_profile_settings(usb_dev, - (struct koneplus_profile_settings const *)buf); - if (!retval) - memcpy(profile_settings, buf, - sizeof(struct koneplus_profile_settings)); - } - mutex_unlock(&koneplus->koneplus_lock); + ssize_t retval; + retval = koneplus_send_control(usb_dev, *(uint *)(attr->private), + KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); if (retval) return retval; - return sizeof(struct koneplus_profile_settings); + return koneplus_sysfs_read(fp, kobj, buf, off, count, + KONEPLUS_SIZE_PROFILE_SETTINGS, + KONEPLUS_COMMAND_PROFILE_SETTINGS); } static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, @@ -320,57 +211,17 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; - struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev)); - - if (off >= sizeof(struct koneplus_profile_buttons)) - return 0; - - if (off + count > sizeof(struct koneplus_profile_buttons)) - count = sizeof(struct koneplus_profile_buttons) - off; - - mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off, - count); - mutex_unlock(&koneplus->koneplus_lock); - - return count; -} - -static ssize_t koneplus_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 koneplus_device *koneplus = 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_number; - struct koneplus_profile_buttons *profile_buttons; - - if (off != 0 || count != sizeof(struct koneplus_profile_buttons)) - return -EINVAL; - - profile_number = ((struct koneplus_profile_buttons const *)buf)->number; - profile_buttons = &koneplus->profile_buttons[profile_number]; - - mutex_lock(&koneplus->koneplus_lock); - difference = memcmp(buf, profile_buttons, - sizeof(struct koneplus_profile_buttons)); - if (difference) { - retval = koneplus_set_profile_buttons(usb_dev, - (struct koneplus_profile_buttons const *)buf); - if (!retval) - memcpy(profile_buttons, buf, - sizeof(struct koneplus_profile_buttons)); - } - mutex_unlock(&koneplus->koneplus_lock); + ssize_t retval; + retval = koneplus_send_control(usb_dev, *(uint *)(attr->private), + KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); if (retval) return retval; - return sizeof(struct koneplus_profile_buttons); + return koneplus_sysfs_read(fp, kobj, buf, off, count, + KONEPLUS_SIZE_PROFILE_BUTTONS, + KONEPLUS_COMMAND_PROFILE_BUTTONS); } static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev, @@ -426,9 +277,20 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev, static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct koneplus_device *koneplus = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->info.firmware_version); + struct koneplus_device *koneplus; + struct usb_device *usb_dev; + struct koneplus_info info; + + dev = dev->parent->parent; + koneplus = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + mutex_lock(&koneplus->koneplus_lock); + roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO, + &info, KONEPLUS_SIZE_INFO); + mutex_unlock(&koneplus->koneplus_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } static struct device_attribute koneplus_attributes[] = { @@ -444,139 +306,84 @@ static struct device_attribute koneplus_attributes[] = { }; static struct bin_attribute koneplus_bin_attributes[] = { - { - .attr = { .name = "info", .mode = 0660 }, - .size = sizeof(struct koneplus_info), - .read = koneplus_sysfs_read_info, - .write = koneplus_sysfs_write_info - }, - { - .attr = { .name = "sensor", .mode = 0660 }, - .size = sizeof(struct koneplus_sensor), - .read = koneplus_sysfs_read_sensor, - .write = koneplus_sysfs_write_sensor - }, - { - .attr = { .name = "tcu", .mode = 0660 }, - .size = sizeof(struct koneplus_tcu), - .read = koneplus_sysfs_read_tcu, - .write = koneplus_sysfs_write_tcu - }, - { - .attr = { .name = "tcu_image", .mode = 0440 }, - .size = sizeof(struct koneplus_tcu_image), - .read = koneplus_sysfs_read_tcu_image - }, - { - .attr = { .name = "profile_settings", .mode = 0220 }, - .size = sizeof(struct koneplus_profile_settings), - .write = koneplus_sysfs_write_profile_settings - }, + KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO), + KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK), + KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO), + KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR), + KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU), + KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), + KONEPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), + KONEPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_settings), + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, .read = koneplus_sysfs_read_profilex_settings, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_settings), + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, .read = koneplus_sysfs_read_profilex_settings, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_settings), + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, .read = koneplus_sysfs_read_profilex_settings, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_settings), + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, .read = koneplus_sysfs_read_profilex_settings, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_settings), + .size = KONEPLUS_SIZE_PROFILE_SETTINGS, .read = koneplus_sysfs_read_profilex_settings, .private = &profile_numbers[4] }, - { - .attr = { .name = "profile_buttons", .mode = 0220 }, - .size = sizeof(struct koneplus_profile_buttons), - .write = koneplus_sysfs_write_profile_buttons - }, { .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_buttons), + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, .read = koneplus_sysfs_read_profilex_buttons, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_buttons), + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, .read = koneplus_sysfs_read_profilex_buttons, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_buttons), + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, .read = koneplus_sysfs_read_profilex_buttons, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_buttons), + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, .read = koneplus_sysfs_read_profilex_buttons, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = sizeof(struct koneplus_profile_buttons), + .size = KONEPLUS_SIZE_PROFILE_BUTTONS, .read = koneplus_sysfs_read_profilex_buttons, .private = &profile_numbers[4] }, - { - .attr = { .name = "macro", .mode = 0220 }, - .size = sizeof(struct koneplus_macro), - .write = koneplus_sysfs_write_macro - }, - { - .attr = { .name = "talk", .mode = 0220 }, - .size = sizeof(struct koneplus_talk), - .write = koneplus_sysfs_write_talk - }, __ATTR_NULL }; static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, struct koneplus_device *koneplus) { - int retval, i; - static uint wait = 200; + int retval; mutex_init(&koneplus->koneplus_lock); - retval = koneplus_get_info(usb_dev, &koneplus->info); - if (retval) - return retval; - - for (i = 0; i < 5; ++i) { - msleep(wait); - retval = koneplus_get_profile_settings(usb_dev, - &koneplus->profile_settings[i], i); - if (retval) - return retval; - - msleep(wait); - retval = koneplus_get_profile_buttons(usb_dev, - &koneplus->profile_buttons[i], i); - if (retval) - return retval; - } - - msleep(wait); retval = koneplus_get_actual_profile(usb_dev); if (retval < 0) return retval; diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h index 7074b2a4b94b..563b9d3fe5fb 100644 --- a/drivers/hid/hid-roccat-koneplus.h +++ b/drivers/hid/hid-roccat-koneplus.h @@ -14,11 +14,18 @@ #include -struct koneplus_talk { - uint8_t command; /* KONEPLUS_COMMAND_TALK */ - uint8_t size; /* always 0x10 */ - uint8_t data[14]; -} __packed; +enum { + KONEPLUS_SIZE_ACTUAL_PROFILE = 0x03, + KONEPLUS_SIZE_FIRMWARE_WRITE = 0x0402, + KONEPLUS_SIZE_INFO = 0x06, + KONEPLUS_SIZE_MACRO = 0x0822, + KONEPLUS_SIZE_PROFILE_SETTINGS = 0x2b, + KONEPLUS_SIZE_PROFILE_BUTTONS = 0x4d, + KONEPLUS_SIZE_SENSOR = 0x06, + KONEPLUS_SIZE_TALK = 0x10, + KONEPLUS_SIZE_TCU = 0x03, + KONEPLUS_SIZE_TCU_IMAGE = 0x0404, +}; enum koneplus_control_requests { KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80, @@ -31,45 +38,6 @@ struct koneplus_actual_profile { uint8_t actual_profile; /* Range 0-4! */ } __attribute__ ((__packed__)); -struct koneplus_profile_settings { - uint8_t command; /* KONEPLUS_COMMAND_PROFILE_SETTINGS */ - uint8_t size; /* always 43 */ - uint8_t number; /* range 0-4 */ - uint8_t advanced_sensitivity; - uint8_t sensitivity_x; - uint8_t sensitivity_y; - uint8_t cpi_levels_enabled; - uint8_t cpi_levels_x[5]; - uint8_t cpi_startup_level; /* range 0-4 */ - uint8_t cpi_levels_y[5]; /* range 1-60 means 100-6000 cpi */ - uint8_t unknown1; - uint8_t polling_rate; - uint8_t lights_enabled; - uint8_t light_effect_mode; - uint8_t color_flow_effect; - uint8_t light_effect_type; - uint8_t light_effect_speed; - uint8_t lights[16]; - uint16_t checksum; -} __attribute__ ((__packed__)); - -struct koneplus_profile_buttons { - uint8_t command; /* KONEPLUS_COMMAND_PROFILE_BUTTONS */ - uint8_t size; /* always 77 */ - uint8_t number; /* range 0-4 */ - uint8_t data[72]; - uint16_t checksum; -} __attribute__ ((__packed__)); - -struct koneplus_macro { - uint8_t command; /* KONEPLUS_COMMAND_MACRO */ - uint16_t size; /* always 0x822 little endian */ - uint8_t profile; /* range 0-4 */ - uint8_t button; /* range 0-23 */ - uint8_t data[2075]; - uint16_t checksum; -} __attribute__ ((__packed__)); - struct koneplus_info { uint8_t command; /* KONEPLUS_COMMAND_INFO */ uint8_t size; /* always 6 */ @@ -77,44 +45,6 @@ struct koneplus_info { uint8_t unknown[3]; } __attribute__ ((__packed__)); -struct koneplus_e { - uint8_t command; /* KONEPLUS_COMMAND_E */ - uint8_t size; /* always 3 */ - uint8_t unknown; /* TODO 1; 0 before firmware update */ -} __attribute__ ((__packed__)); - -struct koneplus_sensor { - uint8_t command; /* KONEPLUS_COMMAND_SENSOR */ - uint8_t size; /* always 6 */ - uint8_t data[4]; -} __attribute__ ((__packed__)); - -struct koneplus_firmware_write { - uint8_t command; /* KONEPLUS_COMMAND_FIRMWARE_WRITE */ - uint8_t unknown[1025]; -} __attribute__ ((__packed__)); - -struct koneplus_firmware_write_control { - uint8_t command; /* KONEPLUS_COMMAND_FIRMWARE_WRITE_CONTROL */ - /* - * value is 1 on success - * 3 means "not finished yet" - */ - uint8_t value; - uint8_t unknown; /* always 0x75 */ -} __attribute__ ((__packed__)); - -struct koneplus_tcu { - uint16_t usb_command; /* KONEPLUS_USB_COMMAND_TCU */ - uint8_t data[2]; -} __attribute__ ((__packed__)); - -struct koneplus_tcu_image { - uint16_t usb_command; /* KONEPLUS_USB_COMMAND_TCU */ - uint8_t data[1024]; - uint16_t checksum; -} __attribute__ ((__packed__)); - enum koneplus_commands { KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5, KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6, @@ -122,6 +52,7 @@ enum koneplus_commands { KONEPLUS_COMMAND_MACRO = 0x8, KONEPLUS_COMMAND_INFO = 0x9, KONEPLUS_COMMAND_TCU = 0xc, + KONEPLUS_COMMAND_TCU_IMAGE = 0xc, KONEPLUS_COMMAND_E = 0xe, KONEPLUS_COMMAND_SENSOR = 0xf, KONEPLUS_COMMAND_TALK = 0x10, @@ -187,10 +118,6 @@ struct koneplus_device { int chrdev_minor; struct mutex koneplus_lock; - - struct koneplus_info info; - struct koneplus_profile_settings profile_settings[5]; - struct koneplus_profile_buttons profile_buttons[5]; }; #endif -- cgit v1.2.3-59-g8ed1b From 729b814acec20db66fc891b5392cb653ad6598ef Mon Sep 17 00:00:00 2001 From: Forest Bond Date: Tue, 6 Nov 2012 13:41:22 -0500 Subject: HID: Ignore D-WAV/eGalax devices handled by usbtouchscreen Previously, both usbhid and usbtouchscreen would bind to D-WAV devices with class HID and protocol None, so they would be claimed by whichever driver was loaded first. Some of these devices do in fact work with usbhid, but not all of them do. OTOH they all work with usbtouchscreen as of commit 037a833ed05a86d01ea27a2c32043b86c549be1b ("Input: usbtouchscreen - initialize eGalax devices"). So we ignore them in usbhid to prevent getting in the way of usbtouchscreen and claiming an interface that we may not be able to do anything useful with. Signed-off-by: Forest Bond Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 11 ++++++++++- drivers/hid/hid-ids.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 0a6b36fbb4ce..a7550bb30836 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2183,7 +2183,16 @@ static bool hid_ignore(struct hid_device *hdev) if (hdev->product == USB_DEVICE_ID_JESS_YUREX && hdev->type == HID_TYPE_USBNONE) return true; - break; + break; + case USB_VENDOR_ID_DWAV: + /* These are handled by usbtouchscreen. hdev->type is probably + * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match + * usbtouchscreen. */ + if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER || + hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) && + hdev->type != HID_TYPE_USBMOUSE) + return true; + break; } if (hdev->type == HID_TYPE_USBMOUSE && diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9bc8d570777a..6a971477535c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -254,6 +254,7 @@ #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 +#define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER 0x0002 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207 -- cgit v1.2.3-59-g8ed1b From 94a8fcf9a1a816199efc29546040172aa1383be0 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:20:52 +0100 Subject: HID: roccat: cleanup of kovaplus module Partially removed unneeded informations and data caching. Moved code nearer to format of newer drivers. Added "info" sysfs attribute to support device reset and deprecate other attributes. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-kovaplus | 41 ++++ .../ABI/testing/sysfs-driver-hid-roccat-kovaplus | 40 +--- drivers/hid/hid-roccat-kovaplus.c | 235 ++++++++++----------- drivers/hid/hid-roccat-kovaplus.h | 14 +- 4 files changed, 167 insertions(+), 163 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus new file mode 100644 index 000000000000..4d9fb26292a2 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus @@ -0,0 +1,41 @@ +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. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +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. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +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. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +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. + Obsoleted by binary sysfs attribute "info". +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 20f937c9d84f..507f2c7321d5 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -1,12 +1,3 @@ -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. -Users: http://roccat.sourceforge.net - What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_profile Date: January 2011 Contact: Stefan Achatz @@ -18,33 +9,12 @@ Description: The integer value of this attribute ranges from 0-4. 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 -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. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/actual_sensitivity_y -Date: January 2011 +What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/info +Date: November 2012 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. -Users: http://roccat.sourceforge.net - -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. +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_buttons diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index ca6527ac655d..1589fd3bcf0f 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -70,13 +70,6 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, return kovaplus_send_control(usb_dev, number, request); } -static int kovaplus_get_info(struct usb_device *usb_dev, - struct kovaplus_info *buf) -{ - return roccat_common2_receive(usb_dev, KOVAPLUS_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) { @@ -88,15 +81,7 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev, return retval; return roccat_common2_receive(usb_dev, KOVAPLUS_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 roccat_common2_send_with_status(usb_dev, - KOVAPLUS_COMMAND_PROFILE_SETTINGS, - settings, sizeof(struct kovaplus_profile_settings)); + buf, KOVAPLUS_SIZE_PROFILE_SETTINGS); } static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, @@ -110,15 +95,7 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, return retval; return roccat_common2_receive(usb_dev, KOVAPLUS_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 roccat_common2_send_with_status(usb_dev, - KOVAPLUS_COMMAND_PROFILE_BUTTONS, - buttons, sizeof(struct kovaplus_profile_buttons)); + buf, KOVAPLUS_SIZE_PROFILE_BUTTONS); } /* retval is 0-4 on success, < 0 on error */ @@ -147,122 +124,140 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev, &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) +static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct kovaplus_profile_settings)) + if (off >= real_size) return 0; - if (off + count > sizeof(struct kovaplus_profile_settings)) - count = sizeof(struct kovaplus_profile_settings) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&kovaplus->kovaplus_lock); - memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&kovaplus->kovaplus_lock); - return count; + if (retval) + return retval; + + return real_size; } -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) +static ssize_t kovaplus_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 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; + int retval; - if (off != 0 || count != sizeof(struct kovaplus_profile_settings)) + if (off != 0 || count != real_size) 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)); - } + retval = roccat_common2_send_with_status(usb_dev, command, + buf, real_size); mutex_unlock(&kovaplus->kovaplus_lock); if (retval) return retval; - return sizeof(struct kovaplus_profile_settings); + return real_size; } -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)); +#define KOVAPLUS_SYSFS_W(thingy, THINGY) \ +static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return kovaplus_sysfs_write(fp, kobj, buf, off, count, \ + KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ +} - if (off >= sizeof(struct kovaplus_profile_buttons)) - return 0; +#define KOVAPLUS_SYSFS_R(thingy, THINGY) \ +static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return kovaplus_sysfs_read(fp, kobj, buf, off, count, \ + KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ +} - if (off + count > sizeof(struct kovaplus_profile_buttons)) - count = sizeof(struct kovaplus_profile_buttons) - off; +#define KOVAPLUS_SYSFS_RW(thingy, THINGY) \ +KOVAPLUS_SYSFS_W(thingy, THINGY) \ +KOVAPLUS_SYSFS_R(thingy, THINGY) - mutex_lock(&kovaplus->kovaplus_lock); - memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off, - count); - mutex_unlock(&kovaplus->kovaplus_lock); +#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .read = kovaplus_sysfs_read_ ## thingy, \ + .write = kovaplus_sysfs_write_ ## thingy \ +} + +#define KOVAPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .read = kovaplus_sysfs_read_ ## thingy, \ +} - return count; +#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = KOVAPLUS_SIZE_ ## THINGY, \ + .write = kovaplus_sysfs_write_ ## thingy \ } -static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp, +KOVAPLUS_SYSFS_RW(info, INFO) +KOVAPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) +KOVAPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) + +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)); 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; + ssize_t retval; - if (off != 0 || count != sizeof(struct kovaplus_profile_buttons)) - return -EINVAL; + retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private), + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); + if (retval) + return retval; - profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index; - profile_buttons = &kovaplus->profile_buttons[profile_index]; + return kovaplus_sysfs_read(fp, kobj, buf, off, count, + KOVAPLUS_SIZE_PROFILE_SETTINGS, + KOVAPLUS_COMMAND_PROFILE_SETTINGS); +} - 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); +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 usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + ssize_t retval; + retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private), + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); if (retval) return retval; - return sizeof(struct kovaplus_profile_buttons); + return kovaplus_sysfs_read(fp, kobj, buf, off, count, + KOVAPLUS_SIZE_PROFILE_BUTTONS, + KOVAPLUS_COMMAND_PROFILE_BUTTONS); } static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, @@ -342,9 +337,20 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, 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); + struct kovaplus_device *kovaplus; + struct usb_device *usb_dev; + struct kovaplus_info info; + + dev = dev->parent->parent; + kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + mutex_lock(&kovaplus->kovaplus_lock); + roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO, + &info, KOVAPLUS_SIZE_INFO); + mutex_unlock(&kovaplus->kovaplus_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } static struct device_attribute kovaplus_attributes[] = { @@ -363,73 +369,66 @@ static struct device_attribute kovaplus_attributes[] = { }; 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 - }, + KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO), + KOVAPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), + KOVAPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, .read = kovaplus_sysfs_read_profilex_settings, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_settings), + .size = KOVAPLUS_SIZE_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), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = sizeof(struct kovaplus_profile_buttons), + .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, .read = kovaplus_sysfs_read_profilex_buttons, .private = &profile_numbers[4] }, @@ -444,10 +443,6 @@ static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, 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, diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h index f82daa1cdcb9..1189573daee2 100644 --- a/drivers/hid/hid-roccat-kovaplus.h +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -14,6 +14,12 @@ #include +enum { + KOVAPLUS_SIZE_INFO = 0x06, + KOVAPLUS_SIZE_PROFILE_SETTINGS = 0x10, + KOVAPLUS_SIZE_PROFILE_BUTTONS = 0x17, +}; + enum kovaplus_control_requests { /* write; value = profile number range 0-4 */ KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, @@ -53,13 +59,6 @@ struct kovaplus_info { 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_ACTUAL_PROFILE = 0x5, KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, @@ -125,7 +124,6 @@ struct kovaplus_device { 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]; }; -- cgit v1.2.3-59-g8ed1b From be34380ef818c182860c06f048cbea821203f9b7 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:20:58 +0100 Subject: HID: roccat: cleanup of pyra module Partially removed unneeded informations and data caching. Moved code nearer to format of newer drivers. Added "info" sysfs attribute to support device reset and deprecate "firmware_version" attribute. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 48 +++ .../ABI/testing/sysfs-driver-hid-roccat-pyra | 45 --- drivers/hid/hid-roccat-pyra.c | 340 +++++++++------------ drivers/hid/hid-roccat-pyra.h | 22 +- 4 files changed, 198 insertions(+), 257 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra new file mode 100644 index 000000000000..0a661b39e701 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra @@ -0,0 +1,48 @@ +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_cpi +Date: August 2010 +Contact: Stefan Achatz +Description: It is possible to switch the cpi setting of the mouse with the + press of a button. + When read, this file returns the raw number of the actual cpi + setting reported by the mouse. This number has to be further + processed to receive the real dpi value. + + VALUE DPI + 1 400 + 2 800 + 4 1600 + + This file is readonly. + Has never been used. If bookkeeping is done, it's done in userland tools. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_profile +Date: August 2010 +Contact: Stefan Achatz +Description: When read, this file returns the number of the actual profile in + range 0-4. + This file is readonly. + Please use binary attribute "settings" which provides this information. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/firmware_version +Date: August 2010 +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 138 means 1.38 + This file is readonly. + Please use binary attribute "info" which provides this information. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile +Date: August 2010 +Contact: Stefan Achatz +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. + Please use binary attribute "settings" which provides this information. +Users: http://roccat.sourceforge.net \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra index 3f8de50e4ff1..b0fab8bcf247 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -1,39 +1,3 @@ -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_cpi -Date: August 2010 -Contact: Stefan Achatz -Description: It is possible to switch the cpi setting of the mouse with the - press of a button. - When read, this file returns the raw number of the actual cpi - setting reported by the mouse. This number has to be further - processed to receive the real dpi value. - - VALUE DPI - 1 400 - 2 800 - 4 1600 - - This file is readonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/actual_profile -Date: August 2010 -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 -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 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 Contact: Stefan Achatz @@ -86,15 +50,6 @@ Description: The mouse can store 5 profiles which can be switched by the This file is readonly. Users: http://roccat.sourceforge.net -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile -Date: August 2010 -Contact: Stefan Achatz -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 Contact: Stefan Achatz diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 1317c177a3e2..76199fa727ed 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -66,48 +66,14 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; return roccat_common2_receive(usb_dev, PYRA_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; - return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, - buf, sizeof(struct pyra_profile_buttons)); + buf, PYRA_SIZE_PROFILE_SETTINGS); } static int pyra_get_settings(struct usb_device *usb_dev, struct pyra_settings *buf) { return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, - buf, sizeof(struct pyra_settings)); -} - -static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) -{ - return roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, - buf, sizeof(struct pyra_info)); -} - -static int pyra_set_profile_settings(struct usb_device *usb_dev, - struct pyra_profile_settings const *settings) -{ - return roccat_common2_send_with_status(usb_dev, - PYRA_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) -{ - return roccat_common2_send_with_status(usb_dev, - PYRA_COMMAND_PROFILE_BUTTONS, buttons, - sizeof(struct pyra_profile_buttons)); + buf, PYRA_SIZE_SETTINGS); } static int pyra_set_settings(struct usb_device *usb_dev, @@ -115,146 +81,143 @@ static int pyra_set_settings(struct usb_device *usb_dev, { return roccat_common2_send_with_status(usb_dev, PYRA_COMMAND_SETTINGS, settings, - sizeof(struct pyra_settings)); + PYRA_SIZE_SETTINGS); } -static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) { struct device *dev = container_of(kobj, struct device, kobj)->parent->parent; struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct pyra_profile_settings)) + if (off >= real_size) return 0; - if (off + count > sizeof(struct pyra_profile_settings)) - count = sizeof(struct pyra_profile_settings) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&pyra->pyra_lock); - return count; + if (retval) + return retval; + + return real_size; } -static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) +static ssize_t pyra_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 pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; - if (off >= sizeof(struct pyra_profile_buttons)) - return 0; - - if (off + count > sizeof(struct pyra_profile_buttons)) - count = sizeof(struct pyra_profile_buttons) - off; + if (off != 0 || count != real_size) + return -EINVAL; mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off, - count); + retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); mutex_unlock(&pyra->pyra_lock); - return count; + if (retval) + return retval; + + return real_size; } -static ssize_t pyra_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 pyra_device *pyra = 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_number; - struct pyra_profile_settings *profile_settings; +#define PYRA_SYSFS_W(thingy, THINGY) \ +static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return pyra_sysfs_write(fp, kobj, buf, off, count, \ + PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ +} - if (off != 0 || count != sizeof(struct pyra_profile_settings)) - return -EINVAL; +#define PYRA_SYSFS_R(thingy, THINGY) \ +static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return pyra_sysfs_read(fp, kobj, buf, off, count, \ + PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ +} - profile_number = ((struct pyra_profile_settings const *)buf)->number; - profile_settings = &pyra->profile_settings[profile_number]; +#define PYRA_SYSFS_RW(thingy, THINGY) \ +PYRA_SYSFS_W(thingy, THINGY) \ +PYRA_SYSFS_R(thingy, THINGY) - mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, profile_settings, - sizeof(struct pyra_profile_settings)); - if (difference) { - retval = pyra_set_profile_settings(usb_dev, - (struct pyra_profile_settings const *)buf); - if (!retval) - memcpy(profile_settings, buf, - sizeof(struct pyra_profile_settings)); - } - mutex_unlock(&pyra->pyra_lock); +#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .read = pyra_sysfs_read_ ## thingy, \ + .write = pyra_sysfs_write_ ## thingy \ +} - if (retval) - return retval; +#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .read = pyra_sysfs_read_ ## thingy, \ +} - return sizeof(struct pyra_profile_settings); +#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = PYRA_SIZE_ ## THINGY, \ + .write = pyra_sysfs_write_ ## thingy \ } -static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, +PYRA_SYSFS_RW(info, INFO) +PYRA_SYSFS_W(profile_settings, PROFILE_SETTINGS) +PYRA_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +PYRA_SYSFS_R(settings, SETTINGS) + +static ssize_t pyra_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 pyra_device *pyra = 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_number; - struct pyra_profile_buttons *profile_buttons; - - if (off != 0 || count != sizeof(struct pyra_profile_buttons)) - return -EINVAL; - - profile_number = ((struct pyra_profile_buttons const *)buf)->number; - profile_buttons = &pyra->profile_buttons[profile_number]; - - mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, profile_buttons, - sizeof(struct pyra_profile_buttons)); - if (difference) { - retval = pyra_set_profile_buttons(usb_dev, - (struct pyra_profile_buttons const *)buf); - if (!retval) - memcpy(profile_buttons, buf, - sizeof(struct pyra_profile_buttons)); - } - mutex_unlock(&pyra->pyra_lock); + ssize_t retval; + retval = pyra_send_control(usb_dev, *(uint *)(attr->private), + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); if (retval) return retval; - return sizeof(struct pyra_profile_buttons); + return pyra_sysfs_read(fp, kobj, buf, off, count, + PYRA_SIZE_PROFILE_SETTINGS, + PYRA_COMMAND_PROFILE_SETTINGS); } -static ssize_t pyra_sysfs_read_settings(struct file *fp, +static ssize_t pyra_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 pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); - - if (off >= sizeof(struct pyra_settings)) - return 0; - - if (off + count > sizeof(struct pyra_settings)) - count = sizeof(struct pyra_settings) - off; + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + ssize_t retval; - mutex_lock(&pyra->pyra_lock); - memcpy(buf, ((char const *)&pyra->settings) + off, count); - mutex_unlock(&pyra->pyra_lock); + retval = pyra_send_control(usb_dev, *(uint *)(attr->private), + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); + if (retval) + return retval; - return count; + return pyra_sysfs_read(fp, kobj, buf, off, count, + PYRA_SIZE_PROFILE_BUTTONS, + PYRA_COMMAND_PROFILE_BUTTONS); } static ssize_t pyra_sysfs_write_settings(struct file *fp, @@ -266,35 +229,32 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval = 0; - int difference; struct pyra_roccat_report roccat_report; + struct pyra_settings const *settings; - if (off != 0 || count != sizeof(struct pyra_settings)) + if (off != 0 || count != PYRA_SIZE_SETTINGS) return -EINVAL; mutex_lock(&pyra->pyra_lock); - difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); - if (difference) { - retval = pyra_set_settings(usb_dev, - (struct pyra_settings const *)buf); - if (retval) { - mutex_unlock(&pyra->pyra_lock); - return retval; - } - - memcpy(&pyra->settings, buf, - sizeof(struct pyra_settings)); - profile_activated(pyra, pyra->settings.startup_profile); + settings = (struct pyra_settings const *)buf; - roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; - roccat_report.value = pyra->settings.startup_profile + 1; - roccat_report.key = 0; - roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report); + retval = pyra_set_settings(usb_dev, settings); + if (retval) { + mutex_unlock(&pyra->pyra_lock); + return retval; } + + profile_activated(pyra, settings->startup_profile); + + roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; + roccat_report.value = settings->startup_profile + 1; + roccat_report.key = 0; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report); + mutex_unlock(&pyra->pyra_lock); - return sizeof(struct pyra_settings); + return PYRA_SIZE_SETTINGS; } @@ -311,23 +271,34 @@ static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, { struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + struct pyra_settings settings; + + mutex_lock(&pyra->pyra_lock); + roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, + &settings, PYRA_SIZE_SETTINGS); + mutex_unlock(&pyra->pyra_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); } static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct pyra_device *pyra = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); -} + struct pyra_device *pyra; + struct usb_device *usb_dev; + struct pyra_info info; -static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pyra_device *pyra = - hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); + dev = dev->parent->parent; + pyra = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + mutex_lock(&pyra->pyra_lock); + roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, + &info, PYRA_SIZE_INFO); + mutex_unlock(&pyra->pyra_lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); } static struct device_attribute pyra_attributes[] = { @@ -336,105 +307,87 @@ static struct device_attribute pyra_attributes[] = { __ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, NULL), __ATTR(startup_profile, 0440, - pyra_sysfs_show_startup_profile, NULL), + pyra_sysfs_show_actual_profile, NULL), __ATTR_NULL }; static struct bin_attribute pyra_bin_attributes[] = { - { - .attr = { .name = "profile_settings", .mode = 0220 }, - .size = sizeof(struct pyra_profile_settings), - .write = pyra_sysfs_write_profile_settings - }, + PYRA_BIN_ATTRIBUTE_RW(info, INFO), + PYRA_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), + PYRA_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), { .attr = { .name = "profile1_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_settings", .mode = 0440 }, - .size = sizeof(struct pyra_profile_settings), + .size = PYRA_SIZE_PROFILE_SETTINGS, .read = pyra_sysfs_read_profilex_settings, .private = &profile_numbers[4] }, - { - .attr = { .name = "profile_buttons", .mode = 0220 }, - .size = sizeof(struct pyra_profile_buttons), - .write = pyra_sysfs_write_profile_buttons - }, { .attr = { .name = "profile1_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[0] }, { .attr = { .name = "profile2_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[1] }, { .attr = { .name = "profile3_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[2] }, { .attr = { .name = "profile4_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[3] }, { .attr = { .name = "profile5_buttons", .mode = 0440 }, - .size = sizeof(struct pyra_profile_buttons), + .size = PYRA_SIZE_PROFILE_BUTTONS, .read = pyra_sysfs_read_profilex_buttons, .private = &profile_numbers[4] }, - { - .attr = { .name = "settings", .mode = 0660 }, - .size = sizeof(struct pyra_settings), - .read = pyra_sysfs_read_settings, - .write = pyra_sysfs_write_settings - }, __ATTR_NULL }; static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, struct pyra_device *pyra) { - struct pyra_info info; + struct pyra_settings settings; int retval, i; mutex_init(&pyra->pyra_lock); - retval = pyra_get_info(usb_dev, &info); - if (retval) - return retval; - - pyra->firmware_version = info.firmware_version; - - retval = pyra_get_settings(usb_dev, &pyra->settings); + retval = pyra_get_settings(usb_dev, &settings); if (retval) return retval; @@ -443,14 +396,9 @@ static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, &pyra->profile_settings[i], i); if (retval) return retval; - - retval = pyra_get_profile_buttons(usb_dev, - &pyra->profile_buttons[i], i); - if (retval) - return retval; } - profile_activated(pyra, pyra->settings.startup_profile); + profile_activated(pyra, settings.startup_profile); return 0; } diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h index eada7830fa99..93e494984976 100644 --- a/drivers/hid/hid-roccat-pyra.h +++ b/drivers/hid/hid-roccat-pyra.h @@ -14,11 +14,12 @@ #include -struct pyra_b { - uint8_t command; /* PYRA_COMMAND_B */ - uint8_t size; /* always 3 */ - uint8_t unknown; /* 1 */ -} __attribute__ ((__packed__)); +enum { + PYRA_SIZE_INFO = 0x06, + PYRA_SIZE_PROFILE_SETTINGS = 0x0d, + PYRA_SIZE_PROFILE_BUTTONS = 0x13, + PYRA_SIZE_SETTINGS = 0x03, +}; enum pyra_control_requests { PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, @@ -46,14 +47,6 @@ struct pyra_profile_settings { uint16_t checksum; /* byte sum */ } __attribute__ ((__packed__)); -struct pyra_profile_buttons { - uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */ - uint8_t size; /* always 0x13 */ - uint8_t number; /* Range 0-4 */ - uint8_t buttons[14]; - uint16_t checksum; /* byte sum */ -} __attribute__ ((__packed__)); - struct pyra_info { uint8_t command; /* PYRA_COMMAND_INFO */ uint8_t size; /* always 6 */ @@ -148,13 +141,10 @@ struct pyra_roccat_report { struct pyra_device { int actual_profile; int actual_cpi; - int firmware_version; int roccat_claimed; int chrdev_minor; struct mutex pyra_lock; - struct pyra_settings settings; struct pyra_profile_settings profile_settings[5]; - struct pyra_profile_buttons profile_buttons[5]; }; #endif -- cgit v1.2.3-59-g8ed1b From 6e5920ddbbc114d9f7864b14a8db42e714a8b99f Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:02 +0100 Subject: HID: roccat: clean up isku driver Removed unneeded structures from header. Using enums instead of sizeof(struct) to unify drivers. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-isku.c | 42 ++++++++++++------------ drivers/hid/hid-roccat-isku.h | 76 +++++++++---------------------------------- 2 files changed, 37 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 5669916c2943..020d6cda8975 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -167,7 +167,7 @@ static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj loff_t off, size_t count) \ { \ return isku_sysfs_write(fp, kobj, buf, off, count, \ - sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \ + ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \ } #define ISKU_SYSFS_R(thingy, THINGY) \ @@ -176,32 +176,32 @@ static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, loff_t off, size_t count) \ { \ return isku_sysfs_read(fp, kobj, buf, off, count, \ - sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \ + ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \ } #define ISKU_SYSFS_RW(thingy, THINGY) \ ISKU_SYSFS_R(thingy, THINGY) \ ISKU_SYSFS_W(thingy, THINGY) -#define ISKU_BIN_ATTR_RW(thingy) \ +#define ISKU_BIN_ATTR_RW(thingy, THINGY) \ { \ .attr = { .name = #thingy, .mode = 0660 }, \ - .size = sizeof(struct isku_ ## thingy), \ + .size = ISKU_SIZE_ ## THINGY, \ .read = isku_sysfs_read_ ## thingy, \ .write = isku_sysfs_write_ ## thingy \ } -#define ISKU_BIN_ATTR_R(thingy) \ +#define ISKU_BIN_ATTR_R(thingy, THINGY) \ { \ .attr = { .name = #thingy, .mode = 0440 }, \ - .size = sizeof(struct isku_ ## thingy), \ + .size = ISKU_SIZE_ ## THINGY, \ .read = isku_sysfs_read_ ## thingy, \ } -#define ISKU_BIN_ATTR_W(thingy) \ +#define ISKU_BIN_ATTR_W(thingy, THINGY) \ { \ .attr = { .name = #thingy, .mode = 0220 }, \ - .size = sizeof(struct isku_ ## thingy), \ + .size = ISKU_SIZE_ ## THINGY, \ .write = isku_sysfs_write_ ## thingy \ } @@ -220,19 +220,19 @@ ISKU_SYSFS_R(info, INFO) ISKU_SYSFS_W(control, CONTROL) static struct bin_attribute isku_bin_attributes[] = { - ISKU_BIN_ATTR_RW(macro), - ISKU_BIN_ATTR_RW(keys_function), - ISKU_BIN_ATTR_RW(keys_easyzone), - ISKU_BIN_ATTR_RW(keys_media), - ISKU_BIN_ATTR_RW(keys_thumbster), - ISKU_BIN_ATTR_RW(keys_macro), - ISKU_BIN_ATTR_RW(keys_capslock), - ISKU_BIN_ATTR_RW(light), - ISKU_BIN_ATTR_RW(key_mask), - ISKU_BIN_ATTR_RW(last_set), - ISKU_BIN_ATTR_W(talk), - ISKU_BIN_ATTR_R(info), - ISKU_BIN_ATTR_W(control), + ISKU_BIN_ATTR_RW(macro, MACRO), + ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION), + ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE), + ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA), + ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER), + ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO), + ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK), + ISKU_BIN_ATTR_RW(light, LIGHT), + ISKU_BIN_ATTR_RW(key_mask, KEY_MASK), + ISKU_BIN_ATTR_RW(last_set, LAST_SET), + ISKU_BIN_ATTR_W(talk, TALK), + ISKU_BIN_ATTR_R(info, INFO), + ISKU_BIN_ATTR_W(control, CONTROL), __ATTR_NULL }; diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index 605b3ce21638..0062ab50861e 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -14,77 +14,33 @@ #include +enum { + ISKU_SIZE_CONTROL = 0x03, + ISKU_SIZE_INFO = 0x06, + ISKU_SIZE_KEY_MASK = 0x06, + ISKU_SIZE_KEYS_FUNCTION = 0x29, + ISKU_SIZE_KEYS_EASYZONE = 0x41, + ISKU_SIZE_KEYS_MEDIA = 0x1d, + ISKU_SIZE_KEYS_THUMBSTER = 0x17, + ISKU_SIZE_KEYS_MACRO = 0x23, + ISKU_SIZE_KEYS_CAPSLOCK = 0x06, + ISKU_SIZE_LAST_SET = 0x14, + ISKU_SIZE_LIGHT = 0x0a, + ISKU_SIZE_MACRO = 0x823, + ISKU_SIZE_TALK = 0x10, +}; + enum { ISKU_PROFILE_NUM = 5, ISKU_USB_INTERFACE_PROTOCOL = 0, }; -struct isku_control { - uint8_t command; /* ISKU_COMMAND_CONTROL */ - uint8_t value; - uint8_t request; -} __packed; - struct isku_actual_profile { uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */ uint8_t size; /* always 3 */ uint8_t actual_profile; } __packed; -struct isku_key_mask { - uint8_t command; /* ISKU_COMMAND_KEY_MASK */ - uint8_t size; /* 6 */ - uint8_t profile_number; /* 0-4 */ - uint8_t mask; - uint16_t checksum; -} __packed; - -struct isku_keys_function { - uint8_t data[0x29]; -} __packed; - -struct isku_keys_easyzone { - uint8_t data[0x41]; -} __packed; - -struct isku_keys_media { - uint8_t data[0x1d]; -} __packed; - -struct isku_keys_thumbster { - uint8_t data[0x17]; -} __packed; - -struct isku_keys_macro { - uint8_t data[0x23]; -} __packed; - -struct isku_keys_capslock { - uint8_t data[0x6]; -} __packed; - -struct isku_macro { - uint8_t data[0x823]; -} __packed; - -struct isku_light { - uint8_t data[0xa]; -} __packed; - -struct isku_info { - uint8_t data[2]; - uint8_t firmware_version; - uint8_t unknown[3]; -} __packed; - -struct isku_talk { - uint8_t data[0x10]; -} __packed; - -struct isku_last_set { - uint8_t data[0x14]; -} __packed; - enum isku_commands { ISKU_COMMAND_CONTROL = 0x4, ISKU_COMMAND_ACTUAL_PROFILE = 0x5, -- cgit v1.2.3-59-g8ed1b From f1da71e1f658c3f1e5d08291258ac87c35e049a4 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:06 +0100 Subject: HID: roccat: add sysfs attr "reset" for Isku Isku needs an extra sysfs attr to support device reset. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-isku | 8 ++++++++ drivers/hid/hid-roccat-isku.c | 2 ++ drivers/hid/hid-roccat-isku.h | 2 ++ 3 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku index 189dc43891bf..9eca5a182e64 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku @@ -117,6 +117,14 @@ Description: When written, this file lets one store macros with max 500 which profile and key to read. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/reset +Date: November 2012 +Contact: Stefan Achatz +Description: When written, this file lets one reset the device. + The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./isku/roccatisku/control Date: June 2011 Contact: Stefan Achatz diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 020d6cda8975..1219998a02d6 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -218,6 +218,7 @@ ISKU_SYSFS_RW(last_set, LAST_SET) ISKU_SYSFS_W(talk, TALK) ISKU_SYSFS_R(info, INFO) ISKU_SYSFS_W(control, CONTROL) +ISKU_SYSFS_W(reset, RESET) static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_RW(macro, MACRO), @@ -233,6 +234,7 @@ static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_W(talk, TALK), ISKU_BIN_ATTR_R(info, INFO), ISKU_BIN_ATTR_W(control, CONTROL), + ISKU_BIN_ATTR_W(reset, RESET), __ATTR_NULL }; diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index 0062ab50861e..cf6896c83867 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -27,6 +27,7 @@ enum { ISKU_SIZE_LAST_SET = 0x14, ISKU_SIZE_LIGHT = 0x0a, ISKU_SIZE_MACRO = 0x823, + ISKU_SIZE_RESET = 0x03, ISKU_SIZE_TALK = 0x10, }; @@ -53,6 +54,7 @@ enum isku_commands { ISKU_COMMAND_MACRO = 0xe, ISKU_COMMAND_INFO = 0xf, ISKU_COMMAND_LIGHT = 0x10, + ISKU_COMMAND_RESET = 0x11, ISKU_COMMAND_KEYS_CAPSLOCK = 0x13, ISKU_COMMAND_LAST_SET = 0x14, ISKU_COMMAND_15 = 0x15, -- cgit v1.2.3-59-g8ed1b From ecbfe7aa55a3583238c0126bbc64f9043d390b50 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:10 +0100 Subject: HID: roccat: deprecated some Pyra attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 27 +++++++++++++++- .../ABI/testing/sysfs-driver-hid-roccat-pyra | 37 +++++++--------------- drivers/hid/hid-roccat-pyra.c | 10 +++--- drivers/hid/hid-roccat-pyra.h | 2 ++ 4 files changed, 46 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra index 0a661b39e701..87ac87e9556d 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-pyra @@ -37,6 +37,31 @@ Description: When read, this file returns the raw integer version number of the Please use binary attribute "info" which provides this information. Users: http://roccat.sourceforge.net +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_buttons +Date: August 2010 +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 information about button layout. + When read, these files return the respective profile buttons. + The returned data is 19 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_settings +Date: August 2010 +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 information like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 13 bytes in size. + This file is readonly. + Write control to select profile and read profile_settings instead. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/startup_profile Date: August 2010 Contact: Stefan Achatz @@ -45,4 +70,4 @@ Description: The integer value of this attribute ranges from 0-4. that's active when the mouse is powered on. This file is readonly. Please use binary attribute "settings" which provides this information. -Users: http://roccat.sourceforge.net \ No newline at end of file +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 b0fab8bcf247..9fa9de30d14b 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra @@ -1,3 +1,11 @@ +What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/info +Date: November 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_settings Date: August 2010 Contact: Stefan Achatz @@ -10,19 +18,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_settings -Date: August 2010 -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 information like resolution, sensitivity - and light effects. - When read, these files return the respective profile settings. - The returned data is 13 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile_buttons @@ -36,18 +33,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/profile[1-5]_buttons -Date: August 2010 -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 information about button layout. - When read, these files return the respective profile buttons. - The returned data is 19 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./pyra/roccatpyra/settings diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 76199fa727ed..d4f1e3bee590 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -177,9 +177,10 @@ PYRA_SYSFS_R(thingy, THINGY) .write = pyra_sysfs_write_ ## thingy \ } +PYRA_SYSFS_W(control, CONTROL) PYRA_SYSFS_RW(info, INFO) -PYRA_SYSFS_W(profile_settings, PROFILE_SETTINGS) -PYRA_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +PYRA_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +PYRA_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) PYRA_SYSFS_R(settings, SETTINGS) static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, @@ -312,9 +313,10 @@ static struct device_attribute pyra_attributes[] = { }; static struct bin_attribute pyra_bin_attributes[] = { + PYRA_BIN_ATTRIBUTE_W(control, CONTROL), PYRA_BIN_ATTRIBUTE_RW(info, INFO), - PYRA_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - PYRA_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), { .attr = { .name = "profile1_settings", .mode = 0440 }, diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h index 93e494984976..beedcf001ceb 100644 --- a/drivers/hid/hid-roccat-pyra.h +++ b/drivers/hid/hid-roccat-pyra.h @@ -15,6 +15,7 @@ #include enum { + PYRA_SIZE_CONTROL = 0x03, PYRA_SIZE_INFO = 0x06, PYRA_SIZE_PROFILE_SETTINGS = 0x0d, PYRA_SIZE_PROFILE_BUTTONS = 0x13, @@ -57,6 +58,7 @@ struct pyra_info { } __attribute__ ((__packed__)); enum pyra_commands { + PYRA_COMMAND_CONTROL = 0x4, PYRA_COMMAND_SETTINGS = 0x5, PYRA_COMMAND_PROFILE_SETTINGS = 0x6, PYRA_COMMAND_PROFILE_BUTTONS = 0x7, -- cgit v1.2.3-59-g8ed1b From bb060d65c3bf1d0211262e8229097d9a1beab20d Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:15 +0100 Subject: HID: roccat: deprecate some Koneplus attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-koneplus | 27 ++++++++++++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-koneplus | 29 +++------------------- drivers/hid/hid-roccat-koneplus.c | 10 +++++--- drivers/hid/hid-roccat-koneplus.h | 2 ++ 4 files changed, 39 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus index 22568b45973e..833fd59926a7 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-koneplus @@ -8,6 +8,7 @@ Description: The integer value of this attribute ranges from 0-4. When written, this file sets the number of the startup profile and the mouse activates this profile immediately. Please use actual_profile, it does the same thing. +Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/firmware_version Date: October 2010 @@ -19,3 +20,29 @@ Description: When read, this file returns the raw integer version number of the left. E.g. a returned value of 121 means 1.21 This file is readonly. Please read binary attribute info which contains firmware version. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons +Date: August 2010 +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 information about button layout. + When read, these files return the respective profile buttons. + The returned data is 77 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings +Date: August 2010 +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 information like resolution, sensitivity + and light effects. + When read, these files return the respective profile settings. + The returned data is 43 bytes in size. + This file is readonly. + Write control to select profile and read profile_settings instead. +Users: http://roccat.sourceforge.net \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus index ed1213defb81..7bd776f9c3c7 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-koneplus @@ -39,18 +39,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_buttons -Date: August 2010 -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 information about button layout. - When read, these files return the respective profile buttons. - The returned data is 77 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile_settings @@ -65,19 +55,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/profile[1-5]_settings -Date: August 2010 -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 information like resolution, sensitivity - and light effects. - When read, these files return the respective profile settings. - The returned data is 43 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./koneplus/roccatkoneplus/sensor diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 9fe445082308..6a48fa3c7da9 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -177,14 +177,15 @@ KONEPLUS_SYSFS_R(thingy, THINGY) .write = koneplus_sysfs_write_ ## thingy \ } +KONEPLUS_SYSFS_W(control, CONTROL) KONEPLUS_SYSFS_RW(info, INFO) KONEPLUS_SYSFS_W(talk, TALK) KONEPLUS_SYSFS_W(macro, MACRO) KONEPLUS_SYSFS_RW(sensor, SENSOR) KONEPLUS_SYSFS_RW(tcu, TCU) KONEPLUS_SYSFS_R(tcu_image, TCU_IMAGE) -KONEPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) -KONEPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +KONEPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KONEPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -306,14 +307,15 @@ static struct device_attribute koneplus_attributes[] = { }; static struct bin_attribute koneplus_bin_attributes[] = { + KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL), KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO), KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK), KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO), KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR), KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU), KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), - KONEPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - KONEPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, .size = KONEPLUS_SIZE_PROFILE_SETTINGS, diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h index 563b9d3fe5fb..d2b55f2c764d 100644 --- a/drivers/hid/hid-roccat-koneplus.h +++ b/drivers/hid/hid-roccat-koneplus.h @@ -16,6 +16,7 @@ enum { KONEPLUS_SIZE_ACTUAL_PROFILE = 0x03, + KONEPLUS_SIZE_CONTROL = 0x03, KONEPLUS_SIZE_FIRMWARE_WRITE = 0x0402, KONEPLUS_SIZE_INFO = 0x06, KONEPLUS_SIZE_MACRO = 0x0822, @@ -47,6 +48,7 @@ struct koneplus_info { enum koneplus_commands { KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KONEPLUS_COMMAND_CONTROL = 0x4, KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7, KONEPLUS_COMMAND_MACRO = 0x8, -- cgit v1.2.3-59-g8ed1b From 172e2abc19b51feef5b7980055725f0b242e988e Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 11 Nov 2012 06:21:19 +0100 Subject: HID: roccat: deprecate some Kovaplus attributes Introduced attribute "control" and made profile_settings and profile_buttons readable, which makes profile[1-5]_settings and profile[1-5]_buttons obsolete. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/obsolete/sysfs-driver-hid-roccat-kovaplus | 25 +++++++++++++++++++ .../ABI/testing/sysfs-driver-hid-roccat-kovaplus | 29 +++------------------- drivers/hid/hid-roccat-kovaplus.c | 10 +++++--- drivers/hid/hid-roccat-kovaplus.h | 2 ++ 4 files changed, 37 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus index 4d9fb26292a2..4a98e02b6c6a 100644 --- a/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/obsolete/sysfs-driver-hid-roccat-kovaplus @@ -39,3 +39,28 @@ Description: When read, this file returns the raw integer version number of the This file is readonly. Obsoleted by binary sysfs attribute "info". Users: http://roccat.sourceforge.net + +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 information about button layout. + When read, these files return the respective profile buttons. + The returned data is 23 bytes in size. + This file is readonly. + Write control to select profile and read profile_buttons instead. +Users: http://roccat.sourceforge.net + +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 information 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. + Write control to select profile and read profile_settings instead. +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 507f2c7321d5..a10404f15a54 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus @@ -28,18 +28,8 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./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 information about button layout. - When read, these files return the respective profile buttons. - The returned data is 23 bytes in size. - This file is readonly. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net What: /sys/bus/usb/devices/-:./::./kovaplus/roccatkovaplus/profile_settings @@ -54,17 +44,6 @@ Description: The mouse can store 5 profiles which can be switched by the The mouse will reject invalid data. Which profile to write is determined by the profile number contained in the data. - This file is writeonly. -Users: http://roccat.sourceforge.net - -What: /sys/bus/usb/devices/-:./::./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 information 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. + Before reading this file, control has to be written to select + which profile to read. Users: http://roccat.sourceforge.net diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1589fd3bcf0f..b8b37789b864 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -218,9 +218,10 @@ KOVAPLUS_SYSFS_R(thingy, THINGY) .write = kovaplus_sysfs_write_ ## thingy \ } +KOVAPLUS_SYSFS_W(control, CONTROL) KOVAPLUS_SYSFS_RW(info, INFO) -KOVAPLUS_SYSFS_W(profile_settings, PROFILE_SETTINGS) -KOVAPLUS_SYSFS_W(profile_buttons, PROFILE_BUTTONS) +KOVAPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KOVAPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -369,9 +370,10 @@ static struct device_attribute kovaplus_attributes[] = { }; static struct bin_attribute kovaplus_bin_attributes[] = { + KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL), KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO), - KOVAPLUS_BIN_ATTRIBUTE_W(profile_settings, PROFILE_SETTINGS), - KOVAPLUS_BIN_ATTRIBUTE_W(profile_buttons, PROFILE_BUTTONS), + KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), { .attr = { .name = "profile1_settings", .mode = 0440 }, .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h index 1189573daee2..fbb7a16a7e54 100644 --- a/drivers/hid/hid-roccat-kovaplus.h +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -15,6 +15,7 @@ #include enum { + KOVAPLUS_SIZE_CONTROL = 0x03, KOVAPLUS_SIZE_INFO = 0x06, KOVAPLUS_SIZE_PROFILE_SETTINGS = 0x10, KOVAPLUS_SIZE_PROFILE_BUTTONS = 0x17, @@ -61,6 +62,7 @@ struct kovaplus_info { enum kovaplus_commands { KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KOVAPLUS_COMMAND_CONTROL = 0x4, KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7, KOVAPLUS_COMMAND_INFO = 0x9, -- cgit v1.2.3-59-g8ed1b From 37cf6e6fc34e2fca4e7c565697e7cd5c317bc316 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:13 +0100 Subject: HID: export hidinput_calc_abs_res Exporting the function allows us to calculate the resolution in third party drivers like hid-multitouch. This patch also complete the function with additional valid axes. Signed-off-by: Benjamin Tissoires Acked-by: Jiri Kosina Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 9 ++++++++- drivers/hid/hid-multitouch.c | 1 + include/linux/hid.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d917c0d53685..d1ec571bdb3b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -208,7 +208,7 @@ static int hidinput_setkeycode(struct input_dev *dev, * Only exponent 1 length units are processed. Centimeters and inches are * converted to millimeters. Degrees are converted to radians. */ -static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) +__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) { __s32 unit_exponent = field->unit_exponent; __s32 logical_extents = field->logical_maximum - @@ -229,6 +229,12 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) case ABS_X: case ABS_Y: case ABS_Z: + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + case ABS_MT_TOOL_X: + case ABS_MT_TOOL_Y: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: if (field->unit == 0x11) { /* If centimeters */ /* Convert to millimeters */ unit_exponent += 1; @@ -283,6 +289,7 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) /* Calculate resolution */ return logical_extents / physical_extents; } +EXPORT_SYMBOL_GPL(hidinput_calc_abs_res); #ifdef CONFIG_HID_BATTERY_STRENGTH static enum power_supply_property hidinput_battery_props[] = { diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7867d69f0efe..3687f797b731 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -297,6 +297,7 @@ static void set_abs(struct input_dev *input, unsigned int code, int fmax = field->logical_maximum; int fuzz = snratio ? (fmax - fmin) / snratio : 0; input_set_abs_params(input, code, fmin, fmax, fuzz, 0); + input_abs_set_res(input, code, hidinput_calc_abs_res(field, code)); } static void mt_store_field(struct hid_usage *usage, struct mt_device *td, diff --git a/include/linux/hid.h b/include/linux/hid.h index c076041a069e..c6bef8f54a82 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -706,6 +706,7 @@ int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); +__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); -- cgit v1.2.3-59-g8ed1b From ccdd699411b77c463022ebac311eb4a06ca56db4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:14 +0100 Subject: HID: round return value of hidinput_calc_abs_res hidinput_calc_abs_res should return the closest int in the division instead of the floor. On a device with a logical_max of 3008 and a physical_max of 255mm, previous implementation gave a resolution of 11 instead of 12. With 11, user-space computes a physical size of 273.5mm and the round_closest results gives 250.6mm. The old implementation introduced an error of 2cm in this example. Signed-off-by: Benjamin Tissoires Acked-by: Jiri Kosina Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d1ec571bdb3b..1b0adc3f7803 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -287,7 +287,7 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) } /* Calculate resolution */ - return logical_extents / physical_extents; + return DIV_ROUND_CLOSEST(logical_extents, physical_extents); } EXPORT_SYMBOL_GPL(hidinput_calc_abs_res); -- cgit v1.2.3-59-g8ed1b From 774638386826621c984ab6994439f474709cac5e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:15 +0100 Subject: HID: fix unit exponent parsing HID spec details special values for the HID field unit exponent. Basically, the range [0x8..0xf] correspond to [-8..-1], so this is a standard two's complement on a half-byte. Signed-off-by: Benjamin Tissoires Acked-by: Jiri Kosina Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 16 +++++++++++++++- drivers/hid/hid-input.c | 13 +++++++++---- include/linux/hid.h | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f4109fd657ff..f5004e2ca0c5 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -315,6 +315,7 @@ static s32 item_sdata(struct hid_item *item) static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) { + __u32 raw_value; switch (item->tag) { case HID_GLOBAL_ITEM_TAG_PUSH: @@ -365,7 +366,14 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) return 0; case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: - parser->global.unit_exponent = item_sdata(item); + /* Units exponent negative numbers are given through a + * two's complement. + * See "6.2.2.7 Global Items" for more information. */ + raw_value = item_udata(item); + if (!(raw_value & 0xfffffff0)) + parser->global.unit_exponent = hid_snto32(raw_value, 4); + else + parser->global.unit_exponent = raw_value; return 0; case HID_GLOBAL_ITEM_TAG_UNIT: @@ -865,6 +873,12 @@ static s32 snto32(__u32 value, unsigned n) return value & (1 << (n - 1)) ? value | (-1 << n) : value; } +s32 hid_snto32(__u32 value, unsigned n) +{ + return snto32(value, n); +} +EXPORT_SYMBOL_GPL(hid_snto32); + /* * Convert a signed 32-bit integer to a signed n-bit integer. */ diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1b0adc3f7803..007a9433bfa7 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -192,7 +192,6 @@ static int hidinput_setkeycode(struct input_dev *dev, return -EINVAL; } - /** * hidinput_calc_abs_res - calculate an absolute axis resolution * @field: the HID report field to calculate resolution for @@ -235,17 +234,23 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) case ABS_MT_TOOL_Y: case ABS_MT_TOUCH_MAJOR: case ABS_MT_TOUCH_MINOR: - if (field->unit == 0x11) { /* If centimeters */ + if (field->unit & 0xffffff00) /* Not a length */ + return 0; + unit_exponent += hid_snto32(field->unit >> 4, 4) - 1; + switch (field->unit & 0xf) { + case 0x1: /* If centimeters */ /* Convert to millimeters */ unit_exponent += 1; - } else if (field->unit == 0x13) { /* If inches */ + break; + case 0x3: /* If inches */ /* Convert to millimeters */ prev = physical_extents; physical_extents *= 254; if (physical_extents < prev) return 0; unit_exponent -= 1; - } else { + break; + default: return 0; } break; diff --git a/include/linux/hid.h b/include/linux/hid.h index c6bef8f54a82..4161bf2c0b5b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -717,6 +717,7 @@ int hid_connect(struct hid_device *hid, unsigned int connect_mask); void hid_disconnect(struct hid_device *hid); const struct hid_device_id *hid_match_id(struct hid_device *hdev, const struct hid_device_id *id); +s32 hid_snto32(__u32 value, unsigned n); /** * hid_map_usage - map usage input bits -- cgit v1.2.3-59-g8ed1b From f262d1fa2c651a5e2f92b6aee8779597631cd5d4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:16 +0100 Subject: HID: add usage_index in struct hid_usage. Currently, there is no way to know the index of the current field in the .input_mapping and .event callbacks when this field is inside an array of HID fields. This patch adds this index to the struct hid_usage so that this information is available to input_mapping and event callbacks. Signed-off-by: Benjamin Tissoires Acked-by: Jiri Kosina Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 ++++ include/linux/hid.h | 1 + 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f5004e2ca0c5..b5974cd45968 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -92,6 +92,7 @@ EXPORT_SYMBOL_GPL(hid_register_report); static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) { struct hid_field *field; + int i; if (report->maxfield == HID_MAX_FIELDS) { hid_err(report->device, "too many fields in report\n"); @@ -110,6 +111,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned field->value = (s32 *)(field->usage + usages); field->report = report; + for (i = 0; i < usages; i++) + field->usage[i].usage_index = i; + return field; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 4161bf2c0b5b..d2c42dd222c1 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -342,6 +342,7 @@ struct hid_collection { struct hid_usage { unsigned hid; /* hid usage code */ unsigned collection_index; /* index into collection array */ + unsigned usage_index; /* index into usage array */ /* hidinput data */ __u16 code; /* input driver code */ __u8 type; /* input driver type */ -- cgit v1.2.3-59-g8ed1b From 54f4c0c3e9d9ad50bf58a4f7c2bbdfb3fa4ab3a7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:17 +0100 Subject: HID: multitouch: support arrays for the split of the touches in a report Win8 certification introduced the ability to transmit two X and two Y per touch. The specification precises that it must be used in an array. This test guarantees that we split the touches on the last element in this array. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3687f797b731..3d2e6bbb0046 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -576,12 +576,15 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, return 0; } - if (usage->hid == td->last_slot_field) - mt_complete_slot(td, field->hidinput->input); - - if (field->index == td->last_field_index - && td->num_received >= td->num_expected) - mt_sync_frame(td, field->hidinput->input); + if (usage->usage_index + 1 == field->report_count) { + /* we only take into account the last report. */ + if (usage->hid == td->last_slot_field) + mt_complete_slot(td, field->hidinput->input); + + if (field->index == td->last_field_index + && td->num_received >= td->num_expected) + mt_sync_frame(td, field->hidinput->input); + } } -- cgit v1.2.3-59-g8ed1b From afbcb04c181f36634f4af207bb0c9cc46b7405e4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:18 +0100 Subject: HID: multitouch: get maxcontacts also from logical_max value Win8 devices are required to present the feature "Maximum Contact Number". Fortunately all win7 devices I've seen presents this feature. If the current value is 0, then, the driver can get the actual supported contact count by refering to the logical_max. This win8 specification ensures that logical_max may not be above 250. This also allows us to detect when devices like irtouch or stantum reports an obviously wrong value of 255. Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3d2e6bbb0046..9c7415ff4aaf 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -121,6 +121,7 @@ struct mt_device { #define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109 #define MT_DEFAULT_MAXCONTACT 10 +#define MT_MAX_MAXCONTACT 250 #define MT_USB_DEVICE(v, p) HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH, v, p) #define MT_BT_DEVICE(v, p) HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH, v, p) @@ -282,6 +283,9 @@ static void mt_feature_mapping(struct hid_device *hdev, case HID_DG_CONTACTMAX: td->maxcontact_report_id = field->report->id; td->maxcontacts = field->value[0]; + if (!td->maxcontacts && + field->logical_maximum <= MT_MAX_MAXCONTACT) + td->maxcontacts = field->logical_maximum; if (td->mtclass.maxcontacts) /* check if the maxcontacts is given by the class */ td->maxcontacts = td->mtclass.maxcontacts; -- cgit v1.2.3-59-g8ed1b From 349fd6705b13946c04794bf27d67282940aa46b8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:19 +0100 Subject: HID: multitouch: support T and C for win8 devices Win8 input specification clarifies the X and Y sent by devices. It distincts the position where the user wants to Touch (T) from the center of the ellipsoide (C). This patch enable supports for this distinction in hid-multitouch. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 46 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 9c7415ff4aaf..a2963b238fde 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_NO_AREA (1 << 9) struct mt_slot { - __s32 x, y, p, w, h; + __s32 x, y, cx, cy, p, w, h; __s32 contactid; /* the device ContactID assigned to this slot */ bool touch_state; /* is the touch valid? */ }; @@ -322,6 +322,7 @@ 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; int code; + struct hid_usage *prev_usage = NULL; /* Only map fields from TouchScreen or TouchPad collections. * We need to ignore fields that belong to other collections @@ -344,23 +345,42 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->physical == HID_DG_STYLUS) return -1; + if (usage->usage_index) + prev_usage = &field->usage[usage->usage_index - 1]; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - hid_map_usage(hi, usage, bit, max, + if (prev_usage && (prev_usage->hid == usage->hid)) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOOL_X); + set_abs(hi->input, ABS_MT_TOOL_X, field, + cls->sn_move); + } else { + hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); - set_abs(hi->input, ABS_MT_POSITION_X, field, - cls->sn_move); + set_abs(hi->input, ABS_MT_POSITION_X, field, + cls->sn_move); + } + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_GD_Y: - hid_map_usage(hi, usage, bit, max, + if (prev_usage && (prev_usage->hid == usage->hid)) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOOL_Y); + set_abs(hi->input, ABS_MT_TOOL_Y, field, + cls->sn_move); + } else { + hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); - set_abs(hi->input, ABS_MT_POSITION_Y, field, - cls->sn_move); + set_abs(hi->input, ABS_MT_POSITION_Y, field, + cls->sn_move); + } + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; @@ -501,6 +521,8 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); + input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx); + input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy); input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); @@ -552,10 +574,16 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, td->curdata.p = value; break; case HID_GD_X: - td->curdata.x = value; + if (usage->code == ABS_MT_TOOL_X) + td->curdata.cx = value; + else + td->curdata.x = value; break; case HID_GD_Y: - td->curdata.y = value; + if (usage->code == ABS_MT_TOOL_Y) + td->curdata.cy = value; + else + td->curdata.y = value; break; case HID_DG_WIDTH: td->curdata.w = value; -- cgit v1.2.3-59-g8ed1b From 20b60e6dee316cba4092cc7aef391fc6f4491028 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:20 +0100 Subject: HID: multitouch: move ALWAYS_VALID quirk check Win 8 device specification changed the requirements for the hid usages of the multitouch devices. Now InRange is optional and must be only used when the device supports hovering. This ensures that the quirk ALWAYS_VALID is taken into account and also ensures its precedence over the other VALID* quirks. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a2963b238fde..ffdbc70eb5ba 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -502,7 +502,7 @@ static int mt_compute_slot(struct mt_device *td, struct input_dev *input) */ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) { - if (td->curvalid) { + if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { int slotnum = mt_compute_slot(td, input); struct mt_slot *s = &td->curdata; @@ -553,9 +553,7 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { case HID_DG_INRANGE: - if (quirks & MT_QUIRK_ALWAYS_VALID) - td->curvalid = true; - else if (quirks & MT_QUIRK_VALID_IS_INRANGE) + if (quirks & MT_QUIRK_VALID_IS_INRANGE) td->curvalid = value; break; case HID_DG_TIPSWITCH: -- cgit v1.2.3-59-g8ed1b From 29807d1e24b7cd696442d2f600057230f084b3c7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:22 +0100 Subject: Input: mt: add input_mt_is_used This patch extracts the test (slot->frame == mt->frame) so that it can be used in third party drivers. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/input/input-mt.c | 2 +- include/linux/input/mt.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index c0ec7d42c3be..475b9d424eed 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -247,7 +247,7 @@ void input_mt_sync_frame(struct input_dev *dev) if (mt->flags & INPUT_MT_DROP_UNUSED) { for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { - if (s->frame == mt->frame) + if (input_mt_is_used(mt, s)) continue; input_mt_slot(dev, s - mt->slots); input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index cc5cca774bab..2e86bd0bfba1 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -69,6 +69,12 @@ static inline bool input_mt_is_active(const struct input_mt_slot *slot) return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0; } +static inline bool input_mt_is_used(const struct input_mt *mt, + const struct input_mt_slot *slot) +{ + return slot->frame == mt->frame; +} + int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, unsigned int flags); void input_mt_destroy_slots(struct input_dev *dev); -- cgit v1.2.3-59-g8ed1b From 2872839904848a43c5222f284bc1168ec3f998e7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:23 +0100 Subject: HID: multitouch: add MT_QUIRK_IGNORE_DUPLICATES This quirk allows a device to reuse a contact id when sending garbage inactive contacts at the end of a report. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index ffdbc70eb5ba..e86fbf4be639 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -52,6 +52,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) #define MT_QUIRK_NO_AREA (1 << 9) +#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) struct mt_slot { __s32 x, y, cx, cy, p, w, h; @@ -505,10 +506,18 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { int slotnum = mt_compute_slot(td, input); struct mt_slot *s = &td->curdata; + struct input_mt *mt = input->mt; if (slotnum < 0 || slotnum >= td->maxcontacts) return; + if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { + struct input_mt_slot *slot = &mt->slots[slotnum]; + if (input_mt_is_active(slot) && + input_mt_is_used(mt, slot)) + return; + } + input_mt_slot(input, slotnum); input_mt_report_slot_state(input, MT_TOOL_FINGER, s->touch_state); -- cgit v1.2.3-59-g8ed1b From 9b3bb9b8b6b221ab5ec96defedb15e4c99e26372 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:24 +0100 Subject: HID: multitouch: support for hovering devices Win8 devices supporting hovering must provides InRange HID field. The information that the finger is here but is not touching the surface is sent to the user space through ABS_MT_DISTANCE as required by the multitouch protocol. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e86fbf4be639..0a0489309905 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -53,11 +53,13 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) #define MT_QUIRK_NO_AREA (1 << 9) #define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) +#define MT_QUIRK_HOVERING (1 << 11) struct mt_slot { __s32 x, y, cx, cy, p, w, h; __s32 contactid; /* the device ContactID assigned to this slot */ bool touch_state; /* is the touch valid? */ + bool inrange_state; /* is the finger in proximity of the sensor? */ }; struct mt_class { @@ -391,6 +393,12 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_DIGITIZER: switch (usage->hid) { case HID_DG_INRANGE: + if (cls->quirks & MT_QUIRK_HOVERING) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_DISTANCE); + input_set_abs_params(hi->input, + ABS_MT_DISTANCE, 0, 1, 0, 0); + } mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; @@ -520,9 +528,9 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) input_mt_slot(input, slotnum); input_mt_report_slot_state(input, MT_TOOL_FINGER, - s->touch_state); - if (s->touch_state) { - /* this finger is on the screen */ + s->touch_state || s->inrange_state); + if (s->touch_state || s->inrange_state) { + /* this finger is in proximity of the sensor */ int wide = (s->w > s->h); /* divided by two to match visual scale of touch */ int major = max(s->w, s->h) >> 1; @@ -532,6 +540,8 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx); input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy); + input_event(input, EV_ABS, ABS_MT_DISTANCE, + !s->touch_state); input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); @@ -564,6 +574,8 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, case HID_DG_INRANGE: if (quirks & MT_QUIRK_VALID_IS_INRANGE) td->curvalid = value; + if (quirks & MT_QUIRK_HOVERING) + td->curdata.inrange_state = value; break; case HID_DG_TIPSWITCH: if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) -- cgit v1.2.3-59-g8ed1b From 1b42fc4a2889729c740321eb487f887161e38428 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 16:59:25 +0100 Subject: HID: multitouch: fix Win 8 protocol The Win 8 protocol specify the fact that each valid touch must be reported within a frame until it is released. We can therefore use the always_valid quirk and dismiss reports when we see duplicate contacts ID. We recognize Win8 certified devices from their vendor feature 0xff0000c5 where Microsoft put a signed blob in the report to check if the device passed the certification. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0a0489309905..61543c02ea0b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -294,6 +294,18 @@ static void mt_feature_mapping(struct hid_device *hdev, td->maxcontacts = td->mtclass.maxcontacts; break; + case 0xff0000c5: + if (field->report_count == 256 && field->report_size == 8) { + /* Win 8 devices need special quirks */ + __s32 *quirks = &td->mtclass.quirks; + *quirks |= MT_QUIRK_ALWAYS_VALID; + *quirks |= MT_QUIRK_IGNORE_DUPLICATES; + *quirks |= MT_QUIRK_HOVERING; + *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; + *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; + *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; + } + break; } } -- cgit v1.2.3-59-g8ed1b From 88c687b3e643d83863746a45240950464c6d07a2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 14 Nov 2012 17:53:56 +0100 Subject: HID: usbhid: add quirk HID_QUIRK_NOGET to TPV optical touchscreen Without this, the device is blocked in dmesg at: hid-multitouch 0003:25AA:8883.000X: usb_submit_urb(ctrl) failed: -1 hid-multitouch 0003:25AA:8883.000X: timeout initializing reports Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6a971477535c..c638e5d5a5cd 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -763,6 +763,9 @@ #define USB_VENDOR_ID_TOUCHPACK 0x1bfd #define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688 +#define USB_VENDOR_ID_TPV 0x25aa +#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN 0x8883 + #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 #define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 11c7932dc7e6..38a7cdd536b9 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -82,6 +82,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT }, -- cgit v1.2.3-59-g8ed1b From d339f61d14692517e9ba5a977fc266769d6c5c28 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Fri, 16 Nov 2012 12:20:43 +0530 Subject: HID: hiddev: Remove redundant check on unsigned variable No need to check whether unsigned variable is less than 0. CC: Jiri Kosina CC: linux-usb@vger.kernel.org CC: linux-input@vger.kernel.org Signed-off-by: Tushar Behera Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 14599e256791..711c9656ec0f 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -625,7 +625,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case HIDIOCAPPLICATION: - if (arg < 0 || arg >= hid->maxapplication) + if (arg >= hid->maxapplication) break; for (i = 0; i < hid->maxcollection; i++) -- cgit v1.2.3-59-g8ed1b From 955dca354c3c02850409e773fcbd625ac68dd5e7 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sat, 17 Nov 2012 21:27:11 +0100 Subject: HID: roccat: fix wrong attr size for koneplus tcu More thorough testing revealed a typo in last koneplus cleanups. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-koneplus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h index d2b55f2c764d..af7f57e8cf3b 100644 --- a/drivers/hid/hid-roccat-koneplus.h +++ b/drivers/hid/hid-roccat-koneplus.h @@ -24,7 +24,7 @@ enum { KONEPLUS_SIZE_PROFILE_BUTTONS = 0x4d, KONEPLUS_SIZE_SENSOR = 0x06, KONEPLUS_SIZE_TALK = 0x10, - KONEPLUS_SIZE_TCU = 0x03, + KONEPLUS_SIZE_TCU = 0x04, KONEPLUS_SIZE_TCU_IMAGE = 0x0404, }; -- cgit v1.2.3-59-g8ed1b From 4a200c3b9a40242652b5734630bdd0bcf3aca75f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 12 Nov 2012 15:42:59 +0100 Subject: HID: i2c-hid: introduce HID over i2c specification implementation Microsoft published the protocol specification of HID over i2c: http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx This patch introduces an implementation of this protocol. This implementation does not includes the ACPI part of the specification. This will come when ACPI 5.0 devices enumeration will be available. Once the ACPI part is done, OEM will not have to declare HID over I2C devices in their platform specific driver. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 1 + drivers/hid/i2c-hid/Kconfig | 21 + drivers/hid/i2c-hid/Makefile | 5 + drivers/hid/i2c-hid/i2c-hid.c | 974 ++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/i2c-hid.h | 35 ++ 6 files changed, 1038 insertions(+) create mode 100644 drivers/hid/i2c-hid/Kconfig create mode 100644 drivers/hid/i2c-hid/Makefile create mode 100644 drivers/hid/i2c-hid/i2c-hid.c create mode 100644 include/linux/i2c/i2c-hid.h (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1630150ad2b1..95cfe9181bf2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -728,4 +728,6 @@ endif # HID source "drivers/hid/usbhid/Kconfig" +source "drivers/hid/i2c-hid/Kconfig" + endmenu diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cef68ca859d3..a1a28befac72 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -118,3 +118,4 @@ obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/ +obj-$(CONFIG_I2C_HID) += i2c-hid/ diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig new file mode 100644 index 000000000000..5b7d4d87a143 --- /dev/null +++ b/drivers/hid/i2c-hid/Kconfig @@ -0,0 +1,21 @@ +menu "I2C HID support" + depends on I2C + +config I2C_HID + tristate "HID over I2C transport layer" + default n + depends on I2C && INPUT + select HID + ---help--- + Say Y here if you want to use the HID over i2c protocol + implementation. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called i2c-hid. + +comment "Input core support is needed for HID over I2C input layer" + depends on I2C_HID && INPUT=n + +endmenu diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile new file mode 100644 index 000000000000..832d8f9aaba2 --- /dev/null +++ b/drivers/hid/i2c-hid/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the I2C input drivers +# + +obj-$(CONFIG_I2C_HID) += i2c-hid.o diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c new file mode 100644 index 000000000000..11140bdae660 --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -0,0 +1,974 @@ +/* + * HID over I2C protocol implementation + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * This code is partly based on "USB HID support for Linux": + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* flags */ +#define I2C_HID_STARTED (1 << 0) +#define I2C_HID_RESET_PENDING (1 << 1) +#define I2C_HID_READ_PENDING (1 << 2) + +#define I2C_HID_PWR_ON 0x00 +#define I2C_HID_PWR_SLEEP 0x01 + +/* debug option */ +static bool debug = false; +module_param(debug, bool, 0444); +MODULE_PARM_DESC(debug, "print a lot of debug information"); + +#define i2c_hid_dbg(ihid, fmt, arg...) \ + if (debug) \ + dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg) + +struct i2c_hid_desc { + __le16 wHIDDescLength; + __le16 bcdVersion; + __le16 wReportDescLength; + __le16 wReportDescRegister; + __le16 wInputRegister; + __le16 wMaxInputLength; + __le16 wOutputRegister; + __le16 wMaxOutputLength; + __le16 wCommandRegister; + __le16 wDataRegister; + __le16 wVendorID; + __le16 wProductID; + __le16 wVersionID; +} __packed; + +struct i2c_hid_cmd { + unsigned int registerIndex; + __u8 opcode; + unsigned int length; + bool wait; +}; + +union command { + u8 data[0]; + struct cmd { + __le16 reg; + __u8 reportTypeID; + __u8 opcode; + } __packed c; +}; + +#define I2C_HID_CMD(opcode_) \ + .opcode = opcode_, .length = 4, \ + .registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister) + +/* fetch HID descriptor */ +static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 }; +/* fetch report descriptors */ +static const struct i2c_hid_cmd hid_report_descr_cmd = { + .registerIndex = offsetof(struct i2c_hid_desc, + wReportDescRegister), + .opcode = 0x00, + .length = 2 }; +/* commands */ +static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01), + .wait = true }; +static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) }; +static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) }; +static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) }; +static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) }; +static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) }; +static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) }; +static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; +/* read/write data register */ +static const struct i2c_hid_cmd hid_data_cmd = { + .registerIndex = offsetof(struct i2c_hid_desc, wDataRegister), + .opcode = 0x00, + .length = 2 }; +/* write output reports */ +static const struct i2c_hid_cmd hid_out_cmd = { + .registerIndex = offsetof(struct i2c_hid_desc, + wOutputRegister), + .opcode = 0x00, + .length = 2 }; + +/* The main device structure */ +struct i2c_hid { + struct i2c_client *client; /* i2c client */ + struct hid_device *hid; /* pointer to corresponding HID dev */ + union { + __u8 hdesc_buffer[sizeof(struct i2c_hid_desc)]; + struct i2c_hid_desc hdesc; /* the HID Descriptor */ + }; + __le16 wHIDDescRegister; /* location of the i2c + * register of the HID + * descriptor. */ + unsigned int bufsize; /* i2c buffer size */ + char *inbuf; /* Input buffer */ + char *cmdbuf; /* Command buffer */ + char *argsbuf; /* Command arguments buffer */ + + unsigned long flags; /* device flags */ + + int irq; /* the interrupt line irq */ + + wait_queue_head_t wait; /* For waiting the interrupt */ +}; + +static int __i2c_hid_command(struct i2c_client *client, + const struct i2c_hid_cmd *command, u8 reportID, + u8 reportType, u8 *args, int args_len, + unsigned char *buf_recv, int data_len) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + union command *cmd = (union command *)ihid->cmdbuf; + int ret; + struct i2c_msg msg[2]; + int msg_num = 1; + + int length = command->length; + bool wait = command->wait; + unsigned int registerIndex = command->registerIndex; + + /* special case for hid_descr_cmd */ + if (command == &hid_descr_cmd) { + cmd->c.reg = ihid->wHIDDescRegister; + } else { + cmd->data[0] = ihid->hdesc_buffer[registerIndex]; + cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1]; + } + + if (length > 2) { + cmd->c.opcode = command->opcode; + cmd->c.reportTypeID = reportID | reportType << 4; + } + + memcpy(cmd->data + length, args, args_len); + length += args_len; + + i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data); + + msg[0].addr = client->addr; + msg[0].flags = client->flags & I2C_M_TEN; + msg[0].len = length; + msg[0].buf = cmd->data; + if (data_len > 0) { + msg[1].addr = client->addr; + msg[1].flags = client->flags & I2C_M_TEN; + msg[1].flags |= I2C_M_RD; + msg[1].len = data_len; + msg[1].buf = buf_recv; + msg_num = 2; + set_bit(I2C_HID_READ_PENDING, &ihid->flags); + } + + if (wait) + set_bit(I2C_HID_RESET_PENDING, &ihid->flags); + + ret = i2c_transfer(client->adapter, msg, msg_num); + + if (data_len > 0) + clear_bit(I2C_HID_READ_PENDING, &ihid->flags); + + if (ret != msg_num) + return ret < 0 ? ret : -EIO; + + ret = 0; + + if (wait) { + i2c_hid_dbg(ihid, "%s: waiting...\n", __func__); + if (!wait_event_timeout(ihid->wait, + !test_bit(I2C_HID_RESET_PENDING, &ihid->flags), + msecs_to_jiffies(5000))) + ret = -ENODATA; + i2c_hid_dbg(ihid, "%s: finished.\n", __func__); + } + + return ret; +} + +static int i2c_hid_command(struct i2c_client *client, + const struct i2c_hid_cmd *command, + unsigned char *buf_recv, int data_len) +{ + return __i2c_hid_command(client, command, 0, 0, NULL, 0, + buf_recv, data_len); +} + +static int i2c_hid_get_report(struct i2c_client *client, u8 reportType, + u8 reportID, unsigned char *buf_recv, int data_len) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + u8 args[3]; + int ret; + int args_len = 0; + u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister); + + i2c_hid_dbg(ihid, "%s\n", __func__); + + if (reportID >= 0x0F) { + args[args_len++] = reportID; + reportID = 0x0F; + } + + args[args_len++] = readRegister & 0xFF; + args[args_len++] = readRegister >> 8; + + ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID, + reportType, args, args_len, buf_recv, data_len); + if (ret) { + dev_err(&client->dev, + "failed to retrieve report from device.\n"); + return -EINVAL; + } + + return 0; +} + +static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, + u8 reportID, unsigned char *buf, size_t data_len) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + u8 *args = ihid->argsbuf; + int ret; + u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); + + /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ + u16 size = 2 /* size */ + + (reportID ? 1 : 0) /* reportID */ + + data_len /* buf */; + int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ + + 2 /* dataRegister */ + + size /* args */; + int index = 0; + + i2c_hid_dbg(ihid, "%s\n", __func__); + + if (reportID >= 0x0F) { + args[index++] = reportID; + reportID = 0x0F; + } + + args[index++] = dataRegister & 0xFF; + args[index++] = dataRegister >> 8; + + args[index++] = size & 0xFF; + args[index++] = size >> 8; + + if (reportID) + args[index++] = reportID; + + memcpy(&args[index], buf, data_len); + + ret = __i2c_hid_command(client, &hid_set_report_cmd, reportID, + reportType, args, args_len, NULL, 0); + if (ret) { + dev_err(&client->dev, "failed to set a report to device.\n"); + return -EINVAL; + } + + return data_len; +} + +static int i2c_hid_set_power(struct i2c_client *client, int power_state) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + + i2c_hid_dbg(ihid, "%s\n", __func__); + + ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state, + 0, NULL, 0, NULL, 0); + if (ret) + dev_err(&client->dev, "failed to change power setting.\n"); + + return ret; +} + +static int i2c_hid_hwreset(struct i2c_client *client) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + + i2c_hid_dbg(ihid, "%s\n", __func__); + + ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + if (ret) + return ret; + + i2c_hid_dbg(ihid, "resetting...\n"); + + ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0); + if (ret) { + dev_err(&client->dev, "failed to reset device.\n"); + i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + return ret; + } + + return 0; +} + +static int i2c_hid_get_input(struct i2c_hid *ihid) +{ + int ret, ret_size; + int size = le16_to_cpu(ihid->hdesc.wMaxInputLength); + + ret = i2c_master_recv(ihid->client, ihid->inbuf, size); + if (ret != size) { + if (ret < 0) + return ret; + + dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n", + __func__, ret, size); + return ret; + } + + ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8; + + if (!ret_size) { + /* host or device initiated RESET completed */ + if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) + wake_up(&ihid->wait); + return 0; + } + + if (ret_size > size) { + dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", + __func__, size, ret_size); + return -EIO; + } + + i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf); + + if (test_bit(I2C_HID_STARTED, &ihid->flags)) + hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2, + ret_size - 2, 1); + + return 0; +} + +static irqreturn_t i2c_hid_irq(int irq, void *dev_id) +{ + struct i2c_hid *ihid = dev_id; + + if (test_bit(I2C_HID_READ_PENDING, &ihid->flags)) + return IRQ_HANDLED; + + i2c_hid_get_input(ihid); + + return IRQ_HANDLED; +} + +static int i2c_hid_get_report_length(struct hid_report *report) +{ + return ((report->size - 1) >> 3) + 1 + + report->device->report_enum[report->type].numbered + 2; +} + +static void i2c_hid_init_report(struct hid_report *report, u8 *buffer, + size_t bufsize) +{ + struct hid_device *hid = report->device; + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + unsigned int size, ret_size; + + size = i2c_hid_get_report_length(report); + i2c_hid_get_report(client, + report->type == HID_FEATURE_REPORT ? 0x03 : 0x01, + report->id, buffer, size); + + i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, ihid->inbuf); + + ret_size = buffer[0] | (buffer[1] << 8); + + if (ret_size != size) { + dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n", + __func__, size, ret_size); + return; + } + + /* hid->driver_lock is held as we are in probe function, + * we just need to setup the input fields, so using + * hid_report_raw_event is safe. */ + hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1); +} + +/* + * Initialize all reports + */ +static void i2c_hid_init_reports(struct hid_device *hid) +{ + struct hid_report *report; + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + u8 *inbuf = kzalloc(ihid->bufsize, GFP_KERNEL); + + if (!inbuf) + return; + + list_for_each_entry(report, + &hid->report_enum[HID_INPUT_REPORT].report_list, list) + i2c_hid_init_report(report, inbuf, ihid->bufsize); + + list_for_each_entry(report, + &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + i2c_hid_init_report(report, inbuf, ihid->bufsize); + + kfree(inbuf); +} + +/* + * Traverse the supplied list of reports and find the longest + */ +static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type, + unsigned int *max) +{ + struct hid_report *report; + unsigned int size; + + /* We should not rely on wMaxInputLength, as some devices may set it to + * a wrong length. */ + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size = i2c_hid_get_report_length(report); + if (*max < size) + *max = size; + } +} + +static int i2c_hid_alloc_buffers(struct i2c_hid *ihid) +{ + /* the worst case is computed from the set_report command with a + * reportID > 15 and the maximum report length */ + int args_len = sizeof(__u8) + /* optional ReportID byte */ + sizeof(__u16) + /* data register */ + sizeof(__u16) + /* size of the report */ + ihid->bufsize; /* report */ + + ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL); + + if (!ihid->inbuf) + return -ENOMEM; + + ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); + + if (!ihid->argsbuf) { + kfree(ihid->inbuf); + return -ENOMEM; + } + + ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL); + + if (!ihid->cmdbuf) { + kfree(ihid->inbuf); + kfree(ihid->argsbuf); + ihid->inbuf = NULL; + ihid->argsbuf = NULL; + return -ENOMEM; + } + + return 0; +} + +static void i2c_hid_free_buffers(struct i2c_hid *ihid) +{ + kfree(ihid->inbuf); + kfree(ihid->argsbuf); + kfree(ihid->cmdbuf); + ihid->inbuf = NULL; + ihid->cmdbuf = NULL; + ihid->argsbuf = NULL; +} + +static int i2c_hid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + + if (report_type == HID_OUTPUT_REPORT) + return -EINVAL; + + if (count > ihid->bufsize) + count = ihid->bufsize; + + ret = i2c_hid_get_report(client, + report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, + report_number, ihid->inbuf, count); + + if (ret < 0) + return ret; + + count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); + + memcpy(buf, ihid->inbuf + 2, count); + + return count; +} + +static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct i2c_client *client = hid->driver_data; + int report_id = buf[0]; + + if (report_type == HID_INPUT_REPORT) + return -EINVAL; + + return i2c_hid_set_report(client, + report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, + report_id, buf, count); +} + +static int i2c_hid_parse(struct hid_device *hid) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct i2c_hid_desc *hdesc = &ihid->hdesc; + unsigned int rsize; + char *rdesc; + int ret; + int tries = 3; + + i2c_hid_dbg(ihid, "entering %s\n", __func__); + + rsize = le16_to_cpu(hdesc->wReportDescLength); + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dbg_hid("weird size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + + do { + ret = i2c_hid_hwreset(client); + if (ret) + msleep(1000); + } while (tries-- > 0 && ret); + + if (ret) + return ret; + + rdesc = kzalloc(rsize, GFP_KERNEL); + + if (!rdesc) { + dbg_hid("couldn't allocate rdesc memory\n"); + return -ENOMEM; + } + + i2c_hid_dbg(ihid, "asking HID report descriptor\n"); + + ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize); + if (ret) { + hid_err(hid, "reading report descriptor failed\n"); + kfree(rdesc); + return -EIO; + } + + i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc); + + ret = hid_parse_report(hid, rdesc, rsize); + kfree(rdesc); + if (ret) { + dbg_hid("parsing report descriptor failed\n"); + return ret; + } + + return 0; +} + +static int i2c_hid_start(struct hid_device *hid) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + int old_bufsize = ihid->bufsize; + + ihid->bufsize = HID_MIN_BUFFER_SIZE; + i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize); + i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize); + i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize); + + if (ihid->bufsize > old_bufsize || !ihid->inbuf || !ihid->cmdbuf) { + i2c_hid_free_buffers(ihid); + + ret = i2c_hid_alloc_buffers(ihid); + + if (ret) { + ihid->bufsize = old_bufsize; + return ret; + } + } + + if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) + i2c_hid_init_reports(hid); + + return 0; +} + +static void i2c_hid_stop(struct hid_device *hid) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + + hid->claimed = 0; + + i2c_hid_free_buffers(ihid); +} + +static int i2c_hid_open(struct hid_device *hid) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + + if (!hid->open++) { + ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + if (ret) { + hid->open--; + return -EIO; + } + set_bit(I2C_HID_STARTED, &ihid->flags); + } + return 0; +} + +static void i2c_hid_close(struct hid_device *hid) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + + /* protecting hid->open to make sure we don't restart + * data acquistion due to a resumption we no longer + * care about + */ + if (!--hid->open) { + clear_bit(I2C_HID_STARTED, &ihid->flags); + + /* Save some power */ + i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + } +} + +static int i2c_hid_power(struct hid_device *hid, int lvl) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret = 0; + + i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl); + + switch (lvl) { + case PM_HINT_FULLON: + ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + break; + case PM_HINT_NORMAL: + ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + break; + } + return ret; +} + +static int i2c_hid_hidinput_input_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + offset = hidinput_find_field(hid, type, code, &field); + + if (offset == -1) { + hid_warn(dev, "event field not found\n"); + return -1; + } + + hid_set_field(field, offset, value); + + return 0; +} + +static struct hid_ll_driver i2c_hid_ll_driver = { + .parse = i2c_hid_parse, + .start = i2c_hid_start, + .stop = i2c_hid_stop, + .open = i2c_hid_open, + .close = i2c_hid_close, + .power = i2c_hid_power, + .hidinput_input_event = i2c_hid_hidinput_input_event, +}; + +static int __devinit i2c_hid_init_irq(struct i2c_client *client) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); + + ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, ihid); + if (ret < 0) { + dev_dbg(&client->dev, + "Could not register for %s interrupt, irq = %d," + " ret = %d\n", + client->name, client->irq, ret); + + return ret; + } + + ihid->irq = client->irq; + + return 0; +} + +static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) +{ + struct i2c_client *client = ihid->client; + struct i2c_hid_desc *hdesc = &ihid->hdesc; + unsigned int dsize; + int ret; + + /* Fetch the length of HID description, retrieve the 4 first bytes: + * bytes 0-1 -> length + * bytes 2-3 -> bcdVersion (has to be 1.00) */ + ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4); + + i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %*ph\n", + __func__, 4, ihid->hdesc_buffer); + + if (ret) { + dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n", + ret); + return -ENODEV; + } + + dsize = le16_to_cpu(hdesc->wHIDDescLength); + if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(&client->dev, "weird size of HID descriptor (%u)\n", + dsize); + return -ENODEV; + } + + /* check bcdVersion == 1.0 */ + if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { + dev_err(&client->dev, + "unexpected HID descriptor bcdVersion (0x%04x)\n", + le16_to_cpu(hdesc->bcdVersion)); + return -ENODEV; + } + + i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); + + ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, + dsize); + if (ret) { + dev_err(&client->dev, "hid_descr_cmd Fail\n"); + return -ENODEV; + } + + i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); + + return 0; +} + +static int __devinit i2c_hid_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret; + struct i2c_hid *ihid; + struct hid_device *hid; + __u16 hidRegister; + struct i2c_hid_platform_data *platform_data = client->dev.platform_data; + + dbg_hid("HID probe called for i2c 0x%02x\n", client->addr); + + if (!platform_data) { + dev_err(&client->dev, "HID register address not provided\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, + "HID over i2c has not been provided an Int IRQ\n"); + return -EINVAL; + } + + ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); + if (!ihid) + return -ENOMEM; + + i2c_set_clientdata(client, ihid); + + ihid->client = client; + + hidRegister = platform_data->hid_descriptor_address; + ihid->wHIDDescRegister = cpu_to_le16(hidRegister); + + init_waitqueue_head(&ihid->wait); + + /* we need to allocate the command buffer without knowing the maximum + * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the + * real computation later. */ + ihid->bufsize = HID_MIN_BUFFER_SIZE; + i2c_hid_alloc_buffers(ihid); + + ret = i2c_hid_fetch_hid_descriptor(ihid); + if (ret < 0) + goto err; + + ret = i2c_hid_init_irq(client); + if (ret < 0) + goto err; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err; + } + + ihid->hid = hid; + + hid->driver_data = client; + hid->ll_driver = &i2c_hid_ll_driver; + hid->hid_get_raw_report = i2c_hid_get_raw_report; + hid->hid_output_raw_report = i2c_hid_output_raw_report; + hid->dev.parent = &client->dev; + hid->bus = BUS_I2C; + hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); + hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); + hid->product = le16_to_cpu(ihid->hdesc.wProductID); + + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", + client->name, hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + if (ret != -ENODEV) + hid_err(client, "can't add hid device: %d\n", ret); + goto err_mem_free; + } + + return 0; + +err_mem_free: + hid_destroy_device(hid); + +err: + if (ihid->irq) + free_irq(ihid->irq, ihid); + + kfree(ihid); + return ret; +} + +static int __devexit i2c_hid_remove(struct i2c_client *client) +{ + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct hid_device *hid; + + if (WARN_ON(!ihid)) + return -1; + + hid = ihid->hid; + hid_destroy_device(hid); + + free_irq(client->irq, ihid); + + kfree(ihid); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int i2c_hid_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(ihid->irq); + + /* Save some power */ + i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); + + return 0; +} + +static int i2c_hid_resume(struct device *dev) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + + ret = i2c_hid_hwreset(client); + if (ret) + return ret; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); + +static const struct i2c_device_id i2c_hid_id_table[] = { + { "i2c_hid", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table); + + +static struct i2c_driver i2c_hid_driver = { + .driver = { + .name = "i2c_hid", + .owner = THIS_MODULE, + .pm = &i2c_hid_pm, + }, + + .probe = i2c_hid_probe, + .remove = __devexit_p(i2c_hid_remove), + + .id_table = i2c_hid_id_table, +}; + +module_i2c_driver(i2c_hid_driver); + +MODULE_DESCRIPTION("HID over I2C core driver"); +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h new file mode 100644 index 000000000000..60e411d764d4 --- /dev/null +++ b/include/linux/i2c/i2c-hid.h @@ -0,0 +1,35 @@ +/* + * HID over I2C protocol implementation + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#ifndef __LINUX_I2C_HID_H +#define __LINUX_I2C_HID_H + +#include + +/** + * struct i2chid_platform_data - used by hid over i2c implementation. + * @hid_descriptor_address: i2c register where the HID descriptor is stored. + * + * Note that it is the responsibility of the platform driver (or the acpi 5.0 + * driver) to setup the irq related to the gpio in the struct i2c_board_info. + * The platform driver should also setup the gpio according to the device: + * + * A typical example is the following: + * irq = gpio_to_irq(intr_gpio); + * hkdk4412_i2c_devs5[0].irq = irq; // store the irq in i2c_board_info + * gpio_request(intr_gpio, "elan-irq"); + * s3c_gpio_setpull(intr_gpio, S3C_GPIO_PULL_UP); + */ +struct i2c_hid_platform_data { + u16 hid_descriptor_address; +}; + +#endif /* __LINUX_I2C_HID_H */ -- cgit v1.2.3-59-g8ed1b From e3bbe8755c9c947d80f88c8a2a81073dbc01bd1f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 20 Nov 2012 16:33:14 +0000 Subject: HID: sensor-hub: Remove pointless NULL check report cannot be NULL, fortunately as we use it before we check ! Signed-off-by: Alan Cox Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index d9d73e9163eb..4ff24977544d 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -437,9 +437,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev, ptr = raw_data; ptr++; /*Skip report id*/ - if (!report) - goto err_report; - spin_lock_irqsave(&pdata->lock, flags); for (i = 0; i < report->maxfield; ++i) { @@ -485,7 +482,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev, callback->pdev); spin_unlock_irqrestore(&pdata->lock, flags); -err_report: return 1; } -- cgit v1.2.3-59-g8ed1b From 3c62602434c13744df62b3ab0ab7950cd36f24db Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 20 Nov 2012 17:09:40 +0100 Subject: HID: i2c-hid: fix memory leak during probe In case we are returning from i2c_hid_probe() through the 'err' or 'err_mem_free' labels, there is noone freeing the buffers allocated by i2c_hid_alloc_buffers(). Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 11140bdae660..67ab5b7b64d8 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -892,6 +892,7 @@ err: if (ihid->irq) free_irq(ihid->irq, ihid); + i2c_hid_free_buffers(ihid); kfree(ihid); return ret; } -- cgit v1.2.3-59-g8ed1b From b8a8f515d234f74ac19fb3597580775d8229ce50 Mon Sep 17 00:00:00 2001 From: Marco Biscaro Date: Thu, 22 Nov 2012 20:40:53 -0200 Subject: HID: add support to novatec wireless mouse This patch adds support to Novatec wireless mouse (device ID 0603:1602). Fixes bug https://bugzilla.kernel.org/show_bug.cgi?id=47031 Signed-off-by: Marco Biscaro Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9d7a42857ea1..b40632eea521 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -603,6 +603,7 @@ #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 +#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602 #define USB_VENDOR_ID_NTRIG 0x1b96 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 11c7932dc7e6..ef49a19b6ae1 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -72,6 +72,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, -- cgit v1.2.3-59-g8ed1b From 7611e8d26d8dd0e5e132b46e905cb579daf9da1e Mon Sep 17 00:00:00 2001 From: Founder Fang Date: Wed, 21 Nov 2012 15:20:31 +0800 Subject: HID: hidraw: fix nonblock read return EAGAIN after device removed When nonblock read the condition check (file->f_flags & O_NONBLOCK) always be true, signal_pending and device exist checking never get a chance to run, so the user mode code always get EAGAIN even if device removed. move nonblock mode checking to the last can fix this problem. Signed-off-by: Founder Fang Reviewed-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 7c47fc3f7b2b..1d8c0219117e 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -57,10 +57,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, set_current_state(TASK_INTERRUPTIBLE); while (list->head == list->tail) { - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } if (signal_pending(current)) { ret = -ERESTARTSYS; break; @@ -69,6 +65,10 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ret = -EIO; break; } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } /* allow O_NONBLOCK to work well from other threads */ mutex_unlock(&list->read_mutex); -- cgit v1.2.3-59-g8ed1b From 436136cec650d661eb662fcb508a99878606d050 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 24 Nov 2012 06:15:57 +0100 Subject: HID: add quirk for Freescale i.MX23 ROM recovery The USB recovery mode present in i.MX23 ROM emulates USB HID. It needs this quirk to behave properly. Even if the official branding of the chip is Freescale i.MX23, I named it Sigmatel STMP3780 since that's what the chip really is and it even reports itself as STMP3780. Signed-off-by: Marek Vasut Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c638e5d5a5cd..74d47aa4c5a1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -697,6 +697,9 @@ #define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 +#define USB_VENDOR_ID_SIGMATEL 0x066F +#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 + #define USB_VENDOR_ID_SKYCABLE 0x1223 #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 38a7cdd536b9..3715043244dd 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -79,6 +79,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET }, -- cgit v1.2.3-59-g8ed1b From 4cc8541782140e9ccbece5c8ac367fbf659b16d9 Mon Sep 17 00:00:00 2001 From: Ortwin Glück Date: Sun, 25 Nov 2012 15:08:34 +0100 Subject: HID: add battery quirk for Apple 2009_ISO keyboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support battery capacity on another Apple wireless keyboard. Signed-off-by: Ortwin Glück Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 10248cf548fc..0887b52f804b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -298,6 +298,9 @@ static enum power_supply_property hidinput_battery_props[] = { #define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */ static const struct hid_device_id hid_battery_quirks[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, -- cgit v1.2.3-59-g8ed1b From 13f19624029f43f5d0f98a96a9e6ba4d38da96a9 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 28 Nov 2012 00:10:44 +0100 Subject: HID: hiddev: fix nonblocking read semantics wrt EIO/ERESTARTSYS When the file has been open in non-blocking mode, EIO or ERESTARTSYS would never be returned even if they should (for example when device has been unplugged, you want EIO and not EAGAIN to be returned). Move the O_NONBLOCK check after other checks have been performed. Base on similar patch done to hidraw by Founder Fang Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 711c9656ec0f..87bd64959a91 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -361,10 +361,6 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE); while (list->head == list->tail) { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } if (signal_pending(current)) { retval = -ERESTARTSYS; break; @@ -373,6 +369,10 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun retval = -EIO; break; } + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } /* let O_NONBLOCK tasks run */ mutex_unlock(&list->thread_lock); -- cgit v1.2.3-59-g8ed1b From b5531318f18b5054c0e4a82beb560a77d85948d8 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 27 Nov 2012 19:02:27 -0800 Subject: HID: hidraw: fix signaling SIGIO when hidraw reports an event This patch fixes sending SIGIO from hidraw_report_event by creating a fasync handler which adds the fasync entry. Signed-off-by: Andrew Duggan Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 1d8c0219117e..413a73187d33 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -295,6 +295,13 @@ out: } +static int hidraw_fasync(int fd, struct file *file, int on) +{ + struct hidraw_list *list = file->private_data; + + return fasync_helper(fd, file, on, &list->fasync); +} + static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); @@ -438,6 +445,7 @@ static const struct file_operations hidraw_ops = { .open = hidraw_open, .release = hidraw_release, .unlocked_ioctl = hidraw_ioctl, + .fasync = hidraw_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = hidraw_ioctl, #endif -- cgit v1.2.3-59-g8ed1b From ae751fa82dd8e5a79932d6b55742e7e298afd733 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 23 Nov 2012 16:31:24 +0100 Subject: HID: hid-input: factorize hid_input allocation This just refactors the allocation of hid_input. No semantic changes. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 61 +++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 0887b52f804b..97c08c5d0973 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1154,6 +1154,38 @@ static void report_features(struct hid_device *hid) } } +static struct hid_input *hidinput_allocate(struct hid_device *hid) +{ + struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); + struct input_dev *input_dev = input_allocate_device(); + if (!hidinput || !input_dev) { + kfree(hidinput); + input_free_device(input_dev); + hid_err(hid, "Out of memory during hid input probe\n"); + return NULL; + } + + input_set_drvdata(input_dev, hid); + input_dev->event = hid->ll_driver->hidinput_input_event; + input_dev->open = hidinput_open; + input_dev->close = hidinput_close; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; + + input_dev->name = hid->name; + input_dev->phys = hid->phys; + input_dev->uniq = hid->uniq; + input_dev->id.bustype = hid->bus; + input_dev->id.vendor = hid->vendor; + input_dev->id.product = hid->product; + input_dev->id.version = hid->version; + input_dev->dev.parent = hid->dev.parent; + hidinput->input = input_dev; + list_add_tail(&hidinput->list, &hid->inputs); + + return hidinput; +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -1165,7 +1197,6 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) struct hid_driver *drv = hid->driver; struct hid_report *report; struct hid_input *hidinput = NULL; - struct input_dev *input_dev; int i, j, k; INIT_LIST_HEAD(&hid->inputs); @@ -1196,33 +1227,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) continue; if (!hidinput) { - hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!hidinput || !input_dev) { - kfree(hidinput); - input_free_device(input_dev); - hid_err(hid, "Out of memory during hid input probe\n"); + hidinput = hidinput_allocate(hid); + if (!hidinput) goto out_unwind; - } - - input_set_drvdata(input_dev, hid); - input_dev->event = - hid->ll_driver->hidinput_input_event; - input_dev->open = hidinput_open; - input_dev->close = hidinput_close; - input_dev->setkeycode = hidinput_setkeycode; - input_dev->getkeycode = hidinput_getkeycode; - - input_dev->name = hid->name; - input_dev->phys = hid->phys; - input_dev->uniq = hid->uniq; - input_dev->id.bustype = hid->bus; - input_dev->id.vendor = hid->vendor; - input_dev->id.product = hid->product; - input_dev->id.version = hid->version; - input_dev->dev.parent = hid->dev.parent; - hidinput->input = input_dev; - list_add_tail(&hidinput->list, &hid->inputs); } for (i = 0; i < report->maxfield; i++) -- cgit v1.2.3-59-g8ed1b From f9af7b9edccb87d4d80b58687ab63e58f3b64c4c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 2 Dec 2012 14:38:23 +0000 Subject: HID: Add Apple wireless keyboard 2011 ANSI to special driver list Commit 0a97e1e9f9a6 ('HID: apple: Add Apple wireless keyboard 2011 ANSI PID') did not update the special driver list in hid-core.c, so hid-generic may still bind to this device. Reported-by: Ari Pollak References: http://bugs.debian.org/694546 Signed-off-by: Ben Hutchings Cc: Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f4109fd657ff..52146dbf080f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1538,6 +1538,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, -- cgit v1.2.3-59-g8ed1b From 24ebb37e6515d999bb27f5f8de6ff30faa7479f5 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:42 +0100 Subject: HID: i2c-hid: change I2C name no I2C driver has "i2c" in its name. It makes more sense to call this i2c driver "hid". Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 67ab5b7b64d8..0fbb22906806 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -949,7 +949,7 @@ static int i2c_hid_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); static const struct i2c_device_id i2c_hid_id_table[] = { - { "i2c_hid", 0 }, + { "hid", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table); -- cgit v1.2.3-59-g8ed1b From 8ee0c05aa0c8310621fa6c8dec43e9415e5542be Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:44 +0100 Subject: HID: i2c-hid: enhance Kconfig The "comment" part can never be displayed, so we can remove it. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/Kconfig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig index 5b7d4d87a143..b66617a020bd 100644 --- a/drivers/hid/i2c-hid/Kconfig +++ b/drivers/hid/i2c-hid/Kconfig @@ -7,15 +7,12 @@ config I2C_HID depends on I2C && INPUT select HID ---help--- - Say Y here if you want to use the HID over i2c protocol - implementation. + Say Y here if you use a keyboard, a touchpad, a touchscreen, or any + other HID based devices which is connected to your computer via I2C. If unsure, say N. This support is also available as a module. If so, the module will be called i2c-hid. -comment "Input core support is needed for HID over I2C input layer" - depends on I2C_HID && INPUT=n - endmenu -- cgit v1.2.3-59-g8ed1b From ee8e8806348732e328d119418a9788aabeceed0a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:45 +0100 Subject: HID: i2c-hid: fix checkpatch.pl warning We should not initialize to 0 static declarations. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 0fbb22906806..3c8fe4673705 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -46,7 +46,7 @@ #define I2C_HID_PWR_SLEEP 0x01 /* debug option */ -static bool debug = false; +static bool debug; module_param(debug, bool, 0444); MODULE_PARM_DESC(debug, "print a lot of debug information"); -- cgit v1.2.3-59-g8ed1b From fa738644e57d5fd54b8c0a4f5a7815972e65ce1a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:46 +0100 Subject: HID: i2c-hid: fix i2c_hid_dbg macro This avoids the problematic case: if (condition) i2c_hid_dbg(ihid, "Blah blah %d\n", i); else do_something_very_important(); Which looks correct, however with the previous macro definition, this expands to the unexpected: if (condition) { if (debug) \ dev_printk(KERN_DEBUG, &ihid->client->dev, "Blah blah %d\n", i); else do_something_very_important(); } Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 3c8fe4673705..d95e8de6a920 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -50,9 +50,11 @@ static bool debug; module_param(debug, bool, 0444); MODULE_PARM_DESC(debug, "print a lot of debug information"); -#define i2c_hid_dbg(ihid, fmt, arg...) \ - if (debug) \ - dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg) +#define i2c_hid_dbg(ihid, fmt, arg...) \ +do { \ + if (debug) \ + dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \ +} while (0) struct i2c_hid_desc { __le16 wHIDDescLength; -- cgit v1.2.3-59-g8ed1b From 6bf6c8bf2ba6548c38eeaccb735992660c4bbaeb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:47 +0100 Subject: HID: i2c-hid: remove unused static declarations These definitions are not used here, but are defined by the specification. Keeping some of them for documentation purposes. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index d95e8de6a920..be5126c281c3 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -105,22 +105,17 @@ static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01), .wait = true }; static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) }; static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) }; -static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) }; -static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) }; -static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) }; -static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) }; static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; -/* read/write data register */ -static const struct i2c_hid_cmd hid_data_cmd = { - .registerIndex = offsetof(struct i2c_hid_desc, wDataRegister), - .opcode = 0x00, - .length = 2 }; -/* write output reports */ -static const struct i2c_hid_cmd hid_out_cmd = { - .registerIndex = offsetof(struct i2c_hid_desc, - wOutputRegister), - .opcode = 0x00, - .length = 2 }; + +/* + * These definitions are not used here, but are defined by the spec. + * Keeping them here for documentation purposes. + * + * static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) }; + * static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) }; + * static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) }; + * static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) }; + */ /* The main device structure */ struct i2c_hid { -- cgit v1.2.3-59-g8ed1b From 317b204a131c36df564bd71214e9e18348a084c9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:48 +0100 Subject: HID: i2c-hid: fix return paths Forwards appropriate return values. As noone use the error returned by i2c_hid_get_input, let's make it returning void. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index be5126c281c3..e0d097deb067 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -244,7 +244,7 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType, if (ret) { dev_err(&client->dev, "failed to retrieve report from device.\n"); - return -EINVAL; + return ret; } return 0; @@ -289,7 +289,7 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, reportType, args, args_len, NULL, 0); if (ret) { dev_err(&client->dev, "failed to set a report to device.\n"); - return -EINVAL; + return ret; } return data_len; @@ -333,7 +333,7 @@ static int i2c_hid_hwreset(struct i2c_client *client) return 0; } -static int i2c_hid_get_input(struct i2c_hid *ihid) +static void i2c_hid_get_input(struct i2c_hid *ihid) { int ret, ret_size; int size = le16_to_cpu(ihid->hdesc.wMaxInputLength); @@ -341,11 +341,11 @@ static int i2c_hid_get_input(struct i2c_hid *ihid) ret = i2c_master_recv(ihid->client, ihid->inbuf, size); if (ret != size) { if (ret < 0) - return ret; + return; dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n", __func__, ret, size); - return ret; + return; } ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8; @@ -354,13 +354,13 @@ static int i2c_hid_get_input(struct i2c_hid *ihid) /* host or device initiated RESET completed */ if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) wake_up(&ihid->wait); - return 0; + return; } if (ret_size > size) { dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", __func__, size, ret_size); - return -EIO; + return; } i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf); @@ -369,7 +369,7 @@ static int i2c_hid_get_input(struct i2c_hid *ihid) hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2, ret_size - 2, 1); - return 0; + return; } static irqreturn_t i2c_hid_irq(int irq, void *dev_id) @@ -429,8 +429,10 @@ static void i2c_hid_init_reports(struct hid_device *hid) struct i2c_hid *ihid = i2c_get_clientdata(client); u8 *inbuf = kzalloc(ihid->bufsize, GFP_KERNEL); - if (!inbuf) + if (!inbuf) { + dev_err(&client->dev, "can not retrieve initial reports\n"); return; + } list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) @@ -714,9 +716,7 @@ static int i2c_hid_hidinput_input_event(struct input_dev *dev, return -1; } - hid_set_field(field, offset, value); - - return 0; + return hid_set_field(field, offset, value); } static struct hid_ll_driver i2c_hid_ll_driver = { -- cgit v1.2.3-59-g8ed1b From 9972dcc29cd1fc1b550656eb04496df6dab3dc42 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:49 +0100 Subject: HID: i2c-hid: fix error messages Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index e0d097deb067..035a0cdd129e 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -740,10 +740,10 @@ static int __devinit i2c_hid_init_irq(struct i2c_client *client) IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { - dev_dbg(&client->dev, + dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," " ret = %d\n", - client->name, client->irq, ret); + client->name, client->irq, ret); return ret; } @@ -769,7 +769,8 @@ static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) __func__, 4, ihid->hdesc_buffer); if (ret) { - dev_err(&client->dev, "HID_DESCR_LENGTH_CMD Fail (ret=%d)\n", + dev_err(&client->dev, + "unable to fetch the size of HID descriptor (ret=%d)\n", ret); return -ENODEV; } @@ -784,7 +785,7 @@ static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) /* check bcdVersion == 1.0 */ if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { dev_err(&client->dev, - "unexpected HID descriptor bcdVersion (0x%04x)\n", + "unexpected HID descriptor bcdVersion (0x%04hx)\n", le16_to_cpu(hdesc->bcdVersion)); return -ENODEV; } @@ -870,7 +871,7 @@ static int __devinit i2c_hid_probe(struct i2c_client *client, hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); hid->product = le16_to_cpu(ihid->hdesc.wProductID); - snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", + snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", client->name, hid->vendor, hid->product); ret = hid_add_device(hid); -- cgit v1.2.3-59-g8ed1b From 134ebfd86b9353a3d98f9f4e93b4e79824a4b49a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:54 +0100 Subject: HID: i2c-hid: also call i2c_hid_free_buffers in i2c_hid_remove In the case where the hid driver in charge of handling the hid part of the device (hid-generic for instance) fails at probe, neither i2c_hid_start nor i2c_hid_stop are called. Thus, the buffers allocated in i2c_hid_probe are never freed. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 035a0cdd129e..aab3357626c7 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -908,6 +908,9 @@ static int __devexit i2c_hid_remove(struct i2c_client *client) free_irq(client->irq, ihid); + if (ihid->bufsize) + i2c_hid_free_buffers(ihid); + kfree(ihid); return 0; -- cgit v1.2.3-59-g8ed1b From c737bcf92ea206d3b5d351890f1ade9e12ad4cbe Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:50 +0100 Subject: HID: i2c-hid: i2c_hid_get_report may fail If i2c_hid_get_report fails, exit i2c_hid_init_report. The printk log is already called by i2c_hid_get_report, so no need to add some more printks. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index aab3357626c7..c823a16f35ac 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -399,9 +399,10 @@ static void i2c_hid_init_report(struct hid_report *report, u8 *buffer, unsigned int size, ret_size; size = i2c_hid_get_report_length(report); - i2c_hid_get_report(client, + if (i2c_hid_get_report(client, report->type == HID_FEATURE_REPORT ? 0x03 : 0x01, - report->id, buffer, size); + report->id, buffer, size)) + return; i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, ihid->inbuf); -- cgit v1.2.3-59-g8ed1b From 6233362852785dbaffe1896d62ec834faf53fb57 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 4 Dec 2012 16:27:52 +0100 Subject: HID: i2c-hid: remove unneeded test in i2c_hid_remove ihid can not be null, so there are no reasons to test it. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c823a16f35ac..3bbd70e839f2 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -901,9 +901,6 @@ static int __devexit i2c_hid_remove(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid; - if (WARN_ON(!ihid)) - return -1; - hid = ihid->hid; hid_destroy_device(hid); -- cgit v1.2.3-59-g8ed1b From addb114dd646f9386f5845cdb059f22a497f9a9d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 5 Dec 2012 11:29:08 +0100 Subject: HID: i2c-hid: remove superfluous include The pointless WARN_ON() has been removed from i2c_hid_remove(), so we don't need bug.h any more. Reported-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 3bbd70e839f2..7062df21da52 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-59-g8ed1b From 27174cff8fcfcb3a60a3e7225cc627f6a4b7f827 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Dec 2012 15:02:53 +0100 Subject: HID: i2c-hid: fix memory corruption due to missing hid declaration HID descriptors contains 4 bytes of reserved field. The previous implementation was overriding the next fields in struct i2c_hid. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 7062df21da52..34cca428a6a9 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -69,6 +69,7 @@ struct i2c_hid_desc { __le16 wVendorID; __le16 wProductID; __le16 wVersionID; + __le32 reserved; } __packed; struct i2c_hid_cmd { @@ -776,7 +777,13 @@ static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) } dsize = le16_to_cpu(hdesc->wHIDDescLength); - if (!dsize || dsize > HID_MAX_DESCRIPTOR_SIZE) { + /* + * the size of the HID descriptor should at least contain + * its size and the bcdVersion (4 bytes), and should not be greater + * than sizeof(struct i2c_hid_desc) as we directly fill this struct + * through i2c_hid_command. + */ + if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) { dev_err(&client->dev, "weird size of HID descriptor (%u)\n", dsize); return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 29b45787d1f57f82a1c37ff5a47786c6a9dbedd8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Dec 2012 15:02:54 +0100 Subject: HID: i2c-hid: reorder allocation/free of buffers Simplifies i2c_hid_alloc_buffers tests, and makes this function responsible of the assignment of ihid->bufsize. The condition for the reallocation in i2c_hid_start is then simpler. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 68 ++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 34cca428a6a9..d00f1854cb82 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -464,48 +464,38 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type, } } -static int i2c_hid_alloc_buffers(struct i2c_hid *ihid) +static void i2c_hid_free_buffers(struct i2c_hid *ihid) +{ + kfree(ihid->inbuf); + kfree(ihid->argsbuf); + kfree(ihid->cmdbuf); + ihid->inbuf = NULL; + ihid->cmdbuf = NULL; + ihid->argsbuf = NULL; + ihid->bufsize = 0; +} + +static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) { /* the worst case is computed from the set_report command with a * reportID > 15 and the maximum report length */ int args_len = sizeof(__u8) + /* optional ReportID byte */ sizeof(__u16) + /* data register */ sizeof(__u16) + /* size of the report */ - ihid->bufsize; /* report */ - - ihid->inbuf = kzalloc(ihid->bufsize, GFP_KERNEL); - - if (!ihid->inbuf) - return -ENOMEM; + report_size; /* report */ + ihid->inbuf = kzalloc(report_size, GFP_KERNEL); ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); - - if (!ihid->argsbuf) { - kfree(ihid->inbuf); - return -ENOMEM; - } - ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL); - if (!ihid->cmdbuf) { - kfree(ihid->inbuf); - kfree(ihid->argsbuf); - ihid->inbuf = NULL; - ihid->argsbuf = NULL; + if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) { + i2c_hid_free_buffers(ihid); return -ENOMEM; } - return 0; -} + ihid->bufsize = report_size; -static void i2c_hid_free_buffers(struct i2c_hid *ihid) -{ - kfree(ihid->inbuf); - kfree(ihid->argsbuf); - kfree(ihid->cmdbuf); - ihid->inbuf = NULL; - ihid->cmdbuf = NULL; - ihid->argsbuf = NULL; + return 0; } static int i2c_hid_get_raw_report(struct hid_device *hid, @@ -610,22 +600,19 @@ static int i2c_hid_start(struct hid_device *hid) struct i2c_client *client = hid->driver_data; struct i2c_hid *ihid = i2c_get_clientdata(client); int ret; - int old_bufsize = ihid->bufsize; + unsigned int bufsize = HID_MIN_BUFFER_SIZE; - ihid->bufsize = HID_MIN_BUFFER_SIZE; - i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &ihid->bufsize); - i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &ihid->bufsize); - i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &ihid->bufsize); + i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); + i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); + i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); - if (ihid->bufsize > old_bufsize || !ihid->inbuf || !ihid->cmdbuf) { + if (bufsize > ihid->bufsize) { i2c_hid_free_buffers(ihid); - ret = i2c_hid_alloc_buffers(ihid); + ret = i2c_hid_alloc_buffers(ihid, bufsize); - if (ret) { - ihid->bufsize = old_bufsize; + if (ret) return ret; - } } if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) @@ -849,8 +836,9 @@ static int __devinit i2c_hid_probe(struct i2c_client *client, /* we need to allocate the command buffer without knowing the maximum * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the * real computation later. */ - ihid->bufsize = HID_MIN_BUFFER_SIZE; - i2c_hid_alloc_buffers(ihid); + ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); + if (ret < 0) + goto err; ret = i2c_hid_fetch_hid_descriptor(ihid); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 8a1bbb5319384dab6568ac2ce30d19b922413bec Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Dec 2012 15:02:55 +0100 Subject: HID: i2c-hid: remove extra .irq field in struct i2c_hid There is no point in keeping the irq in i2c_hid as it's already there in client. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index d00f1854cb82..c6630d442e78 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -135,8 +135,6 @@ struct i2c_hid { unsigned long flags; /* device flags */ - int irq; /* the interrupt line irq */ - wait_queue_head_t wait; /* For waiting the interrupt */ }; @@ -736,8 +734,6 @@ static int __devinit i2c_hid_init_irq(struct i2c_client *client) return ret; } - ihid->irq = client->irq; - return 0; } @@ -851,7 +847,7 @@ static int __devinit i2c_hid_probe(struct i2c_client *client, hid = hid_allocate_device(); if (IS_ERR(hid)) { ret = PTR_ERR(hid); - goto err; + goto err_irq; } ihid->hid = hid; @@ -881,10 +877,10 @@ static int __devinit i2c_hid_probe(struct i2c_client *client, err_mem_free: hid_destroy_device(hid); -err: - if (ihid->irq) - free_irq(ihid->irq, ihid); +err_irq: + free_irq(client->irq, ihid); +err: i2c_hid_free_buffers(ihid); kfree(ihid); return ret; @@ -912,10 +908,9 @@ static int __devexit i2c_hid_remove(struct i2c_client *client) static int i2c_hid_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct i2c_hid *ihid = i2c_get_clientdata(client); if (device_may_wakeup(&client->dev)) - enable_irq_wake(ihid->irq); + enable_irq_wake(client->irq); /* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); -- cgit v1.2.3-59-g8ed1b From e5b50fe7bea43d0658773c89ba410ecc56867ee6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Dec 2012 15:02:56 +0100 Subject: HID: i2c-hid: fix i2c_hid_get_raw_report count mismatches The previous memcpy implementation relied on the size advertized by the device. There were no guarantees that buf was big enough. Some gymnastic is also required with the +2/-2 to take into account the first 2 bytes of the returned buffer where the total returned length is supplied by the device. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c6630d442e78..ce01d5916184 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -502,23 +502,31 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, { struct i2c_client *client = hid->driver_data; struct i2c_hid *ihid = i2c_get_clientdata(client); + size_t ret_count, ask_count; int ret; if (report_type == HID_OUTPUT_REPORT) return -EINVAL; - if (count > ihid->bufsize) - count = ihid->bufsize; + /* +2 bytes to include the size of the reply in the query buffer */ + ask_count = min(count + 2, (size_t)ihid->bufsize); ret = i2c_hid_get_report(client, report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, - report_number, ihid->inbuf, count); + report_number, ihid->inbuf, ask_count); if (ret < 0) return ret; - count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); + ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); + if (!ret_count) + return 0; + + ret_count = min(ret_count, ask_count); + + /* The query buffer contains the size, dropping it in the reply */ + count = min(count, ret_count - 2); memcpy(buf, ihid->inbuf + 2, count); return count; -- cgit v1.2.3-59-g8ed1b From 9afd09a1db93cc9084846c2b9bc0d1194743e5dc Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 6 Dec 2012 10:59:28 +0100 Subject: HID: i2c-hid: fix ret_count check ret_count has to be at least 3, as we have to count the 2 bytes that are used for the size of the reply. Without this, memcpy() might be called with zero or negative count. Reported-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ce01d5916184..6e1774c3a3f9 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -520,7 +520,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); - if (!ret_count) + if (ret_count <= 2) return 0; ret_count = min(ret_count, ask_count); -- cgit v1.2.3-59-g8ed1b From 4529eefad087f97b33c0f31984d924b1f15d7bae Mon Sep 17 00:00:00 2001 From: "Lamarque V. Souza" Date: Thu, 6 Dec 2012 12:39:55 -0200 Subject: HID: hidp: fallback to input session properly if hid is blacklisted This patch against kernel 3.7.0-rc8 fixes a kernel oops when turning on the bluetooth mouse with id 0458:0058 [1]. The mouse in question supports both input and hid sessions, however it is blacklisted in drivers/hid/hid-core.c so the input session is one that should be used. Long ago (around kernel 3.0.0) some changes in the bluetooth subsystem made the kernel do not fallback to input session when hid session is not supported or blacklisted. This patch restore that behaviour by making the kernel try the input session if hid_add_device returns ENODEV. The patch exports hid_ignore() from hid-core.c so that it can be used in the bluetooth subsystem. [1] https://bugzilla.kernel.org/show_bug.cgi?id=39882 Signed-off-by: Lamarque V. Souza Acked-by: Gustavo Padovan Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 11 ++++++++--- include/linux/hid.h | 1 + net/bluetooth/hidp/core.c | 9 ++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a7550bb30836..2629d5240d63 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2145,8 +2145,13 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { } }; -static bool hid_ignore(struct hid_device *hdev) +bool hid_ignore(struct hid_device *hdev) { + if (hdev->quirks & HID_QUIRK_NO_IGNORE) + return false; + if (hdev->quirks & HID_QUIRK_IGNORE) + return true; + switch (hdev->vendor) { case USB_VENDOR_ID_CODEMERCS: /* ignore all Code Mercenaries IOWarrior devices */ @@ -2201,6 +2206,7 @@ static bool hid_ignore(struct hid_device *hdev) return !!hid_match_id(hdev, hid_ignore_list); } +EXPORT_SYMBOL_GPL(hid_ignore); int hid_add_device(struct hid_device *hdev) { @@ -2212,8 +2218,7 @@ int hid_add_device(struct hid_device *hdev) /* we need to kill them here, otherwise they will stay allocated to * wait for coming driver */ - if (!(hdev->quirks & HID_QUIRK_NO_IGNORE) - && (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE))) + if (hid_ignore(hdev)) return -ENODEV; /* diff --git a/include/linux/hid.h b/include/linux/hid.h index 7e1f37db7582..abce7eb4f258 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -721,6 +721,7 @@ struct hid_ll_driver { extern int hid_debug; +extern bool hid_ignore(struct hid_device *); extern int hid_add_device(struct hid_device *); extern void hid_destroy_device(struct hid_device *); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ccd985da6518..1f4a32bb2971 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -941,6 +941,13 @@ static int hidp_setup_hid(struct hidp_session *session, hid->hid_get_raw_report = hidp_get_raw_report; hid->hid_output_raw_report = hidp_output_raw_report; + /* True if device is blacklisted in drivers/hid/hid-core.c */ + if (hid_ignore(hid)) { + hid_destroy_device(session->hid); + session->hid = NULL; + return -ENODEV; + } + return 0; fault: @@ -1013,7 +1020,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (req->rd_size > 0) { err = hidp_setup_hid(session, req); - if (err) + if (err && err != -ENODEV) goto purge; } -- cgit v1.2.3-59-g8ed1b From 83499b52c61f50292f0aae36499de8a8fc3e37c3 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sun, 9 Dec 2012 12:44:30 +0100 Subject: HID: sensors: autodetect USB HID sensor hubs It should not be necessary to add IDs for HID sensor hubs to lists in hid-core.c and hid-sensor-hub.c. So instead of a whitelist, autodetect such USB HID sensor hubs, based on a collection of type physical inside a useage page of type sensor. If some sensor hubs stil must be usable as raw devices, a blacklist might be created. Signed-off-by: Alexander Holler Acked-by: "Pandruvada, Srinivas" Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 11 ++++++++++- drivers/hid/hid-sensor-hub.c | 32 +------------------------------- include/linux/hid-sensor-ids.h | 1 - include/linux/hid.h | 2 ++ 4 files changed, 13 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f4109fd657ff..7df5399a9386 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -713,7 +713,12 @@ static int hid_scan_report(struct hid_device *hid) hid_scan_usage(hid, u); break; } - } + } else if (page == HID_UP_SENSOR && + item.type == HID_ITEM_TYPE_MAIN && + item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION && + (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL && + hid->bus == BUS_USB) + hid->group = HID_GROUP_SENSOR_HUB; } return 0; @@ -1465,6 +1470,10 @@ EXPORT_SYMBOL_GPL(hid_disconnect); * there is a proper autodetection and autoloading in place (based on presence * of HID_DG_CONTACTID), so those devices don't need to be added to this list, * as we are doing the right thing in hid_scan_usage(). + * + * Autodetection for (USB) HID sensor hubs exists too. If a collection of type + * physical is found inside a usage page of type sensor, hid-sensor-hub will be + * used as a driver. See hid_scan_report(). */ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index d9d73e9163eb..ca88ddc2d78f 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -82,23 +82,6 @@ struct hid_sensor_hub_callbacks_list { void *priv; }; -static int sensor_hub_check_for_sensor_page(struct hid_device *hdev) -{ - int i; - int ret = -EINVAL; - - for (i = 0; i < hdev->maxcollection; i++) { - struct hid_collection *col = &hdev->collection[i]; - if (col->type == HID_COLLECTION_PHYSICAL && - (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) { - ret = 0; - break; - } - } - - return ret; -} - static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, int dir) { @@ -524,10 +507,6 @@ static int sensor_hub_probe(struct hid_device *hdev, hid_err(hdev, "parse failed\n"); goto err_free; } - if (sensor_hub_check_for_sensor_page(hdev) < 0) { - hid_err(hdev, "sensor page not found\n"); - goto err_free; - } INIT_LIST_HEAD(&hdev->inputs); ret = hid_hw_start(hdev, 0); @@ -630,16 +609,7 @@ static void sensor_hub_remove(struct hid_device *hdev) } static const struct hid_device_id sensor_hub_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, - USB_DEVICE_ID_SENSOR_HUB_1020) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, - USB_DEVICE_ID_SENSOR_HUB_1020) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, - USB_DEVICE_ID_SENSOR_HUB_09FA) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, - USB_DEVICE_ID_SENSOR_HUB_09FA) }, - { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, - USB_DEVICE_ID_SENSOR_HUB_7014) }, + { HID_DEVICE(BUS_USB, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, sensor_hub_devices); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index ca8d7e94eb3c..55f277372fed 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -19,7 +19,6 @@ #ifndef _HID_SENSORS_IDS_H #define _HID_SENSORS_IDS_H -#define HID_UP_SENSOR 0x00200000 #define HID_MAX_PHY_DEVICES 0xFF /* Accel 3D (200073) */ diff --git a/include/linux/hid.h b/include/linux/hid.h index c076041a069e..c5f6ec2b15c2 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -167,6 +167,7 @@ struct hid_item { #define HID_UP_MSVENDOR 0xff000000 #define HID_UP_CUSTOM 0x00ff0000 #define HID_UP_LOGIVENDOR 0xffbc0000 +#define HID_UP_SENSOR 0x00200000 #define HID_USAGE 0x0000ffff @@ -292,6 +293,7 @@ struct hid_item { */ #define HID_GROUP_GENERIC 0x0001 #define HID_GROUP_MULTITOUCH 0x0002 +#define HID_GROUP_SENSOR_HUB 0x0003 /* * This is the global environment of the parser. This information is -- cgit v1.2.3-59-g8ed1b From 68fd32b8228a62ca7f46ed64da8267daad29480b Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Sun, 9 Dec 2012 02:12:24 +0100 Subject: Revert "HID: sensors: add to special driver list" Those IDs aren't necessary anymore. This reverts commit c8147d9ea19bfe7d8e569351bc7239e118dd6997. Signed-off-by: Alexander Holler Acked-by: "Pandruvada, Srinivas" Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 5 ----- drivers/hid/hid-ids.h | 6 ------ 2 files changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7df5399a9386..c96e548cd7b8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1580,10 +1580,6 @@ 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_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) }, - { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) }, { 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) }, @@ -1681,7 +1677,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9d7a42857ea1..8fd25d387af1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -432,11 +432,6 @@ #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 -#define USB_VENDOR_ID_INTEL_8086 0x8086 -#define USB_VENDOR_ID_INTEL_8087 0x8087 -#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020 -#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA - #define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 @@ -714,7 +709,6 @@ #define USB_VENDOR_ID_STANTUM_STM 0x0483 #define USB_DEVICE_ID_MTP_STM 0x3261 -#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014 #define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 #define USB_DEVICE_ID_MTP_SITRONIX 0x5001 -- cgit v1.2.3-59-g8ed1b From 7a7d6d9c5fcd4b674da38e814cfc0724c67731b2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 12 Dec 2012 18:00:02 +0100 Subject: HID: i2c-hid: add mutex protecting open/close race We should not enter close function while someone else is in open. This mutex prevents this race. There is also no need to override the ret value with -EIO in case of a failure of i2c_hid_set_power. Signed-off-by: Benjamin Tissoires Reviewed-by: Jean Delvare Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 6e1774c3a3f9..9ef222442ca0 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -117,6 +118,8 @@ static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; * static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) }; */ +static DEFINE_MUTEX(i2c_hid_open_mut); + /* The main device structure */ struct i2c_hid { struct i2c_client *client; /* i2c client */ @@ -641,17 +644,20 @@ static int i2c_hid_open(struct hid_device *hid) { struct i2c_client *client = hid->driver_data; struct i2c_hid *ihid = i2c_get_clientdata(client); - int ret; + int ret = 0; + mutex_lock(&i2c_hid_open_mut); if (!hid->open++) { ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); if (ret) { hid->open--; - return -EIO; + goto done; } set_bit(I2C_HID_STARTED, &ihid->flags); } - return 0; +done: + mutex_unlock(&i2c_hid_open_mut); + return ret; } static void i2c_hid_close(struct hid_device *hid) @@ -663,12 +669,14 @@ static void i2c_hid_close(struct hid_device *hid) * data acquistion due to a resumption we no longer * care about */ + mutex_lock(&i2c_hid_open_mut); if (!--hid->open) { clear_bit(I2C_HID_STARTED, &ihid->flags); /* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); } + mutex_unlock(&i2c_hid_open_mut); } static int i2c_hid_power(struct hid_device *hid, int lvl) -- cgit v1.2.3-59-g8ed1b