From e90a6df80dc45ab53d2f4f4db297434e48c0208e Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 25 Feb 2013 11:31:43 +0100 Subject: [PATCH 01/43] HID: Extend the interface with report requests Some drivers send reports directly to underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds hid_hw_request() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Henrik Rydberg Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 13 +++++++++++++ include/linux/hid.h | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 8e0c4bf94ebc..366fd09d257d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1243,6 +1243,18 @@ static int usbhid_power(struct hid_device *hid, int lvl) return r; } +static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_IN); + break; + case HID_REQ_SET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_OUT); + break; + } +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, @@ -1251,6 +1263,7 @@ static struct hid_ll_driver usb_hid_driver = { .close = usbhid_close, .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, + .request = usbhid_request, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index e14b465b1146..261c713d4842 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -662,6 +662,7 @@ struct hid_driver { * @hidinput_input_event: event input event (e.g. ff or leds) * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory + * @request: send report request to device (e.g. feature report) */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -676,6 +677,10 @@ struct hid_ll_driver { unsigned int code, int value); int (*parse)(struct hid_device *hdev); + + void (*request)(struct hid_device *hdev, + struct hid_report *report, int reqtype); + }; #define PM_HINT_FULLON 1<<5 @@ -883,6 +888,21 @@ static inline int hid_hw_power(struct hid_device *hdev, int level) return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; } + +/** + * hid_hw_request - send report request to device + * + * @hdev: hid device + * @report: report to send + * @reqtype: hid request type + */ +static inline void hid_hw_request(struct hid_device *hdev, + struct hid_report *report, int reqtype) +{ + if (hdev->ll_driver->request) + hdev->ll_driver->request(hdev, report, reqtype); +} + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); From 3373443befa73ee60e4275e7699b26058b01455a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 25 Feb 2013 11:31:44 +0100 Subject: [PATCH 02/43] HID: Extend the interface with wait io request Some drivers need to wait for an io from the underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds wait() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Henrik Rydberg Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 1 + include/linux/hid.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 366fd09d257d..99d95d3368b5 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1264,6 +1264,7 @@ static struct hid_ll_driver usb_hid_driver = { .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, .request = usbhid_request, + .wait = usbhid_wait_io, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index 261c713d4842..7071eb3d36c7 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -663,6 +663,7 @@ struct hid_driver { * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) + * @wait: wait for buffered io to complete (send/recv reports) */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -681,6 +682,8 @@ struct hid_ll_driver { void (*request)(struct hid_device *hdev, struct hid_report *report, int reqtype); + int (*wait)(struct hid_device *hdev); + }; #define PM_HINT_FULLON 1<<5 @@ -903,6 +906,17 @@ static inline void hid_hw_request(struct hid_device *hdev, hdev->ll_driver->request(hdev, report, reqtype); } +/** + * hid_hw_wait - wait for buffered io to complete + * + * @hdev: hid device + */ +static inline void hid_hw_wait(struct hid_device *hdev) +{ + if (hdev->ll_driver->wait) + hdev->ll_driver->wait(hdev); +} + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); From f3757cea18fadce23c95a4c4bc3123af73a95e65 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:45 +0100 Subject: [PATCH 03/43] HID: Kconfig: Remove explicit transport layer dependencies Most HID drivers (rightfully) only depend on the HID bus, not the specific transport layer. Remove such dependencies where applicable. Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 68 ++++++++++++++++++------------------ drivers/hid/hid-apple.c | 1 - drivers/hid/hid-magicmouse.c | 1 - drivers/hid/hid-speedlink.c | 2 -- drivers/hid/hid-thingm.c | 1 - 5 files changed, 34 insertions(+), 39 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f07d85c4189..1b737b44c56e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -92,7 +92,7 @@ menu "Special HID drivers" config HID_A4TECH tristate "A4 tech mice" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. @@ -113,7 +113,7 @@ config HID_ACRUX_FF config HID_APPLE tristate "Apple {i,Power,Mac}Books" if EXPERT - depends on (USB_HID || BT_HIDP) + depends on HID default !EXPERT ---help--- Support for some Apple devices which less or more break @@ -124,27 +124,27 @@ config HID_APPLE config HID_AUREAL tristate "Aureal" - depends on USB_HID + depends on HID ---help--- Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes. config HID_BELKIN tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Belkin Flip KVM and Wireless keyboard. config HID_CHERRY tristate "Cherry Cymotion keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY tristate "Chicony Tactical pad" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Chicony Tactical pad. @@ -166,7 +166,7 @@ config HID_PRODIKEYS config HID_CYPRESS tristate "Cypress mouse and barcode readers" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for cypress mouse and barcode readers. @@ -202,13 +202,13 @@ config HID_EMS_FF config HID_ELECOM tristate "ELECOM BM084 bluetooth mouse" - depends on BT_HIDP + depends on HID ---help--- Support for the ELECOM BM084 (bluetooth mouse). config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Ezkey BTC 8193 keyboard. @@ -231,7 +231,7 @@ config HOLTEK_FF config HID_KEYTOUCH tristate "Keytouch HID devices" - depends on USB_HID + depends on HID ---help--- Support for Keytouch HID devices not fully compliant with the specification. Currently supported: @@ -249,25 +249,25 @@ config HID_KYE config HID_UCLOGIC tristate "UC-Logic" - depends on USB_HID + depends on HID ---help--- Support for UC-Logic tablets. config HID_WALTOP tristate "Waltop" - depends on USB_HID + depends on HID ---help--- Support for Waltop tablets. config HID_GYRATION tristate "Gyration remote control" - depends on USB_HID + depends on HID ---help--- Support for Gyration remote control. config HID_ICADE tristate "ION iCade arcade controller" - depends on BT_HIDP + depends on HID ---help--- Support for the ION iCade arcade controller to work as a joystick. @@ -276,20 +276,20 @@ config HID_ICADE config HID_TWINHAN tristate "Twinhan IR remote control" - depends on USB_HID + depends on HID ---help--- Support for Twinhan IR remote control. config HID_KENSINGTON tristate "Kensington Slimblade Trackball" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Kensington Slimblade Trackball. config HID_LCPOWER tristate "LC-Power" - depends on USB_HID + depends on HID ---help--- Support for LC-Power RC1000MCE RF remote control. @@ -308,7 +308,7 @@ config HID_LENOVO_TPKBD config HID_LOGITECH tristate "Logitech devices" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Logitech devices that are not fully compliant with HID standard. @@ -374,7 +374,7 @@ config LOGIWHEELS_FF config HID_MAGICMOUSE tristate "Apple MagicMouse multi-touch support" - depends on BT_HIDP + depends on HID ---help--- Support for the Apple Magic Mouse multi-touch. @@ -383,14 +383,14 @@ config HID_MAGICMOUSE config HID_MICROSOFT tristate "Microsoft non-fully HID-compliant devices" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Microsoft devices that are not fully compliant with HID standard. config HID_MONTEREY tristate "Monterey Genius KB29E keyboard" if EXPERT - depends on USB_HID + depends on HID default !EXPERT ---help--- Support for Monterey Genius KB29E. @@ -445,7 +445,7 @@ config HID_NTRIG config HID_ORTEK tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad" - depends on USB_HID + depends on HID ---help--- There are certain devices which have LogicalMaximum wrong in the keyboard usage page of their report descriptor. The most prevailing ones so far @@ -473,7 +473,7 @@ config PANTHERLORD_FF config HID_PETALYNX tristate "Petalynx Maxter remote control" - depends on USB_HID + depends on HID ---help--- Support for Petalynx Maxter remote control. @@ -545,14 +545,14 @@ config HID_PICOLCD_CIR config HID_PRIMAX tristate "Primax non-fully HID-compliant devices" - depends on USB_HID + depends on HID ---help--- Support for Primax devices that are not fully compliant with the HID standard. config HID_PS3REMOTE tristate "Sony PS3 BD Remote Control" - depends on BT_HIDP + depends on HID ---help--- Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech Harmony Adapter for PS3, which connect over Bluetooth. @@ -569,7 +569,7 @@ config HID_ROCCAT config HID_SAITEK tristate "Saitek non-fully HID-compliant devices" - depends on USB_HID + depends on HID ---help--- Support for Saitek devices that are not fully compliant with the HID standard. @@ -578,7 +578,7 @@ config HID_SAITEK config HID_SAMSUNG tristate "Samsung InfraRed remote control or keyboards" - depends on USB_HID + depends on HID ---help--- Support for Samsung InfraRed remote control or keyboards. @@ -604,7 +604,7 @@ config HID_STEELSERIES config HID_SUNPLUS tristate "Sunplus wireless desktop" - depends on USB_HID + depends on HID ---help--- Support for Sunplus wireless desktop. @@ -650,20 +650,20 @@ config SMARTJOYPLUS_FF config HID_TIVO tristate "TiVo Slide Bluetooth remote control support" - depends on (USB_HID || BT_HIDP) + depends on HID ---help--- Say Y if you have a TiVo Slide Bluetooth remote control. config HID_TOPSEED tristate "TopSeed Cyberlink, BTC Emprex, Conceptronic remote control support" - depends on USB_HID + depends on HID ---help--- Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic CLLRCMCE remote control. config HID_THINGM tristate "ThingM blink(1) USB RGB LED" - depends on USB_HID + depends on HID depends on LEDS_CLASS ---help--- Support for the ThingM blink(1) USB RGB LED. This driver registers a @@ -689,7 +689,7 @@ config THRUSTMASTER_FF config HID_WACOM tristate "Wacom Bluetooth devices support" - depends on BT_HIDP + depends on HID depends on LEDS_CLASS select POWER_SUPPLY ---help--- @@ -697,7 +697,7 @@ config HID_WACOM config HID_WIIMOTE tristate "Nintendo Wii Remote support" - depends on BT_HIDP + depends on HID depends on LEDS_CLASS select POWER_SUPPLY select INPUT_FF_MEMLESS @@ -729,7 +729,7 @@ config ZEROPLUS_FF config HID_ZYDACRON tristate "Zydacron remote control support" - depends on USB_HID + depends on HID ---help--- Support for Zydacron remote control. diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 320a958d4139..9e0c4fbbb840 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "hid-ids.h" diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index f7f113ba083e..ef89573d65fc 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "hid-ids.h" diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c index e94371a059cb..a2f587d004e1 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -16,10 +16,8 @@ #include #include #include -#include #include "hid-ids.h" -#include "usbhid/usbhid.h" static const struct hid_device_id speedlink_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE)}, diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 2055a52e9a20..99342cfa0ea2 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "hid-ids.h" From d881427253da011495f4193663d809d0e9dfa215 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:46 +0100 Subject: [PATCH 04/43] HID: use hid_hw_request() instead of direct call to usbhid This allows the hid drivers to be independent from the transport layer. The patch was constructed by replacing all occurences of usbhid_submit_report() by its hid_hw_request() counterpart. Then, drivers not requiring USB_HID anymore have their USB_HID dependency cleaned in the Kconfig file. Finally, few drivers still depends on USB_HID. Many of them are requiring the io wait callback. They are found in the next patch. Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg For the sensor-hub part: Tested-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 30 +++++++------- drivers/hid/hid-axff.c | 6 +-- drivers/hid/hid-dr.c | 8 ++-- drivers/hid/hid-emsff.c | 6 +-- drivers/hid/hid-gaff.c | 10 ++--- drivers/hid/hid-holtekff.c | 4 +- drivers/hid/hid-kye.c | 4 +- drivers/hid/hid-lenovo-tpkbd.c | 4 +- drivers/hid/hid-lg2ff.c | 6 +-- drivers/hid/hid-lg3ff.c | 6 +-- drivers/hid/hid-lg4ff.c | 18 ++++---- drivers/hid/hid-lgff.c | 8 ++-- drivers/hid/hid-logitech-dj.c | 3 +- drivers/hid/hid-multitouch.c | 4 +- drivers/hid/hid-ntrig.c | 6 +-- drivers/hid/hid-picolcd.h | 4 +- drivers/hid/hid-picolcd_backlight.c | 4 +- drivers/hid/hid-picolcd_cir.c | 2 - drivers/hid/hid-picolcd_core.c | 8 ++-- drivers/hid/hid-picolcd_debugfs.c | 2 - drivers/hid/hid-picolcd_fb.c | 7 ++-- drivers/hid/hid-picolcd_lcd.c | 4 +- drivers/hid/hid-picolcd_leds.c | 4 +- drivers/hid/hid-pl.c | 6 +-- drivers/hid/hid-prodikeys.c | 3 +- drivers/hid/hid-sensor-hub.c | 7 ++-- drivers/hid/hid-sjoy.c | 6 +-- drivers/hid/hid-steelseries.c | 3 +- drivers/hid/hid-tmff.c | 6 +-- drivers/hid/hid-zpff.c | 6 +-- drivers/hid/usbhid/hid-core.c | 3 +- drivers/hid/usbhid/hid-pidff.c | 64 ++++++++++++++--------------- drivers/hid/usbhid/hiddev.c | 4 +- drivers/hid/usbhid/usbhid.h | 2 - 34 files changed, 111 insertions(+), 157 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1b737b44c56e..f0acf2754b69 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -99,7 +99,7 @@ config HID_A4TECH config HID_ACRUX tristate "ACRUX game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you want to enable support for ACRUX game controllers. @@ -151,7 +151,7 @@ config HID_CHICONY config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" - depends on USB_HID && SND + depends on HID && SND select SND_RAWMIDI ---help--- Support for Prodikeys PC-MIDI Keyboard device support. @@ -173,7 +173,7 @@ config HID_CYPRESS config HID_DRAGONRISE tristate "DragonRise Inc. game controller" - depends on USB_HID + depends on HID ---help--- Say Y here if you have DragonRise Inc. game controllers. These might be branded as: @@ -192,7 +192,7 @@ config DRAGONRISE_FF config HID_EMS_FF tristate "EMS Production Inc. force feedback support" - depends on USB_HID + depends on HID select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for devices by @@ -215,7 +215,7 @@ config HID_EZKEY config HID_HOLTEK tristate "Holtek HID devices" - depends on USB_HID + depends on HID ---help--- Support for Holtek based devices: - Holtek On Line Grip based game controller @@ -239,7 +239,7 @@ config HID_KEYTOUCH config HID_KYE tristate "KYE/Genius devices" - depends on USB_HID + depends on HID ---help--- Support for KYE/Genius devices not fully compliant with HID standard: - Ergo Mouse @@ -397,7 +397,7 @@ config HID_MONTEREY config HID_MULTITOUCH tristate "HID Multitouch panels" - depends on USB_HID + depends on HID ---help--- Generic support for HID multitouch panels. @@ -458,7 +458,7 @@ config HID_ORTEK config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a PantherLord/GreenAsia based game controller or adapter. @@ -592,13 +592,13 @@ config HID_SONY config HID_SPEEDLINK tristate "Speedlink VAD Cezanne mouse support" - depends on USB_HID + depends on HID ---help--- Support for Speedlink Vicious and Divine Cezanne mouse. config HID_STEELSERIES tristate "Steelseries SRW-S1 steering wheel support" - depends on USB_HID + depends on HID ---help--- Support for Steelseries SRW-S1 steering wheel @@ -610,7 +610,7 @@ config HID_SUNPLUS config HID_GREENASIA tristate "GreenAsia (Product ID 0x12) game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a GreenAsia (Product ID 0x12) based game controller or adapter. @@ -632,7 +632,7 @@ config HID_HYPERV_MOUSE config HID_SMARTJOYPLUS tristate "SmartJoy PLUS PS2/USB adapter support" - depends on USB_HID + depends on HID ---help--- Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box, Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro. @@ -673,7 +673,7 @@ config HID_THINGM config HID_THRUSTMASTER tristate "ThrustMaster devices support" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or a THRUSTMASTER Ferrari GT Rumble Wheel. @@ -715,7 +715,7 @@ config HID_WIIMOTE_EXT config HID_ZEROPLUS tristate "Zeroplus based game controller support" - depends on USB_HID + depends on HID ---help--- Say Y here if you have a Zeroplus based game controller. @@ -735,7 +735,7 @@ config HID_ZYDACRON config HID_SENSOR_HUB tristate "HID Sensors framework support" - depends on USB_HID && GENERIC_HARDIRQS + depends on HID && GENERIC_HARDIRQS select MFD_CORE default n -- help--- diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 62f0cee032ba..64ab94a55aa7 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -29,14 +29,12 @@ #include #include -#include #include #include #include "hid-ids.h" #ifdef CONFIG_HID_ACRUX_FF -#include "usbhid/usbhid.h" struct axff_device { struct hid_report *report; @@ -68,7 +66,7 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect } dbg_hid("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); return 0; } @@ -114,7 +112,7 @@ static int axff_init(struct hid_device *hid) goto err_free_mem; axff->report = report; - usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun \n"); diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index 0fe8f65ef01a..ce0644424f58 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -29,14 +29,12 @@ #include #include -#include #include #include #include "hid-ids.h" #ifdef CONFIG_DRAGONRISE_FF -#include "usbhid/usbhid.h" struct drff_device { struct hid_report *report; @@ -68,7 +66,7 @@ static int drff_play(struct input_dev *dev, void *data, drff->report->field[0]->value[1] = 0x00; drff->report->field[0]->value[2] = weak; drff->report->field[0]->value[4] = strong; - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); drff->report->field[0]->value[0] = 0xfa; drff->report->field[0]->value[1] = 0xfe; @@ -80,7 +78,7 @@ static int drff_play(struct input_dev *dev, void *data, drff->report->field[0]->value[2] = 0x00; drff->report->field[0]->value[4] = 0x00; dbg_hid("running with 0x%02x 0x%02x", strong, weak); - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); return 0; } @@ -132,7 +130,7 @@ static int drff_init(struct hid_device *hid) drff->report->field[0]->value[4] = 0x00; drff->report->field[0]->value[5] = 0x00; drff->report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for DragonRise Inc. " "game controllers by Richard Walmsley \n"); diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index 2e093ab99b43..d82d75bb11f7 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -23,11 +23,9 @@ #include #include -#include #include #include "hid-ids.h" -#include "usbhid/usbhid.h" struct emsff_device { struct hid_report *report; @@ -52,7 +50,7 @@ static int emsff_play(struct input_dev *dev, void *data, emsff->report->field[0]->value[2] = strong; dbg_hid("running with 0x%02x 0x%02x\n", strong, weak); - usbhid_submit_report(hid, emsff->report, USB_DIR_OUT); + hid_hw_request(hid, emsff->report, HID_REQ_SET_REPORT); return 0; } @@ -104,7 +102,7 @@ static int emsff_init(struct hid_device *hid) emsff->report->field[0]->value[4] = 0x00; emsff->report->field[0]->value[5] = 0x00; emsff->report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, emsff->report, USB_DIR_OUT); + hid_hw_request(hid, emsff->report, HID_REQ_SET_REPORT); hid_info(hid, "force feedback for EMS based devices by Ignaz Forster \n"); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index 04d2e6aca778..2d8cead3adca 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -29,13 +29,11 @@ #include #include -#include #include #include #include "hid-ids.h" #ifdef CONFIG_GREENASIA_FF -#include "usbhid/usbhid.h" struct gaff_device { struct hid_report *report; @@ -63,14 +61,14 @@ static int hid_gaff_play(struct input_dev *dev, void *data, gaff->report->field[0]->value[4] = left; gaff->report->field[0]->value[5] = 0; dbg_hid("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); gaff->report->field[0]->value[0] = 0xfa; gaff->report->field[0]->value[1] = 0xfe; gaff->report->field[0]->value[2] = 0x0; gaff->report->field[0]->value[4] = 0x0; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); return 0; } @@ -122,12 +120,12 @@ static int gaff_init(struct hid_device *hid) gaff->report->field[0]->value[1] = 0x00; gaff->report->field[0]->value[2] = 0x00; gaff->report->field[0]->value[3] = 0x00; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); gaff->report->field[0]->value[0] = 0xfa; gaff->report->field[0]->value[1] = 0xfe; - usbhid_submit_report(hid, gaff->report, USB_DIR_OUT); + hid_hw_request(hid, gaff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force Feedback for GreenAsia 0x12 devices by Lukasz Lubojanski \n"); diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index f34d1186a3e1..9a8f05124525 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -27,12 +27,10 @@ #include #include #include -#include #include "hid-ids.h" #ifdef CONFIG_HOLTEK_FF -#include "usbhid/usbhid.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anssi Hannula "); @@ -102,7 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff, dbg_hid("sending %*ph\n", 7, data); - usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT); + hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT); } static int holtekff_play(struct input_dev *dev, void *data, diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index ef72daecfa16..6af90dbdc3d4 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -16,8 +16,6 @@ #include #include #include -#include -#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -361,7 +359,7 @@ static int kye_tablet_enable(struct hid_device *hdev) value[4] = 0x00; value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); return 0; } diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 956c3b135f64..a0535fd7a798 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -68,7 +68,7 @@ static int tpkbd_features_set(struct hid_device *hdev) report->field[2]->value[0] = data_pointer->sensitivity; report->field[3]->value[0] = data_pointer->press_speed; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); return 0; } @@ -332,7 +332,7 @@ static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); } static int tpkbd_probe_tp(struct hid_device *hdev) diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index 3c31bc650e5d..b3cd1507dda2 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -23,10 +23,8 @@ #include #include -#include #include -#include "usbhid/usbhid.h" #include "hid-lg.h" struct lg2ff_device { @@ -56,7 +54,7 @@ static int play_effect(struct input_dev *dev, void *data, lg2ff->report->field[0]->value[4] = 0x00; } - usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT); + hid_hw_request(hid, lg2ff->report, HID_REQ_SET_REPORT); return 0; } @@ -108,7 +106,7 @@ int lg2ff_init(struct hid_device *hid) report->field[0]->value[5] = 0x00; report->field[0]->value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); hid_info(hid, "Force feedback for Logitech RumblePad/Rumblepad 2 by Anssi Hannula \n"); diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c index f98644c26c1d..e52f181f6aa1 100644 --- a/drivers/hid/hid-lg3ff.c +++ b/drivers/hid/hid-lg3ff.c @@ -22,10 +22,8 @@ #include -#include #include -#include "usbhid/usbhid.h" #include "hid-lg.h" /* @@ -92,7 +90,7 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data, report->field[0]->value[1] = (unsigned char)(-x); report->field[0]->value[31] = (unsigned char)(-y); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -118,7 +116,7 @@ static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude) report->field[0]->value[33] = 0x7F; report->field[0]->value[34] = 0x7F; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 65a6ec8d3742..7da40a1797cd 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -202,7 +202,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -225,7 +225,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends autocentering command compatible with Formula Force EX */ @@ -245,7 +245,7 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude) value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends command to set range compatible with G25/G27/Driving Force GT */ @@ -265,7 +265,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range) value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } /* Sends commands to set range compatible with Driving Force Pro wheel */ @@ -294,7 +294,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) report->field[0]->value[1] = 0x02; full_range = 200; } - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); /* Prepare "fine" limit command */ value[0] = 0x81; @@ -306,7 +306,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) value[6] = 0x00; if (range == 200 || range == 900) { /* Do not apply any fine limit */ - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); return; } @@ -320,7 +320,7 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); value[6] = 0xff; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd) @@ -334,7 +334,7 @@ static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_n for (i = 0; i < 7; i++) report->field[0]->value[i] = cmd->cmd[j++]; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } } @@ -410,7 +410,7 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) value[4] = 0x00; value[5] = 0x00; value[6] = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } static void lg4ff_led_set_brightness(struct led_classdev *led_cdev, diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 27bc54f92f44..d7ea8c845b40 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -30,10 +30,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include -#include "usbhid/usbhid.h" #include "hid-lg.h" struct dev_type { @@ -89,7 +87,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[2] = x; report->field[0]->value[3] = y; dbg_hid("(x, y)=(%04x, %04x)\n", x, y); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; case FF_RUMBLE: @@ -104,7 +102,7 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef report->field[0]->value[2] = left; report->field[0]->value[3] = right; dbg_hid("(left, right)=(%04x, %04x)\n", left, right); - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); break; } return 0; @@ -124,7 +122,7 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude) *value++ = 0x80; *value++ = 0x00; *value = 0x00; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); } int lgff_init(struct hid_device* hid) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9500f2f3f8fe..3cf62be2ca5d 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -27,7 +27,6 @@ #include #include #include -#include "usbhid/usbhid.h" #include "hid-ids.h" #include "hid-logitech-dj.h" @@ -638,7 +637,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); hid_set_field(report->field[0], 2, data[1]); - usbhid_submit_report(dj_rcv_hiddev, report, USB_DIR_OUT); + hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); return 0; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7a1ebb867cf4..32258ba60056 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -736,7 +736,7 @@ static void mt_set_input_mode(struct hid_device *hdev) r = re->report_id_hash[td->inputmode]; if (r) { r->field[0]->value[td->inputmode_index] = 0x02; - usbhid_submit_report(hdev, r, USB_DIR_OUT); + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } } @@ -761,7 +761,7 @@ static void mt_set_maxcontacts(struct hid_device *hdev) max = min(fieldmax, max); if (r->field[0]->value[0] != max) { r->field[0]->value[0] = max; - usbhid_submit_report(hdev, r, USB_DIR_OUT); + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); } } } diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 7757e82416e7..b926592e6c16 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -118,7 +118,7 @@ static inline int ntrig_get_mode(struct hid_device *hdev) if (!report) return -EINVAL; - usbhid_submit_report(hdev, report, USB_DIR_IN); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); usbhid_wait_io(hdev); return (int)report->field[0]->value[0]; } @@ -137,7 +137,7 @@ static inline void ntrig_set_mode(struct hid_device *hdev, const int mode) if (!report) return; - usbhid_submit_report(hdev, report, USB_DIR_IN); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); } static void ntrig_report_version(struct hid_device *hdev) @@ -938,7 +938,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) /* Let the device settle to ensure the wakeup message gets * through */ usbhid_wait_io(hdev); - usbhid_submit_report(hdev, report, USB_DIR_IN); + hid_hw_request(hdev, report, HID_REQ_GET_REPORT); /* * Sanity check: if the current mode is invalid reset it to diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h index 020cef69f6a1..2941891ecac2 100644 --- a/drivers/hid/hid-picolcd.h +++ b/drivers/hid/hid-picolcd.h @@ -142,10 +142,10 @@ struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir); #ifdef CONFIG_DEBUG_FS void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report); -#define usbhid_submit_report(a, b, c) \ +#define hid_hw_request(a, b, c) \ do { \ picolcd_debug_out_report(hid_get_drvdata(a), a, b); \ - usbhid_submit_report(a, b, c); \ + hid_hw_request(a, b, c); \ } while (0) void picolcd_debug_raw_event(struct picolcd_data *data, diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c index b91f30945f9c..a32c5f86b0b3 100644 --- a/drivers/hid/hid-picolcd_backlight.c +++ b/drivers/hid/hid-picolcd_backlight.c @@ -18,8 +18,6 @@ ***************************************************************************/ #include -#include "usbhid/usbhid.h" -#include #include #include @@ -46,7 +44,7 @@ static int picolcd_set_brightness(struct backlight_device *bdev) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index a79e95bb9fb6..e346038f0f11 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -21,8 +21,6 @@ #include #include #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include #include #include diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 31cd93fc3d4b..b48092d0e139 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -21,8 +21,6 @@ #include #include #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include #include #include @@ -110,7 +108,7 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, work = NULL; } else { data->pending = work; - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); wait_for_completion_interruptible_timeout(&work->ready, HZ*2); spin_lock_irqsave(&data->lock, flags); @@ -244,7 +242,7 @@ int picolcd_reset(struct hid_device *hdev) spin_unlock_irqrestore(&data->lock, flags); return -ENODEV; } - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); error = picolcd_check_version(hdev); @@ -303,7 +301,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, timeout & 0xff); hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff); - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return count; } diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 4809aa1bdb9c..59ab8e157e6b 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -19,8 +19,6 @@ #include #include -#include "usbhid/usbhid.h" -#include #include #include diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index eb003574b634..98f61de9a5e0 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -20,7 +20,6 @@ #include #include #include "usbhid/usbhid.h" -#include #include #include @@ -143,8 +142,8 @@ static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, else hid_set_field(report2->field[0], 4 + i - 32, tdata[i]); - usbhid_submit_report(data->hdev, report1, USB_DIR_OUT); - usbhid_submit_report(data->hdev, report2, USB_DIR_OUT); + hid_hw_request(data->hdev, report1, HID_REQ_SET_REPORT); + hid_hw_request(data->hdev, report2, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } @@ -214,7 +213,7 @@ int picolcd_fb_reset(struct picolcd_data *data, int clear) hid_set_field(report->field[0], j, mapcmd[j]); else hid_set_field(report->field[0], j, 0); - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); } spin_unlock_irqrestore(&data->lock, flags); diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c index 2d0ddc5ac65f..89821c2da6d7 100644 --- a/drivers/hid/hid-picolcd_lcd.c +++ b/drivers/hid/hid-picolcd_lcd.c @@ -18,8 +18,6 @@ ***************************************************************************/ #include -#include "usbhid/usbhid.h" -#include #include #include @@ -48,7 +46,7 @@ static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->lcd_contrast); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c index 28cb6a4f9634..e994f9c29012 100644 --- a/drivers/hid/hid-picolcd_leds.c +++ b/drivers/hid/hid-picolcd_leds.c @@ -21,8 +21,6 @@ #include #include #include "hid-ids.h" -#include "usbhid/usbhid.h" -#include #include #include @@ -55,7 +53,7 @@ void picolcd_leds_set(struct picolcd_data *data) spin_lock_irqsave(&data->lock, flags); hid_set_field(report->field[0], 0, data->led_state); if (!(data->status & PICOLCD_FAILED)) - usbhid_submit_report(data->hdev, report, USB_DIR_OUT); + hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); } diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index b0199d27787b..d29112fa5cd5 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -43,13 +43,11 @@ #include #include #include -#include #include #include "hid-ids.h" #ifdef CONFIG_PANTHERLORD_FF -#include "usbhid/usbhid.h" struct plff_device { struct hid_report *report; @@ -75,7 +73,7 @@ static int hid_plff_play(struct input_dev *dev, void *data, *plff->strong = left; *plff->weak = right; debug("running with 0x%02x 0x%02x", left, right); - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT); return 0; } @@ -169,7 +167,7 @@ static int plff_init(struct hid_device *hid) *strong = 0x00; *weak = 0x00; - usbhid_submit_report(hid, plff->report, USB_DIR_OUT); + hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT); } hid_info(hid, "Force feedback for PantherLord/GreenAsia devices by Anssi Hannula \n"); diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 4e1c4bcbdc03..7ed828056414 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -26,7 +26,6 @@ #include #include #include -#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -306,7 +305,7 @@ static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state) report->field[0]->value[0] = 0x01; report->field[0]->value[1] = state; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); } static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data) diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 6679788bf75a..59d29f853ac0 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -18,7 +18,6 @@ */ #include #include -#include #include "usbhid/usbhid.h" #include #include @@ -204,7 +203,7 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, goto done_proc; } hid_set_field(report->field[field_index], 0, value); - usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); + hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT); usbhid_wait_io(hsdev->hdev); done_proc: @@ -227,7 +226,7 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, ret = -EINVAL; goto done_proc; } - usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); usbhid_wait_io(hsdev->hdev); *value = report->field[field_index]->value[0]; @@ -262,7 +261,7 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, spin_unlock_irqrestore(&data->lock, flags); goto err_free; } - usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); spin_unlock_irqrestore(&data->lock, flags); wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); switch (data->pending.raw_size) { diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 28f774003f03..37845eccddb5 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -28,13 +28,11 @@ #include #include -#include #include #include #include "hid-ids.h" #ifdef CONFIG_SMARTJOYPLUS_FF -#include "usbhid/usbhid.h" struct sjoyff_device { struct hid_report *report; @@ -57,7 +55,7 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data, sjoyff->report->field[0]->value[1] = right; sjoyff->report->field[0]->value[2] = left; dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right); - usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); return 0; } @@ -115,7 +113,7 @@ static int sjoyff_init(struct hid_device *hid) sjoyff->report->field[0]->value[0] = 0x01; sjoyff->report->field[0]->value[1] = 0x00; sjoyff->report->field[0]->value[2] = 0x00; - usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); } hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 2ed995cda44a..98e66ac71842 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -16,7 +16,6 @@ #include #include -#include "usbhid/usbhid.h" #include "hid-ids.h" #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) @@ -132,7 +131,7 @@ static void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds) value[14] = 0x00; value[15] = 0x00; - usbhid_submit_report(hdev, report, USB_DIR_OUT); + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); /* Note: LED change does not show on device until the device is read/polled */ } diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index e4fcf3f702a5..b83376077d72 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "hid-ids.h" @@ -46,7 +45,6 @@ static const signed short ff_joystick[] = { }; #ifdef CONFIG_THRUSTMASTER_FF -#include "usbhid/usbhid.h" /* Usages for thrustmaster devices I know about */ #define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) @@ -103,7 +101,7 @@ static int tmff_play(struct input_dev *dev, void *data, dbg_hid("(x, y)=(%04x, %04x)\n", x, y); ff_field->value[0] = x; ff_field->value[1] = y; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); break; case FF_RUMBLE: @@ -117,7 +115,7 @@ static int tmff_play(struct input_dev *dev, void *data, dbg_hid("(left,right)=(%08x, %08x)\n", left, right); ff_field->value[0] = left; ff_field->value[1] = right; - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); break; } return 0; diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index af66452592e9..6ec28a37c146 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -24,13 +24,11 @@ #include #include #include -#include #include #include "hid-ids.h" #ifdef CONFIG_ZEROPLUS_FF -#include "usbhid/usbhid.h" struct zpff_device { struct hid_report *report; @@ -59,7 +57,7 @@ static int zpff_play(struct input_dev *dev, void *data, zpff->report->field[2]->value[0] = left; zpff->report->field[3]->value[0] = right; dbg_hid("running with 0x%02x 0x%02x\n", left, right); - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT); return 0; } @@ -104,7 +102,7 @@ static int zpff_init(struct hid_device *hid) zpff->report->field[1]->value[0] = 0x02; zpff->report->field[2]->value[0] = 0x00; zpff->report->field[3]->value[0] = 0x00; - usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); + hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT); hid_info(hid, "force feedback for Zeroplus based devices by Anssi Hannula \n"); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99d95d3368b5..da68687d2c7c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -639,7 +639,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } } -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +static void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) { struct usbhid_device *usbhid = hid->driver_data; unsigned long flags; @@ -648,7 +648,6 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns __usbhid_submit_report(hid, report, dir); spin_unlock_irqrestore(&usbhid->lock, flags); } -EXPORT_SYMBOL_GPL(usbhid_submit_report); /* Workqueue routine to send requests to change LEDs */ static void hid_led(struct work_struct *work) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index f91c136821f7..0f1efa39ec46 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -263,8 +263,8 @@ static void pidff_set_envelope_report(struct pidff_device *pidff, envelope->attack_level, pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_ENVELOPE], + HID_REQ_SET_REPORT); } /* @@ -290,8 +290,8 @@ static void pidff_set_constant_force_report(struct pidff_device *pidff, pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], effect->u.constant.level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONSTANT], + HID_REQ_SET_REPORT); } /* @@ -325,8 +325,8 @@ static void pidff_set_effect_report(struct pidff_device *pidff, pidff->effect_direction); pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], + HID_REQ_SET_REPORT); } /* @@ -357,8 +357,8 @@ static void pidff_set_periodic_report(struct pidff_device *pidff, pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC], + HID_REQ_SET_REPORT); } @@ -399,8 +399,8 @@ static void pidff_set_condition_report(struct pidff_device *pidff, effect->u.condition[i].left_saturation); pidff_set(&pidff->set_condition[PID_DEAD_BAND], effect->u.condition[i].deadband); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_CONDITION], + HID_REQ_SET_REPORT); } } @@ -440,8 +440,8 @@ static void pidff_set_ramp_force_report(struct pidff_device *pidff, effect->u.ramp.start_level); pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], effect->u.ramp.end_level); - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_RAMP], + HID_REQ_SET_REPORT); } /* @@ -465,8 +465,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) int j; pidff->create_new_effect_type->value[0] = efnum; - usbhid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], + HID_REQ_SET_REPORT); hid_dbg(pidff->hid, "create_new_effect sent, type: %d\n", efnum); pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; @@ -475,8 +475,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) for (j = 0; j < 60; j++) { hid_dbg(pidff->hid, "pid_block_load requested\n"); - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], - USB_DIR_IN); + hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_LOAD], + HID_REQ_GET_REPORT); usbhid_wait_io(pidff->hid); if (pidff->block_load_status->value[0] == pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { @@ -513,8 +513,8 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; } - usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], + HID_REQ_SET_REPORT); } /** @@ -535,8 +535,8 @@ static int pidff_playback(struct input_dev *dev, int effect_id, int value) static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) { pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; - usbhid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_FREE], + HID_REQ_SET_REPORT); } /* @@ -718,8 +718,8 @@ static void pidff_set_gain(struct input_dev *dev, u16 gain) struct pidff_device *pidff = dev->ff->private; pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); - usbhid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + HID_REQ_SET_REPORT); } static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) @@ -744,8 +744,8 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; pidff->set_effect[PID_START_DELAY].value[0] = 0; - usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], - USB_DIR_OUT); + hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], + HID_REQ_SET_REPORT); } /* @@ -1158,18 +1158,18 @@ static void pidff_reset(struct pidff_device *pidff) pidff->device_control->value[0] = pidff->control_id[PID_RESET]; /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); usbhid_wait_io(hid); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); usbhid_wait_io(hid); pidff->device_control->value[0] = pidff->control_id[PID_ENABLE_ACTUATORS]; - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); usbhid_wait_io(hid); /* pool report is sometimes messed up, refetch it */ - usbhid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); + hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); usbhid_wait_io(hid); if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { @@ -1181,8 +1181,8 @@ static void pidff_reset(struct pidff_device *pidff) break; } hid_dbg(pidff->hid, "pid_pool requested again\n"); - usbhid_submit_report(hid, pidff->reports[PID_POOL], - USB_DIR_IN); + hid_hw_request(hid, pidff->reports[PID_POOL], + HID_REQ_GET_REPORT); usbhid_wait_io(hid); } } @@ -1269,8 +1269,8 @@ int hid_pidff_init(struct hid_device *hid) if (test_bit(FF_GAIN, dev->ffbit)) { pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); - usbhid_submit_report(hid, pidff->reports[PID_DEVICE_GAIN], - USB_DIR_OUT); + hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN], + HID_REQ_SET_REPORT); } error = pidff_check_autocenter(pidff, dev); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 87bd64959a91..430d2a9e4521 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -705,7 +705,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (report == NULL) break; - usbhid_submit_report(hid, report, USB_DIR_IN); + hid_hw_request(hid, report, HID_REQ_GET_REPORT); usbhid_wait_io(hid); r = 0; @@ -724,7 +724,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (report == NULL) break; - usbhid_submit_report(hid, report, USB_DIR_OUT); + hid_hw_request(hid, report, HID_REQ_SET_REPORT); usbhid_wait_io(hid); r = 0; diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index bd87a61e5303..aa1d5ff1208f 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -38,8 +38,6 @@ int usbhid_wait_io(struct hid_device* hid); void usbhid_close(struct hid_device *hid); int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); -void usbhid_submit_report -(struct hid_device *hid, struct hid_report *report, unsigned char dir); int usbhid_get_power(struct hid_device *hid); void usbhid_put_power(struct hid_device *hid); struct usb_interface *usbhid_find_interface(int minor); From b7966a4d7be0a10329f03330390f4bdaf453d74a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:47 +0100 Subject: [PATCH 05/43] HID: use hid_hw_wait() instead of direct call to usbhid This removes most of the dependencies between hid drivers and usbhid. The patch was constructed by replacing all occurences of usbhid_wait_io() by its hid_hw_wait() counterpart. Then, drivers not requiring USB_HID anymore have their USB_HID dependency cleaned in the Kconfig file. As of today, few drivers are still requiring an explicit USB layer dependency: * ntrig (a patch is on its way) * multitouch (one patch following and another on its way) * lenovo tpkbd * roccat * sony The last three are two deeply using direct calls to the usb subsystem to be able to be cleaned right now. Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- drivers/hid/hid-ntrig.c | 4 ++-- drivers/hid/hid-picolcd_fb.c | 5 ++--- drivers/hid/hid-sensor-hub.c | 5 ++--- drivers/hid/usbhid/hid-core.c | 3 +-- drivers/hid/usbhid/hid-pidff.c | 16 ++++++++-------- drivers/hid/usbhid/hiddev.c | 4 ++-- drivers/hid/usbhid/usbhid.h | 1 - 8 files changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f0acf2754b69..c802b0716f48 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -479,7 +479,7 @@ config HID_PETALYNX config HID_PICOLCD tristate "PicoLCD (graphic version)" - depends on USB_HID + depends on HID ---help--- This provides support for Minibox PicoLCD devices, currently only the graphical ones are supported. diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index b926592e6c16..ef95102515e4 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -119,7 +119,7 @@ static inline int ntrig_get_mode(struct hid_device *hdev) return -EINVAL; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); - usbhid_wait_io(hdev); + hid_hw_wait(hdev); return (int)report->field[0]->value[0]; } @@ -937,7 +937,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) if (report) { /* Let the device settle to ensure the wakeup message gets * through */ - usbhid_wait_io(hdev); + hid_hw_wait(hdev); hid_hw_request(hdev, report, HID_REQ_GET_REPORT); /* diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 98f61de9a5e0..591f6b22aa94 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -19,7 +19,6 @@ #include #include -#include "usbhid/usbhid.h" #include #include @@ -269,7 +268,7 @@ static void picolcd_fb_update(struct fb_info *info) mutex_unlock(&info->lock); if (!data) return; - usbhid_wait_io(data->hdev); + hid_hw_wait(data->hdev); mutex_lock(&info->lock); n = 0; } @@ -287,7 +286,7 @@ static void picolcd_fb_update(struct fb_info *info) spin_unlock_irqrestore(&fbdata->lock, flags); mutex_unlock(&info->lock); if (data) - usbhid_wait_io(data->hdev); + hid_hw_wait(data->hdev); return; } out: diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 59d29f853ac0..ca7498107327 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -18,7 +18,6 @@ */ #include #include -#include "usbhid/usbhid.h" #include #include #include @@ -204,7 +203,7 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, } hid_set_field(report->field[field_index], 0, value); hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT); - usbhid_wait_io(hsdev->hdev); + hid_hw_wait(hsdev->hdev); done_proc: mutex_unlock(&data->mutex); @@ -227,7 +226,7 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, goto done_proc; } hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); - usbhid_wait_io(hsdev->hdev); + hid_hw_wait(hsdev->hdev); *value = report->field[field_index]->value[0]; done_proc: diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index da68687d2c7c..420466bc481a 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -705,7 +705,7 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return 0; } -int usbhid_wait_io(struct hid_device *hid) +static int usbhid_wait_io(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -719,7 +719,6 @@ int usbhid_wait_io(struct hid_device *hid) return 0; } -EXPORT_SYMBOL_GPL(usbhid_wait_io); static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) { diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 0f1efa39ec46..10b616702780 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -471,13 +471,13 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; pidff->block_load_status->value[0] = 0; - usbhid_wait_io(pidff->hid); + hid_hw_wait(pidff->hid); for (j = 0; j < 60; j++) { hid_dbg(pidff->hid, "pid_block_load requested\n"); hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_LOAD], HID_REQ_GET_REPORT); - usbhid_wait_io(pidff->hid); + hid_hw_wait(pidff->hid); if (pidff->block_load_status->value[0] == pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { hid_dbg(pidff->hid, "device reported free memory: %d bytes\n", @@ -551,7 +551,7 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id) effect_id, pidff->pid_id[effect_id]); /* Wait for the queue to clear. We do not want a full fifo to prevent the effect removal. */ - usbhid_wait_io(pidff->hid); + hid_hw_wait(pidff->hid); pidff_playback_pid(pidff, pid_id, 0); pidff_erase_pid(pidff, pid_id); @@ -1159,18 +1159,18 @@ static void pidff_reset(struct pidff_device *pidff) pidff->device_control->value[0] = pidff->control_id[PID_RESET]; /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); pidff->device_control->value[0] = pidff->control_id[PID_ENABLE_ACTUATORS]; hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); /* pool report is sometimes messed up, refetch it */ hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) { @@ -1183,7 +1183,7 @@ static void pidff_reset(struct pidff_device *pidff) hid_dbg(pidff->hid, "pid_pool requested again\n"); hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); } } } diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 430d2a9e4521..2f1ddca6f2e0 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -706,7 +706,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; hid_hw_request(hid, report, HID_REQ_GET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); r = 0; break; @@ -725,7 +725,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; hid_hw_request(hid, report, HID_REQ_SET_REPORT); - usbhid_wait_io(hid); + hid_hw_wait(hid); r = 0; break; diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index aa1d5ff1208f..dbb6af699135 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -34,7 +34,6 @@ #include /* API provided by hid-core.c for USB HID drivers */ -int usbhid_wait_io(struct hid_device* hid); void usbhid_close(struct hid_device *hid); int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); From 4ebc9b636cdc340196138f35ca0788b8da0e1779 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:48 +0100 Subject: [PATCH 06/43] HID: multitouch: remove explicit usbhid dependency This patch is part of the cleanup of the HID drivers against their low-level transport layer. With new touchscreens relying on I2C, it's better to handle now these dependencies before users get kernel oopses. Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 32258ba60056..184ac0a42121 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -32,7 +32,6 @@ #include #include #include -#include "usbhid/usbhid.h" MODULE_AUTHOR("Stephane Chatty "); @@ -907,7 +906,7 @@ static int mt_resume(struct hid_device *hdev) intf = to_usb_interface(hdev->dev.parent); interface = intf->cur_altsetting; - dev = hid_to_usb_dev(hdev); + dev = interface_to_usbdev(intf); /* Some Elan legacy devices require SET_IDLE to be set on resume. * It should be safe to send it to other devices too. From b0a7868181be0a28fc35231d7435c1fb360eb675 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:49 +0100 Subject: [PATCH 07/43] HID: multitouch: Copyright and note on regression tests This reflects my new company and officialize the regression tests I'm conducting between each change. Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 184ac0a42121..3af9efdd13d9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2,8 +2,9 @@ * HID driver for multitouch panels * * Copyright (c) 2010-2012 Stephane Chatty - * Copyright (c) 2010-2012 Benjamin Tissoires + * Copyright (c) 2010-2013 Benjamin Tissoires * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012-2013 Red Hat, Inc * * This code is partly based on hid-egalax.c: * @@ -26,6 +27,17 @@ * any later version. */ +/* + * This driver is regularly tested thanks to the tool hid-test[1]. + * This tool relies on hid-replay[2] and a database of hid devices[3]. + * Please run these regression tests before patching this module so that + * your patch won't break existing known devices. + * + * [1] https://github.com/bentiss/hid-test + * [2] https://github.com/bentiss/hid-replay + * [3] https://github.com/bentiss/hid-devices + */ + #include #include #include From 545bef681088cbf49c9c11d63872698b81eac7eb Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 25 Feb 2013 11:31:50 +0100 Subject: [PATCH 08/43] HID: i2c-hid: implement request() callback This allows HID drivers to also get/set reports through hid_hw_request(). Tested-by: Mika Westerberg Signed-off-by: Benjamin Tissoires Signed-off-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ec7930217a6d..935f387be95a 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -563,6 +563,37 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, return ret; } +static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, + int reqtype) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + char *buf; + int ret; + + buf = kzalloc(ihid->bufsize, GFP_KERNEL); + if (!buf) + return; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + ret = i2c_hid_get_raw_report(hid, rep->id, buf, ihid->bufsize, + rep->type); + if (ret < 0) + dev_err(&client->dev, "%s: unable to get report: %d\n", + __func__, ret); + else + hid_input_report(hid, rep->type, buf, ret, 0); + break; + case HID_REQ_SET_REPORT: + hid_output_report(rep, buf); + i2c_hid_output_raw_report(hid, buf, ihid->bufsize, rep->type); + break; + } + + kfree(buf); +} + static int i2c_hid_parse(struct hid_device *hid) { struct i2c_client *client = hid->driver_data; @@ -742,6 +773,7 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .open = i2c_hid_open, .close = i2c_hid_close, .power = i2c_hid_power, + .request = i2c_hid_request, .hidinput_input_event = i2c_hid_hidinput_input_event, }; From 6399f335c2c5fb9c244bbb2b8a437498d260bed7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 19 Feb 2013 11:22:33 +0200 Subject: [PATCH 09/43] HID: make sensor autodetection independent of underlying bus Instead of limiting HID sensors to USB and I2C busses we can just make everything that has usage page of HID_UP_SENSOR to be included in HID_GROUP_SENSOR_HUB group. This allows the sensor-hub to work over bluetooth (and other transports) as well. Reported-by: Alexander Holler Signed-off-by: Mika Westerberg Reviewed-By: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ff75cabf7393..fea20c650f3f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -728,8 +728,7 @@ static int hid_scan_report(struct hid_device *hid) } else if (page == HID_UP_SENSOR && item.type == HID_ITEM_TYPE_MAIN && item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION && - (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL && - (hid->bus == BUS_USB || hid->bus == BUS_I2C)) + (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL) hid->group = HID_GROUP_SENSOR_HUB; } From c849a6143bec520aff2a6646518b0d041402428b Mon Sep 17 00:00:00 2001 From: Andrew de los Reyes Date: Mon, 18 Feb 2013 09:20:21 -0800 Subject: [PATCH 10/43] HID: Separate struct hid_device's driver_lock into two locks. This patch separates struct hid_device's driver_lock into two. The goal is to allow hid device drivers to receive input during their probe() or remove() function calls. This is necessary because some drivers need to communicate with the device to determine parameters needed during probe (e.g., size of a multi-touch surface), and if possible, may perfer to communicate with a device on host-initiated disconnect (e.g., to put it into a low-power state). Historically, three functions used driver_lock: - hid_device_probe: blocks to acquire lock - hid_device_remove: blocks to acquire lock - hid_input_report: if locked returns -EBUSY, else acquires lock This patch adds another lock (driver_input_lock) which is used to block input from occurring. The lock behavior is now: - hid_device_probe: blocks to acq. driver_lock, then driver_input_lock - hid_device_remove: blocks to acq. driver_lock, then driver_input_lock - hid_input_report: if driver_input_lock locked returns -EBUSY, else acquires driver_input_lock This patch also adds two helper functions to be called during probe() or remove(): hid_device_io_start() and hid_device_io_stop(). These functions lock and unlock, respectively, driver_input_lock; they also make a note of whether they did so that hid-core knows if a driver has changed the lock state. This patch results in no behavior change for existing devices and drivers. However, during a probe() or remove() function call in a driver, that driver may now selectively call hid_device_io_start() to let input events come through, then optionally call hid_device_io_stop() to stop them. Signed-off-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 24 +++++++++++++++++++--- include/linux/hid.h | 46 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ff75cabf7393..680068c0c46a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1267,7 +1267,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_lock)) + if (down_trylock(&hid->driver_input_lock)) return -EBUSY; if (!hid->driver) { @@ -1324,7 +1324,7 @@ nomem: ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_lock); + up(&hid->driver_input_lock); return ret; } EXPORT_SYMBOL_GPL(hid_input_report); @@ -1845,6 +1845,11 @@ static int hid_device_probe(struct device *dev) if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; if (!hdev->driver) { id = hid_match_device(hdev, hdrv); @@ -1867,6 +1872,9 @@ static int hid_device_probe(struct device *dev) } } unlock: + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); return ret; } @@ -1875,9 +1883,15 @@ static int hid_device_remove(struct device *dev) { struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct hid_driver *hdrv; + int ret = 0; if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; hdrv = hdev->driver; if (hdrv) { @@ -1889,8 +1903,11 @@ static int hid_device_remove(struct device *dev) hdev->driver = NULL; } + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); - return 0; + return ret; } static ssize_t modalias_show(struct device *dev, struct device_attribute *a, @@ -2329,6 +2346,7 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); sema_init(&hdev->driver_lock, 1); + sema_init(&hdev->driver_input_lock, 1); return hdev; } diff --git a/include/linux/hid.h b/include/linux/hid.h index e14b465b1146..895b85639dec 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -456,7 +456,8 @@ struct hid_device { /* device report descriptor */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; - struct semaphore driver_lock; /* protects the current driver */ + struct semaphore driver_lock; /* protects the current driver, except during input */ + struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ struct hid_driver *driver; struct hid_ll_driver *ll_driver; @@ -477,6 +478,7 @@ struct hid_device { /* device report descriptor */ unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ + bool io_started; /* Protected by driver_lock. If IO has started */ struct list_head inputs; /* The list of inputs */ void *hiddev; /* The hiddev structure */ @@ -599,6 +601,10 @@ struct hid_usage_id { * @resume: invoked on resume if device was not reset (NULL means nop) * @reset_resume: invoked on resume if device was reset (NULL means nop) * + * probe should return -errno on error, or 0 on success. During probe, + * input will not be passed to raw_event unless hid_device_io_start is + * called. + * * raw_event and event should return 0 on no action performed, 1 when no * further processing should be done and negative on error * @@ -737,6 +743,44 @@ const struct hid_device_id *hid_match_id(struct hid_device *hdev, const struct hid_device_id *id); s32 hid_snto32(__u32 value, unsigned n); +/** + * hid_device_io_start - enable HID input during probe, remove + * + * @hid - the device + * + * This should only be called during probe or remove and only be + * called by the thread calling probe or remove. It will allow + * incoming packets to be delivered to the driver. + */ +static inline void hid_device_io_start(struct hid_device *hid) { + if (hid->io_started) { + dev_warn(&hid->dev, "io already started"); + return; + } + hid->io_started = true; + up(&hid->driver_input_lock); +} + +/** + * hid_device_io_stop - disable HID input during probe, remove + * + * @hid - the device + * + * Should only be called after hid_device_io_start. It will prevent + * incoming packets from going to the driver for the duration of + * probe, remove. If called during probe, packets will still go to the + * driver after probe is complete. This function should only be called + * by the thread calling probe or remove. + */ +static inline void hid_device_io_stop(struct hid_device *hid) { + if (!hid->io_started) { + dev_warn(&hid->dev, "io already stopped"); + return; + } + hid->io_started = false; + down(&hid->driver_input_lock); +} + /** * hid_map_usage - map usage input bits * From a9dd22b73085734735cfa07398451b061626cc53 Mon Sep 17 00:00:00 2001 From: Andrew de los Reyes Date: Mon, 18 Feb 2013 09:20:22 -0800 Subject: [PATCH 11/43] HID: logitech-dj: Allow incoming packets during probe(). Historically, logitech-dj communicated with the device during probe() to query the list of devices attached. Later, a change was introduced to hid-core that prevented incoming packets for a device during probe(), as many drivers are unable to handle such input. That change broke the device enumeration in logitech-dj, so commit 596264082f10dd4a56 was introduced to workaround that by waiting for normal input before enumerating devices. Now that drivers can opt-in to receive input during probe, this patch changes logitech-dj to do that, so that it can successfully complete enumeration of devices during probe(). Signed-off-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9500f2f3f8fe..bf647ef18086 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -803,6 +803,9 @@ static int logi_dj_probe(struct hid_device *hdev, goto llopen_failed; } + /* Allow incoming packets to arrive: */ + hid_device_io_start(hdev); + retval = logi_dj_recv_query_paired_devices(djrcv_dev); if (retval < 0) { dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices " From 8af6c08830b1ae114d1a8b548b1f8b056e068887 Mon Sep 17 00:00:00 2001 From: Andrew de los Reyes Date: Mon, 18 Feb 2013 09:20:23 -0800 Subject: [PATCH 12/43] Revert "HID: Fix logitech-dj: missing Unifying device issue" This reverts commit 596264082f10dd4a567c43d4526b2f54ac5520bc. The reverted commit was a workaround needed when drivers became unable to communicate with devices during probe(). Now that such communication is possible, the workaround is not needed. Signed-off-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 45 ----------------------------------- drivers/hid/hid-logitech-dj.h | 1 - 2 files changed, 46 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index bf647ef18086..199b78c8a5f3 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -193,7 +193,6 @@ static struct hid_ll_driver logi_dj_ll_driver; static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, size_t count, unsigned char report_type); -static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev); static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, struct dj_report *dj_report) @@ -234,7 +233,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] & SPFUNCTION_DEVICE_LIST_EMPTY) { dbg_hid("%s: device list is empty\n", __func__); - djrcv_dev->querying_devices = false; return; } @@ -245,12 +243,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, return; } - if (djrcv_dev->paired_dj_devices[dj_report->device_index]) { - /* The device is already known. No need to reallocate it. */ - dbg_hid("%s: device is already known\n", __func__); - return; - } - dj_hiddev = hid_allocate_device(); if (IS_ERR(dj_hiddev)) { dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n", @@ -314,7 +306,6 @@ static void delayedwork_callback(struct work_struct *work) struct dj_report dj_report; unsigned long flags; int count; - int retval; dbg_hid("%s\n", __func__); @@ -347,25 +338,6 @@ static void delayedwork_callback(struct work_struct *work) logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report); break; default: - /* A normal report (i. e. not belonging to a pair/unpair notification) - * arriving here, means that the report arrived but we did not have a - * paired dj_device associated to the report's device_index, this - * means that the original "device paired" notification corresponding - * to this dj_device never arrived to this driver. The reason is that - * hid-core discards all packets coming from a device while probe() is - * executing. */ - if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) { - /* ok, we don't know the device, just re-ask the - * receiver for the list of connected devices. */ - retval = logi_dj_recv_query_paired_devices(djrcv_dev); - if (!retval) { - /* everything went fine, so just leave */ - break; - } - dev_err(&djrcv_dev->hdev->dev, - "%s:logi_dj_recv_query_paired_devices " - "error:%d\n", __func__, retval); - } dbg_hid("%s: unexpected report type\n", __func__); } } @@ -396,12 +368,6 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev, if (!djdev) { dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" " is NULL, index %d\n", dj_report->device_index); - kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); - - if (schedule_work(&djrcv_dev->work) == 0) { - dbg_hid("%s: did not schedule the work item, was already " - "queued\n", __func__); - } return; } @@ -432,12 +398,6 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, if (dj_device == NULL) { dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" " is NULL, index %d\n", dj_report->device_index); - kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); - - if (schedule_work(&djrcv_dev->work) == 0) { - dbg_hid("%s: did not schedule the work item, was already " - "queued\n", __func__); - } return; } @@ -479,10 +439,6 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) struct dj_report *dj_report; int retval; - /* no need to protect djrcv_dev->querying_devices */ - if (djrcv_dev->querying_devices) - return 0; - dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); if (!dj_report) return -ENOMEM; @@ -494,7 +450,6 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) return retval; } - static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, unsigned timeout) { diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h index 4a4000340ce1..fd28a5e0ca3b 100644 --- a/drivers/hid/hid-logitech-dj.h +++ b/drivers/hid/hid-logitech-dj.h @@ -101,7 +101,6 @@ struct dj_receiver_dev { struct work_struct work; struct kfifo notif_fifo; spinlock_t lock; - bool querying_devices; }; struct dj_device { From fdc5663c1b686caaaeff01f1b0918489536f7d7d Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 5 Mar 2013 09:49:01 +0100 Subject: [PATCH 13/43] HID: Kconfig: fix "-- help---" Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f07d85c4189..49e29391cb98 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -738,7 +738,7 @@ config HID_SENSOR_HUB depends on USB_HID && GENERIC_HARDIRQS select MFD_CORE default n - -- help--- + ---help--- Support for HID Sensor framework. This creates a MFD instance for a sensor hub and identifies all the sensors connected to it. Each sensor is registered as a MFD cell, so that sensor specific From 5b62efd8250d6a751c31d1972e96bfccd19c4679 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 27 Feb 2013 16:23:15 +0100 Subject: [PATCH 14/43] HID: multitouch: remove useless last_field_index field The aim of last_field_index was to detect the end of the report. With the introduction of .report(), it is not required anymore to detect it on the fly as we can put it in the right place during the .report(). The resulting code path is simpler to read because the terminating condition is not evaluated after each field. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7a1ebb867cf4..3f6849ddd29c 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -86,7 +86,6 @@ struct mt_device { multitouch fields */ int cc_index; /* contact count field index in the report */ int cc_value_index; /* contact count value index in the field */ - unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ @@ -405,7 +404,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_GD_Y: if (prev_usage && (prev_usage->hid == usage->hid)) { @@ -421,7 +419,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; } return 0; @@ -436,21 +433,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, ABS_MT_DISTANCE, 0, 1, 0, 0); } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONFIDENCE: mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_TIPSWITCH: hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); input_set_capability(hi->input, EV_KEY, BTN_TOUCH); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONTACTID: mt_store_field(usage, td, hi); - td->last_field_index = field->index; td->touches_by_report++; td->mt_report_id = field->report->id; return 1; @@ -461,7 +454,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, cls->sn_width); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_HEIGHT: hid_map_usage(hi, usage, bit, max, @@ -473,7 +465,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, ABS_MT_ORIENTATION, 0, 1, 0, 0); } mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_TIPPRESSURE: hid_map_usage(hi, usage, bit, max, @@ -481,17 +472,14 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, set_abs(hi->input, ABS_MT_PRESSURE, field, cls->sn_pressure); mt_store_field(usage, td, hi); - td->last_field_index = field->index; return 1; case HID_DG_CONTACTCOUNT: td->cc_index = field->index; td->cc_value_index = usage->usage_index; - td->last_field_index = field->index; return 1; case HID_DG_CONTACTMAX: /* we don't set td->last_slot_field as contactcount and * contact max are global to the report */ - td->last_field_index = field->index; return -1; case HID_DG_TOUCH: /* Legacy devices use TIPSWITCH and not TOUCH. @@ -677,10 +665,6 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, /* we only take into account the last report. */ if (usage->hid == td->last_slot_field) mt_complete_slot(td, field->hidinput->input); - - if (field->index == td->last_field_index - && td->num_received >= td->num_expected) - mt_sync_frame(td, field->hidinput->input); } } @@ -721,6 +705,9 @@ static void mt_report(struct hid_device *hid, struct hid_report *report) mt_process_mt_event(hid, field, &field->usage[n], field->value[n]); } + + if (td->num_received >= td->num_expected) + mt_sync_frame(td, report->field[0]->hidinput->input); } static void mt_set_input_mode(struct hid_device *hdev) From 777d4bb39a585ff54817815544941c2da70a0bb9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 7 Mar 2013 10:48:29 +0100 Subject: [PATCH 15/43] HID: holtek: Holtek devices depends on USB_HID In the HID drivers tranport cleanup series, I removed the dependency between hid-holtek and usbhid. This was wrong as hid-holtek.c relies extensively on usb calls. This fixes compilation error when CONFIG_USB_SUPPORT is not enabled. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index c802b0716f48..d1dcfc78bed2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -215,7 +215,7 @@ config HID_EZKEY config HID_HOLTEK tristate "Holtek HID devices" - depends on HID + depends on USB_HID ---help--- Support for Holtek based devices: - Holtek On Line Grip based game controller From 9684819b5a29e62acd8265a92d8f3454de9bb71e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 27 Feb 2013 16:38:17 +0100 Subject: [PATCH 16/43] HID: ll_driver: Extend the interface with idle requests Some drivers send the idle command directly to underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds hid_hw_idle() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 15 +++++++++++++++ include/linux/hid.h | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 420466bc481a..effcd3d6f5cf 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1253,6 +1253,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r } } +static int usbhid_idle(struct hid_device *hid, int report, int idle, + int reqtype) +{ + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_host_interface *interface = intf->cur_altsetting; + int ifnum = interface->desc.bInterfaceNumber; + + if (reqtype != HID_REQ_SET_IDLE) + return -EINVAL; + + return hid_set_idle(dev, ifnum, report, idle); +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, @@ -1263,6 +1277,7 @@ static struct hid_ll_driver usb_hid_driver = { .hidinput_input_event = usb_hidinput_input_event, .request = usbhid_request, .wait = usbhid_wait_io, + .idle = usbhid_idle, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7071eb3d36c7..863744c38ddc 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -664,6 +664,7 @@ struct hid_driver { * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) * @wait: wait for buffered io to complete (send/recv reports) + * @idle: send idle request to device */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -683,6 +684,7 @@ struct hid_ll_driver { struct hid_report *report, int reqtype); int (*wait)(struct hid_device *hdev); + int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype); }; @@ -906,6 +908,23 @@ static inline void hid_hw_request(struct hid_device *hdev, hdev->ll_driver->request(hdev, report, reqtype); } +/** + * hid_hw_idle - send idle request to device + * + * @hdev: hid device + * @report: report to control + * @idle: idle state + * @reqtype: hid request type + */ +static inline int hid_hw_idle(struct hid_device *hdev, int report, int idle, + int reqtype) +{ + if (hdev->ll_driver->idle) + return hdev->ll_driver->idle(hdev, report, idle, reqtype); + + return 0; +} + /** * hid_hw_wait - wait for buffered io to complete * From 4ba25d3f87fe3ed6634f61da2a6904e2dfd09192 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 27 Feb 2013 16:38:18 +0100 Subject: [PATCH 17/43] HID: multitouch: remove last usb dependency hid-multitouch used to direclty call for a set_idle in the .resume() callback. With the new hid_hw_idle(), this can be dropped and hid-multitouch is now freed from its transport layer. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3af9efdd13d9..f712772437cb 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -909,26 +909,11 @@ static int mt_reset_resume(struct hid_device *hdev) static int mt_resume(struct hid_device *hdev) { - struct usb_interface *intf; - struct usb_host_interface *interface; - struct usb_device *dev; - - if (hdev->bus != BUS_USB) - return 0; - - intf = to_usb_interface(hdev->dev.parent); - interface = intf->cur_altsetting; - dev = interface_to_usbdev(intf); - /* Some Elan legacy devices require SET_IDLE to be set on resume. * It should be safe to send it to other devices too. * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */ - usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_IDLE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, interface->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); + hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE); return 0; } From 8936aa31cd5fd0bea828416a3c07efd303269a45 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 10 Mar 2013 12:32:44 +0100 Subject: [PATCH 18/43] HID: roccat: add support for Roccat Kone Pure gaming mouse Userland-tools can already be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../testing/sysfs-driver-hid-roccat-konepure | 105 ++++++ drivers/hid/Makefile | 4 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-konepure.c | 304 ++++++++++++++++++ drivers/hid/hid-roccat-konepure.h | 72 +++++ 6 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure create mode 100644 drivers/hid/hid-roccat-konepure.c create mode 100644 drivers/hid/hid-roccat-konepure.h diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure b/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure new file mode 100644 index 000000000000..41a9b7fbfc79 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure @@ -0,0 +1,105 @@ +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/actual_profile +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. actual_profile holds number of actual profile. + This value is persistent, so its value determines the profile + that's active when the mouse is powered on next time. + When written, the mouse activates the set profile immediately. + The data has to be 3 bytes long. + The mouse will reject invalid data. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/control +Date: December 2012 +Contact: Stefan Achatz +Description: When written, this file lets one select which data from which + profile will be read next. The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/info +Date: December 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/macro +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store a macro with max 500 key/button strokes + internally. + When written, this file lets one set the sequence for a specific + button for a specific profile. Button and profile numbers are + included in written data. The data has to be 2082 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/profile_buttons +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 59 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/profile_settings +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 31 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/sensor +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse has a tracking- and a distance-control-unit. These + can be activated/deactivated and the lift-off distance can be + set. The data has to be 6 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/talk +Date: December 2012 +Contact: Stefan Achatz +Description: Used to active some easy* functions of the mouse from outside. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/tcu +Date: December 2012 +Contact: Stefan Achatz +Description: When written a calibration process for the tracking control unit + can be initiated/cancelled. Also lets one read/write sensor + registers. + The data has to be 4 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/tcu_image +Date: December 2012 +Contact: Stefan Achatz +Description: When read the mouse returns a 30x30 pixel image of the + sampled underground. This works only in the course of a + calibration process initiated with tcu. + The returned data is 1028 bytes in size. + This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 72d1b0bc0a97..273515197c37 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -94,8 +94,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \ - hid-roccat-pyra.o hid-roccat-savu.o + hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ + hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 512b01c04ea7..733d7e059708 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1687,6 +1687,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { 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_KONEPURE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92e47e5c9564..007ee7441e34 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -689,6 +689,7 @@ #define USB_DEVICE_ID_ROCCAT_ISKU 0x319c #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe #define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c new file mode 100644 index 000000000000..c79d0b06c143 --- /dev/null +++ b/drivers/hid/hid-roccat-konepure.c @@ -0,0 +1,304 @@ +/* + * Roccat KonePure driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-konepure.h" + +static struct class *konepure_class; + +static ssize_t konepure_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 konepure_device *konepure = 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(&konepure->konepure_lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&konepure->konepure_lock); + + return retval ? retval : real_size; +} + +static ssize_t konepure_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 konepure_device *konepure = 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(&konepure->konepure_lock); + retval = roccat_common2_send_with_status(usb_dev, command, + (void *)buf, real_size); + mutex_unlock(&konepure->konepure_lock); + + return retval ? retval : real_size; +} + +#define KONEPURE_SYSFS_W(thingy, THINGY) \ +static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return konepure_sysfs_write(fp, kobj, buf, off, count, \ + KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ +} + +#define KONEPURE_SYSFS_R(thingy, THINGY) \ +static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return konepure_sysfs_read(fp, kobj, buf, off, count, \ + KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ +} + +#define KONEPURE_SYSFS_RW(thingy, THINGY) \ +KONEPURE_SYSFS_W(thingy, THINGY) \ +KONEPURE_SYSFS_R(thingy, THINGY) + +#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .read = konepure_sysfs_read_ ## thingy, \ + .write = konepure_sysfs_write_ ## thingy \ +} + +#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .read = konepure_sysfs_read_ ## thingy, \ +} + +#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .write = konepure_sysfs_write_ ## thingy \ +} + +KONEPURE_SYSFS_RW(actual_profile, ACTUAL_PROFILE) +KONEPURE_SYSFS_W(control, CONTROL) +KONEPURE_SYSFS_RW(info, INFO) +KONEPURE_SYSFS_W(talk, TALK) +KONEPURE_SYSFS_W(macro, MACRO) +KONEPURE_SYSFS_RW(sensor, SENSOR) +KONEPURE_SYSFS_RW(tcu, TCU) +KONEPURE_SYSFS_R(tcu_image, TCU_IMAGE) +KONEPURE_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KONEPURE_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) + +static struct bin_attribute konepure_bin_attributes[] = { + KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE), + KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL), + KONEPURE_BIN_ATTRIBUTE_RW(info, INFO), + KONEPURE_BIN_ATTRIBUTE_W(talk, TALK), + KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO), + KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR), + KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU), + KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), + KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), + __ATTR_NULL +}; + +static int konepure_init_konepure_device_struct(struct usb_device *usb_dev, + struct konepure_device *konepure) +{ + mutex_init(&konepure->konepure_lock); + + return 0; +} + +static int konepure_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 konepure_device *konepure; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + konepure = kzalloc(sizeof(*konepure), GFP_KERNEL); + if (!konepure) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, konepure); + + retval = konepure_init_konepure_device_struct(usb_dev, konepure); + if (retval) { + hid_err(hdev, "couldn't init struct konepure_device\n"); + goto exit_free; + } + + retval = roccat_connect(konepure_class, hdev, + sizeof(struct konepure_mouse_report_button)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + konepure->chrdev_minor = retval; + konepure->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(konepure); + return retval; +} + +static void konepure_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct konepure_device *konepure; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return; + + konepure = hid_get_drvdata(hdev); + if (konepure->roccat_claimed) + roccat_disconnect(konepure->chrdev_minor); + kfree(konepure); +} + +static int konepure_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 = konepure_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void konepure_remove(struct hid_device *hdev) +{ + konepure_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static int konepure_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 konepure_device *konepure = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + if (data[0] != KONEPURE_MOUSE_REPORT_NUMBER_BUTTON) + return 0; + + if (konepure != NULL && konepure->roccat_claimed) + roccat_report_event(konepure->chrdev_minor, data); + + return 0; +} + +static const struct hid_device_id konepure_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, konepure_devices); + +static struct hid_driver konepure_driver = { + .name = "konepure", + .id_table = konepure_devices, + .probe = konepure_probe, + .remove = konepure_remove, + .raw_event = konepure_raw_event +}; + +static int __init konepure_init(void) +{ + int retval; + + konepure_class = class_create(THIS_MODULE, "konepure"); + if (IS_ERR(konepure_class)) + return PTR_ERR(konepure_class); + konepure_class->dev_bin_attrs = konepure_bin_attributes; + + retval = hid_register_driver(&konepure_driver); + if (retval) + class_destroy(konepure_class); + return retval; +} + +static void __exit konepure_exit(void) +{ + hid_unregister_driver(&konepure_driver); + class_destroy(konepure_class); +} + +module_init(konepure_init); +module_exit(konepure_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat KonePure driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-konepure.h b/drivers/hid/hid-roccat-konepure.h new file mode 100644 index 000000000000..2cd24e93dfd6 --- /dev/null +++ b/drivers/hid/hid-roccat-konepure.h @@ -0,0 +1,72 @@ +#ifndef __HID_ROCCAT_KONEPURE_H +#define __HID_ROCCAT_KONEPURE_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +enum { + KONEPURE_SIZE_ACTUAL_PROFILE = 0x03, + KONEPURE_SIZE_CONTROL = 0x03, + KONEPURE_SIZE_FIRMWARE_WRITE = 0x0402, + KONEPURE_SIZE_INFO = 0x06, + KONEPURE_SIZE_MACRO = 0x0822, + KONEPURE_SIZE_PROFILE_SETTINGS = 0x1f, + KONEPURE_SIZE_PROFILE_BUTTONS = 0x3b, + KONEPURE_SIZE_SENSOR = 0x06, + KONEPURE_SIZE_TALK = 0x10, + KONEPURE_SIZE_TCU = 0x04, + KONEPURE_SIZE_TCU_IMAGE = 0x0404, +}; + +enum konepure_control_requests { + KONEPURE_CONTROL_REQUEST_GENERAL = 0x80, + KONEPURE_CONTROL_REQUEST_BUTTONS = 0x90, +}; + +enum konepure_commands { + KONEPURE_COMMAND_CONTROL = 0x04, + KONEPURE_COMMAND_ACTUAL_PROFILE = 0x05, + KONEPURE_COMMAND_PROFILE_SETTINGS = 0x06, + KONEPURE_COMMAND_PROFILE_BUTTONS = 0x07, + KONEPURE_COMMAND_MACRO = 0x08, + KONEPURE_COMMAND_INFO = 0x09, + KONEPURE_COMMAND_TCU = 0x0c, + KONEPURE_COMMAND_TCU_IMAGE = 0x0c, + KONEPURE_COMMAND_E = 0x0e, + KONEPURE_COMMAND_SENSOR = 0x0f, + KONEPURE_COMMAND_TALK = 0x10, + KONEPURE_COMMAND_FIRMWARE_WRITE = 0x1b, + KONEPURE_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, +}; + +enum { + KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; + +struct konepure_mouse_report_button { + uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */ + uint8_t zero; + uint8_t type; + uint8_t data1; + uint8_t data2; + uint8_t zero2; + uint8_t unknown[2]; +} __packed; + +struct konepure_device { + int roccat_claimed; + int chrdev_minor; + struct mutex konepure_lock; +}; + +#endif From ce7169652532a95bebbf2f02cd330a4e66f171ae Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 10 Mar 2013 12:33:02 +0100 Subject: [PATCH 19/43] HID: roccat: add support for IskuFX Extending isku module with one additional and one changed sysfs attr. IskuFX has larger light sysfs attr. Made the code size tolerant so both devices can be handled. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-roccat-isku | 12 +++++++++++- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-isku.c | 17 ++++++++++------- drivers/hid/hid-roccat-isku.h | 4 +++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku index 9eca5a182e64..c601d0f2ac46 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku @@ -101,7 +101,8 @@ Date: June 2011 Contact: Stefan Achatz Description: When written, this file lets one set the backlight intensity for a specific profile. Profile number is included in written data. - The data has to be 10 bytes long. + The data has to be 10 bytes long for Isku, IskuFX needs 16 bytes + of data. Before reading this file, control has to be written to select which profile to read. Users: http://roccat.sourceforge.net @@ -141,3 +142,12 @@ Description: When written, this file lets one trigger easyshift functionality The data has to be 16 bytes long. This file is writeonly. Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/talkfx +Date: February 2013 +Contact: Stefan Achatz +Description: When written, this file lets one trigger temporary color schemes + from the host. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 007ee7441e34..a2e767b3d5d2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -687,6 +687,7 @@ #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_ISKUFX 0x3264 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 1219998a02d6..8023751d5257 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -130,14 +130,14 @@ static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj, if (off >= real_size) return 0; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); - retval = isku_receive(usb_dev, command, buf, real_size); + retval = isku_receive(usb_dev, command, buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, @@ -150,15 +150,15 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); retval = roccat_common2_send_with_status(usb_dev, command, - (void *)buf, real_size); + (void *)buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } #define ISKU_SYSFS_W(thingy, THINGY) \ @@ -216,6 +216,7 @@ 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_W(talkfx, TALKFX) ISKU_SYSFS_R(info, INFO) ISKU_SYSFS_W(control, CONTROL) ISKU_SYSFS_W(reset, RESET) @@ -232,6 +233,7 @@ static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_RW(key_mask, KEY_MASK), ISKU_BIN_ATTR_RW(last_set, LAST_SET), ISKU_BIN_ATTR_W(talk, TALK), + ISKU_BIN_ATTR_W(talkfx, TALKFX), ISKU_BIN_ATTR_R(info, INFO), ISKU_BIN_ATTR_W(control, CONTROL), ISKU_BIN_ATTR_W(reset, RESET), @@ -405,6 +407,7 @@ static int isku_raw_event(struct hid_device *hdev, static const struct hid_device_id isku_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, { } }; @@ -443,5 +446,5 @@ module_init(isku_init); module_exit(isku_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat Isku driver"); +MODULE_DESCRIPTION("USB Roccat Isku/FX driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index cf6896c83867..53056860d4d8 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -25,10 +25,11 @@ enum { ISKU_SIZE_KEYS_MACRO = 0x23, ISKU_SIZE_KEYS_CAPSLOCK = 0x06, ISKU_SIZE_LAST_SET = 0x14, - ISKU_SIZE_LIGHT = 0x0a, + ISKU_SIZE_LIGHT = 0x10, ISKU_SIZE_MACRO = 0x823, ISKU_SIZE_RESET = 0x03, ISKU_SIZE_TALK = 0x10, + ISKU_SIZE_TALKFX = 0x10, }; enum { @@ -59,6 +60,7 @@ enum isku_commands { ISKU_COMMAND_LAST_SET = 0x14, ISKU_COMMAND_15 = 0x15, ISKU_COMMAND_TALK = 0x16, + ISKU_COMMAND_TALKFX = 0x17, ISKU_COMMAND_FIRMWARE_WRITE = 0x1b, ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, }; From 02060045cd93b886381b02ec816e312f19d5e505 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 10 Mar 2013 12:33:04 +0100 Subject: [PATCH 20/43] HID: roccat: fix comments on chardevice Fixed parameter documentation. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index d7437ef5c695..b59b3df9ca95 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -242,7 +242,6 @@ static int roccat_release(struct inode *inode, struct file *file) * roccat_report_event() - output data to readers * @minor: minor device number returned by roccat_connect() * @data: pointer to data - * @len: size of data * * Return value is zero on success, a negative error code on failure. * @@ -290,6 +289,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * @class: the class thats used to create the device. Meant to hold device * specific sysfs attributes. * @hid: the hid device the char device should be connected to. + * @report_size: size of reports * * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. From bb64469af5908d89766b116d91c0691fe3c9237a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 15 Mar 2013 23:07:02 +0800 Subject: [PATCH 21/43] HID: steelseries: use module_hid_driver() to simplify the code module_hid_driver() makes the code simpler by eliminating boilerplate code.. Signed-off-by: Wei Yongjun Signed-off-by: Jiri Kosina --- drivers/hid/hid-steelseries.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 2ed995cda44a..136d156d0a44 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -378,16 +378,5 @@ static struct hid_driver steelseries_srws1_driver = { .report_fixup = steelseries_srws1_report_fixup }; -static int __init steelseries_srws1_init(void) -{ - return hid_register_driver(&steelseries_srws1_driver); -} - -static void __exit steelseries_srws1_exit(void) -{ - hid_unregister_driver(&steelseries_srws1_driver); -} - -module_init(steelseries_srws1_init); -module_exit(steelseries_srws1_exit); +module_hid_driver(steelseries_srws1_driver); MODULE_LICENSE("GPL"); From 4e55467efbc7e52e9e0d8c797c72e6b45c5ee270 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 25 Mar 2013 20:52:01 +0100 Subject: [PATCH 22/43] HID: fix ifdef comment for "CONFIG_HID_PICOLCD_CIR" Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/hid/hid-picolcd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h index 020cef69f6a1..88c7c934c603 100644 --- a/drivers/hid/hid-picolcd.h +++ b/drivers/hid/hid-picolcd.h @@ -302,7 +302,7 @@ static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report static inline void picolcd_exit_cir(struct picolcd_data *data) { } -#endif /* CONFIG_HID_PICOLCD_LIRC */ +#endif /* CONFIG_HID_PICOLCD_CIR */ int picolcd_reset(struct hid_device *hdev); struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, From 4f22decf9b6329acfe59091c5cba6b378b9b31db Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:28 +0100 Subject: [PATCH 23/43] HID: input: don't register unmapped input devices There is no need to register an input device containing no events. This allows drivers using the quirk MULTI_INPUT to register one input per report effectively used. For backward compatibility, we need to add a quirk to request this behavior. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 77 +++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 1 + 2 files changed, 78 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 21b196c394b1..945b8158ec4c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1198,6 +1198,67 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) return hidinput; } +static bool hidinput_has_been_populated(struct hid_input *hidinput) +{ + int i; + unsigned long r = 0; + + for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++) + r |= hidinput->input->evbit[i]; + + for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) + r |= hidinput->input->keybit[i]; + + for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++) + r |= hidinput->input->relbit[i]; + + for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++) + r |= hidinput->input->absbit[i]; + + for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++) + r |= hidinput->input->mscbit[i]; + + for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++) + r |= hidinput->input->ledbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++) + r |= hidinput->input->sndbit[i]; + + for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++) + r |= hidinput->input->ffbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++) + r |= hidinput->input->swbit[i]; + + return !!r; +} + +static void hidinput_cleanup_hidinput(struct hid_device *hid, + struct hid_input *hidinput) +{ + struct hid_report *report; + int i, k; + + list_del(&hidinput->list); + input_free_device(hidinput->input); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + if (k == HID_OUTPUT_REPORT && + hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) + continue; + + list_for_each_entry(report, &hid->report_enum[k].report_list, + list) { + + for (i = 0; i < report->maxfield; i++) + if (report->field[i]->hidinput == hidinput) + report->field[i]->hidinput = NULL; + } + } + + kfree(hidinput); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -1249,6 +1310,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hidinput_configure_usage(hidinput, report->field[i], report->field[i]->usage + j); + if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && + !hidinput_has_been_populated(hidinput)) + continue; + if (hid->quirks & HID_QUIRK_MULTI_INPUT) { /* This will leave hidinput NULL, so that it * allocates another one if we have more inputs on @@ -1265,6 +1330,18 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } + if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && + !hidinput_has_been_populated(hidinput)) { + /* no need to register an input device not populated */ + hidinput_cleanup_hidinput(hid, hidinput); + hidinput = NULL; + } + + if (list_empty(&hid->inputs)) { + hid_err(hid, "No inputs registered, leaving\n"); + goto out_unwind; + } + if (hidinput) { if (drv->input_configured) drv->input_configured(hid, hidinput); diff --git a/include/linux/hid.h b/include/linux/hid.h index 863744c38ddc..fffa06bc4880 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -282,6 +282,7 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_HIDINPUT_FORCE 0x00000080 +#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 From a69c5f8b16c05e68d41e4858c1c2c55fddf085e1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:29 +0100 Subject: [PATCH 24/43] HID: multitouch: breaks out touch handling in specific functions This will allow easier integration of hybrid pen and touch devices. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 123 +++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 40 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 1f544a48507d..35b6025a064f 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -114,6 +114,9 @@ struct mt_device { unsigned mt_flags; /* flags to pass to input-mt */ }; +static void mt_post_parse_default_settings(struct mt_device *td); +static void mt_post_parse(struct mt_device *td); + /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 @@ -364,7 +367,7 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td, f->usages[f->length++] = usage->hid; } -static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, +static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -373,13 +376,8 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, int code; struct hid_usage *prev_usage = NULL; - /* Only map fields from TouchScreen or TouchPad collections. - * We need to ignore fields that belong to other collections - * such as Mouse that might have the same GenericDesktop usages. */ if (field->application == HID_DG_TOUCHSCREEN) td->mt_flags |= INPUT_MT_DIRECT; - else if (field->application != HID_DG_TOUCHPAD) - return 0; /* * Model touchscreens providing buttons as touchpads. @@ -388,12 +386,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) td->mt_flags |= INPUT_MT_POINTER; - /* 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; - if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; @@ -514,7 +506,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, +static int mt_touch_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -605,7 +597,7 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) td->num_received = 0; } -static int mt_event(struct hid_device *hid, struct hid_field *field, +static int mt_touch_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { /* we will handle the hidinput part later, now remains hiddev */ @@ -681,19 +673,13 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, } } -static void mt_report(struct hid_device *hid, struct hid_report *report) +static void mt_touch_report(struct hid_device *hid, struct hid_report *report) { struct mt_device *td = hid_get_drvdata(hid); struct hid_field *field; unsigned count; int r, n; - if (report->id != td->mt_report_id) - return; - - if (!(hid->claimed & HID_CLAIMED_INPUT)) - return; - /* * Includes multi-packet support where subsequent * packets are sent with zero contactcount. @@ -721,6 +707,81 @@ static void mt_report(struct hid_device *hid, struct hid_report *report) mt_sync_frame(td, report->field[0]->hidinput->input); } +static void mt_touch_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = &td->mtclass; + struct input_dev *input = hi->input; + + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; + + mt_post_parse(td); + if (td->serial_maybe) + mt_post_parse_default_settings(td); + + if (cls->is_indirect) + td->mt_flags |= INPUT_MT_POINTER; + + if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->mt_flags |= INPUT_MT_DROP_UNUSED; + + input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + + td->mt_flags = 0; +} + +static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_TOUCHPAD) + 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; + + return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); +} + +static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); +} + +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); + + if (field->report->id == td->mt_report_id) + return mt_touch_event(hid, field, usage, value); + + /* ignore other reports */ + return 1; +} + +static void mt_report(struct hid_device *hid, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hid); + + if (!(hid->claimed & HID_CLAIMED_INPUT)) + return; + + if (report->id == td->mt_report_id) + mt_touch_report(hid, report); +} + static void mt_set_input_mode(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); @@ -795,32 +856,14 @@ static void mt_post_parse(struct mt_device *td) } static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) - { - struct mt_device *td = hid_get_drvdata(hdev); - struct mt_class *cls = &td->mtclass; struct input_dev *input = hi->input; /* Only initialize slots for MT input devices */ if (!test_bit(ABS_MT_POSITION_X, input->absbit)) return; - if (!td->maxcontacts) - td->maxcontacts = MT_DEFAULT_MAXCONTACT; - - mt_post_parse(td); - if (td->serial_maybe) - mt_post_parse_default_settings(td); - - if (cls->is_indirect) - td->mt_flags |= INPUT_MT_POINTER; - - if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) - td->mt_flags |= INPUT_MT_DROP_UNUSED; - - input_mt_init_slots(input, td->maxcontacts, td->mt_flags); - - td->mt_flags = 0; + mt_touch_input_configured(hdev, hi); } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) From 6f492f28779d12f7d76555e76ab49120ec01bca6 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:30 +0100 Subject: [PATCH 25/43] HID: multitouch: do not map usage from non used reports hid-multitouch only handles touch events, so there is no point in mapping other kind of events. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 35b6025a064f..611c88cbc40e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -741,7 +741,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, * such as Mouse that might have the same GenericDesktop usages. */ if (field->application != HID_DG_TOUCHSCREEN && field->application != HID_DG_TOUCHPAD) - return 0; + return -1; /* eGalax devices provide a Digitizer.Stylus input which overrides * the correct Digitizers.Finger X/Y ranges. From 5b6498a1c89be9f3a545ab163e33e5abf45493e9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:31 +0100 Subject: [PATCH 26/43] HID: multitouch: change touch sensor detection in mt_input_configured() To implement different methods for pen and touch, the previous implementation has to be reworked. This detection of the input attached to the touch sensor is the same than the one used in mt_report(). Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 611c88cbc40e..6a934dee1b82 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -857,13 +857,10 @@ static void mt_post_parse(struct mt_device *td) static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { - struct input_dev *input = hi->input; + struct mt_device *td = hid_get_drvdata(hdev); - /* Only initialize slots for MT input devices */ - if (!test_bit(ABS_MT_POSITION_X, input->absbit)) - return; - - mt_touch_input_configured(hdev, hi); + if (hi->report->id == td->mt_report_id) + mt_touch_input_configured(hdev, hi); } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) From fa11aa72bd3c72d8129f433aaf6e1af7dd11fa3d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:32 +0100 Subject: [PATCH 27/43] HID: multitouch: add handling for pen in dual-sensors device Dual sensors devices reports pen and touch on two different reports. Using the quirk HID_QUIRK_MULTI_INPUT allows us to create a new input device to forward pen events. The quirk HID_QUIRK_NO_EMPTY_INPUT avoids the creation of input devices for the not used mouse emulation present on Win7 certified devices. Since hid-multitouch sets the quirk HID_QUIRK_NO_INPUT_SYNC, we need to manually send SYN events for pen report too. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 66 +++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 6a934dee1b82..099a7ada13c7 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -99,6 +99,7 @@ struct mt_device { int cc_value_index; /* contact count value index in the field */ unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ + unsigned pen_report_id; /* the report ID of the pen device */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ __s8 inputmode_index; /* InputMode HID feature index in the report */ __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, @@ -367,6 +368,43 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td, f->usages[f->length++] = usage->hid; } +static int mt_pen_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct mt_device *td = hid_get_drvdata(hdev); + + td->pen_report_id = field->report->id; + + return 0; +} + +static int mt_pen_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + return 0; +} + +static int mt_pen_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + /* let hid-input handle it */ + return 0; +} + +static void mt_pen_report(struct hid_device *hid, struct hid_report *report) +{ + struct hid_field *field = report->field[0]; + + input_sync(field->hidinput->input); +} + +static void mt_pen_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ +} + static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -740,14 +778,12 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, * We need to ignore fields that belong to other collections * such as Mouse that might have the same GenericDesktop usages. */ if (field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_PEN && field->application != HID_DG_TOUCHPAD) return -1; - /* 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; + return mt_pen_input_mapping(hdev, hi, field, usage, bit, max); return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); } @@ -756,6 +792,9 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + if (field->physical == HID_DG_STYLUS) + return mt_pen_input_mapped(hdev, hi, field, usage, bit, max); + return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); } @@ -767,6 +806,9 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, if (field->report->id == td->mt_report_id) return mt_touch_event(hid, field, usage, value); + if (field->report->id == td->pen_report_id) + return mt_pen_event(hid, field, usage, value); + /* ignore other reports */ return 1; } @@ -780,6 +822,9 @@ static void mt_report(struct hid_device *hid, struct hid_report *report) if (report->id == td->mt_report_id) mt_touch_report(hid, report); + + if (report->id == td->pen_report_id) + mt_pen_report(hid, report); } static void mt_set_input_mode(struct hid_device *hdev) @@ -861,6 +906,9 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) if (hi->report->id == td->mt_report_id) mt_touch_input_configured(hdev, hi); + + if (hi->report->id == td->pen_report_id) + mt_pen_input_configured(hdev, hi); } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -881,6 +929,14 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) */ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + /* + * This allows the driver to handle different input sensors + * that emits events through different reports on the same HID + * device. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); @@ -890,6 +946,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->inputmode = -1; td->maxcontact_report_id = -1; td->cc_index = -1; + td->mt_report_id = -1; + td->pen_report_id = -1; hid_set_drvdata(hdev, td); td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); From 49a5a827a0ba952612371af65b45b7b57d7ffece Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:33 +0100 Subject: [PATCH 28/43] HID: multitouch: append " Pen" to the name of the stylus input This is not just cosmetics, it can help to write udev and X.org rules. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 099a7ada13c7..55551746d235 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -44,6 +44,7 @@ #include #include #include +#include MODULE_AUTHOR("Stephane Chatty "); @@ -260,6 +261,14 @@ static struct mt_class mt_classes[] = { { } }; +static void mt_free_input_name(struct hid_input *hi) +{ + struct hid_device *hdev = hi->report->device; + + if (hi->input->name != hdev->name) + kfree(hi->input->name); +} + static ssize_t mt_show_quirks(struct device *dev, struct device_attribute *attr, char *buf) @@ -403,6 +412,12 @@ static void mt_pen_report(struct hid_device *hid, struct hid_report *report) static void mt_pen_input_configured(struct hid_device *hdev, struct hid_input *hi) { + char *name = kzalloc(strlen(hi->input->name) + 5, GFP_KERNEL); + if (name) { + sprintf(name, "%s Pen", hi->input->name); + mt_free_input_name(hi); + hi->input->name = name; + } } static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -903,6 +918,10 @@ static void mt_post_parse(struct mt_device *td) static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); + char *name = kstrdup(hdev->name, GFP_KERNEL); + + if (name) + hi->input->name = name; if (hi->report->id == td->mt_report_id) mt_touch_input_configured(hdev, hi); @@ -916,6 +935,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret, i; struct mt_device *td; struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ + struct hid_input *hi; for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { @@ -966,7 +986,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) - goto fail; + goto hid_fail; ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); @@ -978,6 +998,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) return 0; +hid_fail: + list_for_each_entry(hi, &hdev->inputs, list) + mt_free_input_name(hi); fail: kfree(td->fields); kfree(td); @@ -1007,8 +1030,14 @@ static int mt_resume(struct hid_device *hdev) static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + struct hid_input *hi; + sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); + + list_for_each_entry(hi, &hdev->inputs, list) + mt_free_input_name(hi); + kfree(td); hid_set_drvdata(hdev, NULL); } From fb4d8d98dc24f66f7f98e6506fad63e1c320cd82 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 22 Mar 2013 18:38:34 +0100 Subject: [PATCH 29/43] HID: multitouch: force BTN_STYLUS for pen devices The "tablet" udev rule relies on BTN_STYLUS to be set. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 55551746d235..aceaf6c08878 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -418,6 +418,9 @@ static void mt_pen_input_configured(struct hid_device *hdev, mt_free_input_name(hi); hi->input->name = name; } + + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); } static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, From f37130533f68711fd6bae2c79950b8e72002bad6 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 27 Mar 2013 17:29:18 +0100 Subject: [PATCH 30/43] HID: hidraw: warn if userspace headers are outdated Put a warning into sample hidraw code in samples/hidraw/hid-example.c in case the userspace headers are missing the necessary defines and need to be updated. Signed-off-by: Jiri Kosina --- samples/hidraw/hid-example.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/hidraw/hid-example.c b/samples/hidraw/hid-example.c index 816e2dcda7ca..512a7e50bcae 100644 --- a/samples/hidraw/hid-example.c +++ b/samples/hidraw/hid-example.c @@ -17,10 +17,9 @@ /* * Ugly hack to work around failing compilation on systems that don't * yet populate new version of hidraw.h to userspace. - * - * If you need this, please have your distro update the kernel headers. */ #ifndef HIDIOCSFEATURE +#warning Please have your distro update the userspace kernel headers #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #endif From 001dac87052903d6850c064737bab004bed94789 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 2 Apr 2013 19:58:34 +0200 Subject: [PATCH 31/43] HID: wiimote: use unique battery names Battery device names must be unique, otherwise registration fails if multiple Wii Remotes are connected. This breaks the sysfs API, but there is no known application that uses the Wii Remote battery that I know of so we should go ahead and apply this. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index 0fb8ab93db68..f1c7a113b614 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1160,6 +1160,7 @@ static void wiimote_destroy(struct wiimote_data *wdata) wiimote_leds_destroy(wdata); power_supply_unregister(&wdata->battery); + kfree(wdata->battery.name); input_unregister_device(wdata->accel); input_unregister_device(wdata->ir); input_unregister_device(wdata->input); @@ -1216,9 +1217,14 @@ static int wiimote_hid_probe(struct hid_device *hdev, wdata->battery.properties = wiimote_battery_props; wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props); wdata->battery.get_property = wiimote_battery_get_property; - wdata->battery.name = "wiimote_battery"; wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; wdata->battery.use_for_apm = 0; + wdata->battery.name = kasprintf(GFP_KERNEL, "wiimote_battery_%s", + wdata->hdev->uniq); + if (!wdata->battery.name) { + ret = -ENOMEM; + goto err_battery_name; + } ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); if (ret) { @@ -1254,6 +1260,8 @@ err_free: return ret; err_battery: + kfree(wdata->battery.name); +err_battery_name: input_unregister_device(wdata->input); wdata->input = NULL; err_input: From a33042fafdda4c4fb11981c2db95df86682e1083 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 2 Apr 2013 19:58:35 +0200 Subject: [PATCH 32/43] HID: wiimote: add 2nd generation Wii Remote IDs This adds the 2nd generation Wii Remote IDs. They have a different Bluetooth chipset (CSR instead of Broadcom) and are more restrictive in what they accept as input. Hence, you need up-to-date BlueZ and Bluetooth HIDP modules to use these devices. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-wiimote-core.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 512b01c04ea7..eb0309eae7b9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1747,6 +1747,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, { } }; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c4388776f4e4..4905346b03f8 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -614,6 +614,7 @@ #define USB_VENDOR_ID_NINTENDO 0x057e #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 +#define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index f1c7a113b614..f7b521aa1b3d 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -1291,6 +1291,8 @@ static void wiimote_hid_remove(struct hid_device *hdev) static const struct hid_device_id wiimote_hid_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, { } }; MODULE_DEVICE_TABLE(hid, wiimote_hid_devices); From 2d44e3d26891e9530e29395f5a86b751c2f69ee8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 2 Apr 2013 19:58:36 +0200 Subject: [PATCH 33/43] HID: wiimote: parse reduced status reports It turns out the Wii accepts any status reports from clients reduced to "BB BB" key data only, as long as the report actually includes key data at the first two bytes. The official devices don't send these reduced reports, but of course, 3rd party devices make great use of this feature. Hence, add parsers for these reduced reports for every matching report. Also change the logic how we find handlers. There is no reason to call multiple handlers on a single report, but instead find the best handler and call it only once. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index f7b521aa1b3d..e5ee1f20bbd9 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -789,12 +789,20 @@ static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, input_report_abs(wdata->ir, yid, y); } -static void handler_status(struct wiimote_data *wdata, const __u8 *payload) +/* reduced status report with "BB BB" key data only */ +static void handler_status_K(struct wiimote_data *wdata, + const __u8 *payload) { handler_keys(wdata, payload); /* on status reports the drm is reset so we need to resend the drm */ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); +} + +/* extended status report with "BB BB LF 00 00 VV" data */ +static void handler_status(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_status_K(wdata, payload); wiiext_event(wdata, payload[2] & 0x02); @@ -804,6 +812,12 @@ static void handler_status(struct wiimote_data *wdata, const __u8 *payload) } } +/* reduced generic report with "BB BB" key data only */ +static void handler_generic_K(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + static void handler_data(struct wiimote_data *wdata, const __u8 *payload) { __u16 offset = payload[3] << 8 | payload[4]; @@ -947,16 +961,26 @@ struct wiiproto_handler { static struct wiiproto_handler handlers[] = { { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status }, + { .id = WIIPROTO_REQ_STATUS, .size = 2, .func = handler_status_K }, { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data }, + { .id = WIIPROTO_REQ_DATA, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return }, + { .id = WIIPROTO_REQ_RETURN, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys }, { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA }, + { .id = WIIPROTO_REQ_DRM_KA, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE }, + { .id = WIIPROTO_REQ_DRM_KE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI }, + { .id = WIIPROTO_REQ_DRM_KAI, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE }, + { .id = WIIPROTO_REQ_DRM_KEE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE }, + { .id = WIIPROTO_REQ_DRM_KAE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE }, + { .id = WIIPROTO_REQ_DRM_KIE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE }, + { .id = WIIPROTO_REQ_DRM_KAIE, .size = 2, .func = handler_generic_K }, { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E }, { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 }, { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 }, @@ -970,7 +994,6 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, struct wiiproto_handler *h; int i; unsigned long flags; - bool handled = false; if (size < 1) return -EINVAL; @@ -981,11 +1004,11 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, h = &handlers[i]; if (h->id == raw_data[0] && h->size < size) { h->func(wdata, &raw_data[1]); - handled = true; + break; } } - if (!handled) + if (!handlers[i].id) hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0], size); From 7c4d577367fb710ea74a56955c5e3546fe5dd7ac Mon Sep 17 00:00:00 2001 From: Huzefa Kankroliwala Date: Wed, 3 Apr 2013 05:45:21 -0700 Subject: [PATCH 34/43] HID: i2c-hid: fix length for set/get report in i2c hid With the current i2c hid driver set/get report does not work as expected, for e.g sensor hub properties like power state, frequency etc is not set properly on the device as a result we do not get events. The problem is that i2c hid driver in function i2c_hid_request sets length equal to default buffer size for which the sensor hub does not respond on get/set commands. Use report length and calculate it based on report size and id. Reviewed-by: Mika Westerberg Reviewed-by: Benjamin Tissoires Signed-off-by: Huzefa Kankroliwala Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 935f387be95a..2b1799a3b212 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -567,18 +567,17 @@ static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) { struct i2c_client *client = hid->driver_data; - struct i2c_hid *ihid = i2c_get_clientdata(client); char *buf; int ret; + int len = i2c_hid_get_report_length(rep) - 2; - buf = kzalloc(ihid->bufsize, GFP_KERNEL); + buf = kzalloc(len, GFP_KERNEL); if (!buf) return; switch (reqtype) { case HID_REQ_GET_REPORT: - ret = i2c_hid_get_raw_report(hid, rep->id, buf, ihid->bufsize, - rep->type); + ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); if (ret < 0) dev_err(&client->dev, "%s: unable to get report: %d\n", __func__, ret); @@ -587,7 +586,7 @@ static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, break; case HID_REQ_SET_REPORT: hid_output_report(rep, buf); - i2c_hid_output_raw_report(hid, buf, ihid->bufsize, rep->type); + i2c_hid_output_raw_report(hid, buf, len, rep->type); break; } From 3a7f134f3061c242659a92fd8ddcfe53aaeaec22 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 4 Apr 2013 22:32:12 +0200 Subject: [PATCH 35/43] HID: hid-lenovo-tpkbd: remove doubled hid_get_drvdata In pointer_press_speed_show, we do data_pointer = hid_get_drvdata(hdev); twice in a row. Remove one of those. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/hid-lenovo-tpkbd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 956c3b135f64..c842ece2be37 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -228,8 +228,6 @@ static ssize_t pointer_press_speed_show(struct device *dev, struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_speed); } From b42065f31704686e69002343403f45fff863a120 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 7 Apr 2013 07:08:44 +0200 Subject: [PATCH 36/43] HID: roccat: added media key support for Kone Kone now reports media key events through it's chardev to userspace. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-kone.c | 3 ++- drivers/hid/hid-roccat-kone.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 9ce2d0b615a4..7fae070788fa 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -818,8 +818,9 @@ static void kone_report_to_chrdev(struct kone_device const *kone, (uint8_t *)&roccat_report); break; case kone_mouse_event_call_overlong_macro: + case kone_mouse_event_multimedia: if (event->value == kone_keystroke_action_press) { - roccat_report.event = kone_mouse_event_call_overlong_macro; + roccat_report.event = event->event; roccat_report.value = kone->actual_profile; roccat_report.key = event->macro_key; roccat_report_event(kone->chrdev_minor, diff --git a/drivers/hid/hid-roccat-kone.h b/drivers/hid/hid-roccat-kone.h index 64abb5b8a59a..52c6167d023e 100644 --- a/drivers/hid/hid-roccat-kone.h +++ b/drivers/hid/hid-roccat-kone.h @@ -169,6 +169,7 @@ enum kone_mouse_events { /* TODO clarify meaning and occurence of kone_mouse_event_calibration */ kone_mouse_event_calibration = 0xc0, kone_mouse_event_call_overlong_macro = 0xe0, + kone_mouse_event_multimedia = 0xe1, /* switch events notify if user changed values with mousebutton click */ kone_mouse_event_switch_dpi = 0xf0, kone_mouse_event_switch_profile = 0xf1 From 9a4a5574ce427c364d81746fc7fb82d86b5f1a7e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 17 Apr 2013 18:15:15 +0200 Subject: [PATCH 37/43] HID: appleir: add support for Apple ir devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver was originally written by James McKenzie, updated by Greg Kroah-Hartman, further updated by Bastien Nocera, with suspend support added. I ported it to the HID subsystem, in order to simplify it a litle and allow lirc to use it through hiddev. More recent versions of the IR receiver are also supported through a patch by Alex Karpenko. The patch also adds support for the 2nd and 5th generation of the controller, and the menu key on newer brushed metal remotes. Tested-by: Fabien André Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 11 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-apple.c | 4 - drivers/hid/hid-appleir.c | 352 ++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 7 +- drivers/hid/hid-ids.h | 5 +- 6 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 drivers/hid/hid-appleir.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f07d85c4189..7f5e0aa5fe5a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -122,6 +122,17 @@ config HID_APPLE Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, MacBooks, MacBook Pros and Apple Aluminum. +config HID_APPLEIR + tristate "Apple infrared receiver" + depends on (USB_HID) + ---help--- + Support for Apple infrared remote control. All the Apple computers from + 2005 onwards include such a port, except the unibody Macbook (2009), + and Mac Pros. This receiver is also used in the Apple TV set-top box + prior to the 2010 model. + + Say Y here if you want support for Apple infrared remote control. + config HID_AUREAL tristate "Aureal" depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 72d1b0bc0a97..cec36715a457 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -39,6 +39,7 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 320a958d4139..5c5c57b6d59a 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -390,10 +390,6 @@ static void apple_remove(struct hid_device *hdev) } static const struct hid_device_id apple_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL), - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c new file mode 100644 index 000000000000..a42e6a394c5e --- /dev/null +++ b/drivers/hid/hid-appleir.c @@ -0,0 +1,352 @@ +/* + * HID driver for the apple ir device + * + * Original driver written by James McKenzie + * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman + * Updated to support newer remotes by Bastien Nocera + * Ported to HID subsystem by Benjamin Tissoires + * + * Copyright (C) 2006 James McKenzie + * Copyright (C) 2008 Greg Kroah-Hartman + * Copyright (C) 2008 Novell Inc. + * Copyright (C) 2010, 2012 Bastien Nocera + * Copyright (C) 2013 Benjamin Tissoires + * Copyright (C) 2013 Red Hat Inc. All Rights Reserved + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that 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 +#include +#include +#include "hid-ids.h" + +MODULE_AUTHOR("James McKenzie"); +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_DESCRIPTION("HID Apple IR remote controls"); +MODULE_LICENSE("GPL"); + +#define KEY_MASK 0x0F +#define TWO_PACKETS_MASK 0x40 + +/* + * James McKenzie has two devices both of which report the following + * 25 87 ee 83 0a + + * 25 87 ee 83 0c - + * 25 87 ee 83 09 << + * 25 87 ee 83 06 >> + * 25 87 ee 83 05 >" + * 25 87 ee 83 03 menu + * 26 00 00 00 00 for key repeat + */ + +/* + * Thomas Glanzmann reports the following responses + * 25 87 ee ca 0b + + * 25 87 ee ca 0d - + * 25 87 ee ca 08 << + * 25 87 ee ca 07 >> + * 25 87 ee ca 04 >" + * 25 87 ee ca 02 menu + * 26 00 00 00 00 for key repeat + * + * He also observes the following event sometimes + * sent after a key is release, which I interpret + * as a flat battery message + * 25 87 e0 ca 06 flat battery + */ + +/* + * Alexandre Karpenko reports the following responses for Device ID 0x8242 + * 25 87 ee 47 0b + + * 25 87 ee 47 0d - + * 25 87 ee 47 08 << + * 25 87 ee 47 07 >> + * 25 87 ee 47 04 >" + * 25 87 ee 47 02 menu + * 26 87 ee 47 ** for key repeat (** is the code of the key being held) + */ + +/* + * Bastien Nocera's remote + * 25 87 ee 91 5f followed by + * 25 87 ee 91 05 gives you >" + * + * 25 87 ee 91 5c followed by + * 25 87 ee 91 05 gives you the middle button + */ + +/* + * Fabien Andre's remote + * 25 87 ee a3 5e followed by + * 25 87 ee a3 04 gives you >" + * + * 25 87 ee a3 5d followed by + * 25 87 ee a3 04 gives you the middle button + */ + +static const unsigned short appleir_key_table[] = { + KEY_RESERVED, + KEY_MENU, + KEY_PLAYPAUSE, + KEY_FORWARD, + KEY_BACK, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_ENTER, + KEY_PLAYPAUSE, + KEY_RESERVED, +}; + +struct appleir { + struct input_dev *input_dev; + struct hid_device *hid; + unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; + struct timer_list key_up_timer; /* timer for key up */ + spinlock_t lock; /* protects .current_key */ + int current_key; /* the currently pressed key */ + int prev_key_idx; /* key index in a 2 packets message */ +}; + +static int get_key(int data) +{ + /* + * The key is coded accross bits 2..9: + * + * 0x00 or 0x01 ( ) key: 0 -> KEY_RESERVED + * 0x02 or 0x03 ( menu ) key: 1 -> KEY_MENU + * 0x04 or 0x05 ( >" ) key: 2 -> KEY_PLAYPAUSE + * 0x06 or 0x07 ( >> ) key: 3 -> KEY_FORWARD + * 0x08 or 0x09 ( << ) key: 4 -> KEY_BACK + * 0x0a or 0x0b ( + ) key: 5 -> KEY_VOLUMEUP + * 0x0c or 0x0d ( - ) key: 6 -> KEY_VOLUMEDOWN + * 0x0e or 0x0f ( ) key: 7 -> KEY_RESERVED + * 0x50 or 0x51 ( ) key: 8 -> KEY_RESERVED + * 0x52 or 0x53 ( ) key: 9 -> KEY_RESERVED + * 0x54 or 0x55 ( ) key: 10 -> KEY_RESERVED + * 0x56 or 0x57 ( ) key: 11 -> KEY_RESERVED + * 0x58 or 0x59 ( ) key: 12 -> KEY_RESERVED + * 0x5a or 0x5b ( ) key: 13 -> KEY_RESERVED + * 0x5c or 0x5d ( middle ) key: 14 -> KEY_ENTER + * 0x5e or 0x5f ( >" ) key: 15 -> KEY_PLAYPAUSE + * + * Packets starting with 0x5 are part of a two-packets message, + * we notify the caller by sending a negative value. + */ + int key = (data >> 1) & KEY_MASK; + + if ((data & TWO_PACKETS_MASK)) + /* Part of a 2 packets-command */ + key = -key; + + return key; +} + +static void key_up(struct hid_device *hid, struct appleir *appleir, int key) +{ + input_report_key(appleir->input_dev, key, 0); + input_sync(appleir->input_dev); +} + +static void key_down(struct hid_device *hid, struct appleir *appleir, int key) +{ + input_report_key(appleir->input_dev, key, 1); + input_sync(appleir->input_dev); +} + +static void battery_flat(struct appleir *appleir) +{ + dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); +} + +static void key_up_tick(unsigned long data) +{ + struct appleir *appleir = (struct appleir *)data; + struct hid_device *hid = appleir->hid; + unsigned long flags; + + spin_lock_irqsave(&appleir->lock, flags); + if (appleir->current_key) { + key_up(hid, appleir, appleir->current_key); + appleir->current_key = 0; + } + spin_unlock_irqrestore(&appleir->lock, flags); +} + +static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, + u8 *data, int len) +{ + struct appleir *appleir = hid_get_drvdata(hid); + static const u8 keydown[] = { 0x25, 0x87, 0xee }; + static const u8 keyrepeat[] = { 0x26, }; + static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; + unsigned long flags; + + if (len != 5) + goto out; + + if (!memcmp(data, keydown, sizeof(keydown))) { + int index; + + spin_lock_irqsave(&appleir->lock, flags); + /* + * If we already have a key down, take it up before marking + * this one down + */ + if (appleir->current_key) + key_up(hid, appleir, appleir->current_key); + + /* Handle dual packet commands */ + if (appleir->prev_key_idx > 0) + index = appleir->prev_key_idx; + else + index = get_key(data[4]); + + if (index >= 0) { + appleir->current_key = appleir->keymap[index]; + + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in + * the test above, or here set a timer which pulls + * them up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + appleir->prev_key_idx = 0; + } else + /* Remember key for next packet */ + appleir->prev_key_idx = -index; + spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } + + appleir->prev_key_idx = 0; + + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { + key_down(hid, appleir, appleir->current_key); + /* + * Remote doesn't do key up, either pull them up, in the test + * above, or here set a timer which pulls them up after 1/8 s + */ + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); + goto out; + } + + if (!memcmp(data, flatbattery, sizeof(flatbattery))) { + battery_flat(appleir); + /* Fall through */ + } + +out: + /* let hidraw and hiddev handle the report */ + return 0; +} + +static void appleir_input_configured(struct hid_device *hid, + struct hid_input *hidinput) +{ + struct input_dev *input_dev = hidinput->input; + struct appleir *appleir = hid_get_drvdata(hid); + int i; + + appleir->input_dev = input_dev; + + input_dev->keycode = appleir->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + + memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); + for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) + set_bit(appleir->keymap[i], input_dev->keybit); + clear_bit(KEY_RESERVED, input_dev->keybit); +} + +static int appleir_input_mapping(struct hid_device *hid, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + return -1; +} + +static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) +{ + int ret; + struct appleir *appleir; + + appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); + if (!appleir) { + ret = -ENOMEM; + goto allocfail; + } + + appleir->hid = hid; + + spin_lock_init(&appleir->lock); + setup_timer(&appleir->key_up_timer, + key_up_tick, (unsigned long) appleir); + + hid_set_drvdata(hid, appleir); + + ret = hid_parse(hid); + if (ret) { + hid_err(hid, "parse failed\n"); + goto fail; + } + + ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE); + if (ret) { + hid_err(hid, "hw start failed\n"); + goto fail; + } + + return 0; +fail: + kfree(appleir); +allocfail: + return ret; +} + +static void appleir_remove(struct hid_device *hid) +{ + struct appleir *appleir = hid_get_drvdata(hid); + hid_hw_stop(hid); + del_timer_sync(&appleir->key_up_timer); + kfree(appleir); +} + +static const struct hid_device_id appleir_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, + { } +}; +MODULE_DEVICE_TABLE(hid, appleir_devices); + +static struct hid_driver appleir_driver = { + .name = "appleir", + .id_table = appleir_devices, + .raw_event = appleir_raw_event, + .input_configured = appleir_input_configured, + .probe = appleir_probe, + .remove = appleir_remove, + .input_mapping = appleir_input_mapping, +}; +module_hid_driver(appleir_driver); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aa341d135867..bf434a0f813f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1502,8 +1502,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, @@ -1527,6 +1525,11 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5309fd5eb0eb..68a73740ac99 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -137,8 +137,11 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b -#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 #define USB_VENDOR_ID_ASUS 0x0486 #define USB_DEVICE_ID_ASUS_T91MT 0x0185 From cdfee4ff4158361239e67ae2fab8c1e5d8a4ec05 Mon Sep 17 00:00:00 2001 From: David King Date: Sat, 20 Apr 2013 20:13:52 +0100 Subject: [PATCH 38/43] HID: clarify Magic Mouse Kconfig description The Magic Mouse driver also supports the Magic Trackpad, so mention it in the KConfig description for the driver. Signed-off-by: David King Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 49e29391cb98..edb15c8741d0 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -373,13 +373,13 @@ config LOGIWHEELS_FF - Logitech Formula Force EX config HID_MAGICMOUSE - tristate "Apple MagicMouse multi-touch support" + tristate "Apple Magic Mouse/Trackpad multi-touch support" depends on BT_HIDP ---help--- - Support for the Apple Magic Mouse multi-touch. + Support for the Apple Magic Mouse/Trackpad multi-touch. Say Y here if you want support for the multi-touch features of the - Apple Wireless "Magic" Mouse. + Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. config HID_MICROSOFT tristate "Microsoft non-fully HID-compliant devices" if EXPERT From d856501413e5d72eeb76e1c35e26458e80c27447 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sun, 7 Apr 2013 12:55:44 +0800 Subject: [PATCH 39/43] HID: icade: u16 which never < 0 from is u16 which never < 0. Signed-off-by: Chen Gang Signed-off-by: Jiri Kosina --- drivers/hid/hid-icade.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c index 09dcc04595f3..76b5a7570780 100644 --- a/drivers/hid/hid-icade.c +++ b/drivers/hid/hid-icade.c @@ -159,7 +159,7 @@ static const struct icade_key icade_usage_table[30] = { static const struct icade_key *icade_find_translation(u16 from) { - if (from < 0 || from > ICADE_MAX_USAGE) + if (from > ICADE_MAX_USAGE) return NULL; return &icade_usage_table[from]; } From d991938a807a63c58c9c0fa6cd05cd2490aa6993 Mon Sep 17 00:00:00 2001 From: Simon Wood Date: Fri, 26 Apr 2013 19:29:54 -0600 Subject: [PATCH 40/43] HID: hid-lg4ff add support for new version of DFGT wheel It has been reported that there is a new version (different USB rev) of the Logitech DFGT in the 'wild'. This patch allows the kernel to recognise this wheel and send it the command to enter native mode. Reported-by: "Denis Jovic" Signed-off-by: Simon Wood Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg4ff.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 65a6ec8d3742..2dd73604c68f 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -34,6 +34,7 @@ #define DFGT_REV_MAJ 0x13 #define DFGT_REV_MIN 0x22 +#define DFGT2_REV_MIN 0x26 #define DFP_REV_MAJ 0x11 #define DFP_REV_MIN 0x06 #define FFEX_REV_MAJ 0x21 @@ -125,6 +126,7 @@ static const struct lg4ff_native_cmd native_g27 = { static const struct lg4ff_usb_revision lg4ff_revs[] = { {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */ + {DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt}, /* Driving Force GT v2 */ {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ From 89759e20d9571e0496c7e12fc4a3ce2a5233f18e Mon Sep 17 00:00:00 2001 From: Adam Jiang Date: Mon, 29 Apr 2013 13:27:31 +0900 Subject: [PATCH 41/43] HID: Add PID for Japanese version of NE4K keyboard This patche adds PID of Japanese Natual Ergonomic Keyboard 4000. HID NE4K driver depends on this PID for determining its quirks. F14-F18 keys would not work without the patch. Signed-off-by: Adam Jiang Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-microsoft.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ff75cabf7393..f4e1c34ec11d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1654,6 +1654,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6e5c2ffa8d96..89ef50c52da5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -575,6 +575,7 @@ #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d #define USB_DEVICE_ID_MS_NE4K 0x00db +#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc #define USB_DEVICE_ID_MS_LK6K 0x00f9 #define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 29d27f65a118..551795b7da1d 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -195,6 +195,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP), + .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), .driver_data = MS_ERGONOMY | MS_RDESC }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), From a5f04b9df1113e0c16271afe5e43028f0d763f13 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 17 Apr 2013 19:38:13 +0200 Subject: [PATCH 42/43] HID: debug: break out hid_dump_report() into hid-debug No semantic changes, but hid_dump_report should be in hid-debug.c, not in hid-core.c Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 25 ++----------------------- drivers/hid/hid-debug.c | 30 ++++++++++++++++++++++++++++++ include/linux/hid-debug.h | 6 ++++-- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aa341d135867..f86dd9708ca5 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1260,8 +1260,6 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i struct hid_report_enum *report_enum; struct hid_driver *hdrv; struct hid_report *report; - char *buf; - unsigned int i; int ret = 0; if (!hid) @@ -1284,28 +1282,9 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i } /* Avoid unnecessary overhead if debugfs is disabled */ - if (list_empty(&hid->debug_list)) - goto nomem; + if (!list_empty(&hid->debug_list)) + hid_dump_report(hid, type, data, size); - buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); - - if (!buf) - goto nomem; - - /* dump the report */ - snprintf(buf, HID_DEBUG_BUFSIZE - 1, - "\nreport (size %u) (%snumbered) = ", size, report_enum->numbered ? "" : "un"); - hid_debug_event(hid, buf); - - for (i = 0; i < size; i++) { - snprintf(buf, HID_DEBUG_BUFSIZE - 1, - " %02x", data[i]); - hid_debug_event(hid, buf); - } - hid_debug_event(hid, "\n"); - kfree(buf); - -nomem: report = hid_get_report(report_enum, data); if (!report) { diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 933fff0fff1f..094cbcfe1e1a 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -591,6 +591,36 @@ void hid_debug_event(struct hid_device *hdev, char *buf) } EXPORT_SYMBOL_GPL(hid_debug_event); +void hid_dump_report(struct hid_device *hid, int type, u8 *data, + int size) +{ + struct hid_report_enum *report_enum; + char *buf; + unsigned int i; + + buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + + if (!buf) + return; + + report_enum = hid->report_enum + type; + + /* dump the report */ + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + "\nreport (size %u) (%snumbered) = ", size, + report_enum->numbered ? "" : "un"); + hid_debug_event(hid, buf); + + for (i = 0; i < size; i++) { + snprintf(buf, HID_DEBUG_BUFSIZE - 1, + " %02x", data[i]); + hid_debug_event(hid, buf); + } + hid_debug_event(hid, "\n"); + kfree(buf); +} +EXPORT_SYMBOL_GPL(hid_dump_report); + void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value) { char *buf; diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h index 53744fa1c8b7..8663f216c563 100644 --- a/include/linux/hid-debug.h +++ b/include/linux/hid-debug.h @@ -22,11 +22,12 @@ * */ -#define HID_DEBUG_BUFSIZE 512 - #ifdef CONFIG_DEBUG_FS +#define HID_DEBUG_BUFSIZE 512 + void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); +void hid_dump_report(struct hid_device *, int , u8 *, int); void hid_dump_device(struct hid_device *, struct seq_file *); void hid_dump_field(struct hid_field *, int, struct seq_file *); char *hid_resolv_usage(unsigned, struct seq_file *); @@ -50,6 +51,7 @@ struct hid_debug_list { #else #define hid_dump_input(a,b,c) do { } while (0) +#define hid_dump_report(a,b,c,d) do { } while (0) #define hid_dump_device(a,b) do { } while (0) #define hid_dump_field(a,b,c) do { } while (0) #define hid_resolv_usage(a,b) do { } while (0) From 2353f2bea307390e015493118e425152b8a5a431 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 16 Apr 2013 15:40:09 -0700 Subject: [PATCH 43/43] HID: protect hid_debug_list Accesses to hid_device->hid_debug_list are not serialized properly, which could result in SMP concurrency issues when HID debugfs events are accessesed by multiple userspace processess. Serialize all the list operations by a mutex. Spotted by Al Viro. Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-debug.c | 6 ++++++ include/linux/hid.h | 1 + 3 files changed, 8 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f86dd9708ca5..e7765ede339e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2319,6 +2319,7 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); + mutex_init(&hdev->debug_list_lock); sema_init(&hdev->driver_lock, 1); return hdev; diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 094cbcfe1e1a..7e56cb3855e3 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -580,12 +580,14 @@ void hid_debug_event(struct hid_device *hdev, char *buf) int i; struct hid_debug_list *list; + mutex_lock(&hdev->debug_list_lock); list_for_each_entry(list, &hdev->debug_list, node) { for (i = 0; i < strlen(buf); i++) list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] = buf[i]; list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; } + mutex_unlock(&hdev->debug_list_lock); wake_up_interruptible(&hdev->debug_wait); } @@ -990,7 +992,9 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) file->private_data = list; mutex_init(&list->read_mutex); + mutex_lock(&list->hdev->debug_list_lock); list_add_tail(&list->node, &list->hdev->debug_list); + mutex_unlock(&list->hdev->debug_list_lock); out: return err; @@ -1085,7 +1089,9 @@ static int hid_debug_events_release(struct inode *inode, struct file *file) { struct hid_debug_list *list = file->private_data; + mutex_lock(&list->hdev->debug_list_lock); list_del(&list->node); + mutex_unlock(&list->hdev->debug_list_lock); kfree(list->hid_debug_buf); kfree(list); diff --git a/include/linux/hid.h b/include/linux/hid.h index e14b465b1146..06579c72d195 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -512,6 +512,7 @@ struct hid_device { /* device report descriptor */ struct dentry *debug_rdesc; struct dentry *debug_events; struct list_head debug_list; + struct mutex debug_list_lock; wait_queue_head_t debug_wait; };