diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 23:56:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 23:56:28 -0400 |
commit | 172bfe09dc52aef29f9c5c0bd9f77a558120faf4 (patch) | |
tree | ffbcbb384ef1a46c469d484d0e6ede828099da1e /drivers | |
parent | a1b0a006ea7eb0c272cef33740a9563cd5190974 (diff) | |
parent | cf6f3976045468049038cfc45586b8b16c33dd05 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina:
"Some highlights:
- hid-sony improvements of Sixaxis device support by Antonio Ospite
- hid-hyperv driven devices can now be used as wakeup source, by
Dexuan Cui
- hid-lenovo driver is now more generic and supports more devices, by
Jamie Lentin
- hid-huion now supports wider range of tablets, by Nikolai
Kondrashov
- other various unsorted fixes and device ID additions"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (30 commits)
HID: hyperv: register as a wakeup source
HID: sony: Default initialize all elements of the LED max_brightness array to 1
HID: huion: Fix sparse warnings
HID: usbhid: Use flag HID_DISCONNECTED when a usb device is removed
HID: ignore jabra gn9350e
HID: cp2112: add I2C mode
HID: use multi input quirk for 22b9:2968
HID: rmi: only bind the hid-rmi driver to the mouse interface of composite USB devices
HID: rmi: check that report ids exist in the report_id_hash before accessing their size
HID: lenovo: Add support for Compact (BT|USB) keyboard
HID: lenovo: Don't call function in condition, show error codes
HID: lenovo: Prepare support for adding other devices
HID: lenovo: Rename hid-lenovo-tpkbd to hid-lenovo
HID: huion: Handle tablets with UC-Logic vendor ID
HID: huion: Switch to generating report descriptor
HID: huion: Don't ignore other interfaces
HID: huion: Use "tablet" instead of specific model
HID: add quirk for 0x04d9:0xa096 device
HID: i2c-hid: call the hid driver's suspend and resume callbacks
HID: rmi: change logging level of log messages related to unexpected reports
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/Kconfig | 18 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 11 | ||||
-rw-r--r-- | drivers/hid/hid-cp2112.c | 111 | ||||
-rw-r--r-- | drivers/hid/hid-huion.c | 265 | ||||
-rw-r--r-- | drivers/hid/hid-hyperv.c | 6 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-lenovo-tpkbd.c | 462 | ||||
-rw-r--r-- | drivers/hid/hid-lenovo.c | 708 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_debugfs.c | 9 | ||||
-rw-r--r-- | drivers/hid/hid-rmi.c | 67 | ||||
-rw-r--r-- | drivers/hid/hid-roccat-lua.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-sony.c | 132 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 15 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 8 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 |
16 files changed, 1181 insertions, 642 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5e79c6ad914f..e02cf59b048d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -331,18 +331,20 @@ config HID_LCPOWER | |||
331 | ---help--- | 331 | ---help--- |
332 | Support for LC-Power RC1000MCE RF remote control. | 332 | Support for LC-Power RC1000MCE RF remote control. |
333 | 333 | ||
334 | config HID_LENOVO_TPKBD | 334 | config HID_LENOVO |
335 | tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" | 335 | tristate "Lenovo / Thinkpad devices" |
336 | depends on HID | 336 | depends on HID |
337 | select NEW_LEDS | 337 | select NEW_LEDS |
338 | select LEDS_CLASS | 338 | select LEDS_CLASS |
339 | ---help--- | 339 | ---help--- |
340 | Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. | 340 | Support for Lenovo devices that are not fully compliant with HID standard. |
341 | 341 | ||
342 | Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint | 342 | Say Y if you want support for the non-compliant features of the Lenovo |
343 | and would like to use device-specific features like changing the | 343 | Thinkpad standalone keyboards, e.g: |
344 | sensitivity of the trackpoint, using the microphone mute button or | 344 | - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint |
345 | controlling the mute and microphone mute LEDs. | 345 | configuration) |
346 | - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys) | ||
347 | - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) | ||
346 | 348 | ||
347 | config HID_LOGITECH | 349 | config HID_LOGITECH |
348 | tristate "Logitech devices" if EXPERT | 350 | tristate "Logitech devices" if EXPERT |
@@ -785,7 +787,7 @@ config HID_XINMO | |||
785 | depends on HID | 787 | depends on HID |
786 | ---help--- | 788 | ---help--- |
787 | Support for Xin-Mo devices that are not fully compliant with the HID | 789 | Support for Xin-Mo devices that are not fully compliant with the HID |
788 | standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here | 790 | standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here |
789 | if you have a Xin-Mo Dual Arcade controller. | 791 | if you have a Xin-Mo Dual Arcade controller. |
790 | 792 | ||
791 | config HID_ZEROPLUS | 793 | config HID_ZEROPLUS |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a6fa6baf368e..5e96be3ab280 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -59,7 +59,7 @@ obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o | |||
59 | obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o | 59 | obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o |
60 | obj-$(CONFIG_HID_KYE) += hid-kye.o | 60 | obj-$(CONFIG_HID_KYE) += hid-kye.o |
61 | obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o | 61 | obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o |
62 | obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o | 62 | obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o |
63 | obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o | 63 | obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o |
64 | obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o | 64 | obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o |
65 | obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o | 65 | obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd1ea87..6c813c6092f8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -783,7 +783,9 @@ static int hid_scan_report(struct hid_device *hid) | |||
783 | * Vendor specific handlings | 783 | * Vendor specific handlings |
784 | */ | 784 | */ |
785 | if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && | 785 | if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && |
786 | (hid->group == HID_GROUP_GENERIC)) | 786 | (hid->group == HID_GROUP_GENERIC) && |
787 | /* only bind to the mouse interface of composite USB devices */ | ||
788 | (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) | ||
787 | /* hid-rmi should take care of them, not hid-generic */ | 789 | /* hid-rmi should take care of them, not hid-generic */ |
788 | hid->group = HID_GROUP_RMI; | 790 | hid->group = HID_GROUP_RMI; |
789 | 791 | ||
@@ -1782,7 +1784,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1782 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, | 1784 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, |
1783 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, | 1785 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, |
1784 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, | 1786 | { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, |
1785 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, | 1787 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, |
1786 | { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, | 1788 | { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, |
1787 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, | 1789 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, |
1788 | { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, | 1790 | { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, |
@@ -1796,8 +1798,10 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1796 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, | 1798 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, |
1797 | { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, | 1799 | { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, |
1798 | { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, | 1800 | { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, |
1799 | #if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD) | 1801 | #if IS_ENABLED(CONFIG_HID_LENOVO) |
1800 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, | 1802 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, |
1803 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, | ||
1804 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, | ||
1801 | #endif | 1805 | #endif |
1802 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, | 1806 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, |
1803 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, | 1807 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, |
@@ -2266,6 +2270,7 @@ static const struct hid_device_id hid_ignore_list[] = { | |||
2266 | { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, | 2270 | { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, |
2267 | { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, | 2271 | { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, |
2268 | { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, | 2272 | { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, |
2273 | { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_GN9350E) }, | ||
2269 | { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, | 2274 | { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, |
2270 | { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, | 2275 | { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, |
2271 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, | 2276 | { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, |
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 56be85a9a77c..a822db5a8338 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c | |||
@@ -240,8 +240,6 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, | |||
240 | u8 buf[5]; | 240 | u8 buf[5]; |
241 | int ret; | 241 | int ret; |
242 | 242 | ||
243 | cp2112_gpio_set(chip, offset, value); | ||
244 | |||
245 | ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, | 243 | ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, |
246 | sizeof(buf), HID_FEATURE_REPORT, | 244 | sizeof(buf), HID_FEATURE_REPORT, |
247 | HID_REQ_GET_REPORT); | 245 | HID_REQ_GET_REPORT); |
@@ -260,6 +258,12 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, | |||
260 | return ret; | 258 | return ret; |
261 | } | 259 | } |
262 | 260 | ||
261 | /* | ||
262 | * Set gpio value when output direction is already set, | ||
263 | * as specified in AN495, Rev. 0.2, cpt. 4.4 | ||
264 | */ | ||
265 | cp2112_gpio_set(chip, offset, value); | ||
266 | |||
263 | return 0; | 267 | return 0; |
264 | } | 268 | } |
265 | 269 | ||
@@ -425,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, | |||
425 | return data_length + 4; | 429 | return data_length + 4; |
426 | } | 430 | } |
427 | 431 | ||
432 | static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data, | ||
433 | u8 data_length) | ||
434 | { | ||
435 | struct cp2112_write_req_report *report = buf; | ||
436 | |||
437 | if (data_length > sizeof(report->data)) | ||
438 | return -EINVAL; | ||
439 | |||
440 | report->report = CP2112_DATA_WRITE_REQUEST; | ||
441 | report->slave_address = slave_address << 1; | ||
442 | report->length = data_length; | ||
443 | memcpy(report->data, data, data_length); | ||
444 | return data_length + 3; | ||
445 | } | ||
446 | |||
447 | static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, | ||
448 | int num) | ||
449 | { | ||
450 | struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; | ||
451 | struct hid_device *hdev = dev->hdev; | ||
452 | u8 buf[64]; | ||
453 | ssize_t count; | ||
454 | unsigned int retries; | ||
455 | int ret; | ||
456 | |||
457 | hid_dbg(hdev, "I2C %d messages\n", num); | ||
458 | |||
459 | if (num != 1) { | ||
460 | hid_err(hdev, | ||
461 | "Multi-message I2C transactions not supported\n"); | ||
462 | return -EOPNOTSUPP; | ||
463 | } | ||
464 | |||
465 | if (msgs->flags & I2C_M_RD) | ||
466 | count = cp2112_read_req(buf, msgs->addr, msgs->len); | ||
467 | else | ||
468 | count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, | ||
469 | msgs->len); | ||
470 | |||
471 | if (count < 0) | ||
472 | return count; | ||
473 | |||
474 | ret = hid_hw_power(hdev, PM_HINT_FULLON); | ||
475 | if (ret < 0) { | ||
476 | hid_err(hdev, "power management error: %d\n", ret); | ||
477 | return ret; | ||
478 | } | ||
479 | |||
480 | ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); | ||
481 | if (ret < 0) { | ||
482 | hid_warn(hdev, "Error starting transaction: %d\n", ret); | ||
483 | goto power_normal; | ||
484 | } | ||
485 | |||
486 | for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { | ||
487 | ret = cp2112_xfer_status(dev); | ||
488 | if (-EBUSY == ret) | ||
489 | continue; | ||
490 | if (ret < 0) | ||
491 | goto power_normal; | ||
492 | break; | ||
493 | } | ||
494 | |||
495 | if (XFER_STATUS_RETRIES <= retries) { | ||
496 | hid_warn(hdev, "Transfer timed out, cancelling.\n"); | ||
497 | buf[0] = CP2112_CANCEL_TRANSFER; | ||
498 | buf[1] = 0x01; | ||
499 | |||
500 | ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); | ||
501 | if (ret < 0) | ||
502 | hid_warn(hdev, "Error cancelling transaction: %d\n", | ||
503 | ret); | ||
504 | |||
505 | ret = -ETIMEDOUT; | ||
506 | goto power_normal; | ||
507 | } | ||
508 | |||
509 | if (!(msgs->flags & I2C_M_RD)) | ||
510 | goto finish; | ||
511 | |||
512 | ret = cp2112_read(dev, msgs->buf, msgs->len); | ||
513 | if (ret < 0) | ||
514 | goto power_normal; | ||
515 | if (ret != msgs->len) { | ||
516 | hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len); | ||
517 | ret = -EIO; | ||
518 | goto power_normal; | ||
519 | } | ||
520 | |||
521 | finish: | ||
522 | /* return the number of transferred messages */ | ||
523 | ret = 1; | ||
524 | |||
525 | power_normal: | ||
526 | hid_hw_power(hdev, PM_HINT_NORMAL); | ||
527 | hid_dbg(hdev, "I2C transfer finished: %d\n", ret); | ||
528 | return ret; | ||
529 | } | ||
530 | |||
428 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, | 531 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, |
429 | unsigned short flags, char read_write, u8 command, | 532 | unsigned short flags, char read_write, u8 command, |
430 | int size, union i2c_smbus_data *data) | 533 | int size, union i2c_smbus_data *data) |
@@ -591,7 +694,8 @@ power_normal: | |||
591 | 694 | ||
592 | static u32 cp2112_functionality(struct i2c_adapter *adap) | 695 | static u32 cp2112_functionality(struct i2c_adapter *adap) |
593 | { | 696 | { |
594 | return I2C_FUNC_SMBUS_BYTE | | 697 | return I2C_FUNC_I2C | |
698 | I2C_FUNC_SMBUS_BYTE | | ||
595 | I2C_FUNC_SMBUS_BYTE_DATA | | 699 | I2C_FUNC_SMBUS_BYTE_DATA | |
596 | I2C_FUNC_SMBUS_WORD_DATA | | 700 | I2C_FUNC_SMBUS_WORD_DATA | |
597 | I2C_FUNC_SMBUS_BLOCK_DATA | | 701 | I2C_FUNC_SMBUS_BLOCK_DATA | |
@@ -601,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap) | |||
601 | } | 705 | } |
602 | 706 | ||
603 | static const struct i2c_algorithm smbus_algorithm = { | 707 | static const struct i2c_algorithm smbus_algorithm = { |
708 | .master_xfer = cp2112_i2c_xfer, | ||
604 | .smbus_xfer = cp2112_xfer, | 709 | .smbus_xfer = cp2112_xfer, |
605 | .functionality = cp2112_functionality, | 710 | .functionality = cp2112_functionality, |
606 | }; | 711 | }; |
diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index cbf4da4689ba..60f44cd1b0ed 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * HID driver for Huion devices not fully compliant with HID standard | 2 | * HID driver for Huion devices not fully compliant with HID standard |
3 | * | 3 | * |
4 | * Copyright (c) 2013 Martin Rusko | 4 | * Copyright (c) 2013 Martin Rusko |
5 | * Copyright (c) 2014 Nikolai Kondrashov | ||
5 | */ | 6 | */ |
6 | 7 | ||
7 | /* | 8 | /* |
@@ -15,67 +16,89 @@ | |||
15 | #include <linux/hid.h> | 16 | #include <linux/hid.h> |
16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
17 | #include <linux/usb.h> | 18 | #include <linux/usb.h> |
19 | #include <asm/unaligned.h> | ||
18 | #include "usbhid/usbhid.h" | 20 | #include "usbhid/usbhid.h" |
19 | 21 | ||
20 | #include "hid-ids.h" | 22 | #include "hid-ids.h" |
21 | 23 | ||
22 | /* Original Huion 580 report descriptor size */ | 24 | /* Report descriptor template placeholder head */ |
23 | #define HUION_580_RDESC_ORIG_SIZE 177 | 25 | #define HUION_PH_HEAD 0xFE, 0xED, 0x1D |
24 | 26 | ||
25 | /* Fixed Huion 580 report descriptor */ | 27 | /* Report descriptor template placeholder IDs */ |
26 | static __u8 huion_580_rdesc_fixed[] = { | 28 | enum huion_ph_id { |
27 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 29 | HUION_PH_ID_X_LM, |
28 | 0x09, 0x02, /* Usage (Pen), */ | 30 | HUION_PH_ID_X_PM, |
29 | 0xA1, 0x01, /* Collection (Application), */ | 31 | HUION_PH_ID_Y_LM, |
30 | 0x85, 0x07, /* Report ID (7), */ | 32 | HUION_PH_ID_Y_PM, |
31 | 0x09, 0x20, /* Usage (Stylus), */ | 33 | HUION_PH_ID_PRESSURE_LM, |
32 | 0xA0, /* Collection (Physical), */ | 34 | HUION_PH_ID_NUM |
33 | 0x14, /* Logical Minimum (0), */ | 35 | }; |
34 | 0x25, 0x01, /* Logical Maximum (1), */ | 36 | |
35 | 0x75, 0x01, /* Report Size (1), */ | 37 | /* Report descriptor template placeholder */ |
36 | 0x09, 0x42, /* Usage (Tip Switch), */ | 38 | #define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID |
37 | 0x09, 0x44, /* Usage (Barrel Switch), */ | 39 | |
38 | 0x09, 0x46, /* Usage (Tablet Pick), */ | 40 | /* Fixed report descriptor template */ |
39 | 0x95, 0x03, /* Report Count (3), */ | 41 | static const __u8 huion_tablet_rdesc_template[] = { |
40 | 0x81, 0x02, /* Input (Variable), */ | 42 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
41 | 0x95, 0x03, /* Report Count (3), */ | 43 | 0x09, 0x02, /* Usage (Pen), */ |
42 | 0x81, 0x03, /* Input (Constant, Variable), */ | 44 | 0xA1, 0x01, /* Collection (Application), */ |
43 | 0x09, 0x32, /* Usage (In Range), */ | 45 | 0x85, 0x07, /* Report ID (7), */ |
44 | 0x95, 0x01, /* Report Count (1), */ | 46 | 0x09, 0x20, /* Usage (Stylus), */ |
45 | 0x81, 0x02, /* Input (Variable), */ | 47 | 0xA0, /* Collection (Physical), */ |
46 | 0x95, 0x01, /* Report Count (1), */ | 48 | 0x14, /* Logical Minimum (0), */ |
47 | 0x81, 0x03, /* Input (Constant, Variable), */ | 49 | 0x25, 0x01, /* Logical Maximum (1), */ |
48 | 0x75, 0x10, /* Report Size (16), */ | 50 | 0x75, 0x01, /* Report Size (1), */ |
49 | 0x95, 0x01, /* Report Count (1), */ | 51 | 0x09, 0x42, /* Usage (Tip Switch), */ |
50 | 0xA4, /* Push, */ | 52 | 0x09, 0x44, /* Usage (Barrel Switch), */ |
51 | 0x05, 0x01, /* Usage Page (Desktop), */ | 53 | 0x09, 0x46, /* Usage (Tablet Pick), */ |
52 | 0x65, 0x13, /* Unit (Inch), */ | 54 | 0x95, 0x03, /* Report Count (3), */ |
53 | 0x55, 0xFD, /* Unit Exponent (-3), */ | 55 | 0x81, 0x02, /* Input (Variable), */ |
54 | 0x34, /* Physical Minimum (0), */ | 56 | 0x95, 0x03, /* Report Count (3), */ |
55 | 0x09, 0x30, /* Usage (X), */ | 57 | 0x81, 0x03, /* Input (Constant, Variable), */ |
56 | 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ | 58 | 0x09, 0x32, /* Usage (In Range), */ |
57 | 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ | 59 | 0x95, 0x01, /* Report Count (1), */ |
58 | 0x81, 0x02, /* Input (Variable), */ | 60 | 0x81, 0x02, /* Input (Variable), */ |
59 | 0x09, 0x31, /* Usage (Y), */ | 61 | 0x95, 0x01, /* Report Count (1), */ |
60 | 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ | 62 | 0x81, 0x03, /* Input (Constant, Variable), */ |
61 | 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ | 63 | 0x75, 0x10, /* Report Size (16), */ |
62 | 0x81, 0x02, /* Input (Variable), */ | 64 | 0x95, 0x01, /* Report Count (1), */ |
63 | 0xB4, /* Pop, */ | 65 | 0xA4, /* Push, */ |
64 | 0x09, 0x30, /* Usage (Tip Pressure), */ | 66 | 0x05, 0x01, /* Usage Page (Desktop), */ |
65 | 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ | 67 | 0x65, 0x13, /* Unit (Inch), */ |
66 | 0x81, 0x02, /* Input (Variable), */ | 68 | 0x55, 0xFD, /* Unit Exponent (-3), */ |
67 | 0xC0, /* End Collection, */ | 69 | 0x34, /* Physical Minimum (0), */ |
68 | 0xC0 /* End Collection */ | 70 | 0x09, 0x30, /* Usage (X), */ |
71 | 0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ | ||
72 | 0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ | ||
73 | 0x81, 0x02, /* Input (Variable), */ | ||
74 | 0x09, 0x31, /* Usage (Y), */ | ||
75 | 0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ | ||
76 | 0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ | ||
77 | 0x81, 0x02, /* Input (Variable), */ | ||
78 | 0xB4, /* Pop, */ | ||
79 | 0x09, 0x30, /* Usage (Tip Pressure), */ | ||
80 | 0x27, | ||
81 | HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */ | ||
82 | 0x81, 0x02, /* Input (Variable), */ | ||
83 | 0xC0, /* End Collection, */ | ||
84 | 0xC0 /* End Collection */ | ||
85 | }; | ||
86 | |||
87 | /* Driver data */ | ||
88 | struct huion_drvdata { | ||
89 | __u8 *rdesc; | ||
90 | unsigned int rsize; | ||
69 | }; | 91 | }; |
70 | 92 | ||
71 | static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 93 | static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
72 | unsigned int *rsize) | 94 | unsigned int *rsize) |
73 | { | 95 | { |
96 | struct huion_drvdata *drvdata = hid_get_drvdata(hdev); | ||
74 | switch (hdev->product) { | 97 | switch (hdev->product) { |
75 | case USB_DEVICE_ID_HUION_580: | 98 | case USB_DEVICE_ID_HUION_TABLET: |
76 | if (*rsize == HUION_580_RDESC_ORIG_SIZE) { | 99 | if (drvdata->rdesc != NULL) { |
77 | rdesc = huion_580_rdesc_fixed; | 100 | rdesc = drvdata->rdesc; |
78 | *rsize = sizeof(huion_580_rdesc_fixed); | 101 | *rsize = drvdata->rsize; |
79 | } | 102 | } |
80 | break; | 103 | break; |
81 | } | 104 | } |
@@ -83,82 +106,144 @@ static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
83 | } | 106 | } |
84 | 107 | ||
85 | /** | 108 | /** |
86 | * Enable fully-functional tablet mode by reading special string | 109 | * Enable fully-functional tablet mode and determine device parameters. |
87 | * descriptor. | ||
88 | * | 110 | * |
89 | * @hdev: HID device | 111 | * @hdev: HID device |
90 | * | ||
91 | * The specific string descriptor and data were discovered by sniffing | ||
92 | * the Windows driver traffic. | ||
93 | */ | 112 | */ |
94 | static int huion_tablet_enable(struct hid_device *hdev) | 113 | static int huion_tablet_enable(struct hid_device *hdev) |
95 | { | 114 | { |
96 | int rc; | 115 | int rc; |
97 | char buf[22]; | 116 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); |
117 | struct huion_drvdata *drvdata = hid_get_drvdata(hdev); | ||
118 | __le16 buf[6]; | ||
98 | 119 | ||
99 | rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); | 120 | /* |
100 | if (rc < 0) | 121 | * Read string descriptor containing tablet parameters. The specific |
101 | return rc; | 122 | * string descriptor and data were discovered by sniffing the Windows |
123 | * driver traffic. | ||
124 | * NOTE: This enables fully-functional tablet mode. | ||
125 | */ | ||
126 | rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
127 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, | ||
128 | (USB_DT_STRING << 8) + 0x64, | ||
129 | 0x0409, buf, sizeof(buf), | ||
130 | USB_CTRL_GET_TIMEOUT); | ||
131 | if (rc == -EPIPE) | ||
132 | hid_warn(hdev, "device parameters not found\n"); | ||
133 | else if (rc < 0) | ||
134 | hid_warn(hdev, "failed to get device parameters: %d\n", rc); | ||
135 | else if (rc != sizeof(buf)) | ||
136 | hid_warn(hdev, "invalid device parameters\n"); | ||
137 | else { | ||
138 | s32 params[HUION_PH_ID_NUM]; | ||
139 | s32 resolution; | ||
140 | __u8 *p; | ||
141 | s32 v; | ||
142 | |||
143 | /* Extract device parameters */ | ||
144 | params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[1]); | ||
145 | params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[2]); | ||
146 | params[HUION_PH_ID_PRESSURE_LM] = le16_to_cpu(buf[4]); | ||
147 | resolution = le16_to_cpu(buf[5]); | ||
148 | if (resolution == 0) { | ||
149 | params[HUION_PH_ID_X_PM] = 0; | ||
150 | params[HUION_PH_ID_Y_PM] = 0; | ||
151 | } else { | ||
152 | params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * | ||
153 | 1000 / resolution; | ||
154 | params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * | ||
155 | 1000 / resolution; | ||
156 | } | ||
157 | |||
158 | /* Allocate fixed report descriptor */ | ||
159 | drvdata->rdesc = devm_kmalloc(&hdev->dev, | ||
160 | sizeof(huion_tablet_rdesc_template), | ||
161 | GFP_KERNEL); | ||
162 | if (drvdata->rdesc == NULL) { | ||
163 | hid_err(hdev, "failed to allocate fixed rdesc\n"); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | drvdata->rsize = sizeof(huion_tablet_rdesc_template); | ||
167 | |||
168 | /* Format fixed report descriptor */ | ||
169 | memcpy(drvdata->rdesc, huion_tablet_rdesc_template, | ||
170 | drvdata->rsize); | ||
171 | for (p = drvdata->rdesc; | ||
172 | p <= drvdata->rdesc + drvdata->rsize - 4;) { | ||
173 | if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && | ||
174 | p[3] < sizeof(params)) { | ||
175 | v = params[p[3]]; | ||
176 | put_unaligned(cpu_to_le32(v), (s32 *)p); | ||
177 | p += 4; | ||
178 | } else { | ||
179 | p++; | ||
180 | } | ||
181 | } | ||
182 | } | ||
102 | 183 | ||
103 | return 0; | 184 | return 0; |
104 | } | 185 | } |
105 | 186 | ||
106 | static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) | 187 | static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) |
107 | { | 188 | { |
108 | int ret; | 189 | int rc; |
109 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | 190 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
191 | struct huion_drvdata *drvdata; | ||
192 | |||
193 | /* Allocate and assign driver data */ | ||
194 | drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
195 | if (drvdata == NULL) { | ||
196 | hid_err(hdev, "failed to allocate driver data\n"); | ||
197 | return -ENOMEM; | ||
198 | } | ||
199 | hid_set_drvdata(hdev, drvdata); | ||
110 | 200 | ||
111 | /* Ignore interfaces 1 (mouse) and 2 (keyboard) for Huion 580 tablet, | ||
112 | * as they are not used | ||
113 | */ | ||
114 | switch (id->product) { | 201 | switch (id->product) { |
115 | case USB_DEVICE_ID_HUION_580: | 202 | case USB_DEVICE_ID_HUION_TABLET: |
116 | if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) | 203 | /* If this is the pen interface */ |
117 | return -ENODEV; | 204 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { |
205 | rc = huion_tablet_enable(hdev); | ||
206 | if (rc) { | ||
207 | hid_err(hdev, "tablet enabling failed\n"); | ||
208 | return rc; | ||
209 | } | ||
210 | } | ||
118 | break; | 211 | break; |
119 | } | 212 | } |
120 | 213 | ||
121 | ret = hid_parse(hdev); | 214 | rc = hid_parse(hdev); |
122 | if (ret) { | 215 | if (rc) { |
123 | hid_err(hdev, "parse failed\n"); | 216 | hid_err(hdev, "parse failed\n"); |
124 | goto err; | 217 | return rc; |
125 | } | 218 | } |
126 | 219 | ||
127 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | 220 | rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
128 | if (ret) { | 221 | if (rc) { |
129 | hid_err(hdev, "hw start failed\n"); | 222 | hid_err(hdev, "hw start failed\n"); |
130 | goto err; | 223 | return rc; |
131 | } | ||
132 | |||
133 | switch (id->product) { | ||
134 | case USB_DEVICE_ID_HUION_580: | ||
135 | ret = huion_tablet_enable(hdev); | ||
136 | if (ret) { | ||
137 | hid_err(hdev, "tablet enabling failed\n"); | ||
138 | goto enabling_err; | ||
139 | } | ||
140 | break; | ||
141 | } | 224 | } |
142 | 225 | ||
143 | return 0; | 226 | return 0; |
144 | enabling_err: | ||
145 | hid_hw_stop(hdev); | ||
146 | err: | ||
147 | return ret; | ||
148 | } | 227 | } |
149 | 228 | ||
150 | static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, | 229 | static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, |
151 | u8 *data, int size) | 230 | u8 *data, int size) |
152 | { | 231 | { |
153 | /* If this is a pen input report then invert the in-range bit */ | 232 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
154 | if (report->type == HID_INPUT_REPORT && report->id == 0x07 && size >= 2) | 233 | |
234 | /* If this is a pen input report */ | ||
235 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0 && | ||
236 | report->type == HID_INPUT_REPORT && | ||
237 | report->id == 0x07 && size >= 2) | ||
238 | /* Invert the in-range bit */ | ||
155 | data[1] ^= 0x40; | 239 | data[1] ^= 0x40; |
156 | 240 | ||
157 | return 0; | 241 | return 0; |
158 | } | 242 | } |
159 | 243 | ||
160 | static const struct hid_device_id huion_devices[] = { | 244 | static const struct hid_device_id huion_devices[] = { |
161 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, | 245 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, |
246 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, | ||
162 | { } | 247 | { } |
163 | }; | 248 | }; |
164 | MODULE_DEVICE_TABLE(hid, huion_devices); | 249 | MODULE_DEVICE_TABLE(hid, huion_devices); |
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index f52dbcb7133b..31fad641b744 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c | |||
@@ -308,6 +308,9 @@ static void mousevsc_on_receive(struct hv_device *device, | |||
308 | memcpy(input_dev->input_buf, input_report->buffer, len); | 308 | memcpy(input_dev->input_buf, input_report->buffer, len); |
309 | hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, | 309 | hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, |
310 | input_dev->input_buf, len, 1); | 310 | input_dev->input_buf, len, 1); |
311 | |||
312 | pm_wakeup_event(&input_dev->device->device, 0); | ||
313 | |||
311 | break; | 314 | break; |
312 | default: | 315 | default: |
313 | pr_err("unsupported hid msg type - type %d len %d", | 316 | pr_err("unsupported hid msg type - type %d len %d", |
@@ -549,6 +552,8 @@ static int mousevsc_probe(struct hv_device *device, | |||
549 | goto probe_err2; | 552 | goto probe_err2; |
550 | } | 553 | } |
551 | 554 | ||
555 | device_init_wakeup(&device->device, true); | ||
556 | |||
552 | input_dev->connected = true; | 557 | input_dev->connected = true; |
553 | input_dev->init_complete = true; | 558 | input_dev->init_complete = true; |
554 | 559 | ||
@@ -571,6 +576,7 @@ static int mousevsc_remove(struct hv_device *dev) | |||
571 | { | 576 | { |
572 | struct mousevsc_dev *input_dev = hv_get_drvdata(dev); | 577 | struct mousevsc_dev *input_dev = hv_get_drvdata(dev); |
573 | 578 | ||
579 | device_init_wakeup(&dev->device, false); | ||
574 | vmbus_close(dev->channel); | 580 | vmbus_close(dev->channel); |
575 | hid_hw_stop(input_dev->hid_device); | 581 | hid_hw_stop(input_dev->hid_device); |
576 | hid_destroy_device(input_dev->hid_device); | 582 | hid_destroy_device(input_dev->hid_device); |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 48b66bbffc94..d53bdda26207 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -448,7 +448,7 @@ | |||
448 | #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 | 448 | #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 |
449 | 449 | ||
450 | #define USB_VENDOR_ID_HUION 0x256c | 450 | #define USB_VENDOR_ID_HUION 0x256c |
451 | #define USB_DEVICE_ID_HUION_580 0x006e | 451 | #define USB_DEVICE_ID_HUION_TABLET 0x006e |
452 | 452 | ||
453 | #define USB_VENDOR_ID_IDEACOM 0x1cb6 | 453 | #define USB_VENDOR_ID_IDEACOM 0x1cb6 |
454 | #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 | 454 | #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 |
@@ -479,6 +479,7 @@ | |||
479 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 | 479 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 |
480 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 | 480 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 |
481 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 | 481 | #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 |
482 | #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096 | ||
482 | 483 | ||
483 | #define USB_VENDOR_ID_IMATION 0x0718 | 484 | #define USB_VENDOR_ID_IMATION 0x0718 |
484 | #define USB_DEVICE_ID_DISC_STAKKA 0xd000 | 485 | #define USB_DEVICE_ID_DISC_STAKKA 0xd000 |
@@ -489,6 +490,7 @@ | |||
489 | #define USB_VENDOR_ID_JABRA 0x0b0e | 490 | #define USB_VENDOR_ID_JABRA 0x0b0e |
490 | #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 | 491 | #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 |
491 | #define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 | 492 | #define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 |
493 | #define USB_DEVICE_ID_JABRA_GN9350E 0x9350 | ||
492 | 494 | ||
493 | #define USB_VENDOR_ID_JESS 0x0c45 | 495 | #define USB_VENDOR_ID_JESS 0x0c45 |
494 | #define USB_DEVICE_ID_JESS_YUREX 0x1010 | 496 | #define USB_DEVICE_ID_JESS_YUREX 0x1010 |
@@ -561,6 +563,8 @@ | |||
561 | 563 | ||
562 | #define USB_VENDOR_ID_LENOVO 0x17ef | 564 | #define USB_VENDOR_ID_LENOVO 0x17ef |
563 | #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 | 565 | #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 |
566 | #define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 | ||
567 | #define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048 | ||
564 | 568 | ||
565 | #define USB_VENDOR_ID_LG 0x1fd2 | 569 | #define USB_VENDOR_ID_LG 0x1fd2 |
566 | #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 | 570 | #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 |
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c deleted file mode 100644 index 2d25b6cbbc05..000000000000 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ /dev/null | |||
@@ -1,462 +0,0 @@ | |||
1 | /* | ||
2 | * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint | ||
3 | * | ||
4 | * Copyright (c) 2012 Bernhard Seibold | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/sysfs.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/hid.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/leds.h> | ||
20 | |||
21 | #include "hid-ids.h" | ||
22 | |||
23 | /* This is only used for the trackpoint part of the driver, hence _tp */ | ||
24 | struct tpkbd_data_pointer { | ||
25 | int led_state; | ||
26 | struct led_classdev led_mute; | ||
27 | struct led_classdev led_micmute; | ||
28 | int press_to_select; | ||
29 | int dragging; | ||
30 | int release_to_select; | ||
31 | int select_right; | ||
32 | int sensitivity; | ||
33 | int press_speed; | ||
34 | }; | ||
35 | |||
36 | #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) | ||
37 | |||
38 | static int tpkbd_input_mapping(struct hid_device *hdev, | ||
39 | struct hid_input *hi, struct hid_field *field, | ||
40 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
41 | { | ||
42 | if (usage->hid == (HID_UP_BUTTON | 0x0010)) { | ||
43 | /* mark the device as pointer */ | ||
44 | hid_set_drvdata(hdev, (void *)1); | ||
45 | map_key_clear(KEY_MICMUTE); | ||
46 | return 1; | ||
47 | } | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | #undef map_key_clear | ||
52 | |||
53 | static int tpkbd_features_set(struct hid_device *hdev) | ||
54 | { | ||
55 | struct hid_report *report; | ||
56 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
57 | |||
58 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; | ||
59 | |||
60 | report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; | ||
61 | report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; | ||
62 | report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; | ||
63 | report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; | ||
64 | report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver | ||
65 | report->field[2]->value[0] = data_pointer->sensitivity; | ||
66 | report->field[3]->value[0] = data_pointer->press_speed; | ||
67 | |||
68 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static ssize_t pointer_press_to_select_show(struct device *dev, | ||
73 | struct device_attribute *attr, | ||
74 | char *buf) | ||
75 | { | ||
76 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
77 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
78 | |||
79 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); | ||
80 | } | ||
81 | |||
82 | static ssize_t pointer_press_to_select_store(struct device *dev, | ||
83 | struct device_attribute *attr, | ||
84 | const char *buf, | ||
85 | size_t count) | ||
86 | { | ||
87 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
88 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
89 | int value; | ||
90 | |||
91 | if (kstrtoint(buf, 10, &value)) | ||
92 | return -EINVAL; | ||
93 | if (value < 0 || value > 1) | ||
94 | return -EINVAL; | ||
95 | |||
96 | data_pointer->press_to_select = value; | ||
97 | tpkbd_features_set(hdev); | ||
98 | |||
99 | return count; | ||
100 | } | ||
101 | |||
102 | static ssize_t pointer_dragging_show(struct device *dev, | ||
103 | struct device_attribute *attr, | ||
104 | char *buf) | ||
105 | { | ||
106 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
107 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
108 | |||
109 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); | ||
110 | } | ||
111 | |||
112 | static ssize_t pointer_dragging_store(struct device *dev, | ||
113 | struct device_attribute *attr, | ||
114 | const char *buf, | ||
115 | size_t count) | ||
116 | { | ||
117 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
118 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
119 | int value; | ||
120 | |||
121 | if (kstrtoint(buf, 10, &value)) | ||
122 | return -EINVAL; | ||
123 | if (value < 0 || value > 1) | ||
124 | return -EINVAL; | ||
125 | |||
126 | data_pointer->dragging = value; | ||
127 | tpkbd_features_set(hdev); | ||
128 | |||
129 | return count; | ||
130 | } | ||
131 | |||
132 | static ssize_t pointer_release_to_select_show(struct device *dev, | ||
133 | struct device_attribute *attr, | ||
134 | char *buf) | ||
135 | { | ||
136 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
137 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
138 | |||
139 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); | ||
140 | } | ||
141 | |||
142 | static ssize_t pointer_release_to_select_store(struct device *dev, | ||
143 | struct device_attribute *attr, | ||
144 | const char *buf, | ||
145 | size_t count) | ||
146 | { | ||
147 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
148 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
149 | int value; | ||
150 | |||
151 | if (kstrtoint(buf, 10, &value)) | ||
152 | return -EINVAL; | ||
153 | if (value < 0 || value > 1) | ||
154 | return -EINVAL; | ||
155 | |||
156 | data_pointer->release_to_select = value; | ||
157 | tpkbd_features_set(hdev); | ||
158 | |||
159 | return count; | ||
160 | } | ||
161 | |||
162 | static ssize_t pointer_select_right_show(struct device *dev, | ||
163 | struct device_attribute *attr, | ||
164 | char *buf) | ||
165 | { | ||
166 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
167 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
168 | |||
169 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); | ||
170 | } | ||
171 | |||
172 | static ssize_t pointer_select_right_store(struct device *dev, | ||
173 | struct device_attribute *attr, | ||
174 | const char *buf, | ||
175 | size_t count) | ||
176 | { | ||
177 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
178 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
179 | int value; | ||
180 | |||
181 | if (kstrtoint(buf, 10, &value)) | ||
182 | return -EINVAL; | ||
183 | if (value < 0 || value > 1) | ||
184 | return -EINVAL; | ||
185 | |||
186 | data_pointer->select_right = value; | ||
187 | tpkbd_features_set(hdev); | ||
188 | |||
189 | return count; | ||
190 | } | ||
191 | |||
192 | static ssize_t pointer_sensitivity_show(struct device *dev, | ||
193 | struct device_attribute *attr, | ||
194 | char *buf) | ||
195 | { | ||
196 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
197 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
198 | |||
199 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
200 | data_pointer->sensitivity); | ||
201 | } | ||
202 | |||
203 | static ssize_t pointer_sensitivity_store(struct device *dev, | ||
204 | struct device_attribute *attr, | ||
205 | const char *buf, | ||
206 | size_t count) | ||
207 | { | ||
208 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
209 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
210 | int value; | ||
211 | |||
212 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
213 | return -EINVAL; | ||
214 | |||
215 | data_pointer->sensitivity = value; | ||
216 | tpkbd_features_set(hdev); | ||
217 | |||
218 | return count; | ||
219 | } | ||
220 | |||
221 | static ssize_t pointer_press_speed_show(struct device *dev, | ||
222 | struct device_attribute *attr, | ||
223 | char *buf) | ||
224 | { | ||
225 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
226 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
227 | |||
228 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
229 | data_pointer->press_speed); | ||
230 | } | ||
231 | |||
232 | static ssize_t pointer_press_speed_store(struct device *dev, | ||
233 | struct device_attribute *attr, | ||
234 | const char *buf, | ||
235 | size_t count) | ||
236 | { | ||
237 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
238 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
239 | int value; | ||
240 | |||
241 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
242 | return -EINVAL; | ||
243 | |||
244 | data_pointer->press_speed = value; | ||
245 | tpkbd_features_set(hdev); | ||
246 | |||
247 | return count; | ||
248 | } | ||
249 | |||
250 | static struct device_attribute dev_attr_pointer_press_to_select = | ||
251 | __ATTR(press_to_select, S_IWUSR | S_IRUGO, | ||
252 | pointer_press_to_select_show, | ||
253 | pointer_press_to_select_store); | ||
254 | |||
255 | static struct device_attribute dev_attr_pointer_dragging = | ||
256 | __ATTR(dragging, S_IWUSR | S_IRUGO, | ||
257 | pointer_dragging_show, | ||
258 | pointer_dragging_store); | ||
259 | |||
260 | static struct device_attribute dev_attr_pointer_release_to_select = | ||
261 | __ATTR(release_to_select, S_IWUSR | S_IRUGO, | ||
262 | pointer_release_to_select_show, | ||
263 | pointer_release_to_select_store); | ||
264 | |||
265 | static struct device_attribute dev_attr_pointer_select_right = | ||
266 | __ATTR(select_right, S_IWUSR | S_IRUGO, | ||
267 | pointer_select_right_show, | ||
268 | pointer_select_right_store); | ||
269 | |||
270 | static struct device_attribute dev_attr_pointer_sensitivity = | ||
271 | __ATTR(sensitivity, S_IWUSR | S_IRUGO, | ||
272 | pointer_sensitivity_show, | ||
273 | pointer_sensitivity_store); | ||
274 | |||
275 | static struct device_attribute dev_attr_pointer_press_speed = | ||
276 | __ATTR(press_speed, S_IWUSR | S_IRUGO, | ||
277 | pointer_press_speed_show, | ||
278 | pointer_press_speed_store); | ||
279 | |||
280 | static struct attribute *tpkbd_attributes_pointer[] = { | ||
281 | &dev_attr_pointer_press_to_select.attr, | ||
282 | &dev_attr_pointer_dragging.attr, | ||
283 | &dev_attr_pointer_release_to_select.attr, | ||
284 | &dev_attr_pointer_select_right.attr, | ||
285 | &dev_attr_pointer_sensitivity.attr, | ||
286 | &dev_attr_pointer_press_speed.attr, | ||
287 | NULL | ||
288 | }; | ||
289 | |||
290 | static const struct attribute_group tpkbd_attr_group_pointer = { | ||
291 | .attrs = tpkbd_attributes_pointer, | ||
292 | }; | ||
293 | |||
294 | static enum led_brightness tpkbd_led_brightness_get( | ||
295 | struct led_classdev *led_cdev) | ||
296 | { | ||
297 | struct device *dev = led_cdev->dev->parent; | ||
298 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
299 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
300 | int led_nr = 0; | ||
301 | |||
302 | if (led_cdev == &data_pointer->led_micmute) | ||
303 | led_nr = 1; | ||
304 | |||
305 | return data_pointer->led_state & (1 << led_nr) | ||
306 | ? LED_FULL | ||
307 | : LED_OFF; | ||
308 | } | ||
309 | |||
310 | static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, | ||
311 | enum led_brightness value) | ||
312 | { | ||
313 | struct device *dev = led_cdev->dev->parent; | ||
314 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
315 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
316 | struct hid_report *report; | ||
317 | int led_nr = 0; | ||
318 | |||
319 | if (led_cdev == &data_pointer->led_micmute) | ||
320 | led_nr = 1; | ||
321 | |||
322 | if (value == LED_OFF) | ||
323 | data_pointer->led_state &= ~(1 << led_nr); | ||
324 | else | ||
325 | data_pointer->led_state |= 1 << led_nr; | ||
326 | |||
327 | report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; | ||
328 | report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; | ||
329 | report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; | ||
330 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
331 | } | ||
332 | |||
333 | static int tpkbd_probe_tp(struct hid_device *hdev) | ||
334 | { | ||
335 | struct device *dev = &hdev->dev; | ||
336 | struct tpkbd_data_pointer *data_pointer; | ||
337 | size_t name_sz = strlen(dev_name(dev)) + 16; | ||
338 | char *name_mute, *name_micmute; | ||
339 | int i; | ||
340 | |||
341 | /* Validate required reports. */ | ||
342 | for (i = 0; i < 4; i++) { | ||
343 | if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) | ||
344 | return -ENODEV; | ||
345 | } | ||
346 | if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) | ||
347 | return -ENODEV; | ||
348 | |||
349 | if (sysfs_create_group(&hdev->dev.kobj, | ||
350 | &tpkbd_attr_group_pointer)) { | ||
351 | hid_warn(hdev, "Could not create sysfs group\n"); | ||
352 | } | ||
353 | |||
354 | data_pointer = devm_kzalloc(&hdev->dev, | ||
355 | sizeof(struct tpkbd_data_pointer), | ||
356 | GFP_KERNEL); | ||
357 | if (data_pointer == NULL) { | ||
358 | hid_err(hdev, "Could not allocate memory for driver data\n"); | ||
359 | return -ENOMEM; | ||
360 | } | ||
361 | |||
362 | // set same default values as windows driver | ||
363 | data_pointer->sensitivity = 0xa0; | ||
364 | data_pointer->press_speed = 0x38; | ||
365 | |||
366 | name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
367 | name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
368 | if (name_mute == NULL || name_micmute == NULL) { | ||
369 | hid_err(hdev, "Could not allocate memory for led data\n"); | ||
370 | return -ENOMEM; | ||
371 | } | ||
372 | snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); | ||
373 | snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); | ||
374 | |||
375 | hid_set_drvdata(hdev, data_pointer); | ||
376 | |||
377 | data_pointer->led_mute.name = name_mute; | ||
378 | data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get; | ||
379 | data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set; | ||
380 | data_pointer->led_mute.dev = dev; | ||
381 | led_classdev_register(dev, &data_pointer->led_mute); | ||
382 | |||
383 | data_pointer->led_micmute.name = name_micmute; | ||
384 | data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get; | ||
385 | data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set; | ||
386 | data_pointer->led_micmute.dev = dev; | ||
387 | led_classdev_register(dev, &data_pointer->led_micmute); | ||
388 | |||
389 | tpkbd_features_set(hdev); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int tpkbd_probe(struct hid_device *hdev, | ||
395 | const struct hid_device_id *id) | ||
396 | { | ||
397 | int ret; | ||
398 | |||
399 | ret = hid_parse(hdev); | ||
400 | if (ret) { | ||
401 | hid_err(hdev, "hid_parse failed\n"); | ||
402 | goto err; | ||
403 | } | ||
404 | |||
405 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
406 | if (ret) { | ||
407 | hid_err(hdev, "hid_hw_start failed\n"); | ||
408 | goto err; | ||
409 | } | ||
410 | |||
411 | if (hid_get_drvdata(hdev)) { | ||
412 | hid_set_drvdata(hdev, NULL); | ||
413 | ret = tpkbd_probe_tp(hdev); | ||
414 | if (ret) | ||
415 | goto err_hid; | ||
416 | } | ||
417 | |||
418 | return 0; | ||
419 | err_hid: | ||
420 | hid_hw_stop(hdev); | ||
421 | err: | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | static void tpkbd_remove_tp(struct hid_device *hdev) | ||
426 | { | ||
427 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
428 | |||
429 | sysfs_remove_group(&hdev->dev.kobj, | ||
430 | &tpkbd_attr_group_pointer); | ||
431 | |||
432 | led_classdev_unregister(&data_pointer->led_micmute); | ||
433 | led_classdev_unregister(&data_pointer->led_mute); | ||
434 | |||
435 | hid_set_drvdata(hdev, NULL); | ||
436 | } | ||
437 | |||
438 | static void tpkbd_remove(struct hid_device *hdev) | ||
439 | { | ||
440 | if (hid_get_drvdata(hdev)) | ||
441 | tpkbd_remove_tp(hdev); | ||
442 | |||
443 | hid_hw_stop(hdev); | ||
444 | } | ||
445 | |||
446 | static const struct hid_device_id tpkbd_devices[] = { | ||
447 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, | ||
448 | { } | ||
449 | }; | ||
450 | |||
451 | MODULE_DEVICE_TABLE(hid, tpkbd_devices); | ||
452 | |||
453 | static struct hid_driver tpkbd_driver = { | ||
454 | .name = "lenovo_tpkbd", | ||
455 | .id_table = tpkbd_devices, | ||
456 | .input_mapping = tpkbd_input_mapping, | ||
457 | .probe = tpkbd_probe, | ||
458 | .remove = tpkbd_remove, | ||
459 | }; | ||
460 | module_hid_driver(tpkbd_driver); | ||
461 | |||
462 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c new file mode 100644 index 000000000000..bf227f7679af --- /dev/null +++ b/drivers/hid/hid-lenovo.c | |||
@@ -0,0 +1,708 @@ | |||
1 | /* | ||
2 | * HID driver for Lenovo: | ||
3 | * - ThinkPad USB Keyboard with TrackPoint (tpkbd) | ||
4 | * - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd) | ||
5 | * - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd) | ||
6 | * | ||
7 | * Copyright (c) 2012 Bernhard Seibold | ||
8 | * Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk> | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the Free | ||
14 | * Software Foundation; either version 2 of the License, or (at your option) | ||
15 | * any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/sysfs.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/hid.h> | ||
22 | #include <linux/input.h> | ||
23 | #include <linux/leds.h> | ||
24 | |||
25 | #include "hid-ids.h" | ||
26 | |||
27 | struct lenovo_drvdata_tpkbd { | ||
28 | int led_state; | ||
29 | struct led_classdev led_mute; | ||
30 | struct led_classdev led_micmute; | ||
31 | int press_to_select; | ||
32 | int dragging; | ||
33 | int release_to_select; | ||
34 | int select_right; | ||
35 | int sensitivity; | ||
36 | int press_speed; | ||
37 | }; | ||
38 | |||
39 | struct lenovo_drvdata_cptkbd { | ||
40 | bool fn_lock; | ||
41 | }; | ||
42 | |||
43 | #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) | ||
44 | |||
45 | static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, | ||
46 | struct hid_input *hi, struct hid_field *field, | ||
47 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
48 | { | ||
49 | if (usage->hid == (HID_UP_BUTTON | 0x0010)) { | ||
50 | /* This sub-device contains trackpoint, mark it */ | ||
51 | hid_set_drvdata(hdev, (void *)1); | ||
52 | map_key_clear(KEY_MICMUTE); | ||
53 | return 1; | ||
54 | } | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, | ||
59 | struct hid_input *hi, struct hid_field *field, | ||
60 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
61 | { | ||
62 | /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */ | ||
63 | if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR || | ||
64 | (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) { | ||
65 | set_bit(EV_REP, hi->input->evbit); | ||
66 | switch (usage->hid & HID_USAGE) { | ||
67 | case 0x00f1: /* Fn-F4: Mic mute */ | ||
68 | map_key_clear(KEY_MICMUTE); | ||
69 | return 1; | ||
70 | case 0x00f2: /* Fn-F5: Brightness down */ | ||
71 | map_key_clear(KEY_BRIGHTNESSDOWN); | ||
72 | return 1; | ||
73 | case 0x00f3: /* Fn-F6: Brightness up */ | ||
74 | map_key_clear(KEY_BRIGHTNESSUP); | ||
75 | return 1; | ||
76 | case 0x00f4: /* Fn-F7: External display (projector) */ | ||
77 | map_key_clear(KEY_SWITCHVIDEOMODE); | ||
78 | return 1; | ||
79 | case 0x00f5: /* Fn-F8: Wireless */ | ||
80 | map_key_clear(KEY_WLAN); | ||
81 | return 1; | ||
82 | case 0x00f6: /* Fn-F9: Control panel */ | ||
83 | map_key_clear(KEY_CONFIG); | ||
84 | return 1; | ||
85 | case 0x00f8: /* Fn-F11: View open applications (3 boxes) */ | ||
86 | map_key_clear(KEY_SCALE); | ||
87 | return 1; | ||
88 | case 0x00fa: /* Fn-Esc: Fn-lock toggle */ | ||
89 | map_key_clear(KEY_FN_ESC); | ||
90 | return 1; | ||
91 | case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */ | ||
92 | /* NB: This mapping is invented in raw_event below */ | ||
93 | map_key_clear(KEY_FILE); | ||
94 | return 1; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static int lenovo_input_mapping(struct hid_device *hdev, | ||
102 | struct hid_input *hi, struct hid_field *field, | ||
103 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
104 | { | ||
105 | switch (hdev->product) { | ||
106 | case USB_DEVICE_ID_LENOVO_TPKBD: | ||
107 | return lenovo_input_mapping_tpkbd(hdev, hi, field, | ||
108 | usage, bit, max); | ||
109 | case USB_DEVICE_ID_LENOVO_CUSBKBD: | ||
110 | case USB_DEVICE_ID_LENOVO_CBTKBD: | ||
111 | return lenovo_input_mapping_cptkbd(hdev, hi, field, | ||
112 | usage, bit, max); | ||
113 | default: | ||
114 | return 0; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | #undef map_key_clear | ||
119 | |||
120 | /* Send a config command to the keyboard */ | ||
121 | static int lenovo_send_cmd_cptkbd(struct hid_device *hdev, | ||
122 | unsigned char byte2, unsigned char byte3) | ||
123 | { | ||
124 | int ret; | ||
125 | unsigned char buf[] = {0x18, byte2, byte3}; | ||
126 | |||
127 | switch (hdev->product) { | ||
128 | case USB_DEVICE_ID_LENOVO_CUSBKBD: | ||
129 | ret = hid_hw_raw_request(hdev, 0x13, buf, sizeof(buf), | ||
130 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | ||
131 | break; | ||
132 | case USB_DEVICE_ID_LENOVO_CBTKBD: | ||
133 | ret = hid_hw_output_report(hdev, buf, sizeof(buf)); | ||
134 | break; | ||
135 | default: | ||
136 | ret = -EINVAL; | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */ | ||
141 | } | ||
142 | |||
143 | static void lenovo_features_set_cptkbd(struct hid_device *hdev) | ||
144 | { | ||
145 | int ret; | ||
146 | struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); | ||
147 | |||
148 | ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); | ||
149 | if (ret) | ||
150 | hid_err(hdev, "Fn-lock setting failed: %d\n", ret); | ||
151 | } | ||
152 | |||
153 | static ssize_t attr_fn_lock_show_cptkbd(struct device *dev, | ||
154 | struct device_attribute *attr, | ||
155 | char *buf) | ||
156 | { | ||
157 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
158 | struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); | ||
159 | |||
160 | return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock); | ||
161 | } | ||
162 | |||
163 | static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, | ||
164 | struct device_attribute *attr, | ||
165 | const char *buf, | ||
166 | size_t count) | ||
167 | { | ||
168 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
169 | struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); | ||
170 | int value; | ||
171 | |||
172 | if (kstrtoint(buf, 10, &value)) | ||
173 | return -EINVAL; | ||
174 | if (value < 0 || value > 1) | ||
175 | return -EINVAL; | ||
176 | |||
177 | cptkbd_data->fn_lock = !!value; | ||
178 | lenovo_features_set_cptkbd(hdev); | ||
179 | |||
180 | return count; | ||
181 | } | ||
182 | |||
183 | static struct device_attribute dev_attr_fn_lock_cptkbd = | ||
184 | __ATTR(fn_lock, S_IWUSR | S_IRUGO, | ||
185 | attr_fn_lock_show_cptkbd, | ||
186 | attr_fn_lock_store_cptkbd); | ||
187 | |||
188 | static struct attribute *lenovo_attributes_cptkbd[] = { | ||
189 | &dev_attr_fn_lock_cptkbd.attr, | ||
190 | NULL | ||
191 | }; | ||
192 | |||
193 | static const struct attribute_group lenovo_attr_group_cptkbd = { | ||
194 | .attrs = lenovo_attributes_cptkbd, | ||
195 | }; | ||
196 | |||
197 | static int lenovo_raw_event(struct hid_device *hdev, | ||
198 | struct hid_report *report, u8 *data, int size) | ||
199 | { | ||
200 | /* | ||
201 | * Compact USB keyboard's Fn-F12 report holds down many other keys, and | ||
202 | * its own key is outside the usage page range. Remove extra | ||
203 | * keypresses and remap to inside usage page. | ||
204 | */ | ||
205 | if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD | ||
206 | && size == 3 | ||
207 | && data[0] == 0x15 | ||
208 | && data[1] == 0x94 | ||
209 | && data[2] == 0x01)) { | ||
210 | data[1] = 0x0; | ||
211 | data[2] = 0x4; | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int lenovo_features_set_tpkbd(struct hid_device *hdev) | ||
218 | { | ||
219 | struct hid_report *report; | ||
220 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
221 | |||
222 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; | ||
223 | |||
224 | report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; | ||
225 | report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; | ||
226 | report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; | ||
227 | report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; | ||
228 | report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver | ||
229 | report->field[2]->value[0] = data_pointer->sensitivity; | ||
230 | report->field[3]->value[0] = data_pointer->press_speed; | ||
231 | |||
232 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static ssize_t attr_press_to_select_show_tpkbd(struct device *dev, | ||
237 | struct device_attribute *attr, | ||
238 | char *buf) | ||
239 | { | ||
240 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
241 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
242 | |||
243 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); | ||
244 | } | ||
245 | |||
246 | static ssize_t attr_press_to_select_store_tpkbd(struct device *dev, | ||
247 | struct device_attribute *attr, | ||
248 | const char *buf, | ||
249 | size_t count) | ||
250 | { | ||
251 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
252 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
253 | int value; | ||
254 | |||
255 | if (kstrtoint(buf, 10, &value)) | ||
256 | return -EINVAL; | ||
257 | if (value < 0 || value > 1) | ||
258 | return -EINVAL; | ||
259 | |||
260 | data_pointer->press_to_select = value; | ||
261 | lenovo_features_set_tpkbd(hdev); | ||
262 | |||
263 | return count; | ||
264 | } | ||
265 | |||
266 | static ssize_t attr_dragging_show_tpkbd(struct device *dev, | ||
267 | struct device_attribute *attr, | ||
268 | char *buf) | ||
269 | { | ||
270 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
271 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
272 | |||
273 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); | ||
274 | } | ||
275 | |||
276 | static ssize_t attr_dragging_store_tpkbd(struct device *dev, | ||
277 | struct device_attribute *attr, | ||
278 | const char *buf, | ||
279 | size_t count) | ||
280 | { | ||
281 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
282 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
283 | int value; | ||
284 | |||
285 | if (kstrtoint(buf, 10, &value)) | ||
286 | return -EINVAL; | ||
287 | if (value < 0 || value > 1) | ||
288 | return -EINVAL; | ||
289 | |||
290 | data_pointer->dragging = value; | ||
291 | lenovo_features_set_tpkbd(hdev); | ||
292 | |||
293 | return count; | ||
294 | } | ||
295 | |||
296 | static ssize_t attr_release_to_select_show_tpkbd(struct device *dev, | ||
297 | struct device_attribute *attr, | ||
298 | char *buf) | ||
299 | { | ||
300 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
301 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
302 | |||
303 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); | ||
304 | } | ||
305 | |||
306 | static ssize_t attr_release_to_select_store_tpkbd(struct device *dev, | ||
307 | struct device_attribute *attr, | ||
308 | const char *buf, | ||
309 | size_t count) | ||
310 | { | ||
311 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
312 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
313 | int value; | ||
314 | |||
315 | if (kstrtoint(buf, 10, &value)) | ||
316 | return -EINVAL; | ||
317 | if (value < 0 || value > 1) | ||
318 | return -EINVAL; | ||
319 | |||
320 | data_pointer->release_to_select = value; | ||
321 | lenovo_features_set_tpkbd(hdev); | ||
322 | |||
323 | return count; | ||
324 | } | ||
325 | |||
326 | static ssize_t attr_select_right_show_tpkbd(struct device *dev, | ||
327 | struct device_attribute *attr, | ||
328 | char *buf) | ||
329 | { | ||
330 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
331 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
332 | |||
333 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); | ||
334 | } | ||
335 | |||
336 | static ssize_t attr_select_right_store_tpkbd(struct device *dev, | ||
337 | struct device_attribute *attr, | ||
338 | const char *buf, | ||
339 | size_t count) | ||
340 | { | ||
341 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
342 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
343 | int value; | ||
344 | |||
345 | if (kstrtoint(buf, 10, &value)) | ||
346 | return -EINVAL; | ||
347 | if (value < 0 || value > 1) | ||
348 | return -EINVAL; | ||
349 | |||
350 | data_pointer->select_right = value; | ||
351 | lenovo_features_set_tpkbd(hdev); | ||
352 | |||
353 | return count; | ||
354 | } | ||
355 | |||
356 | static ssize_t attr_sensitivity_show_tpkbd(struct device *dev, | ||
357 | struct device_attribute *attr, | ||
358 | char *buf) | ||
359 | { | ||
360 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
361 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
362 | |||
363 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
364 | data_pointer->sensitivity); | ||
365 | } | ||
366 | |||
367 | static ssize_t attr_sensitivity_store_tpkbd(struct device *dev, | ||
368 | struct device_attribute *attr, | ||
369 | const char *buf, | ||
370 | size_t count) | ||
371 | { | ||
372 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
373 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
374 | int value; | ||
375 | |||
376 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
377 | return -EINVAL; | ||
378 | |||
379 | data_pointer->sensitivity = value; | ||
380 | lenovo_features_set_tpkbd(hdev); | ||
381 | |||
382 | return count; | ||
383 | } | ||
384 | |||
385 | static ssize_t attr_press_speed_show_tpkbd(struct device *dev, | ||
386 | struct device_attribute *attr, | ||
387 | char *buf) | ||
388 | { | ||
389 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
390 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
391 | |||
392 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
393 | data_pointer->press_speed); | ||
394 | } | ||
395 | |||
396 | static ssize_t attr_press_speed_store_tpkbd(struct device *dev, | ||
397 | struct device_attribute *attr, | ||
398 | const char *buf, | ||
399 | size_t count) | ||
400 | { | ||
401 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
402 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
403 | int value; | ||
404 | |||
405 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
406 | return -EINVAL; | ||
407 | |||
408 | data_pointer->press_speed = value; | ||
409 | lenovo_features_set_tpkbd(hdev); | ||
410 | |||
411 | return count; | ||
412 | } | ||
413 | |||
414 | static struct device_attribute dev_attr_press_to_select_tpkbd = | ||
415 | __ATTR(press_to_select, S_IWUSR | S_IRUGO, | ||
416 | attr_press_to_select_show_tpkbd, | ||
417 | attr_press_to_select_store_tpkbd); | ||
418 | |||
419 | static struct device_attribute dev_attr_dragging_tpkbd = | ||
420 | __ATTR(dragging, S_IWUSR | S_IRUGO, | ||
421 | attr_dragging_show_tpkbd, | ||
422 | attr_dragging_store_tpkbd); | ||
423 | |||
424 | static struct device_attribute dev_attr_release_to_select_tpkbd = | ||
425 | __ATTR(release_to_select, S_IWUSR | S_IRUGO, | ||
426 | attr_release_to_select_show_tpkbd, | ||
427 | attr_release_to_select_store_tpkbd); | ||
428 | |||
429 | static struct device_attribute dev_attr_select_right_tpkbd = | ||
430 | __ATTR(select_right, S_IWUSR | S_IRUGO, | ||
431 | attr_select_right_show_tpkbd, | ||
432 | attr_select_right_store_tpkbd); | ||
433 | |||
434 | static struct device_attribute dev_attr_sensitivity_tpkbd = | ||
435 | __ATTR(sensitivity, S_IWUSR | S_IRUGO, | ||
436 | attr_sensitivity_show_tpkbd, | ||
437 | attr_sensitivity_store_tpkbd); | ||
438 | |||
439 | static struct device_attribute dev_attr_press_speed_tpkbd = | ||
440 | __ATTR(press_speed, S_IWUSR | S_IRUGO, | ||
441 | attr_press_speed_show_tpkbd, | ||
442 | attr_press_speed_store_tpkbd); | ||
443 | |||
444 | static struct attribute *lenovo_attributes_tpkbd[] = { | ||
445 | &dev_attr_press_to_select_tpkbd.attr, | ||
446 | &dev_attr_dragging_tpkbd.attr, | ||
447 | &dev_attr_release_to_select_tpkbd.attr, | ||
448 | &dev_attr_select_right_tpkbd.attr, | ||
449 | &dev_attr_sensitivity_tpkbd.attr, | ||
450 | &dev_attr_press_speed_tpkbd.attr, | ||
451 | NULL | ||
452 | }; | ||
453 | |||
454 | static const struct attribute_group lenovo_attr_group_tpkbd = { | ||
455 | .attrs = lenovo_attributes_tpkbd, | ||
456 | }; | ||
457 | |||
458 | static enum led_brightness lenovo_led_brightness_get_tpkbd( | ||
459 | struct led_classdev *led_cdev) | ||
460 | { | ||
461 | struct device *dev = led_cdev->dev->parent; | ||
462 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
463 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
464 | int led_nr = 0; | ||
465 | |||
466 | if (led_cdev == &data_pointer->led_micmute) | ||
467 | led_nr = 1; | ||
468 | |||
469 | return data_pointer->led_state & (1 << led_nr) | ||
470 | ? LED_FULL | ||
471 | : LED_OFF; | ||
472 | } | ||
473 | |||
474 | static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev, | ||
475 | enum led_brightness value) | ||
476 | { | ||
477 | struct device *dev = led_cdev->dev->parent; | ||
478 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
479 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
480 | struct hid_report *report; | ||
481 | int led_nr = 0; | ||
482 | |||
483 | if (led_cdev == &data_pointer->led_micmute) | ||
484 | led_nr = 1; | ||
485 | |||
486 | if (value == LED_OFF) | ||
487 | data_pointer->led_state &= ~(1 << led_nr); | ||
488 | else | ||
489 | data_pointer->led_state |= 1 << led_nr; | ||
490 | |||
491 | report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; | ||
492 | report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; | ||
493 | report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; | ||
494 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
495 | } | ||
496 | |||
497 | static int lenovo_probe_tpkbd(struct hid_device *hdev) | ||
498 | { | ||
499 | struct device *dev = &hdev->dev; | ||
500 | struct lenovo_drvdata_tpkbd *data_pointer; | ||
501 | size_t name_sz = strlen(dev_name(dev)) + 16; | ||
502 | char *name_mute, *name_micmute; | ||
503 | int i; | ||
504 | int ret; | ||
505 | |||
506 | /* | ||
507 | * Only register extra settings against subdevice where input_mapping | ||
508 | * set drvdata to 1, i.e. the trackpoint. | ||
509 | */ | ||
510 | if (!hid_get_drvdata(hdev)) | ||
511 | return 0; | ||
512 | |||
513 | hid_set_drvdata(hdev, NULL); | ||
514 | |||
515 | /* Validate required reports. */ | ||
516 | for (i = 0; i < 4; i++) { | ||
517 | if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) | ||
518 | return -ENODEV; | ||
519 | } | ||
520 | if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) | ||
521 | return -ENODEV; | ||
522 | |||
523 | ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd); | ||
524 | if (ret) | ||
525 | hid_warn(hdev, "Could not create sysfs group: %d\n", ret); | ||
526 | |||
527 | data_pointer = devm_kzalloc(&hdev->dev, | ||
528 | sizeof(struct lenovo_drvdata_tpkbd), | ||
529 | GFP_KERNEL); | ||
530 | if (data_pointer == NULL) { | ||
531 | hid_err(hdev, "Could not allocate memory for driver data\n"); | ||
532 | return -ENOMEM; | ||
533 | } | ||
534 | |||
535 | // set same default values as windows driver | ||
536 | data_pointer->sensitivity = 0xa0; | ||
537 | data_pointer->press_speed = 0x38; | ||
538 | |||
539 | name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
540 | name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
541 | if (name_mute == NULL || name_micmute == NULL) { | ||
542 | hid_err(hdev, "Could not allocate memory for led data\n"); | ||
543 | return -ENOMEM; | ||
544 | } | ||
545 | snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); | ||
546 | snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); | ||
547 | |||
548 | hid_set_drvdata(hdev, data_pointer); | ||
549 | |||
550 | data_pointer->led_mute.name = name_mute; | ||
551 | data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd; | ||
552 | data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd; | ||
553 | data_pointer->led_mute.dev = dev; | ||
554 | led_classdev_register(dev, &data_pointer->led_mute); | ||
555 | |||
556 | data_pointer->led_micmute.name = name_micmute; | ||
557 | data_pointer->led_micmute.brightness_get = | ||
558 | lenovo_led_brightness_get_tpkbd; | ||
559 | data_pointer->led_micmute.brightness_set = | ||
560 | lenovo_led_brightness_set_tpkbd; | ||
561 | data_pointer->led_micmute.dev = dev; | ||
562 | led_classdev_register(dev, &data_pointer->led_micmute); | ||
563 | |||
564 | lenovo_features_set_tpkbd(hdev); | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static int lenovo_probe_cptkbd(struct hid_device *hdev) | ||
570 | { | ||
571 | int ret; | ||
572 | struct lenovo_drvdata_cptkbd *cptkbd_data; | ||
573 | |||
574 | /* All the custom action happens on the USBMOUSE device for USB */ | ||
575 | if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD | ||
576 | && hdev->type != HID_TYPE_USBMOUSE) { | ||
577 | hid_dbg(hdev, "Ignoring keyboard half of device\n"); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | cptkbd_data = devm_kzalloc(&hdev->dev, | ||
582 | sizeof(*cptkbd_data), | ||
583 | GFP_KERNEL); | ||
584 | if (cptkbd_data == NULL) { | ||
585 | hid_err(hdev, "can't alloc keyboard descriptor\n"); | ||
586 | return -ENOMEM; | ||
587 | } | ||
588 | hid_set_drvdata(hdev, cptkbd_data); | ||
589 | |||
590 | /* | ||
591 | * Tell the keyboard a driver understands it, and turn F7, F9, F11 into | ||
592 | * regular keys | ||
593 | */ | ||
594 | ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03); | ||
595 | if (ret) | ||
596 | hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); | ||
597 | |||
598 | /* Turn Fn-Lock on by default */ | ||
599 | cptkbd_data->fn_lock = true; | ||
600 | lenovo_features_set_cptkbd(hdev); | ||
601 | |||
602 | ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd); | ||
603 | if (ret) | ||
604 | hid_warn(hdev, "Could not create sysfs group: %d\n", ret); | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | static int lenovo_probe(struct hid_device *hdev, | ||
610 | const struct hid_device_id *id) | ||
611 | { | ||
612 | int ret; | ||
613 | |||
614 | ret = hid_parse(hdev); | ||
615 | if (ret) { | ||
616 | hid_err(hdev, "hid_parse failed\n"); | ||
617 | goto err; | ||
618 | } | ||
619 | |||
620 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
621 | if (ret) { | ||
622 | hid_err(hdev, "hid_hw_start failed\n"); | ||
623 | goto err; | ||
624 | } | ||
625 | |||
626 | switch (hdev->product) { | ||
627 | case USB_DEVICE_ID_LENOVO_TPKBD: | ||
628 | ret = lenovo_probe_tpkbd(hdev); | ||
629 | break; | ||
630 | case USB_DEVICE_ID_LENOVO_CUSBKBD: | ||
631 | case USB_DEVICE_ID_LENOVO_CBTKBD: | ||
632 | ret = lenovo_probe_cptkbd(hdev); | ||
633 | break; | ||
634 | default: | ||
635 | ret = 0; | ||
636 | break; | ||
637 | } | ||
638 | if (ret) | ||
639 | goto err_hid; | ||
640 | |||
641 | return 0; | ||
642 | err_hid: | ||
643 | hid_hw_stop(hdev); | ||
644 | err: | ||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | static void lenovo_remove_tpkbd(struct hid_device *hdev) | ||
649 | { | ||
650 | struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev); | ||
651 | |||
652 | /* | ||
653 | * Only the trackpoint half of the keyboard has drvdata and stuff that | ||
654 | * needs unregistering. | ||
655 | */ | ||
656 | if (data_pointer == NULL) | ||
657 | return; | ||
658 | |||
659 | sysfs_remove_group(&hdev->dev.kobj, | ||
660 | &lenovo_attr_group_tpkbd); | ||
661 | |||
662 | led_classdev_unregister(&data_pointer->led_micmute); | ||
663 | led_classdev_unregister(&data_pointer->led_mute); | ||
664 | |||
665 | hid_set_drvdata(hdev, NULL); | ||
666 | } | ||
667 | |||
668 | static void lenovo_remove_cptkbd(struct hid_device *hdev) | ||
669 | { | ||
670 | sysfs_remove_group(&hdev->dev.kobj, | ||
671 | &lenovo_attr_group_cptkbd); | ||
672 | } | ||
673 | |||
674 | static void lenovo_remove(struct hid_device *hdev) | ||
675 | { | ||
676 | switch (hdev->product) { | ||
677 | case USB_DEVICE_ID_LENOVO_TPKBD: | ||
678 | lenovo_remove_tpkbd(hdev); | ||
679 | break; | ||
680 | case USB_DEVICE_ID_LENOVO_CUSBKBD: | ||
681 | case USB_DEVICE_ID_LENOVO_CBTKBD: | ||
682 | lenovo_remove_cptkbd(hdev); | ||
683 | break; | ||
684 | } | ||
685 | |||
686 | hid_hw_stop(hdev); | ||
687 | } | ||
688 | |||
689 | static const struct hid_device_id lenovo_devices[] = { | ||
690 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, | ||
691 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, | ||
692 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, | ||
693 | { } | ||
694 | }; | ||
695 | |||
696 | MODULE_DEVICE_TABLE(hid, lenovo_devices); | ||
697 | |||
698 | static struct hid_driver lenovo_driver = { | ||
699 | .name = "lenovo", | ||
700 | .id_table = lenovo_devices, | ||
701 | .input_mapping = lenovo_input_mapping, | ||
702 | .probe = lenovo_probe, | ||
703 | .remove = lenovo_remove, | ||
704 | .raw_event = lenovo_raw_event, | ||
705 | }; | ||
706 | module_hid_driver(lenovo_driver); | ||
707 | |||
708 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 024cdf3c2297..3c13af684410 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c | |||
@@ -883,16 +883,13 @@ void picolcd_exit_devfs(struct picolcd_data *data) | |||
883 | 883 | ||
884 | dent = data->debug_reset; | 884 | dent = data->debug_reset; |
885 | data->debug_reset = NULL; | 885 | data->debug_reset = NULL; |
886 | if (dent) | 886 | debugfs_remove(dent); |
887 | debugfs_remove(dent); | ||
888 | dent = data->debug_eeprom; | 887 | dent = data->debug_eeprom; |
889 | data->debug_eeprom = NULL; | 888 | data->debug_eeprom = NULL; |
890 | if (dent) | 889 | debugfs_remove(dent); |
891 | debugfs_remove(dent); | ||
892 | dent = data->debug_flash; | 890 | dent = data->debug_flash; |
893 | data->debug_flash = NULL; | 891 | data->debug_flash = NULL; |
894 | if (dent) | 892 | debugfs_remove(dent); |
895 | debugfs_remove(dent); | ||
896 | mutex_destroy(&data->mutex_flash); | 893 | mutex_destroy(&data->mutex_flash); |
897 | } | 894 | } |
898 | 895 | ||
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 578bbe65902b..0dc25142f451 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c | |||
@@ -377,7 +377,7 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) | |||
377 | irq_mask |= hdata->f30.irq_mask; | 377 | irq_mask |= hdata->f30.irq_mask; |
378 | 378 | ||
379 | if (data[1] & ~irq_mask) | 379 | if (data[1] & ~irq_mask) |
380 | hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", | 380 | hid_dbg(hdev, "unknown intr source:%02lx %s:%d\n", |
381 | data[1] & ~irq_mask, __FILE__, __LINE__); | 381 | data[1] & ~irq_mask, __FILE__, __LINE__); |
382 | 382 | ||
383 | if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { | 383 | if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { |
@@ -400,7 +400,7 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) | |||
400 | struct rmi_data *hdata = hid_get_drvdata(hdev); | 400 | struct rmi_data *hdata = hid_get_drvdata(hdev); |
401 | 401 | ||
402 | if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { | 402 | if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { |
403 | hid_err(hdev, "no read request pending\n"); | 403 | hid_dbg(hdev, "no read request pending\n"); |
404 | return 0; | 404 | return 0; |
405 | } | 405 | } |
406 | 406 | ||
@@ -549,10 +549,12 @@ static int rmi_populate_f11(struct hid_device *hdev) | |||
549 | u8 buf[20]; | 549 | u8 buf[20]; |
550 | int ret; | 550 | int ret; |
551 | bool has_query9; | 551 | bool has_query9; |
552 | bool has_query10; | 552 | bool has_query10 = false; |
553 | bool has_query11; | 553 | bool has_query11; |
554 | bool has_query12; | 554 | bool has_query12; |
555 | bool has_physical_props; | 555 | bool has_physical_props; |
556 | bool has_gestures; | ||
557 | bool has_rel; | ||
556 | unsigned x_size, y_size; | 558 | unsigned x_size, y_size; |
557 | u16 query12_offset; | 559 | u16 query12_offset; |
558 | 560 | ||
@@ -589,19 +591,32 @@ static int rmi_populate_f11(struct hid_device *hdev) | |||
589 | return -ENODEV; | 591 | return -ENODEV; |
590 | } | 592 | } |
591 | 593 | ||
592 | /* query 8 to find out if query 10 exists */ | 594 | has_rel = !!(buf[0] & BIT(3)); |
593 | ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); | 595 | has_gestures = !!(buf[0] & BIT(5)); |
594 | if (ret) { | 596 | |
595 | hid_err(hdev, "can not read gesture information: %d.\n", ret); | 597 | if (has_gestures) { |
596 | return ret; | 598 | /* query 8 to find out if query 10 exists */ |
599 | ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); | ||
600 | if (ret) { | ||
601 | hid_err(hdev, "can not read gesture information: %d.\n", | ||
602 | ret); | ||
603 | return ret; | ||
604 | } | ||
605 | has_query10 = !!(buf[0] & BIT(2)); | ||
597 | } | 606 | } |
598 | has_query10 = !!(buf[0] & BIT(2)); | ||
599 | 607 | ||
600 | /* | 608 | /* |
601 | * At least 8 queries are guaranteed to be present in F11 | 609 | * At least 4 queries are guaranteed to be present in F11 |
602 | * +1 for query12. | 610 | * +1 for query 5 which is present since absolute events are |
611 | * reported and +1 for query 12. | ||
603 | */ | 612 | */ |
604 | query12_offset = 9; | 613 | query12_offset = 6; |
614 | |||
615 | if (has_rel) | ||
616 | ++query12_offset; /* query 6 is present */ | ||
617 | |||
618 | if (has_gestures) | ||
619 | query12_offset += 2; /* query 7 and 8 are present */ | ||
605 | 620 | ||
606 | if (has_query9) | 621 | if (has_query9) |
607 | ++query12_offset; | 622 | ++query12_offset; |
@@ -833,6 +848,8 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
833 | struct rmi_data *data = NULL; | 848 | struct rmi_data *data = NULL; |
834 | int ret; | 849 | int ret; |
835 | size_t alloc_size; | 850 | size_t alloc_size; |
851 | struct hid_report *input_report; | ||
852 | struct hid_report *output_report; | ||
836 | 853 | ||
837 | data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); | 854 | data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); |
838 | if (!data) | 855 | if (!data) |
@@ -851,12 +868,26 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
851 | return ret; | 868 | return ret; |
852 | } | 869 | } |
853 | 870 | ||
854 | data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] | 871 | input_report = hdev->report_enum[HID_INPUT_REPORT] |
855 | .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) | 872 | .report_id_hash[RMI_ATTN_REPORT_ID]; |
856 | + 1 /* report id */; | 873 | if (!input_report) { |
857 | data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] | 874 | hid_err(hdev, "device does not have expected input report\n"); |
858 | .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) | 875 | ret = -ENODEV; |
859 | + 1 /* report id */; | 876 | return ret; |
877 | } | ||
878 | |||
879 | data->input_report_size = (input_report->size >> 3) + 1 /* report id */; | ||
880 | |||
881 | output_report = hdev->report_enum[HID_OUTPUT_REPORT] | ||
882 | .report_id_hash[RMI_WRITE_REPORT_ID]; | ||
883 | if (!output_report) { | ||
884 | hid_err(hdev, "device does not have expected output report\n"); | ||
885 | ret = -ENODEV; | ||
886 | return ret; | ||
887 | } | ||
888 | |||
889 | data->output_report_size = (output_report->size >> 3) | ||
890 | + 1 /* report id */; | ||
860 | 891 | ||
861 | alloc_size = data->output_report_size + data->input_report_size; | 892 | alloc_size = data->output_report_size + data->input_report_size; |
862 | 893 | ||
diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 6adc0fa08d96..65e2e76bf2fe 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c | |||
@@ -61,7 +61,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, | |||
61 | return -EINVAL; | 61 | return -EINVAL; |
62 | 62 | ||
63 | mutex_lock(&lua->lua_lock); | 63 | mutex_lock(&lua->lua_lock); |
64 | retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size); | 64 | retval = roccat_common2_send(usb_dev, command, buf, real_size); |
65 | mutex_unlock(&lua->lua_lock); | 65 | mutex_unlock(&lua->lua_lock); |
66 | 66 | ||
67 | return retval ? retval : real_size; | 67 | return retval ? retval : real_size; |
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 2259eaa8b988..c372368e438c 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
@@ -56,32 +56,81 @@ | |||
56 | 56 | ||
57 | #define MAX_LEDS 4 | 57 | #define MAX_LEDS 4 |
58 | 58 | ||
59 | static const u8 sixaxis_rdesc_fixup[] = { | 59 | static __u8 sixaxis_rdesc[] = { |
60 | 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, | 60 | 0x05, 0x01, /* Usage Page (Desktop), */ |
61 | 0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF, | 61 | 0x09, 0x04, /* Usage (Joystik), */ |
62 | 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02 | 62 | 0xA1, 0x01, /* Collection (Application), */ |
63 | }; | 63 | 0xA1, 0x02, /* Collection (Logical), */ |
64 | 64 | 0x85, 0x01, /* Report ID (1), */ | |
65 | static const u8 sixaxis_rdesc_fixup2[] = { | 65 | 0x75, 0x08, /* Report Size (8), */ |
66 | 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, | 66 | 0x95, 0x01, /* Report Count (1), */ |
67 | 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, | 67 | 0x15, 0x00, /* Logical Minimum (0), */ |
68 | 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, | 68 | 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ |
69 | 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, | 69 | 0x81, 0x03, /* Input (Constant, Variable), */ |
70 | 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, | 70 | 0x75, 0x01, /* Report Size (1), */ |
71 | 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, | 71 | 0x95, 0x13, /* Report Count (19), */ |
72 | 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, | 72 | 0x15, 0x00, /* Logical Minimum (0), */ |
73 | 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, | 73 | 0x25, 0x01, /* Logical Maximum (1), */ |
74 | 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, | 74 | 0x35, 0x00, /* Physical Minimum (0), */ |
75 | 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, | 75 | 0x45, 0x01, /* Physical Maximum (1), */ |
76 | 0xc0, 0x05, 0x01, 0x95, 0x13, 0x09, 0x01, 0x81, | 76 | 0x05, 0x09, /* Usage Page (Button), */ |
77 | 0x02, 0x95, 0x0c, 0x81, 0x01, 0x75, 0x10, 0x95, | 77 | 0x19, 0x01, /* Usage Minimum (01h), */ |
78 | 0x04, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x09, | 78 | 0x29, 0x13, /* Usage Maximum (13h), */ |
79 | 0x01, 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, | 79 | 0x81, 0x02, /* Input (Variable), */ |
80 | 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, | 80 | 0x75, 0x01, /* Report Size (1), */ |
81 | 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, | 81 | 0x95, 0x0D, /* Report Count (13), */ |
82 | 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, | 82 | 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ |
83 | 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, | 83 | 0x81, 0x03, /* Input (Constant, Variable), */ |
84 | 0xb1, 0x02, 0xc0, 0xc0, | 84 | 0x15, 0x00, /* Logical Minimum (0), */ |
85 | 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ | ||
86 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
87 | 0x09, 0x01, /* Usage (Pointer), */ | ||
88 | 0xA1, 0x00, /* Collection (Physical), */ | ||
89 | 0x75, 0x08, /* Report Size (8), */ | ||
90 | 0x95, 0x04, /* Report Count (4), */ | ||
91 | 0x35, 0x00, /* Physical Minimum (0), */ | ||
92 | 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ | ||
93 | 0x09, 0x30, /* Usage (X), */ | ||
94 | 0x09, 0x31, /* Usage (Y), */ | ||
95 | 0x09, 0x32, /* Usage (Z), */ | ||
96 | 0x09, 0x35, /* Usage (Rz), */ | ||
97 | 0x81, 0x02, /* Input (Variable), */ | ||
98 | 0xC0, /* End Collection, */ | ||
99 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
100 | 0x95, 0x13, /* Report Count (19), */ | ||
101 | 0x09, 0x01, /* Usage (Pointer), */ | ||
102 | 0x81, 0x02, /* Input (Variable), */ | ||
103 | 0x95, 0x0C, /* Report Count (12), */ | ||
104 | 0x81, 0x01, /* Input (Constant), */ | ||
105 | 0x75, 0x10, /* Report Size (16), */ | ||
106 | 0x95, 0x04, /* Report Count (4), */ | ||
107 | 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ | ||
108 | 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ | ||
109 | 0x09, 0x01, /* Usage (Pointer), */ | ||
110 | 0x81, 0x02, /* Input (Variable), */ | ||
111 | 0xC0, /* End Collection, */ | ||
112 | 0xA1, 0x02, /* Collection (Logical), */ | ||
113 | 0x85, 0x02, /* Report ID (2), */ | ||
114 | 0x75, 0x08, /* Report Size (8), */ | ||
115 | 0x95, 0x30, /* Report Count (48), */ | ||
116 | 0x09, 0x01, /* Usage (Pointer), */ | ||
117 | 0xB1, 0x02, /* Feature (Variable), */ | ||
118 | 0xC0, /* End Collection, */ | ||
119 | 0xA1, 0x02, /* Collection (Logical), */ | ||
120 | 0x85, 0xEE, /* Report ID (238), */ | ||
121 | 0x75, 0x08, /* Report Size (8), */ | ||
122 | 0x95, 0x30, /* Report Count (48), */ | ||
123 | 0x09, 0x01, /* Usage (Pointer), */ | ||
124 | 0xB1, 0x02, /* Feature (Variable), */ | ||
125 | 0xC0, /* End Collection, */ | ||
126 | 0xA1, 0x02, /* Collection (Logical), */ | ||
127 | 0x85, 0xEF, /* Report ID (239), */ | ||
128 | 0x75, 0x08, /* Report Size (8), */ | ||
129 | 0x95, 0x30, /* Report Count (48), */ | ||
130 | 0x09, 0x01, /* Usage (Pointer), */ | ||
131 | 0xB1, 0x02, /* Feature (Variable), */ | ||
132 | 0xC0, /* End Collection, */ | ||
133 | 0xC0 /* End Collection */ | ||
85 | }; | 134 | }; |
86 | 135 | ||
87 | /* | 136 | /* |
@@ -778,6 +827,13 @@ struct sony_sc { | |||
778 | __u8 led_count; | 827 | __u8 led_count; |
779 | }; | 828 | }; |
780 | 829 | ||
830 | static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc, | ||
831 | unsigned int *rsize) | ||
832 | { | ||
833 | *rsize = sizeof(sixaxis_rdesc); | ||
834 | return sixaxis_rdesc; | ||
835 | } | ||
836 | |||
781 | static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, | 837 | static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, |
782 | unsigned int *rsize) | 838 | unsigned int *rsize) |
783 | { | 839 | { |
@@ -819,8 +875,6 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
819 | return 1; | 875 | return 1; |
820 | } | 876 | } |
821 | 877 | ||
822 | |||
823 | /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ | ||
824 | static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 878 | static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
825 | unsigned int *rsize) | 879 | unsigned int *rsize) |
826 | { | 880 | { |
@@ -857,20 +911,8 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
857 | *rsize = sizeof(dualshock4_bt_rdesc); | 911 | *rsize = sizeof(dualshock4_bt_rdesc); |
858 | } | 912 | } |
859 | 913 | ||
860 | /* The HID descriptor exposed over BT has a trailing zero byte */ | 914 | if (sc->quirks & SIXAXIS_CONTROLLER) |
861 | if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) || | 915 | return sixaxis_fixup(hdev, rdesc, rsize); |
862 | ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && | ||
863 | rdesc[83] == 0x75) { | ||
864 | hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n"); | ||
865 | memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup, | ||
866 | sizeof(sixaxis_rdesc_fixup)); | ||
867 | } else if (sc->quirks & SIXAXIS_CONTROLLER_USB && | ||
868 | *rsize > sizeof(sixaxis_rdesc_fixup2)) { | ||
869 | hid_info(hdev, "Sony Sixaxis clone detected. Using original report descriptor (size: %d clone; %d new)\n", | ||
870 | *rsize, (int)sizeof(sixaxis_rdesc_fixup2)); | ||
871 | *rsize = sizeof(sixaxis_rdesc_fixup2); | ||
872 | memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize); | ||
873 | } | ||
874 | 916 | ||
875 | if (sc->quirks & PS3REMOTE) | 917 | if (sc->quirks & PS3REMOTE) |
876 | return ps3remote_fixup(hdev, rdesc, rsize); | 918 | return ps3remote_fixup(hdev, rdesc, rsize); |
@@ -1307,7 +1349,7 @@ static int sony_leds_init(struct sony_sc *sc) | |||
1307 | static const char * const ds4_name_str[] = { "red", "green", "blue", | 1349 | static const char * const ds4_name_str[] = { "red", "green", "blue", |
1308 | "global" }; | 1350 | "global" }; |
1309 | __u8 initial_values[MAX_LEDS] = { 0 }; | 1351 | __u8 initial_values[MAX_LEDS] = { 0 }; |
1310 | __u8 max_brightness[MAX_LEDS] = { 1 }; | 1352 | __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; |
1311 | __u8 use_hw_blink[MAX_LEDS] = { 0 }; | 1353 | __u8 use_hw_blink[MAX_LEDS] = { 0 }; |
1312 | 1354 | ||
1313 | BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); | 1355 | BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); |
@@ -1830,9 +1872,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
1830 | 1872 | ||
1831 | if (sc->quirks & VAIO_RDESC_CONSTANT) | 1873 | if (sc->quirks & VAIO_RDESC_CONSTANT) |
1832 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; | 1874 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; |
1833 | else if (sc->quirks & SIXAXIS_CONTROLLER_USB) | 1875 | else if (sc->quirks & SIXAXIS_CONTROLLER) |
1834 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; | ||
1835 | else if (sc->quirks & SIXAXIS_CONTROLLER_BT) | ||
1836 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; | 1876 | connect_mask |= HID_CONNECT_HIDDEV_FORCE; |
1837 | 1877 | ||
1838 | ret = hid_hw_start(hdev, connect_mask); | 1878 | ret = hid_hw_start(hdev, connect_mask); |
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 21aafc8f48c8..747d54421e73 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c | |||
@@ -1054,21 +1054,29 @@ static int i2c_hid_remove(struct i2c_client *client) | |||
1054 | static int i2c_hid_suspend(struct device *dev) | 1054 | static int i2c_hid_suspend(struct device *dev) |
1055 | { | 1055 | { |
1056 | struct i2c_client *client = to_i2c_client(dev); | 1056 | struct i2c_client *client = to_i2c_client(dev); |
1057 | struct i2c_hid *ihid = i2c_get_clientdata(client); | ||
1058 | struct hid_device *hid = ihid->hid; | ||
1059 | int ret = 0; | ||
1057 | 1060 | ||
1058 | disable_irq(client->irq); | 1061 | disable_irq(client->irq); |
1059 | if (device_may_wakeup(&client->dev)) | 1062 | if (device_may_wakeup(&client->dev)) |
1060 | enable_irq_wake(client->irq); | 1063 | enable_irq_wake(client->irq); |
1061 | 1064 | ||
1065 | if (hid->driver && hid->driver->suspend) | ||
1066 | ret = hid->driver->suspend(hid, PMSG_SUSPEND); | ||
1067 | |||
1062 | /* Save some power */ | 1068 | /* Save some power */ |
1063 | i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); | 1069 | i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); |
1064 | 1070 | ||
1065 | return 0; | 1071 | return ret; |
1066 | } | 1072 | } |
1067 | 1073 | ||
1068 | static int i2c_hid_resume(struct device *dev) | 1074 | static int i2c_hid_resume(struct device *dev) |
1069 | { | 1075 | { |
1070 | int ret; | 1076 | int ret; |
1071 | struct i2c_client *client = to_i2c_client(dev); | 1077 | struct i2c_client *client = to_i2c_client(dev); |
1078 | struct i2c_hid *ihid = i2c_get_clientdata(client); | ||
1079 | struct hid_device *hid = ihid->hid; | ||
1072 | 1080 | ||
1073 | enable_irq(client->irq); | 1081 | enable_irq(client->irq); |
1074 | ret = i2c_hid_hwreset(client); | 1082 | ret = i2c_hid_hwreset(client); |
@@ -1078,6 +1086,11 @@ static int i2c_hid_resume(struct device *dev) | |||
1078 | if (device_may_wakeup(&client->dev)) | 1086 | if (device_may_wakeup(&client->dev)) |
1079 | disable_irq_wake(client->irq); | 1087 | disable_irq_wake(client->irq); |
1080 | 1088 | ||
1089 | if (hid->driver && hid->driver->reset_resume) { | ||
1090 | ret = hid->driver->reset_resume(hid); | ||
1091 | return ret; | ||
1092 | } | ||
1093 | |||
1081 | return 0; | 1094 | return 0; |
1082 | } | 1095 | } |
1083 | #endif | 1096 | #endif |
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 7b88f4cb9902..79cf503e37bf 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -58,7 +58,7 @@ module_param_named(ignoreled, ignoreled, uint, 0644); | |||
58 | MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds"); | 58 | MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds"); |
59 | 59 | ||
60 | /* Quirks specified at module load time */ | 60 | /* Quirks specified at module load time */ |
61 | static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; | 61 | static char *quirks_param[MAX_USBHID_BOOT_QUIRKS]; |
62 | module_param_array_named(quirks, quirks_param, charp, NULL, 0444); | 62 | module_param_array_named(quirks, quirks_param, charp, NULL, 0444); |
63 | MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " | 63 | MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " |
64 | " quirks=vendorID:productID:quirks" | 64 | " quirks=vendorID:productID:quirks" |
@@ -536,7 +536,8 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re | |||
536 | int head; | 536 | int head; |
537 | struct usbhid_device *usbhid = hid->driver_data; | 537 | struct usbhid_device *usbhid = hid->driver_data; |
538 | 538 | ||
539 | if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) | 539 | if (((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) || |
540 | test_bit(HID_DISCONNECTED, &usbhid->iofl)) | ||
540 | return; | 541 | return; |
541 | 542 | ||
542 | if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { | 543 | if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { |
@@ -1366,6 +1367,9 @@ static void usbhid_disconnect(struct usb_interface *intf) | |||
1366 | return; | 1367 | return; |
1367 | 1368 | ||
1368 | usbhid = hid->driver_data; | 1369 | usbhid = hid->driver_data; |
1370 | spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ | ||
1371 | set_bit(HID_DISCONNECTED, &usbhid->iofl); | ||
1372 | spin_unlock_irq(&usbhid->lock); | ||
1369 | hid_destroy_device(hid); | 1373 | hid_destroy_device(hid); |
1370 | kfree(usbhid); | 1374 | kfree(usbhid); |
1371 | } | 1375 | } |
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 31e6727cd009..0dd568170d6e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c | |||
@@ -124,6 +124,7 @@ static const struct hid_blacklist { | |||
124 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, | 124 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, |
125 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, | 125 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, |
126 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, | 126 | { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, |
127 | { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS }, | ||
127 | 128 | ||
128 | { 0, 0 } | 129 | { 0, 0 } |
129 | }; | 130 | }; |