diff options
Diffstat (limited to 'drivers/hid')
31 files changed, 2052 insertions, 491 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7a0c6f956d3e..a421abdd1ab7 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -31,6 +31,11 @@ config HID If unsure, say Y. +config HID_BATTERY_STRENGTH + bool + depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY + default y + config HIDRAW bool "/dev/hidraw raw HID device support" depends on HID @@ -335,6 +340,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - 3M PCT touch screens - ActionStar dual touch panels + - Atmel panels - Cando dual touch panels - Chunghwa panels - CVTouch panels @@ -349,12 +355,15 @@ config HID_MULTITOUCH - Lumio CrystalTouch panels - MosArt dual-touch panels - PenMount dual touch panels + - PixArt optical touch screen - Pixcir dual touch panels + - Quanta panels - eGalax dual-touch panels, including the Joojoo and Wetab tablets - Stantum multitouch panels - Touch International Panels - Unitec Panels - XAT optical touch panels + - Xiroku optical touch panels If unsure, say N. @@ -466,12 +475,6 @@ config HID_PRIMAX Support for Primax devices that are not fully compliant with the HID standard. -config HID_QUANTA - tristate "Quanta Optical Touch panels" - depends on USB_HID - ---help--- - Support for Quanta Optical Touch dual-touch panels. - config HID_ROCCAT tristate "Roccat special event support" depends on USB_HID @@ -492,6 +495,13 @@ config HID_ROCCAT_ARVO ---help--- Support for Roccat Arvo keyboard. +config HID_ROCCAT_ISKU + tristate "Roccat Isku keyboard support" + depends on USB_HID + depends on HID_ROCCAT + ---help--- + Support for Roccat Isku keyboard. + config HID_ROCCAT_KONE tristate "Roccat Kone Mouse support" depends on USB_HID @@ -560,6 +570,12 @@ config GREENASIA_FF (like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter and want to enable force feedback support for it. +config HID_HYPERV_MOUSE + tristate "Microsoft Hyper-V mouse driver" + depends on HYPERV + ---help--- + Select this option to enable the Hyper-V mouse driver. + config HID_SMARTJOYPLUS tristate "SmartJoy PLUS PS2/USB adapter support" depends on USB_HID @@ -620,6 +636,7 @@ config HID_WIIMOTE depends on BT_HIDP depends on LEDS_CLASS select POWER_SUPPLY + select INPUT_FF_MEMLESS ---help--- Support for the Nintendo Wii Remote bluetooth device. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 15c1a84537eb..8aefdc963cce 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o +obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o @@ -59,7 +60,6 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o -obj-$(CONFIG_HID_QUANTA) += hid-quanta.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o @@ -67,6 +67,7 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o +obj-$(CONFIG_HID_ROCCAT_ISKU) += hid-roccat-isku.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 8cdb4b45b30a..299d23871122 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -183,9 +183,6 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) table = macbookair_fn_keys; - else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI && - hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) - table = macbookair_fn_keys; else if (hid->product < 0x21d || hid->product >= 0x300) table = powerbook_fn_keys; else diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 3bdb4500f95e..5be858dd9a15 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/hid.h> +#include <linux/module.h> #include "hid-ids.h" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 848a56c0279c..b88ae113d367 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -90,7 +90,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned struct hid_field *field; if (report->maxfield == HID_MAX_FIELDS) { - dbg_hid("too many fields in report\n"); + hid_err(report->device, "too many fields in report\n"); return NULL; } @@ -121,7 +121,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { - dbg_hid("collection stack overflow\n"); + hid_err(parser->device, "collection stack overflow\n"); return -1; } @@ -129,7 +129,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) collection = kmalloc(sizeof(struct hid_collection) * parser->device->collection_size * 2, GFP_KERNEL); if (collection == NULL) { - dbg_hid("failed to reallocate collection array\n"); + hid_err(parser->device, "failed to reallocate collection array\n"); return -1; } memcpy(collection, parser->device->collection, @@ -165,7 +165,7 @@ static int open_collection(struct hid_parser *parser, unsigned type) static int close_collection(struct hid_parser *parser) { if (!parser->collection_stack_ptr) { - dbg_hid("collection stack underflow\n"); + hid_err(parser->device, "collection stack underflow\n"); return -1; } parser->collection_stack_ptr--; @@ -197,7 +197,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) static int hid_add_usage(struct hid_parser *parser, unsigned usage) { if (parser->local.usage_index >= HID_MAX_USAGES) { - dbg_hid("usage index exceeded\n"); + hid_err(parser->device, "usage index exceeded\n"); return -1; } parser->local.usage[parser->local.usage_index] = usage; @@ -222,12 +222,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign report = hid_register_report(parser->device, report_type, parser->global.report_id); if (!report) { - dbg_hid("hid_register_report failed\n"); + hid_err(parser->device, "hid_register_report failed\n"); return -1; } if (parser->global.logical_maximum < parser->global.logical_minimum) { - dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum); + hid_err(parser->device, "logical range invalid %d %d\n", + parser->global.logical_minimum, parser->global.logical_maximum); return -1; } @@ -307,7 +308,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_PUSH: if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { - dbg_hid("global environment stack overflow\n"); + hid_err(parser->device, "global environment stack overflow\n"); return -1; } @@ -318,7 +319,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_POP: if (!parser->global_stack_ptr) { - dbg_hid("global environment stack underflow\n"); + hid_err(parser->device, "global environment stack underflow\n"); return -1; } @@ -362,8 +363,8 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); - if (parser->global.report_size > 32) { - dbg_hid("invalid report_size %d\n", + if (parser->global.report_size > 96) { + hid_err(parser->device, "invalid report_size %d\n", parser->global.report_size); return -1; } @@ -372,7 +373,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: parser->global.report_count = item_udata(item); if (parser->global.report_count > HID_MAX_USAGES) { - dbg_hid("invalid report_count %d\n", + hid_err(parser->device, "invalid report_count %d\n", parser->global.report_count); return -1; } @@ -381,13 +382,13 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); if (parser->global.report_id == 0) { - dbg_hid("report_id 0 is invalid\n"); + hid_err(parser->device, "report_id 0 is invalid\n"); return -1; } return 0; default: - dbg_hid("unknown global tag 0x%x\n", item->tag); + hid_err(parser->device, "unknown global tag 0x%x\n", item->tag); return -1; } } @@ -414,14 +415,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) * items and the first delimiter set. */ if (parser->local.delimiter_depth != 0) { - dbg_hid("nested delimiters\n"); + hid_err(parser->device, "nested delimiters\n"); return -1; } parser->local.delimiter_depth++; parser->local.delimiter_branch++; } else { if (parser->local.delimiter_depth < 1) { - dbg_hid("bogus close delimiter\n"); + hid_err(parser->device, "bogus close delimiter\n"); return -1; } parser->local.delimiter_depth--; @@ -506,7 +507,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) ret = hid_add_field(parser, HID_FEATURE_REPORT, data); break; default: - dbg_hid("unknown main item tag 0x%x\n", item->tag); + hid_err(parser->device, "unknown main item tag 0x%x\n", item->tag); ret = 0; } @@ -678,12 +679,12 @@ int hid_parse_report(struct hid_device *device, __u8 *start, while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { - dbg_hid("unexpected long global item\n"); + hid_err(device, "unexpected long global item\n"); goto err; } if (dispatch_type[item.type](parser, &item)) { - dbg_hid("item %u %u %u %u parsing failed\n", + hid_err(device, "item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); goto err; @@ -691,11 +692,11 @@ int hid_parse_report(struct hid_device *device, __u8 *start, if (start == end) { if (parser->collection_stack_ptr) { - dbg_hid("unbalanced collection at end of report description\n"); + hid_err(device, "unbalanced collection at end of report description\n"); goto err; } if (parser->local.delimiter_depth) { - dbg_hid("unbalanced delimiter at end of report description\n"); + hid_err(device, "unbalanced delimiter at end of report description\n"); goto err; } vfree(parser); @@ -703,7 +704,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start, } } - dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); + hid_err(device, "item fetching failed at offset %d\n", (int)(end - start)); err: vfree(parser); return ret; @@ -873,7 +874,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, ret = hdrv->event(hid, field, usage, value); if (ret != 0) { if (ret < 0) - dbg_hid("%s's event failed with %d\n", + hid_err(hid, "%s's event failed with %d\n", hdrv->name, ret); return; } @@ -995,12 +996,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) hid_dump_input(field->report->device, field->usage + offset, value); if (offset >= field->report_count) { - dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); + hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n", + offset, field->report_count); return -1; } if (field->logical_minimum < 0) { if (value != snto32(s32ton(value, size), size)) { - dbg_hid("value %d is out of range\n", value); + hid_err(field->report->device, "value %d is out of range\n", value); return -1; } } @@ -1404,11 +1406,13 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, @@ -1423,6 +1427,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, @@ -1498,11 +1503,15 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_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_PYRA_WIRED) }, @@ -1544,11 +1553,21 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, @@ -1768,11 +1787,12 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) }, { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) }, diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 9a243ca96e6d..01dd9a7daf7a 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -31,6 +31,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/sched.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/poll.h> @@ -113,6 +114,14 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0xbd, "FlareRelease"}, {0, 0xbe, "LandingGear"}, {0, 0xbf, "ToeBrake"}, + { 6, 0, "GenericDeviceControls" }, + {0, 0x20, "BatteryStrength" }, + {0, 0x21, "WirelessChannel" }, + {0, 0x22, "WirelessID" }, + {0, 0x23, "DiscoverWirelessControl" }, + {0, 0x24, "SecurityCodeCharacterEntered" }, + {0, 0x25, "SecurityCodeCharactedErased" }, + {0, 0x26, "SecurityCodeCleared" }, { 7, 0, "Keyboard" }, { 8, 0, "LED" }, {0, 0x01, "NumLock"}, diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index 61eece47204d..e832f44ae383 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/hid.h> +#include <linux/module.h> #include "hid-ids.h" diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index a5dc13fe367b..2630d483d262 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -24,6 +24,7 @@ #include <linux/hid.h> #include <linux/input.h> #include <linux/usb.h> +#include <linux/module.h> #include "hid-ids.h" #include "usbhid/usbhid.h" @@ -139,7 +140,7 @@ err: } static const struct hid_device_id ems_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_EMS, 0x118) }, + { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { } }; MODULE_DEVICE_TABLE(hid, ems_devices); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index 279ba530003c..f1e1bcf67427 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/hid.h> +#include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_GREENASIA_FF diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index 91e3a032112b..4e7542151e22 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -25,6 +25,7 @@ #include <linux/hid.h> #include <linux/input.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/usb.h> diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c new file mode 100644 index 000000000000..0c33ae9cf0f0 --- /dev/null +++ b/drivers/hid/hid-hyperv.c @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2009, Citrix Systems, Inc. + * Copyright (c) 2010, Microsoft Corporation. + * Copyright (c) 2011, Novell Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/completion.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/hiddev.h> +#include <linux/hyperv.h> + + +struct hv_input_dev_info { + unsigned int size; + unsigned short vendor; + unsigned short product; + unsigned short version; + unsigned short reserved[11]; +}; + +/* The maximum size of a synthetic input message. */ +#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16 + +/* + * Current version + * + * History: + * Beta, RC < 2008/1/22 1,0 + * RC > 2008/1/22 2,0 + */ +#define SYNTHHID_INPUT_VERSION_MAJOR 2 +#define SYNTHHID_INPUT_VERSION_MINOR 0 +#define SYNTHHID_INPUT_VERSION (SYNTHHID_INPUT_VERSION_MINOR | \ + (SYNTHHID_INPUT_VERSION_MAJOR << 16)) + + +#pragma pack(push, 1) +/* + * Message types in the synthetic input protocol + */ +enum synthhid_msg_type { + SYNTH_HID_PROTOCOL_REQUEST, + SYNTH_HID_PROTOCOL_RESPONSE, + SYNTH_HID_INITIAL_DEVICE_INFO, + SYNTH_HID_INITIAL_DEVICE_INFO_ACK, + SYNTH_HID_INPUT_REPORT, + SYNTH_HID_MAX +}; + +/* + * Basic message structures. + */ +struct synthhid_msg_hdr { + enum synthhid_msg_type type; + u32 size; +}; + +struct synthhid_msg { + struct synthhid_msg_hdr header; + char data[1]; /* Enclosed message */ +}; + +union synthhid_version { + struct { + u16 minor_version; + u16 major_version; + }; + u32 version; +}; + +/* + * Protocol messages + */ +struct synthhid_protocol_request { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; +}; + +struct synthhid_protocol_response { + struct synthhid_msg_hdr header; + union synthhid_version version_requested; + unsigned char approved; +}; + +struct synthhid_device_info { + struct synthhid_msg_hdr header; + struct hv_input_dev_info hid_dev_info; + struct hid_descriptor hid_descriptor; +}; + +struct synthhid_device_info_ack { + struct synthhid_msg_hdr header; + unsigned char reserved; +}; + +struct synthhid_input_report { + struct synthhid_msg_hdr header; + char buffer[1]; +}; + +#pragma pack(pop) + +#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE) +#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE) + + +enum pipe_prot_msg_type { + PIPE_MESSAGE_INVALID, + PIPE_MESSAGE_DATA, + PIPE_MESSAGE_MAXIMUM +}; + + +struct pipe_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + char data[1]; +}; + +struct mousevsc_prt_msg { + enum pipe_prot_msg_type type; + u32 size; + union { + struct synthhid_protocol_request request; + struct synthhid_protocol_response response; + struct synthhid_device_info_ack ack; + }; +}; + +/* + * Represents an mousevsc device + */ +struct mousevsc_dev { + struct hv_device *device; + bool init_complete; + bool connected; + struct mousevsc_prt_msg protocol_req; + struct mousevsc_prt_msg protocol_resp; + /* Synchronize the request/response if needed */ + struct completion wait_event; + int dev_info_status; + + struct hid_descriptor *hid_desc; + unsigned char *report_desc; + u32 report_desc_size; + struct hv_input_dev_info hid_dev_info; + struct hid_device *hid_device; +}; + + +static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device) +{ + struct mousevsc_dev *input_dev; + + input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL); + + if (!input_dev) + return NULL; + + input_dev->device = device; + hv_set_drvdata(device, input_dev); + init_completion(&input_dev->wait_event); + input_dev->init_complete = false; + + return input_dev; +} + +static void mousevsc_free_device(struct mousevsc_dev *device) +{ + kfree(device->hid_desc); + kfree(device->report_desc); + hv_set_drvdata(device->device, NULL); + kfree(device); +} + +static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, + struct synthhid_device_info *device_info) +{ + int ret = 0; + struct hid_descriptor *desc; + struct mousevsc_prt_msg ack; + + input_device->dev_info_status = -ENOMEM; + + input_device->hid_dev_info = device_info->hid_dev_info; + desc = &device_info->hid_descriptor; + if (desc->bLength == 0) + goto cleanup; + + input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); + + if (!input_device->hid_desc) + goto cleanup; + + memcpy(input_device->hid_desc, desc, desc->bLength); + + input_device->report_desc_size = desc->desc[0].wDescriptorLength; + if (input_device->report_desc_size == 0) { + input_device->dev_info_status = -EINVAL; + goto cleanup; + } + + input_device->report_desc = kzalloc(input_device->report_desc_size, + GFP_ATOMIC); + + if (!input_device->report_desc) { + input_device->dev_info_status = -ENOMEM; + goto cleanup; + } + + memcpy(input_device->report_desc, + ((unsigned char *)desc) + desc->bLength, + desc->desc[0].wDescriptorLength); + + /* Send the ack */ + memset(&ack, 0, sizeof(struct mousevsc_prt_msg)); + + ack.type = PIPE_MESSAGE_DATA; + ack.size = sizeof(struct synthhid_device_info_ack); + + ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK; + ack.ack.header.size = 1; + ack.ack.reserved = 0; + + ret = vmbus_sendpacket(input_device->device->channel, + &ack, + sizeof(struct pipe_prt_msg) - sizeof(unsigned char) + + sizeof(struct synthhid_device_info_ack), + (unsigned long)&ack, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + + if (!ret) + input_device->dev_info_status = 0; + +cleanup: + complete(&input_device->wait_event); + + return; +} + +static void mousevsc_on_receive(struct hv_device *device, + struct vmpacket_descriptor *packet) +{ + struct pipe_prt_msg *pipe_msg; + struct synthhid_msg *hid_msg; + struct mousevsc_dev *input_dev = hv_get_drvdata(device); + struct synthhid_input_report *input_report; + + pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet + + (packet->offset8 << 3)); + + if (pipe_msg->type != PIPE_MESSAGE_DATA) + return; + + hid_msg = (struct synthhid_msg *)pipe_msg->data; + + switch (hid_msg->header.type) { + case SYNTH_HID_PROTOCOL_RESPONSE: + /* + * While it will be impossible for us to protect against + * malicious/buggy hypervisor/host, add a check here to + * ensure we don't corrupt memory. + */ + if ((pipe_msg->size + sizeof(struct pipe_prt_msg) + - sizeof(unsigned char)) + > sizeof(struct mousevsc_prt_msg)) { + WARN_ON(1); + break; + } + + memcpy(&input_dev->protocol_resp, pipe_msg, + pipe_msg->size + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char)); + complete(&input_dev->wait_event); + break; + + case SYNTH_HID_INITIAL_DEVICE_INFO: + WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info)); + + /* + * Parse out the device info into device attr, + * hid desc and report desc + */ + mousevsc_on_receive_device_info(input_dev, + (struct synthhid_device_info *)pipe_msg->data); + break; + case SYNTH_HID_INPUT_REPORT: + input_report = + (struct synthhid_input_report *)pipe_msg->data; + if (!input_dev->init_complete) + break; + hid_input_report(input_dev->hid_device, + HID_INPUT_REPORT, input_report->buffer, + input_report->header.size, 1); + break; + default: + pr_err("unsupported hid msg type - type %d len %d", + hid_msg->header.type, hid_msg->header.size); + break; + } + +} + +static void mousevsc_on_channel_callback(void *context) +{ + const int packet_size = 0x100; + int ret; + struct hv_device *device = context; + u32 bytes_recvd; + u64 req_id; + struct vmpacket_descriptor *desc; + unsigned char *buffer; + int bufferlen = packet_size; + + buffer = kmalloc(bufferlen, GFP_ATOMIC); + if (!buffer) + return; + + do { + ret = vmbus_recvpacket_raw(device->channel, buffer, + bufferlen, &bytes_recvd, &req_id); + + switch (ret) { + case 0: + if (bytes_recvd <= 0) { + kfree(buffer); + return; + } + desc = (struct vmpacket_descriptor *)buffer; + + switch (desc->type) { + case VM_PKT_COMP: + break; + + case VM_PKT_DATA_INBAND: + mousevsc_on_receive(device, desc); + break; + + default: + pr_err("unhandled packet type %d, tid %llx len %d\n", + desc->type, req_id, bytes_recvd); + break; + } + + break; + + case -ENOBUFS: + kfree(buffer); + /* Handle large packet */ + bufferlen = bytes_recvd; + buffer = kmalloc(bytes_recvd, GFP_ATOMIC); + + if (!buffer) + return; + + break; + } + } while (1); + +} + +static int mousevsc_connect_to_vsp(struct hv_device *device) +{ + int ret = 0; + int t; + struct mousevsc_dev *input_dev = hv_get_drvdata(device); + struct mousevsc_prt_msg *request; + struct mousevsc_prt_msg *response; + + request = &input_dev->protocol_req; + memset(request, 0, sizeof(struct mousevsc_prt_msg)); + + request->type = PIPE_MESSAGE_DATA; + request->size = sizeof(struct synthhid_protocol_request); + request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST; + request->request.header.size = sizeof(unsigned int); + request->request.version_requested.version = SYNTHHID_INPUT_VERSION; + + ret = vmbus_sendpacket(device->channel, request, + sizeof(struct pipe_prt_msg) - + sizeof(unsigned char) + + sizeof(struct synthhid_protocol_request), + (unsigned long)request, + VM_PKT_DATA_INBAND, + VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); + if (ret) + goto cleanup; + + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (!t) { + ret = -ETIMEDOUT; + goto cleanup; + } + + response = &input_dev->protocol_resp; + + if (!response->response.approved) { + pr_err("synthhid protocol request failed (version %d)\n", + SYNTHHID_INPUT_VERSION); + ret = -ENODEV; + goto cleanup; + } + + t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ); + if (!t) { + ret = -ETIMEDOUT; + goto cleanup; + } + + /* + * We should have gotten the device attr, hid desc and report + * desc at this point + */ + ret = input_dev->dev_info_status; + +cleanup: + return ret; +} + +static int mousevsc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static int mousevsc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void mousevsc_hid_close(struct hid_device *hid) +{ +} + +static void mousevsc_hid_stop(struct hid_device *hid) +{ +} + +static struct hid_ll_driver mousevsc_ll_driver = { + .open = mousevsc_hid_open, + .close = mousevsc_hid_close, + .start = mousevsc_hid_start, + .stop = mousevsc_hid_stop, +}; + +static struct hid_driver mousevsc_hid_driver; + +static int mousevsc_probe(struct hv_device *device, + const struct hv_vmbus_device_id *dev_id) +{ + int ret; + struct mousevsc_dev *input_dev; + struct hid_device *hid_dev; + + input_dev = mousevsc_alloc_device(device); + + if (!input_dev) + return -ENOMEM; + + ret = vmbus_open(device->channel, + INPUTVSC_SEND_RING_BUFFER_SIZE, + INPUTVSC_RECV_RING_BUFFER_SIZE, + NULL, + 0, + mousevsc_on_channel_callback, + device + ); + + if (ret) + goto probe_err0; + + ret = mousevsc_connect_to_vsp(device); + + if (ret) + goto probe_err1; + + /* workaround SA-167 */ + if (input_dev->report_desc[14] == 0x25) + input_dev->report_desc[14] = 0x29; + + hid_dev = hid_allocate_device(); + if (IS_ERR(hid_dev)) { + ret = PTR_ERR(hid_dev); + goto probe_err1; + } + + hid_dev->ll_driver = &mousevsc_ll_driver; + hid_dev->driver = &mousevsc_hid_driver; + hid_dev->bus = BUS_VIRTUAL; + hid_dev->vendor = input_dev->hid_dev_info.vendor; + hid_dev->product = input_dev->hid_dev_info.product; + hid_dev->version = input_dev->hid_dev_info.version; + input_dev->hid_device = hid_dev; + + sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + + ret = hid_add_device(hid_dev); + if (ret) + goto probe_err1; + + ret = hid_parse_report(hid_dev, input_dev->report_desc, + input_dev->report_desc_size); + + if (ret) { + hid_err(hid_dev, "parse failed\n"); + goto probe_err2; + } + + ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); + + if (ret) { + hid_err(hid_dev, "hw start failed\n"); + goto probe_err2; + } + + input_dev->connected = true; + input_dev->init_complete = true; + + return ret; + +probe_err2: + hid_destroy_device(hid_dev); + +probe_err1: + vmbus_close(device->channel); + +probe_err0: + mousevsc_free_device(input_dev); + + return ret; +} + + +static int mousevsc_remove(struct hv_device *dev) +{ + struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + + vmbus_close(dev->channel); + hid_destroy_device(input_dev->hid_device); + mousevsc_free_device(input_dev); + + return 0; +} + +static const struct hv_vmbus_device_id id_table[] = { + /* Mouse guid */ + { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, + 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) }, + { }, +}; + +MODULE_DEVICE_TABLE(vmbus, id_table); + +static struct hv_driver mousevsc_drv = { + .name = KBUILD_MODNAME, + .id_table = id_table, + .probe = mousevsc_probe, + .remove = mousevsc_remove, +}; + +static int __init mousevsc_init(void) +{ + return vmbus_driver_register(&mousevsc_drv); +} + +static void __exit mousevsc_exit(void) +{ + vmbus_driver_unregister(&mousevsc_drv); +} + +MODULE_LICENSE("GPL"); +MODULE_VERSION(HV_DRV_VERSION); +module_init(mousevsc_init); +module_exit(mousevsc_exit); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 06ce996b8b65..448e9d89f8d0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -21,6 +21,7 @@ #define USB_VENDOR_ID_3M 0x0596 #define USB_DEVICE_ID_3M1968 0x0500 #define USB_DEVICE_ID_3M2256 0x0502 +#define USB_DEVICE_ID_3M3266 0x0506 #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 @@ -145,6 +146,9 @@ #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 +#define USB_VENDOR_ID_ATMEL 0x03eb +#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c + #define USB_VENDOR_ID_AVERMEDIA 0x07ca #define USB_DEVICE_ID_AVER_FM_MR800 0xb800 @@ -230,11 +234,14 @@ #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 0x72a1 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 0x480e -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 0x726b +#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_720C 0x720c +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 @@ -266,7 +273,7 @@ #define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002 #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc -#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0001 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003 #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 @@ -356,6 +363,9 @@ #define USB_VENDOR_ID_HANVON 0x20b3 #define USB_DEVICE_ID_HANVON_MULTITOUCH 0x0a18 +#define USB_VENDOR_ID_HANVON_ALT 0x22ed +#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH 0x1010 + #define USB_VENDOR_ID_HAPP 0x078b #define USB_DEVICE_ID_UGCI_DRIVING 0x0010 #define USB_DEVICE_ID_UGCI_FLYING 0x0020 @@ -571,6 +581,11 @@ #define USB_VENDOR_ID_PI_ENGINEERING 0x05f3 #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff +#define USB_VENDOR_ID_PIXART 0x093a +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002 +#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003 + #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 @@ -581,11 +596,14 @@ #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 #define USB_VENDOR_ID_QUANTA 0x0408 -#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 +#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 @@ -679,6 +697,7 @@ #define USB_VENDOR_ID_WACOM 0x056a #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81 +#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD #define USB_VENDOR_ID_WALTOP 0x172f #define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH 0x0032 @@ -707,6 +726,17 @@ #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 +#define USB_VENDOR_ID_XIROKU 0x1477 +#define USB_DEVICE_ID_XIROKU_SPX 0x1006 +#define USB_DEVICE_ID_XIROKU_MPX 0x1007 +#define USB_DEVICE_ID_XIROKU_CSR 0x100e +#define USB_DEVICE_ID_XIROKU_SPX1 0x1021 +#define USB_DEVICE_ID_XIROKU_CSR1 0x1022 +#define USB_DEVICE_ID_XIROKU_MPX1 0x1023 +#define USB_DEVICE_ID_XIROKU_SPX2 0x1024 +#define USB_DEVICE_ID_XIROKU_CSR2 0x1025 +#define USB_DEVICE_ID_XIROKU_MPX2 0x1026 + #define USB_VENDOR_ID_YEALINK 0x6993 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f333139d1a48..c6ee632bfd68 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -271,6 +271,97 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) return logical_extents / physical_extents; } +#ifdef CONFIG_HID_BATTERY_STRENGTH +static enum power_supply_property hidinput_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +static int hidinput_get_battery_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct hid_device *dev = container_of(psy, struct hid_device, battery); + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + if (dev->battery_min < dev->battery_max && + dev->battery_val >= dev->battery_min && + dev->battery_val <= dev->battery_max) + val->intval = (100 * (dev->battery_val - dev->battery_min)) / + (dev->battery_max - dev->battery_min); + else + ret = -EINVAL; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = dev->name; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ + struct power_supply *battery = &dev->battery; + int ret; + + if (battery->name != NULL) + return; /* already initialized? */ + + battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); + if (battery->name == NULL) + return; + + battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->properties = hidinput_battery_props; + battery->num_properties = ARRAY_SIZE(hidinput_battery_props); + battery->use_for_apm = 0; + battery->get_property = hidinput_get_battery_property; + + dev->battery_min = min; + dev->battery_max = max; + + ret = power_supply_register(&dev->dev, battery); + if (ret != 0) { + hid_warn(dev, "can't register power supply: %d\n", ret); + kfree(battery->name); + battery->name = NULL; + } +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ + if (!dev->battery.name) + return; + + power_supply_unregister(&dev->battery); + kfree(dev->battery.name); + dev->battery.name = NULL; +} +#else /* !CONFIG_HID_BATTERY_STRENGTH */ +static void hidinput_setup_battery(struct hid_device *dev, s32 min, s32 max) +{ +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ +} +#endif /* CONFIG_HID_BATTERY_STRENGTH */ + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { @@ -629,6 +720,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case HID_UP_GENDEVCTRLS: + if ((usage->hid & HID_USAGE) == 0x20) { /* Battery Strength */ + hidinput_setup_battery(device, + field->logical_minimum, + field->logical_maximum); + goto ignore; + } else + goto unknown; + break; + case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); switch (usage->hid & HID_USAGE) { @@ -760,6 +861,14 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input = field->hidinput->input; +#ifdef CONFIG_HID_BATTERY_STRENGTH + if (usage->hid == HID_DC_BATTERYSTRENGTH) { + hid->battery_val = value; + hid_dbg(hid, "battery value is %d (range %d-%d)\n", + value, hid->battery_min, hid->battery_max); + return; + } +#endif if (!usage->type) return; @@ -822,6 +931,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* Ignore out-of-range values as per HID specification, section 5.10 */ + if (value < field->logical_minimum || value > field->logical_maximum) { + dbg_hid("Ignoring out-of-range value %x\n", value); + return; + } + /* report the usage code as scancode if the key status has changed */ if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) input_event(input, EV_MSC, MSC_SCAN, usage->hid); @@ -861,6 +976,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int } EXPORT_SYMBOL_GPL(hidinput_find_field); +struct hid_field *hidinput_get_led_field(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED) + return field; + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(hidinput_get_led_field); + +unsigned int hidinput_count_leds(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + unsigned int count = 0; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED && + field->value[j]) + count += 1; + } + } + return count; +} +EXPORT_SYMBOL_GPL(hidinput_count_leds); + static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); @@ -1010,6 +1167,8 @@ void hidinput_disconnect(struct hid_device *hid) { struct hid_input *hidinput, *next; + hidinput_cleanup_battery(hid); + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { list_del(&hidinput->list); input_unregister_device(hidinput->input); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 103f30d93f76..6ecc9e220440 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -430,7 +430,7 @@ int lg4ff_init(struct hid_device *hid) } /* Add the device to device_list */ - entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); + entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); if (!entry) { hid_err(hid, "Cannot add device, insufficient memory.\n"); return -ENOMEM; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f1c909f1b239..24fc4423b937 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -50,7 +50,6 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_ALWAYS_VALID (1 << 4) #define MT_QUIRK_VALID_IS_INRANGE (1 << 5) #define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) -#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7) #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) struct mt_slot { @@ -60,9 +59,19 @@ struct mt_slot { bool seen_in_this_frame;/* has this slot been updated */ }; +struct mt_class { + __s32 name; /* MT_CLS */ + __s32 quirks; + __s32 sn_move; /* Signal/noise ratio for move events */ + __s32 sn_width; /* Signal/noise ratio for width events */ + __s32 sn_height; /* Signal/noise ratio for height events */ + __s32 sn_pressure; /* Signal/noise ratio for pressure events */ + __u8 maxcontacts; +}; + struct mt_device { struct mt_slot curdata; /* placeholder of incoming data */ - struct mt_class *mtclass; /* our mt device class */ + struct mt_class mtclass; /* our mt device class */ unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ int last_mt_collection; /* last known mt-related collection */ @@ -74,30 +83,23 @@ struct mt_device { struct mt_slot *slots; }; -struct mt_class { - __s32 name; /* MT_CLS */ - __s32 quirks; - __s32 sn_move; /* Signal/noise ratio for move events */ - __s32 sn_width; /* Signal/noise ratio for width events */ - __s32 sn_height; /* Signal/noise ratio for height events */ - __s32 sn_pressure; /* Signal/noise ratio for pressure events */ - __u8 maxcontacts; -}; - /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 #define MT_CLS_SERIAL 0x0002 #define MT_CLS_CONFIDENCE 0x0003 -#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004 -#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005 -#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006 -#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007 +#define MT_CLS_CONFIDENCE_CONTACT_ID 0x0004 +#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005 +#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 +#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 +#define MT_CLS_INRANGE_CONTACTNUMBER 0x0009 /* vendor specific classes */ #define MT_CLS_3M 0x0101 #define MT_CLS_CYPRESS 0x0102 #define MT_CLS_EGALAX 0x0103 +#define MT_CLS_EGALAX_SERIAL 0x0104 #define MT_DEFAULT_MAXCONTACT 10 @@ -133,13 +135,16 @@ static int find_slot_from_contactid(struct mt_device *td) return -1; } -struct mt_class mt_classes[] = { +static struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, { .name = MT_CLS_SERIAL, .quirks = MT_QUIRK_ALWAYS_VALID}, { .name = MT_CLS_CONFIDENCE, .quirks = MT_QUIRK_VALID_IS_CONFIDENCE }, + { .name = MT_CLS_CONFIDENCE_CONTACT_ID, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID }, { .name = MT_CLS_CONFIDENCE_MINUS_ONE, .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE }, @@ -155,6 +160,9 @@ struct mt_class mt_classes[] = { .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, + { .name = MT_CLS_INRANGE_CONTACTNUMBER, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER }, /* * vendor specific classes @@ -171,9 +179,13 @@ struct mt_class mt_classes[] = { .maxcontacts = 10 }, { .name = MT_CLS_EGALAX, .quirks = MT_QUIRK_SLOT_IS_CONTACTID | - MT_QUIRK_VALID_IS_INRANGE | - MT_QUIRK_EGALAX_XYZ_FIXUP, - .maxcontacts = 2, + MT_QUIRK_VALID_IS_INRANGE, + .sn_move = 4096, + .sn_pressure = 32, + }, + { .name = MT_CLS_EGALAX_SERIAL, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_ALWAYS_VALID, .sn_move = 4096, .sn_pressure = 32, }, @@ -181,6 +193,44 @@ struct mt_class mt_classes[] = { { } }; +static ssize_t mt_show_quirks(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct mt_device *td = hid_get_drvdata(hdev); + + return sprintf(buf, "%u\n", td->mtclass.quirks); +} + +static ssize_t mt_set_quirks(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct mt_device *td = hid_get_drvdata(hdev); + + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + td->mtclass.quirks = val; + + return count; +} + +static DEVICE_ATTR(quirks, S_IWUSR | S_IRUGO, mt_show_quirks, mt_set_quirks); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_quirks.attr, + NULL +}; + +static struct attribute_group mt_attribute_group = { + .attrs = sysfs_attrs +}; + static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -192,9 +242,9 @@ static void mt_feature_mapping(struct hid_device *hdev, break; case HID_DG_CONTACTMAX: td->maxcontacts = field->value[0]; - if (td->mtclass->maxcontacts) + if (td->mtclass.maxcontacts) /* check if the maxcontacts is given by the class */ - td->maxcontacts = td->mtclass->maxcontacts; + td->maxcontacts = td->mtclass.maxcontacts; break; } @@ -214,8 +264,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, unsigned long **bit, int *max) { struct mt_device *td = hid_get_drvdata(hdev); - struct mt_class *cls = td->mtclass; - __s32 quirks = cls->quirks; + struct mt_class *cls = &td->mtclass; /* Only map fields from TouchScreen or TouchPad collections. * We need to ignore fields that belong to other collections @@ -227,13 +276,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, else return 0; + /* eGalax devices provide a Digitizer.Stylus input which overrides + * the correct Digitizers.Finger X/Y ranges. + * Let's just ignore this input. */ + if (field->physical == HID_DG_STYLUS) + return -1; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); set_abs(hi->input, ABS_MT_POSITION_X, field, @@ -246,8 +299,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } return 1; case HID_GD_Y: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_maximum = 32760; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); set_abs(hi->input, ABS_MT_POSITION_Y, field, @@ -315,8 +366,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } return 1; case HID_DG_TIPPRESSURE: - if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) - field->logical_minimum = 0; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); set_abs(hi->input, ABS_MT_PRESSURE, field, @@ -363,7 +412,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int mt_compute_slot(struct mt_device *td) { - __s32 quirks = td->mtclass->quirks; + __s32 quirks = td->mtclass.quirks; if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) return td->curdata.contactid; @@ -407,7 +456,7 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input) for (i = 0; i < td->maxcontacts; ++i) { struct mt_slot *s = &(td->slots[i]); - if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && + if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && !s->seen_in_this_frame) { s->touch_state = false; } @@ -444,7 +493,7 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct mt_device *td = hid_get_drvdata(hid); - __s32 quirks = td->mtclass->quirks; + __s32 quirks = td->mtclass.quirks; if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { switch (usage->hid) { @@ -552,7 +601,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) dev_err(&hdev->dev, "cannot allocate multitouch data\n"); return -ENOMEM; } - td->mtclass = mtclass; + td->mtclass = *mtclass; td->inputmode = -1; td->last_mt_collection = -1; hid_set_drvdata(hdev, td); @@ -574,6 +623,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) goto fail; } + ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); + mt_set_input_mode(hdev); return 0; @@ -594,6 +645,7 @@ static int mt_reset_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); kfree(td->slots); kfree(td); @@ -609,12 +661,20 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_3M, HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, + { .driver_data = MT_CLS_3M, + HID_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M3266) }, /* ActionStar panels */ { .driver_data = MT_CLS_DEFAULT, HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, + /* Atmel panels */ + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MULTITOUCH) }, + /* Cando panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, HID_USB_DEVICE(USB_VENDOR_ID_CANDO, @@ -645,23 +705,32 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, /* eGalax devices (resistive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, - { .driver_data = MT_CLS_EGALAX, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, @@ -678,6 +747,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, + /* Hanvon panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, + USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, + /* Ideacom panel */ { .driver_data = MT_CLS_SERIAL, HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, @@ -722,6 +796,17 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, + /* PixArt optical touch screen */ + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, + /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, @@ -730,6 +815,17 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, + /* Quanta-based panels */ + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) }, + /* Stantum panels */ { .driver_data = MT_CLS_CONFIDENCE, HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, @@ -758,6 +854,35 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, + /* Xiroku */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR1) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX2) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX2) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR2) }, + { } }; MODULE_DEVICE_TABLE(hid, mt_devices); diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 1782693819f3..12f9777c385d 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -36,6 +36,7 @@ #include <linux/completion.h> #include <linux/uaccess.h> +#include <linux/module.h> #define PICOLCD_NAME "PicoLCD (graphic)" @@ -632,7 +633,7 @@ struct picolcd_fb_cleanup_item { struct picolcd_fb_cleanup_item *next; }; static struct picolcd_fb_cleanup_item *fb_pending; -DEFINE_SPINLOCK(fb_pending_lock); +static DEFINE_SPINLOCK(fb_pending_lock); static void picolcd_fb_do_cleanup(struct work_struct *data) { @@ -657,7 +658,7 @@ static void picolcd_fb_do_cleanup(struct work_struct *data) } while (item); } -DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); +static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); static int picolcd_fb_open(struct fb_info *info, int u) { diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 06e5300d43d2..070f93a5c11b 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -40,6 +40,7 @@ #include <linux/input.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/usb.h> #include <linux/hid.h> diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c deleted file mode 100644 index 87a54df4d4ac..000000000000 --- a/drivers/hid/hid-quanta.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * HID driver for Quanta Optical Touch dual-touch panels - * - * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> - * - */ - -/* - * 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 <linux/slab.h> - -MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); -MODULE_DESCRIPTION("Quanta dual-touch panel"); -MODULE_LICENSE("GPL"); - -#include "hid-ids.h" - -struct quanta_data { - __u16 x, y; - __u8 id; - bool valid; /* valid finger data, or just placeholder? */ - bool first; /* is this the first finger in this frame? */ - bool activity_now; /* at least one active finger in this frame? */ - bool activity; /* at least one active finger previously? */ -}; - -static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - switch (usage->hid & HID_USAGE_PAGE) { - - case HID_UP_GENDESK: - switch (usage->hid) { - case HID_GD_X: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_X); - /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_X, - field->logical_minimum, - field->logical_maximum, 0, 0); - return 1; - case HID_GD_Y: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_POSITION_Y); - /* touchscreen emulation */ - input_set_abs_params(hi->input, ABS_Y, - field->logical_minimum, - field->logical_maximum, 0, 0); - return 1; - } - return 0; - - case HID_UP_DIGITIZER: - switch (usage->hid) { - case HID_DG_CONFIDENCE: - case HID_DG_TIPSWITCH: - case HID_DG_INPUTMODE: - case HID_DG_DEVICEINDEX: - case HID_DG_CONTACTCOUNT: - case HID_DG_CONTACTMAX: - case HID_DG_TIPPRESSURE: - case HID_DG_WIDTH: - case HID_DG_HEIGHT: - return -1; - case HID_DG_INRANGE: - /* touchscreen emulation */ - hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); - return 1; - case HID_DG_CONTACTID: - hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TRACKING_ID); - return 1; - } - return 0; - - case 0xff000000: - /* ignore vendor-specific features */ - return -1; - } - - return 0; -} - -static int quanta_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 || usage->type == EV_ABS) - clear_bit(usage->code, *bit); - - return 0; -} - -/* - * this function is called when a whole finger has been parsed, - * so that it can decide what to send to the input layer. - */ -static void quanta_filter_event(struct quanta_data *td, struct input_dev *input) -{ - - td->first = !td->first; /* touchscreen emulation */ - - if (!td->valid) { - /* - * touchscreen emulation: if no finger in this frame is valid - * and there previously was finger activity, this is a release - */ - if (!td->first && !td->activity_now && td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 0); - td->activity = false; - } - return; - } - - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); - - input_mt_sync(input); - td->valid = false; - - /* touchscreen emulation: if first active finger in this frame... */ - if (!td->activity_now) { - /* if there was no previous activity, emit touch event */ - if (!td->activity) { - input_event(input, EV_KEY, BTN_TOUCH, 1); - td->activity = true; - } - td->activity_now = true; - /* and in any case this is our preferred finger */ - input_event(input, EV_ABS, ABS_X, td->x); - input_event(input, EV_ABS, ABS_Y, td->y); - } -} - - -static int quanta_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - struct quanta_data *td = hid_get_drvdata(hid); - - if (hid->claimed & HID_CLAIMED_INPUT) { - struct input_dev *input = field->hidinput->input; - - switch (usage->hid) { - case HID_DG_INRANGE: - td->valid = !!value; - break; - case HID_GD_X: - td->x = value; - break; - case HID_GD_Y: - td->y = value; - quanta_filter_event(td, input); - break; - case HID_DG_CONTACTID: - td->id = value; - break; - case HID_DG_CONTACTCOUNT: - /* touch emulation: this is the last field in a frame */ - td->first = false; - td->activity_now = false; - break; - case HID_DG_CONFIDENCE: - case HID_DG_TIPSWITCH: - /* avoid interference from generic hidinput handling */ - break; - - default: - /* fallback to the generic hidinput handling */ - return 0; - } - } - - /* we have handled the hidinput part, now remains hiddev */ - if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); - - return 1; -} - -static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - struct quanta_data *td; - - td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL); - if (!td) { - hid_err(hdev, "cannot allocate Quanta Touch data\n"); - return -ENOMEM; - } - td->valid = false; - td->activity = false; - td->activity_now = false; - td->first = false; - hid_set_drvdata(hdev, td); - - ret = hid_parse(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - - if (ret) - kfree(td); - - return ret; -} - -static void quanta_remove(struct hid_device *hdev) -{ - hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); - hid_set_drvdata(hdev, NULL); -} - -static const struct hid_device_id quanta_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, - USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, - { } -}; -MODULE_DEVICE_TABLE(hid, quanta_devices); - -static const struct hid_usage_id quanta_grabbed_usages[] = { - { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} -}; - -static struct hid_driver quanta_driver = { - .name = "quanta-touch", - .id_table = quanta_devices, - .probe = quanta_probe, - .remove = quanta_remove, - .input_mapping = quanta_input_mapping, - .input_mapped = quanta_input_mapped, - .usage_table = quanta_grabbed_usages, - .event = quanta_event, -}; - -static int __init quanta_init(void) -{ - return hid_register_driver(&quanta_driver); -} - -static void __exit quanta_exit(void) -{ - hid_unregister_driver(&quanta_driver); -} - -module_init(quanta_init); -module_exit(quanta_exit); - diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index edf898dee28b..a6d93992c75a 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -13,6 +13,7 @@ #include <linux/hid.h> #include <linux/slab.h> +#include <linux/module.h> #include "hid-roccat-common.h" static inline uint16_t roccat_common_feature_report(uint8_t report_id) @@ -48,12 +49,10 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id, char *buf; int len; - buf = kmalloc(size, GFP_KERNEL); + buf = kmemdup(data, size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - memcpy(buf, data, size); - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c new file mode 100644 index 000000000000..0e4a0ab47142 --- /dev/null +++ b/drivers/hid/hid-roccat-isku.c @@ -0,0 +1,487 @@ +/* + * Roccat Isku driver for Linux + * + * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * 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 Isku is a gamer keyboard with macro keys that can be configured in + * 5 profiles. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-isku.h" + +static struct class *isku_class; + +static void isku_profile_activated(struct isku_device *isku, uint new_profile) +{ + isku->actual_profile = new_profile; +} + +static int isku_receive(struct usb_device *usb_dev, uint command, + void *buf, uint size) +{ + return roccat_common_receive(usb_dev, command, buf, size); +} + +static int isku_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct isku_control control; + + do { + msleep(50); + retval = isku_receive(usb_dev, ISKU_COMMAND_CONTROL, + &control, sizeof(struct isku_control)); + + if (retval) + return retval; + + switch (control.value) { + case ISKU_CONTROL_VALUE_STATUS_OK: + return 0; + case ISKU_CONTROL_VALUE_STATUS_WAIT: + continue; + case ISKU_CONTROL_VALUE_STATUS_INVALID: + /* seems to be critical - replug necessary */ + case ISKU_CONTROL_VALUE_STATUS_OVERLOAD: + return -EINVAL; + default: + hid_err(usb_dev, "isku_receive_control_status: " + "unknown response value 0x%x\n", + control.value); + return -EINVAL; + } + + } while (1); +} + +static int isku_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + return isku_receive_control_status(usb_dev); +} + +static int isku_get_actual_profile(struct usb_device *usb_dev) +{ + struct isku_actual_profile buf; + int retval; + + retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct isku_actual_profile)); + return retval ? retval : buf.actual_profile; +} + +static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile) +{ + struct isku_actual_profile buf; + + buf.command = ISKU_COMMAND_ACTUAL_PROFILE; + buf.size = sizeof(struct isku_actual_profile); + buf.actual_profile = new_profile; + return isku_send(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf, + sizeof(struct isku_actual_profile)); +} + +static ssize_t isku_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct isku_device *isku = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile); +} + +static ssize_t isku_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct isku_device *isku; + struct usb_device *usb_dev; + unsigned long profile; + int retval; + struct isku_roccat_report roccat_report; + + dev = dev->parent->parent; + isku = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + return retval; + + if (profile > 4) + return -EINVAL; + + mutex_lock(&isku->isku_lock); + + retval = isku_set_actual_profile(usb_dev, profile); + if (retval) { + mutex_unlock(&isku->isku_lock); + return retval; + } + + isku_profile_activated(isku, profile); + + roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE; + roccat_report.data1 = profile + 1; + roccat_report.data2 = 0; + roccat_report.profile = profile + 1; + roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report); + + mutex_unlock(&isku->isku_lock); + + return size; +} + +static struct device_attribute isku_attributes[] = { + __ATTR(actual_profile, 0660, + isku_sysfs_show_actual_profile, + isku_sysfs_set_actual_profile), + __ATTR_NULL +}; + +static ssize_t isku_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 isku_device *isku = 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(&isku->isku_lock); + retval = isku_receive(usb_dev, command, buf, real_size); + mutex_unlock(&isku->isku_lock); + + return retval ? retval : real_size; +} + +static ssize_t isku_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 isku_device *isku = 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(&isku->isku_lock); + retval = isku_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&isku->isku_lock); + + return retval ? retval : real_size; +} + +#define ISKU_SYSFS_W(thingy, THINGY) \ +static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \ + struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return isku_sysfs_write(fp, kobj, buf, off, count, \ + sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \ +} + +#define ISKU_SYSFS_R(thingy, THINGY) \ +static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \ + struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return isku_sysfs_read(fp, kobj, buf, off, count, \ + sizeof(struct isku_ ## 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) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .read = isku_sysfs_read_ ## thingy, \ + .write = isku_sysfs_write_ ## thingy \ +} + +#define ISKU_BIN_ATTR_R(thingy) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .read = isku_sysfs_read_ ## thingy, \ +} + +#define ISKU_BIN_ATTR_W(thingy) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = sizeof(struct isku_ ## thingy), \ + .write = isku_sysfs_write_ ## thingy \ +} + +ISKU_SYSFS_RW(macro, MACRO) +ISKU_SYSFS_RW(keys_function, KEYS_FUNCTION) +ISKU_SYSFS_RW(keys_easyzone, KEYS_EASYZONE) +ISKU_SYSFS_RW(keys_media, KEYS_MEDIA) +ISKU_SYSFS_RW(keys_thumbster, KEYS_THUMBSTER) +ISKU_SYSFS_RW(keys_macro, KEYS_MACRO) +ISKU_SYSFS_RW(keys_capslock, KEYS_CAPSLOCK) +ISKU_SYSFS_RW(light, LIGHT) +ISKU_SYSFS_RW(key_mask, KEY_MASK) +ISKU_SYSFS_RW(last_set, LAST_SET) +ISKU_SYSFS_W(talk, TALK) +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), + __ATTR_NULL +}; + +static int isku_init_isku_device_struct(struct usb_device *usb_dev, + struct isku_device *isku) +{ + int retval; + + mutex_init(&isku->isku_lock); + + retval = isku_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + isku_profile_activated(isku, retval); + + return 0; +} + +static int isku_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 isku_device *isku; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + isku = kzalloc(sizeof(*isku), GFP_KERNEL); + if (!isku) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, isku); + + retval = isku_init_isku_device_struct(usb_dev, isku); + if (retval) { + hid_err(hdev, "couldn't init struct isku_device\n"); + goto exit_free; + } + + retval = roccat_connect(isku_class, hdev, + sizeof(struct isku_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + isku->chrdev_minor = retval; + isku->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(isku); + return retval; +} + +static void isku_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct isku_device *isku; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) + return; + + isku = hid_get_drvdata(hdev); + if (isku->roccat_claimed) + roccat_disconnect(isku->chrdev_minor); + kfree(isku); +} + +static int isku_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 = isku_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install keyboard\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void isku_remove(struct hid_device *hdev) +{ + isku_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void isku_keep_values_up_to_date(struct isku_device *isku, + u8 const *data) +{ + struct isku_report_button const *button_report; + + switch (data[0]) { + case ISKU_REPORT_NUMBER_BUTTON: + button_report = (struct isku_report_button const *)data; + switch (button_report->event) { + case ISKU_REPORT_BUTTON_EVENT_PROFILE: + isku_profile_activated(isku, button_report->data1 - 1); + break; + } + break; + } +} + +static void isku_report_to_chrdev(struct isku_device const *isku, + u8 const *data) +{ + struct isku_roccat_report roccat_report; + struct isku_report_button const *button_report; + + if (data[0] != ISKU_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct isku_report_button const *)data; + + roccat_report.event = button_report->event; + roccat_report.data1 = button_report->data1; + roccat_report.data2 = button_report->data2; + roccat_report.profile = isku->actual_profile + 1; + roccat_report_event(isku->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int isku_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct isku_device *isku = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != ISKU_USB_INTERFACE_PROTOCOL) + return 0; + + if (isku == NULL) + return 0; + + isku_keep_values_up_to_date(isku, data); + + if (isku->roccat_claimed) + isku_report_to_chrdev(isku, data); + + return 0; +} + +static const struct hid_device_id isku_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, isku_devices); + +static struct hid_driver isku_driver = { + .name = "isku", + .id_table = isku_devices, + .probe = isku_probe, + .remove = isku_remove, + .raw_event = isku_raw_event +}; + +static int __init isku_init(void) +{ + int retval; + isku_class = class_create(THIS_MODULE, "isku"); + if (IS_ERR(isku_class)) + return PTR_ERR(isku_class); + isku_class->dev_attrs = isku_attributes; + isku_class->dev_bin_attrs = isku_bin_attributes; + + retval = hid_register_driver(&isku_driver); + if (retval) + class_destroy(isku_class); + return retval; +} + +static void __exit isku_exit(void) +{ + hid_unregister_driver(&isku_driver); + class_destroy(isku_class); +} + +module_init(isku_init); +module_exit(isku_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Isku driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h new file mode 100644 index 000000000000..075f6efaec58 --- /dev/null +++ b/drivers/hid/hid-roccat-isku.h @@ -0,0 +1,147 @@ +#ifndef __HID_ROCCAT_ISKU_H +#define __HID_ROCCAT_ISKU_H + +/* + * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * 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/types.h> + +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; + +enum isku_control_values { + ISKU_CONTROL_VALUE_STATUS_OVERLOAD = 0, + ISKU_CONTROL_VALUE_STATUS_OK = 1, + ISKU_CONTROL_VALUE_STATUS_INVALID = 2, + ISKU_CONTROL_VALUE_STATUS_WAIT = 3, +}; + +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, + ISKU_COMMAND_KEY_MASK = 0x7, + ISKU_COMMAND_KEYS_FUNCTION = 0x8, + ISKU_COMMAND_KEYS_EASYZONE = 0x9, + ISKU_COMMAND_KEYS_MEDIA = 0xa, + ISKU_COMMAND_KEYS_THUMBSTER = 0xb, + ISKU_COMMAND_KEYS_MACRO = 0xd, + ISKU_COMMAND_MACRO = 0xe, + ISKU_COMMAND_INFO = 0xf, + ISKU_COMMAND_LIGHT = 0x10, + ISKU_COMMAND_KEYS_CAPSLOCK = 0x13, + ISKU_COMMAND_LAST_SET = 0x14, + ISKU_COMMAND_15 = 0x15, + ISKU_COMMAND_TALK = 0x16, + ISKU_COMMAND_FIRMWARE_WRITE = 0x1b, + ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, +}; + +struct isku_report_button { + uint8_t number; /* ISKU_REPORT_NUMBER_BUTTON */ + uint8_t zero; + uint8_t event; + uint8_t data1; + uint8_t data2; +}; + +enum isku_report_numbers { + ISKU_REPORT_NUMBER_BUTTON = 3, +}; + +enum isku_report_button_events { + ISKU_REPORT_BUTTON_EVENT_PROFILE = 0x2, +}; + +struct isku_roccat_report { + uint8_t event; + uint8_t data1; + uint8_t data2; + uint8_t profile; +} __packed; + +struct isku_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex isku_lock; + + int actual_profile; +}; + +#endif diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index e2072afb34bb..40090d602158 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -78,12 +78,10 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command, char *buf; int len; - buf = kmalloc(size, GFP_KERNEL); + buf = kmemdup(data, size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - memcpy(buf, data, size); - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 56ce12c23b02..b685b04dbf9d 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -27,6 +27,7 @@ #include <linux/poll.h> #include <linux/sched.h> #include <linux/hid-roccat.h> +#include <linux/module.h> #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 670da9109f86..4b1448613ea6 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -30,6 +30,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/hid.h> +#include <linux/module.h> #include "hid-ids.h" #ifdef CONFIG_SMARTJOYPLUS_FF diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 575862b0688e..83a933b9c2e9 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -31,6 +31,7 @@ #include <linux/input.h> #include <linux/slab.h> #include <linux/usb.h> +#include <linux/module.h> #include "hid-ids.h" diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 17bb88f782b6..f2183486a9b6 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -9,6 +9,7 @@ * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> * Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru> * Copyright (c) 2009 Bastien Nocera <hadess@hadess.net> + * Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu> */ /* @@ -33,6 +34,7 @@ struct wacom_data { __u16 tool; unsigned char butstate; + __u8 features; unsigned char high_speed; #ifdef CONFIG_HID_WACOM_POWER_SUPPLY int battery_capacity; @@ -107,6 +109,19 @@ static int wacom_ac_get_property(struct power_supply *psy, } #endif +static void wacom_set_features(struct hid_device *hdev) +{ + int ret; + __u8 rep_data[2]; + + /*set high speed, tablet mode*/ + rep_data[0] = 0x03; + rep_data[1] = 0x20; + ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + HID_FEATURE_REPORT); + return; +} + static void wacom_poke(struct hid_device *hdev, u8 speed) { struct wacom_data *wdata = hid_get_drvdata(hdev); @@ -177,26 +192,13 @@ static ssize_t wacom_store_speed(struct device *dev, static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, wacom_show_speed, wacom_store_speed); -static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *raw_data, int size) +static int wacom_gr_parse_report(struct hid_device *hdev, + struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) { - struct wacom_data *wdata = hid_get_drvdata(hdev); - struct hid_input *hidinput; - struct input_dev *input; - unsigned char *data = (unsigned char *) raw_data; int tool, x, y, rw; - if (!(hdev->claimed & HID_CLAIMED_INPUT)) - return 0; - tool = 0; - hidinput = list_entry(hdev->inputs.next, struct hid_input, list); - input = hidinput->input; - - /* Check if this is a tablet report */ - if (data[0] != 0x03) - return 0; - /* Get X & Y positions */ x = le16_to_cpu(*(__le16 *) &data[2]); y = le16_to_cpu(*(__le16 *) &data[4]); @@ -304,6 +306,121 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, return 1; } +static void wacom_i4_parse_pen_report(struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) +{ + __u16 x, y, pressure; + __u32 id; + + switch (data[1]) { + case 0x80: /* Out of proximity report */ + wdata->tool = 0; + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_report_key(input, wdata->tool, 0); + input_sync(input); + break; + case 0xC2: /* Tool report */ + id = ((data[2] << 4) | (data[3] >> 4) | + ((data[7] & 0x0f) << 20) | + ((data[8] & 0xf0) << 12)) & 0xfffff; + + switch (id) { + case 0x802: + wdata->tool = BTN_TOOL_PEN; + break; + case 0x80A: + wdata->tool = BTN_TOOL_RUBBER; + break; + } + break; + default: /* Position/pressure report */ + x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1); + y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01); + pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5) + | (data[1] & 0x01); + + input_report_key(input, BTN_TOUCH, pressure > 1); + + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x04); + input_report_key(input, wdata->tool, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, pressure); + input_sync(input); + break; + } + + return; +} + +static void wacom_i4_parse_report(struct hid_device *hdev, + struct wacom_data *wdata, + struct input_dev *input, unsigned char *data) +{ + switch (data[0]) { + case 0x00: /* Empty report */ + break; + case 0x02: /* Pen report */ + wacom_i4_parse_pen_report(wdata, input, data); + break; + case 0x03: /* Features Report */ + wdata->features = data[2]; + break; + case 0x0C: /* Button report */ + break; + default: + hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]); + break; + } +} + +static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) +{ + struct wacom_data *wdata = hid_get_drvdata(hdev); + struct hid_input *hidinput; + struct input_dev *input; + unsigned char *data = (unsigned char *) raw_data; + int i; + + if (!(hdev->claimed & HID_CLAIMED_INPUT)) + return 0; + + hidinput = list_entry(hdev->inputs.next, struct hid_input, list); + input = hidinput->input; + + /* Check if this is a tablet report */ + if (data[0] != 0x03) + return 0; + + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + return wacom_gr_parse_report(hdev, wdata, input, data); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + i = 1; + + switch (data[0]) { + case 0x04: + wacom_i4_parse_report(hdev, wdata, input, data + i); + i += 10; + /* fall through */ + case 0x03: + wacom_i4_parse_report(hdev, wdata, input, data + i); + i += 10; + wacom_i4_parse_report(hdev, wdata, input, data + i); + break; + default: + hid_err(hdev, "Unknown report: %d,%d size:%d\n", + data[0], data[1], size); + return 0; + } + } + return 1; +} + static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -338,10 +455,19 @@ static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, __set_bit(BTN_TOOL_RUBBER, input->keybit); __set_bit(BTN_TOOL_MOUSE, input->keybit); - input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); - input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); - input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + input_set_abs_params(input, ABS_X, 0, 40640, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0); + break; + } return 0; } @@ -378,8 +504,16 @@ static int wacom_probe(struct hid_device *hdev, hid_warn(hdev, "can't create sysfs speed attribute err: %d\n", ret); - /* Set Wacom mode 2 with high reporting speed */ - wacom_poke(hdev, 1); + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + /* Set Wacom mode 2 with high reporting speed */ + wacom_poke(hdev, 1); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + wdata->features = 0; + wacom_set_features(hdev); + break; + } #ifdef CONFIG_HID_WACOM_POWER_SUPPLY wdata->battery.properties = wacom_battery_props; @@ -441,6 +575,7 @@ static void wacom_remove(struct hid_device *hdev) static const struct hid_device_id wacom_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, { } }; diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index f31fab012f2f..f6ba81df71bd 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -25,6 +25,7 @@ #include <linux/input.h> #include <linux/slab.h> #include <linux/usb.h> +#include <linux/module.h> #include "hid-ids.h" diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b403fcef0b86..5bf91dbad59d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -197,16 +197,24 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid) { struct hid_device *hid = usb_get_intfdata(usbhid->intf); int kicked; + int r; if (!hid) return 0; if ((kicked = (usbhid->outhead != usbhid->outtail))) { dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail); + + r = usb_autopm_get_interface_async(usbhid->intf); + if (r < 0) + return r; + /* Asynchronously flush queue. */ + set_bit(HID_OUT_RUNNING, &usbhid->iofl); if (hid_submit_out(hid)) { clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); + usb_autopm_put_interface_async(usbhid->intf); } + wake_up(&usbhid->wait); } return kicked; } @@ -215,6 +223,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) { struct hid_device *hid = usb_get_intfdata(usbhid->intf); int kicked; + int r; WARN_ON(hid == NULL); if (!hid) @@ -222,10 +231,17 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) { dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail); + + r = usb_autopm_get_interface_async(usbhid->intf); + if (r < 0) + return r; + /* Asynchronously flush queue. */ + set_bit(HID_CTRL_RUNNING, &usbhid->iofl); if (hid_submit_ctrl(hid)) { clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); + usb_autopm_put_interface_async(usbhid->intf); } + wake_up(&usbhid->wait); } return kicked; } @@ -304,30 +320,21 @@ static int hid_submit_out(struct hid_device *hid) report = usbhid->out[usbhid->outtail].report; raw_report = usbhid->out[usbhid->outtail].raw_report; - r = usb_autopm_get_interface_async(usbhid->intf); - if (r < 0) - return -1; - - /* - * if the device hasn't been woken, we leave the output - * to resume() - */ - if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); - usbhid->urbout->dev = hid_to_usb_dev(hid); - memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); - kfree(raw_report); + usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + + 1 + (report->id > 0); + usbhid->urbout->dev = hid_to_usb_dev(hid); + memcpy(usbhid->outbuf, raw_report, + usbhid->urbout->transfer_buffer_length); + kfree(raw_report); - dbg_hid("submitting out urb\n"); + dbg_hid("submitting out urb\n"); - if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - hid_err(hid, "usb_submit_urb(out) failed\n"); - usb_autopm_put_interface_async(usbhid->intf); - return -1; - } - usbhid->last_out = jiffies; + r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC); + if (r < 0) { + hid_err(hid, "usb_submit_urb(out) failed: %d\n", r); + return r; } - + usbhid->last_out = jiffies; return 0; } @@ -343,50 +350,48 @@ static int hid_submit_ctrl(struct hid_device *hid) raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; dir = usbhid->ctrl[usbhid->ctrltail].dir; - r = usb_autopm_get_interface_async(usbhid->intf); - if (r < 0) - return -1; - if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (dir == USB_DIR_OUT) { - usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; - memcpy(usbhid->ctrlbuf, raw_report, len); - kfree(raw_report); - } else { - int maxpacket, padlen; - - usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); - maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = DIV_ROUND_UP(len, maxpacket); - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; - } - usbhid->urbctrl->dev = hid_to_usb_dev(hid); - - usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; - usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; - usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); - usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); - usbhid->cr->wLength = cpu_to_le16(len); - - dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", - usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", - usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); - - if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - usb_autopm_put_interface_async(usbhid->intf); - hid_err(hid, "usb_submit_urb(ctrl) failed\n"); - return -1; - } - usbhid->last_ctrl = jiffies; + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (dir == USB_DIR_OUT) { + usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); + usbhid->urbctrl->transfer_buffer_length = len; + memcpy(usbhid->ctrlbuf, raw_report, len); + kfree(raw_report); + } else { + int maxpacket, padlen; + + usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); + maxpacket = usb_maxpacket(hid_to_usb_dev(hid), + usbhid->urbctrl->pipe, 0); + if (maxpacket > 0) { + padlen = DIV_ROUND_UP(len, maxpacket); + padlen *= maxpacket; + if (padlen > usbhid->bufsize) + padlen = usbhid->bufsize; + } else + padlen = 0; + usbhid->urbctrl->transfer_buffer_length = padlen; } - + usbhid->urbctrl->dev = hid_to_usb_dev(hid); + + usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; + usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : + HID_REQ_GET_REPORT; + usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | + report->id); + usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); + usbhid->cr->wLength = cpu_to_le16(len); + + dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", + usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : + "Get_Report", + usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); + + r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC); + if (r < 0) { + hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r); + return r; + } + usbhid->last_ctrl = jiffies; return 0; } @@ -423,11 +428,8 @@ static void hid_irq_out(struct urb *urb) else usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); - if (usbhid->outhead != usbhid->outtail) { - if (hid_submit_out(hid)) { - clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); - } + if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) { + /* Successfully submitted next urb in queue */ spin_unlock_irqrestore(&usbhid->lock, flags); return; } @@ -474,13 +476,9 @@ static void hid_ctrl(struct urb *urb) else usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); - if (usbhid->ctrlhead != usbhid->ctrltail) { - if (hid_submit_ctrl(hid)) { - clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - wake_up(&usbhid->wait); - } + if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) { + /* Successfully submitted next urb in queue */ spin_unlock(&usbhid->lock); - usb_autopm_put_interface_async(usbhid->intf); return; } @@ -515,9 +513,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; + /* Try to awake from autosuspend... */ + if (usb_autopm_get_interface_async(usbhid->intf) < 0) + return; + + /* + * But if still suspended, leave urb enqueued, don't submit. + * Submission will occur if/when resume() drains the queue. + */ + if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) + return; + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { - if (hid_submit_out(hid)) + if (hid_submit_out(hid)) { clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + usb_autopm_put_interface_async(usbhid->intf); + } + wake_up(&usbhid->wait); } else { /* * the queue is known to run @@ -549,9 +561,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; + /* Try to awake from autosuspend... */ + if (usb_autopm_get_interface_async(usbhid->intf) < 0) + return; + + /* + * If already suspended, leave urb enqueued, but don't submit. + * Submission will occur if/when resume() drains the queue. + */ + if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) + return; + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { - if (hid_submit_ctrl(hid)) + if (hid_submit_ctrl(hid)) { clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + usb_autopm_put_interface_async(usbhid->intf); + } + wake_up(&usbhid->wait); } else { /* * the queue is known to run @@ -576,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns } EXPORT_SYMBOL_GPL(usbhid_submit_report); +/* Workqueue routine to send requests to change LEDs */ +static void hid_led(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, led_work); + struct hid_device *hid = usbhid->hid; + struct hid_field *field; + unsigned long flags; + + field = hidinput_get_led_field(hid); + if (!field) { + hid_warn(hid, "LED event field not found\n"); + return; + } + + spin_lock_irqsave(&usbhid->lock, flags); + if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) { + usbhid->ledcount = hidinput_count_leds(hid); + hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount); + __usbhid_submit_report(hid, field->report, USB_DIR_OUT); + } + spin_unlock_irqrestore(&usbhid->lock, flags); +} + static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = input_get_drvdata(dev); @@ -595,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return -1; } + spin_lock_irqsave(&usbhid->lock, flags); hid_set_field(field, offset, value); - if (value) { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount++; - spin_unlock_irqrestore(&usbhid->lock, flags); - } else { - spin_lock_irqsave(&usbhid->lock, flags); - usbhid->ledcount--; - spin_unlock_irqrestore(&usbhid->lock, flags); - } - usbhid_submit_report(hid, field->report, USB_DIR_OUT); + spin_unlock_irqrestore(&usbhid->lock, flags); + + /* + * Defer performing requested LED action. + * This is more likely gather all LED changes into a single URB. + */ + schedule_work(&usbhid->led_work); return 0; } @@ -1100,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid) return; clear_bit(HID_STARTED, &usbhid->iofl); - spin_lock_irq(&usbhid->lock); /* Sync with error handler */ + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); usb_kill_urb(usbhid->urbin); @@ -1234,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); spin_lock_init(&usbhid->lock); + INIT_WORK(&usbhid->led_work, hid_led); + ret = hid_add_device(hid); if (ret) { if (ret != -ENODEV) @@ -1266,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { del_timer_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); + cancel_work_sync(&usbhid->led_work); } static void hid_cease_io(struct usbhid_device *usbhid) @@ -1367,16 +1418,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) return -EIO; } - if (!ignoreled && PMSG_IS_AUTO(message)) { - spin_lock_irq(&usbhid->lock); - if (test_bit(HID_LED_ON, &usbhid->iofl)) { - spin_unlock_irq(&usbhid->lock); - usbhid_mark_busy(usbhid); - return -EBUSY; - } - spin_unlock_irq(&usbhid->lock); - } - hid_cancel_delayed_stuff(usbhid); hid_cease_io(usbhid); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 4ea464151c3b..c831af937481 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -16,6 +16,7 @@ */ #include <linux/hid.h> +#include <linux/export.h> #include <linux/slab.h> #include "../hid-ids.h" @@ -46,6 +47,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, + { USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, @@ -66,6 +68,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, + { 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 }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 1673cac93d77..cb8f703efde5 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -55,7 +55,6 @@ struct usb_interface *usbhid_find_interface(int minor); #define HID_STARTED 8 #define HID_REPORTED_IDLE 9 #define HID_KEYS_PRESSED 10 -#define HID_LED_ON 11 /* * USB-specific HID struct, to be pointed to @@ -97,6 +96,8 @@ struct usbhid_device { struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ int ledcount; /* counting the number of active leds */ + + struct work_struct led_work; /* Task context for setting LEDs */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 065817329f03..a7b925aeaf2e 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -64,6 +64,32 @@ static const unsigned char usb_kbd_keycode[256] = { 150,158,159,128,136,177,178,176,142,152,173,140 }; + +/** + * struct usb_kbd - state of each attached keyboard + * @dev: input device associated with this keyboard + * @usbdev: usb device associated with this keyboard + * @old: data received in the past from the @irq URB representing which + * keys were pressed. By comparing with the current list of keys + * that are pressed, we are able to see key releases. + * @irq: URB for receiving a list of keys that are pressed when a + * new key is pressed or a key that was pressed is released. + * @led: URB for sending LEDs (e.g. numlock, ...) + * @newleds: data that will be sent with the @led URB representing which LEDs + should be on + * @name: Name of the keyboard. @dev's name field points to this buffer + * @phys: Physical path of the keyboard. @dev's phys field points to this + * buffer + * @new: Buffer for the @irq URB + * @cr: Control request for @led URB + * @leds: Buffer for the @led URB + * @new_dma: DMA address for @irq URB + * @leds_dma: DMA address for @led URB + * @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted + * @led_urb_submitted: indicates whether @led is in progress, i.e. it has been + * submitted and its completion handler has not returned yet + * without resubmitting @led + */ struct usb_kbd { struct input_dev *dev; struct usb_device *usbdev; @@ -78,6 +104,10 @@ struct usb_kbd { unsigned char *leds; dma_addr_t new_dma; dma_addr_t leds_dma; + + spinlock_t leds_lock; + bool led_urb_submitted; + }; static void usb_kbd_irq(struct urb *urb) @@ -136,44 +166,66 @@ resubmit: static int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { + unsigned long flags; struct usb_kbd *kbd = input_get_drvdata(dev); if (type != EV_LED) return -1; + spin_lock_irqsave(&kbd->leds_lock, flags); kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); - if (kbd->led->status == -EINPROGRESS) + if (kbd->led_urb_submitted){ + spin_unlock_irqrestore(&kbd->leds_lock, flags); return 0; + } - if (*(kbd->leds) == kbd->newleds) + if (*(kbd->leds) == kbd->newleds){ + spin_unlock_irqrestore(&kbd->leds_lock, flags); return 0; + } *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) pr_err("usb_submit_urb(leds) failed\n"); - + else + kbd->led_urb_submitted = true; + + spin_unlock_irqrestore(&kbd->leds_lock, flags); + return 0; } static void usb_kbd_led(struct urb *urb) { + unsigned long flags; struct usb_kbd *kbd = urb->context; if (urb->status) hid_warn(urb->dev, "led urb status %d received\n", urb->status); - if (*(kbd->leds) == kbd->newleds) + spin_lock_irqsave(&kbd->leds_lock, flags); + + if (*(kbd->leds) == kbd->newleds){ + kbd->led_urb_submitted = false; + spin_unlock_irqrestore(&kbd->leds_lock, flags); return; + } *(kbd->leds) = kbd->newleds; + kbd->led->dev = kbd->usbdev; - if (usb_submit_urb(kbd->led, GFP_ATOMIC)) + if (usb_submit_urb(kbd->led, GFP_ATOMIC)){ hid_err(urb->dev, "usb_submit_urb(leds) failed\n"); + kbd->led_urb_submitted = false; + } + spin_unlock_irqrestore(&kbd->leds_lock, flags); + } static int usb_kbd_open(struct input_dev *dev) @@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface, kbd->usbdev = dev; kbd->dev = input_dev; + spin_lock_init(&kbd->leds_lock); if (dev->manufacturer) strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); @@ -334,6 +387,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf) if (kbd) { usb_kill_urb(kbd->irq); input_unregister_device(kbd->dev); + usb_kill_urb(kbd->led); usb_kbd_free_mem(interface_to_usbdev(intf), kbd); kfree(kbd); } |