diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 11 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-apple.c | 9 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 66 | ||||
-rw-r--r-- | drivers/hid/hid-icade.c | 259 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 20 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 97 | ||||
-rw-r--r-- | drivers/hid/hid-microsoft.c | 18 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 114 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-sensor-hub.c | 36 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-ext.c | 2 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 85 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/Kconfig | 18 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/Makefile | 5 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 979 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 3 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 10 |
18 files changed, 1582 insertions, 154 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1630150..e7d6a13 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 @@ -728,4 +737,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 ffe848f..b622157 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 @@ -118,3 +119,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/hid-apple.c b/drivers/hid/hid-apple.c index 06ebdbb..d0f7662 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 }, @@ -522,6 +523,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9904776..eb2ee11 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; } @@ -315,6 +319,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 +370,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: @@ -713,7 +725,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; @@ -865,6 +882,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. */ @@ -1465,6 +1488,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) }, @@ -1532,9 +1559,13 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, { 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) }, @@ -1568,10 +1599,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_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_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) }, @@ -1670,7 +1698,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) }, @@ -2140,13 +2167,21 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, { 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) }, { } }; -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 */ @@ -2183,7 +2218,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 && @@ -2192,6 +2236,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) { @@ -2203,8 +2248,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/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c new file mode 100644 index 0000000..1d6565e3 --- /dev/null +++ b/drivers/hid/hid-icade.c @@ -0,0 +1,259 @@ +/* + * ION iCade input driver + * + * Copyright (c) 2012 Bastien Nocera <hadess@hadess.net> + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + */ + +/* + * 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 <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#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 <linux/input.h> + * #include <stdio.h> + * #include <assert.h> + * + * #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 <hadess@hadess.net>"); +MODULE_DESCRIPTION("ION iCade input driver"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 71806ed..4dfa605 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -118,6 +118,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI 0x0252 #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO 0x0253 #define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS 0x0254 +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a +#define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a #define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b @@ -254,6 +257,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 @@ -420,6 +424,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 @@ -429,11 +436,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 @@ -600,6 +602,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 @@ -695,6 +698,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 @@ -713,7 +719,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 @@ -761,6 +766,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/hid-input.c b/drivers/hid/hid-input.c index d917c0d..21b196c 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 @@ -208,7 +207,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,17 +228,29 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) case ABS_X: case ABS_Y: case ABS_Z: - if (field->unit == 0x11) { /* If centimeters */ + 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 & 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; @@ -281,8 +292,9 @@ static __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); #ifdef CONFIG_HID_BATTERY_STRENGTH static enum power_supply_property hidinput_battery_props[] = { @@ -299,6 +311,9 @@ static enum power_supply_property hidinput_battery_props[] = { 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 }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, @@ -502,9 +517,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: @@ -1146,6 +1166,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 @@ -1157,7 +1209,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); @@ -1188,33 +1239,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++) diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 3acdcfc..6fcd466d0 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -28,22 +28,30 @@ #define MS_RDESC 0x08 #define MS_NOGET 0x10 #define MS_DUPLICATE_USAGES 0x20 +#define MS_RDESC_3K 0x40 -/* - * Microsoft Wireless Desktop Receiver (Model 1028) has - * 'Usage Min/Max' where it ought to have 'Physical Min/Max' - */ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + /* + * Microsoft Wireless Desktop Receiver (Model 1028) has + * 'Usage Min/Max' where it ought to have 'Physical Min/Max' + */ if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 && rdesc[559] == 0x29) { hid_info(hdev, "fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n"); rdesc[557] = 0x35; rdesc[559] = 0x45; } + /* the same as above (s/usage/physical/) */ + if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 && + rdesc[95] == 0x00 && rdesc[96] == 0x29 && + rdesc[97] == 0xff) { + rdesc[94] = 0x35; + rdesc[96] = 0x45; + } return rdesc; } @@ -192,7 +200,7 @@ static const struct hid_device_id ms_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), .driver_data = MS_PRESENTER }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), - .driver_data = MS_ERGONOMY }, + .driver_data = MS_ERGONOMY | MS_RDESC_3K }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0), .driver_data = MS_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500), diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3eb02b9..61543c0 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -52,11 +52,14 @@ 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) +#define MT_QUIRK_HOVERING (1 << 11) 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? */ + bool inrange_state; /* is the finger in proximity of the sensor? */ }; struct mt_class { @@ -121,6 +124,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) @@ -210,8 +214,7 @@ static struct mt_class mt_classes[] = { }, { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | - MT_QUIRK_SLOT_IS_CONTACTNUMBER, - .maxcontacts = 10 + MT_QUIRK_SLOT_IS_CONTACTNUMBER }, { .name = MT_CLS_FLATFROG, @@ -283,11 +286,26 @@ 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; 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; } } @@ -298,6 +316,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, @@ -318,6 +337,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 @@ -340,23 +360,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; @@ -366,6 +405,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; @@ -421,11 +466,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, * contact max are global to the report */ td->last_field_index = field->index; return -1; - } case HID_DG_TOUCH: /* Legacy devices use TIPSWITCH and not TOUCH. * Let's just ignore this field. */ return -1; + } /* let hid-input decide for the others */ return 0; @@ -478,18 +523,26 @@ 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; + 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); - 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; @@ -497,6 +550,10 @@ 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_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); @@ -527,10 +584,10 @@ 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; + if (quirks & MT_QUIRK_HOVERING) + td->curdata.inrange_state = value; break; case HID_DG_TIPSWITCH: if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) @@ -548,10 +605,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; @@ -576,12 +639,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 (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); + if (field->index == td->last_field_index + && td->num_received >= td->num_expected) + mt_sync_frame(td, field->hidinput->input); + } } diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 0008a51..eb00357 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -608,7 +608,7 @@ void picolcd_exit_framebuffer(struct picolcd_data *data) /* make sure there is no running update - thus that fbdata->picolcd * once obtained under lock is guaranteed not to get free() under * the feet of the deferred work */ - flush_delayed_work_sync(&info->deferred_work); + flush_delayed_work(&info->deferred_work); data->fb_info = NULL; unregister_framebuffer(info); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index d9d73e9..0bc58bd 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) { @@ -437,9 +420,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 +465,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev, callback->pdev); spin_unlock_irqrestore(&pdata->lock, flags); -err_report: return 1; } @@ -524,10 +503,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 +605,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/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index bc85bf2..38ae8777 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -229,7 +229,7 @@ static void wiiext_worker(struct work_struct *work) /* schedule work only once, otherwise mark for reschedule */ static void wiiext_schedule(struct wiimote_ext *ext) { - queue_work(system_nrt_wq, &ext->worker); + schedule_work(&ext->worker); } /* diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 17d15bb..413a731 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -42,7 +42,6 @@ static struct cdev hidraw_cdev; static struct class *hidraw_class; static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; static DEFINE_MUTEX(minors_lock); -static void drop_ref(struct hidraw *hid, int exists_bit); static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -58,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; @@ -70,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); @@ -114,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, __u8 *buf; int ret = 0; - if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { + if (!hidraw_table[minor]) { ret = -ENODEV; goto out; } @@ -262,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file) } mutex_lock(&minors_lock); - if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { + if (!hidraw_table[minor]) { err = -ENODEV; goto out_unlock; } @@ -296,15 +295,46 @@ 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); + struct hidraw *dev; struct hidraw_list *list = file->private_data; + int ret; + int i; + + mutex_lock(&minors_lock); + if (!hidraw_table[minor]) { + ret = -ENODEV; + goto unlock; + } - drop_ref(hidraw_table[minor], 0); list_del(&list->node); + dev = hidraw_table[minor]; + if (!--dev->open) { + if (list->hidraw->exist) { + hid_hw_power(dev->hid, PM_HINT_NORMAL); + hid_hw_close(dev->hid); + } else { + kfree(list->hidraw); + } + } + + for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) + kfree(list->buffer[i].value); kfree(list); - return 0; + ret = 0; +unlock: + mutex_unlock(&minors_lock); + + return ret; } static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -415,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 @@ -506,7 +537,21 @@ EXPORT_SYMBOL_GPL(hidraw_connect); void hidraw_disconnect(struct hid_device *hid) { struct hidraw *hidraw = hid->hidraw; - drop_ref(hidraw, 1); + + mutex_lock(&minors_lock); + hidraw->exist = 0; + + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + + hidraw_table[hidraw->minor] = NULL; + + if (hidraw->open) { + hid_hw_close(hid); + wake_up_interruptible(&hidraw->wait); + } else { + kfree(hidraw); + } + mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); @@ -555,23 +600,3 @@ void hidraw_exit(void) unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); } - -static void drop_ref(struct hidraw *hidraw, int exists_bit) -{ - mutex_lock(&minors_lock); - if (exists_bit) { - hid_hw_close(hidraw->hid); - hidraw->exist = 0; - if (hidraw->open) - wake_up_interruptible(&hidraw->wait); - } else { - --hidraw->open; - } - - if (!hidraw->open && !hidraw->exist) { - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - hidraw_table[hidraw->minor] = NULL; - kfree(hidraw); - } - mutex_unlock(&minors_lock); -} diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig new file mode 100644 index 0000000..b66617a --- /dev/null +++ b/drivers/hid/i2c-hid/Kconfig @@ -0,0 +1,18 @@ +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 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. + +endmenu diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile new file mode 100644 index 0000000..832d8f9 --- /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 0000000..9ef22244 --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -0,0 +1,979 @@ +/* + * HID over I2C protocol implementation + * + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * 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 <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> 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 <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/hid.h> +#include <linux/mutex.h> + +#include <linux/i2c/i2c-hid.h> + +/* 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; +module_param(debug, bool, 0444); +MODULE_PARM_DESC(debug, "print a lot of debug information"); + +#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; + __le16 bcdVersion; + __le16 wReportDescLength; + __le16 wReportDescRegister; + __le16 wInputRegister; + __le16 wMaxInputLength; + __le16 wOutputRegister; + __le16 wMaxOutputLength; + __le16 wCommandRegister; + __le16 wDataRegister; + __le16 wVendorID; + __le16 wProductID; + __le16 wVersionID; + __le32 reserved; +} __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_set_power_cmd = { I2C_HID_CMD(0x08) }; + +/* + * 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) }; + */ + +static DEFINE_MUTEX(i2c_hid_open_mut); + +/* 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 */ + + 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 ret; + } + + 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 ret; + } + + 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 void 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; + + dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n", + __func__, ret, size); + return; + } + + 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; + } + + if (ret_size > size) { + dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", + __func__, size, ret_size); + return; + } + + 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; +} + +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); + if (i2c_hid_get_report(client, + report->type == HID_FEATURE_REPORT ? 0x03 : 0x01, + report->id, buffer, size)) + return; + + 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) { + 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) + 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 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 */ + report_size; /* report */ + + ihid->inbuf = kzalloc(report_size, GFP_KERNEL); + ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); + ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL); + + if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) { + i2c_hid_free_buffers(ihid); + return -ENOMEM; + } + + ihid->bufsize = report_size; + + return 0; +} + +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); + size_t ret_count, ask_count; + int ret; + + if (report_type == HID_OUTPUT_REPORT) + return -EINVAL; + + /* +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, ask_count); + + if (ret < 0) + return ret; + + ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); + + if (ret_count <= 2) + 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; +} + +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; + unsigned int bufsize = HID_MIN_BUFFER_SIZE; + + 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 (bufsize > ihid->bufsize) { + i2c_hid_free_buffers(ihid); + + ret = i2c_hid_alloc_buffers(ihid, bufsize); + + if (ret) + 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 = 0; + + mutex_lock(&i2c_hid_open_mut); + if (!hid->open++) { + ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + if (ret) { + hid->open--; + goto done; + } + set_bit(I2C_HID_STARTED, &ihid->flags); + } +done: + mutex_unlock(&i2c_hid_open_mut); + return ret; +} + +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 + */ + 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) +{ + 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; + } + + return hid_set_field(field, offset, value); +} + +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_warn(&client->dev, + "Could not register for %s interrupt, irq = %d," + " ret = %d\n", + client->name, client->irq, ret); + + return ret; + } + + 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, + "unable to fetch the size of HID descriptor (ret=%d)\n", + ret); + return -ENODEV; + } + + dsize = le16_to_cpu(hdesc->wHIDDescLength); + /* + * 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; + } + + /* check bcdVersion == 1.0 */ + if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) { + dev_err(&client->dev, + "unexpected HID descriptor bcdVersion (0x%04hx)\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. */ + 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) + 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_irq; + } + + 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 %04hX:%04hX", + 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_irq: + free_irq(client->irq, ihid); + +err: + i2c_hid_free_buffers(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; + + hid = ihid->hid; + hid_destroy_device(hid); + + free_irq(client->irq, ihid); + + if (ihid->bufsize) + i2c_hid_free_buffers(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); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->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[] = { + { "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 <benjamin.tissoires@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 11c7932..ac9e352 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 }, @@ -79,9 +80,11 @@ 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 }, + { 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 }, diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 14599e2..87bd649 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); @@ -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++) |