summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig6
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-cmedia.c168
-rw-r--r--drivers/hid/hid-core.c99
-rw-r--r--drivers/hid/hid-corsair.c3
-rw-r--r--drivers/hid/hid-dr.c4
-rw-r--r--drivers/hid/hid-ids.h15
-rw-r--r--drivers/hid/hid-lenovo.c16
-rw-r--r--drivers/hid/hid-lg.c10
-rw-r--r--drivers/hid/hid-logitech-hidpp.c701
-rw-r--r--drivers/hid/hid-microsoft.c8
-rw-r--r--drivers/hid/hid-multitouch.c26
-rw-r--r--drivers/hid/hid-penmount.c8
-rw-r--r--drivers/hid/hid-rmi.c11
-rw-r--r--drivers/hid/hid-sony.c182
-rw-r--r--drivers/hid/hid-thingm.c135
-rw-r--r--drivers/hid/hid-wiimote-modules.c14
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c44
-rw-r--r--drivers/hid/uhid.c2
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hid/wacom_sys.c465
-rw-r--r--drivers/hid/wacom_wac.c250
-rw-r--r--drivers/hid/wacom_wac.h8
23 files changed, 1520 insertions, 659 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 513a16cc6..411722570 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -196,6 +196,12 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys.
+config HID_CMEDIA
+ tristate "CMedia CM6533 HID audio jack controls"
+ depends on HID
+ ---help---
+ Support for CMedia CM6533 HID audio jack controls.
+
config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
depends on USB_HID && I2C && GPIOLIB
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 00011fee0..be56ab6f7 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
+obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
diff --git a/drivers/hid/hid-cmedia.c b/drivers/hid/hid-cmedia.c
new file mode 100644
index 000000000..7230f8513
--- /dev/null
+++ b/drivers/hid/hid-cmedia.c
@@ -0,0 +1,168 @@
+/*
+ * HID driver for CMedia CM6533 audio jack controls
+ *
+ * Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Ben Chen");
+MODULE_DESCRIPTION("CM6533 HID jack controls");
+MODULE_LICENSE("GPL");
+
+#define CM6533_JD_TYPE_COUNT 1
+#define CM6533_JD_RAWEV_LEN 16
+#define CM6533_JD_SFX_OFFSET 8
+
+/*
+*
+*CM6533 audio jack HID raw events:
+*
+*Plug in:
+*01000600 002083xx 080008c0 10000000
+*about 3 seconds later...
+*01000a00 002083xx 08000380 10000000
+*01000600 002083xx 08000380 10000000
+*
+*Plug out:
+*01000400 002083xx 080008c0 x0000000
+*/
+
+static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
+static const u8 ji_in[] = { 0x01, 0x00, 0x06, 0x00 };
+static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
+
+static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
+ SW_HEADPHONE_INSERT,
+};
+
+struct cmhid {
+ struct input_dev *input_dev;
+ struct hid_device *hid;
+ unsigned short switch_map[CM6533_JD_TYPE_COUNT];
+};
+
+static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
+{
+ input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
+ input_sync(cm->input_dev);
+}
+
+static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
+ u8 *data, int len)
+{
+ struct cmhid *cm = hid_get_drvdata(hid);
+
+ if (len != CM6533_JD_RAWEV_LEN)
+ goto out;
+ if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
+ goto out;
+
+ if (!memcmp(data, ji_out, sizeof(ji_out))) {
+ hp_ev(hid, cm, 0);
+ goto out;
+ }
+ if (!memcmp(data, ji_in, sizeof(ji_in))) {
+ hp_ev(hid, cm, 1);
+ goto out;
+ }
+
+out:
+ return 0;
+}
+
+static int cmhid_input_configured(struct hid_device *hid,
+ struct hid_input *hidinput)
+{
+ struct input_dev *input_dev = hidinput->input;
+ struct cmhid *cm = hid_get_drvdata(hid);
+ int i;
+
+ cm->input_dev = input_dev;
+ memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
+ input_dev->evbit[0] = BIT(EV_SW);
+ for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
+ input_set_capability(cm->input_dev,
+ EV_SW, jack_switch_types[i]);
+ return 0;
+}
+
+static int cmhid_input_mapping(struct hid_device *hid,
+ struct hid_input *hi, struct hid_field *field,
+ struct hid_usage *usage, unsigned long **bit, int *max)
+{
+ return -1;
+}
+
+static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
+{
+ int ret;
+ struct cmhid *cm;
+
+ cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
+ if (!cm) {
+ ret = -ENOMEM;
+ goto allocfail;
+ }
+
+ cm->hid = hid;
+
+ hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
+ hid_set_drvdata(hid, cm);
+
+ ret = hid_parse(hid);
+ if (ret) {
+ hid_err(hid, "parse failed\n");
+ goto fail;
+ }
+
+ ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
+ if (ret) {
+ hid_err(hid, "hw start failed\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ kfree(cm);
+allocfail:
+ return ret;
+}
+
+static void cmhid_remove(struct hid_device *hid)
+{
+ struct cmhid *cm = hid_get_drvdata(hid);
+
+ hid_hw_stop(hid);
+ kfree(cm);
+}
+
+static const struct hid_device_id cmhid_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, cmhid_devices);
+
+static struct hid_driver cmhid_driver = {
+ .name = "cm6533_jd",
+ .id_table = cmhid_devices,
+ .raw_event = cmhid_raw_event,
+ .input_configured = cmhid_input_configured,
+ .probe = cmhid_probe,
+ .remove = cmhid_remove,
+ .input_mapping = cmhid_input_mapping,
+};
+module_hid_driver(cmhid_driver);
+
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 99446ffd7..4f9c5c6de 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1075,7 +1075,7 @@ static u32 s32ton(__s32 value, unsigned n)
* Extract/implement a data field from/to a little endian report (bit array).
*
* Code sort-of follows HID spec:
- * http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ * http://www.usb.org/developers/hidpage/HID1_11.pdf
*
* While the USB HID spec allows unlimited length bit fields in "report
* descriptors", most devices never use more than 16 bits.
@@ -1083,20 +1083,37 @@ static u32 s32ton(__s32 value, unsigned n)
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
*/
-__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
- unsigned offset, unsigned n)
-{
- u64 x;
+static u32 __extract(u8 *report, unsigned offset, int n)
+{
+ unsigned int idx = offset / 8;
+ unsigned int bit_nr = 0;
+ unsigned int bit_shift = offset % 8;
+ int bits_to_copy = 8 - bit_shift;
+ u32 value = 0;
+ u32 mask = n < 32 ? (1U << n) - 1 : ~0U;
+
+ while (n > 0) {
+ value |= ((u32)report[idx] >> bit_shift) << bit_nr;
+ n -= bits_to_copy;
+ bit_nr += bits_to_copy;
+ bits_to_copy = 8;
+ bit_shift = 0;
+ idx++;
+ }
+
+ return value & mask;
+}
- if (n > 32)
+u32 hid_field_extract(const struct hid_device *hid, u8 *report,
+ unsigned offset, unsigned n)
+{
+ if (n > 32) {
hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
n, current->comm);
+ n = 32;
+ }
- report += offset >> 3; /* adjust byte index */
- offset &= 7; /* now only need bit offset into one byte */
- x = get_unaligned_le64(report);
- x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
- return (u32) x;
+ return __extract(report, offset, n);
}
EXPORT_SYMBOL_GPL(hid_field_extract);
@@ -1106,31 +1123,56 @@ EXPORT_SYMBOL_GPL(hid_field_extract);
* The data mangled in the bit stream remains in little endian
* order the whole time. It make more sense to talk about
* endianness of register values by considering a register
- * a "cached" copy of the little endiad bit stream.
+ * a "cached" copy of the little endian bit stream.
*/
-static void implement(const struct hid_device *hid, __u8 *report,
- unsigned offset, unsigned n, __u32 value)
+
+static void __implement(u8 *report, unsigned offset, int n, u32 value)
+{
+ unsigned int idx = offset / 8;
+ unsigned int size = offset + n;
+ unsigned int bit_shift = offset % 8;
+ int bits_to_set = 8 - bit_shift;
+ u8 bit_mask = 0xff << bit_shift;
+
+ while (n - bits_to_set >= 0) {
+ report[idx] &= ~bit_mask;
+ report[idx] |= value << bit_shift;
+ value >>= bits_to_set;
+ n -= bits_to_set;
+ bits_to_set = 8;
+ bit_mask = 0xff;
+ bit_shift = 0;
+ idx++;
+ }
+
+ /* last nibble */
+ if (n) {
+ if (size % 8)
+ bit_mask &= (1U << (size % 8)) - 1;
+ report[idx] &= ~bit_mask;
+ report[idx] |= (value << bit_shift) & bit_mask;
+ }
+}
+
+static void implement(const struct hid_device *hid, u8 *report,
+ unsigned offset, unsigned n, u32 value)
{
- u64 x;
- u64 m = (1ULL << n) - 1;
+ u64 m;
- if (n > 32)
+ if (n > 32) {
hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n",
__func__, n, current->comm);
+ n = 32;
+ }
+ m = (1ULL << n) - 1;
if (value > m)
hid_warn(hid, "%s() called with too large value %d! (%s)\n",
__func__, value, current->comm);
WARN_ON(value > m);
value &= m;
- report += offset >> 3;
- offset &= 7;
-
- x = get_unaligned_le64(report);
- x &= ~(m << offset);
- x |= ((u64)value) << offset;
- put_unaligned_le64(x, report);
+ __implement(report, offset, n, value);
}
/*
@@ -1251,6 +1293,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
value[n] >= min && value[n] <= max &&
+ value[n] - min < field->maxusage &&
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
goto exit;
}
@@ -1263,11 +1306,13 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
}
if (field->value[n] >= min && field->value[n] <= max
+ && field->value[n] - min < field->maxusage
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
if (value[n] >= min && value[n] <= max
+ && value[n] - min < field->maxusage
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
@@ -1920,6 +1965,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
@@ -1933,6 +1979,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
@@ -2004,6 +2053,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
@@ -2052,6 +2102,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
{ }
};
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 58551964c..717704e9a 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -595,6 +595,9 @@ static int corsair_input_mapping(struct hid_device *dev,
{
int gkey;
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
+ return 0;
+
gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);
if (gkey != 0) {
hid_map_usage_clear(input, usage, bit, max, EV_KEY,
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index 1d78ba3b7..8fd4bf77f 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -151,7 +151,7 @@ static inline int drff_init(struct hid_device *hid)
* descriptor. In any case, it's a wonder it works on Windows.
*
* Usage Page (Desktop), ; Generic desktop controls (01h)
- * Usage (Joystik), ; Joystik (04h, application collection)
+ * Usage (Joystick), ; Joystick (04h, application collection)
* Collection (Application),
* Collection (Logical),
* Report Size (8),
@@ -207,7 +207,7 @@ static inline int drff_init(struct hid_device *hid)
/* Fixed report descriptor for PID 0x011 joystick */
static __u8 pid0011_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
- 0x09, 0x04, /* Usage (Joystik), */
+ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x14, /* Logical Minimum (0), */
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 14c14c827..0238f0169 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -61,6 +61,9 @@
#define USB_VENDOR_ID_AIREN 0x1a2c
#define USB_DEVICE_ID_AIREN_SLIMPLUS 0x0002
+#define USB_VENDOR_ID_AKAI 0x2011
+#define USB_DEVICE_ID_AKAI_MPKMINI2 0x0715
+
#define USB_VENDOR_ID_ALCOR 0x058f
#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
@@ -246,6 +249,7 @@
#define USB_VENDOR_ID_CMEDIA 0x0d8c
#define USB_DEVICE_ID_CM109 0x000e
+#define USB_DEVICE_ID_CM6533 0x0022
#define USB_VENDOR_ID_CODEMERCS 0x07c0
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
@@ -673,6 +677,7 @@
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
#define USB_DEVICE_ID_MS_OFFICE_KB 0x0048
#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
+#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K 0x00b4
#define USB_DEVICE_ID_MS_NE4K 0x00db
#define USB_DEVICE_ID_MS_NE4K_JP 0x00dc
#define USB_DEVICE_ID_MS_LK6K 0x00f9
@@ -680,7 +685,10 @@
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
#define USB_DEVICE_ID_MS_NE7K 0x071d
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
+#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732
+#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
+#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3
#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799
#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
@@ -801,6 +809,7 @@
#define USB_VENDOR_ID_QUANTA 0x0408
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003 0x3003
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
#define USB_VENDOR_ID_RAZER 0x1532
@@ -840,6 +849,7 @@
#define USB_VENDOR_ID_SEMICO 0x1a2c
#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023
+#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2 0x0027
#define USB_VENDOR_ID_SENNHEISER 0x1395
#define USB_DEVICE_ID_SENNHEISER_BTD500USB 0x002c
@@ -873,6 +883,9 @@
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
+#define USB_VENDOR_ID_SINO_LITE 0x1345
+#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
+
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
@@ -1048,7 +1061,7 @@
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
#define USB_VENDOR_ID_MULTIPLE_1781 0x1781
-#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a9d
#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
index 0125e356b..1ac4ff4d5 100644
--- a/drivers/hid/hid-lenovo.c
+++ b/drivers/hid/hid-lenovo.c
@@ -184,21 +184,31 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
unsigned char byte2, unsigned char byte3)
{
int ret;
- unsigned char buf[] = {0x18, byte2, byte3};
+ unsigned char *buf;
+
+ buf = kzalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = 0x18;
+ buf[1] = byte2;
+ buf[2] = byte3;
switch (hdev->product) {
case USB_DEVICE_ID_LENOVO_CUSBKBD:
- ret = hid_hw_raw_request(hdev, 0x13, buf, sizeof(buf),
+ ret = hid_hw_raw_request(hdev, 0x13, buf, 3,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
break;
case USB_DEVICE_ID_LENOVO_CBTKBD:
- ret = hid_hw_output_report(hdev, buf, sizeof(buf));
+ ret = hid_hw_output_report(hdev, buf, 3);
break;
default:
ret = -EINVAL;
break;
}
+ kfree(buf);
+
return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */
}
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index c690fae02..feb2be71f 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -61,7 +61,7 @@
*/
static __u8 df_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
-0x09, 0x04, /* Usage (Joystik), */
+0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
@@ -127,7 +127,7 @@ static __u8 df_rdesc_fixed[] = {
static __u8 dfp_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
-0x09, 0x04, /* Usage (Joystik), */
+0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
@@ -175,7 +175,7 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 fv_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
-0x09, 0x04, /* Usage (Joystik), */
+0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
@@ -242,7 +242,7 @@ static __u8 fv_rdesc_fixed[] = {
static __u8 momo_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
-0x09, 0x04, /* Usage (Joystik), */
+0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
@@ -288,7 +288,7 @@ static __u8 momo_rdesc_fixed[] = {
static __u8 momo2_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
-0x09, 0x04, /* Usage (Joystik), */
+0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index bd2ab476c..2e2515a4c 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -15,13 +15,19 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/usb.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/kfifo.h>
#include <linux/input/mt.h>
+#include <linux/workqueue.h>
+#include <linux/atomic.h>
+#include <linux/fixp-arith.h>
#include <asm/unaligned.h>
+#include "usbhid/usbhid.h"
#include "hid-ids.h"
MODULE_LICENSE("GPL");
@@ -773,6 +779,589 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
}
}
+/* -------------------------------------------------------------------------- */
+/* 0x8123: Force feedback support */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_FF_GET_INFO 0x01
+#define HIDPP_FF_RESET_ALL 0x11
+#define HIDPP_FF_DOWNLOAD_EFFECT 0x21
+#define HIDPP_FF_SET_EFFECT_STATE 0x31
+#define HIDPP_FF_DESTROY_EFFECT 0x41
+#define HIDPP_FF_GET_APERTURE 0x51
+#define HIDPP_FF_SET_APERTURE 0x61
+#define HIDPP_FF_GET_GLOBAL_GAINS 0x71
+#define HIDPP_FF_SET_GLOBAL_GAINS 0x81
+
+#define HIDPP_FF_EFFECT_STATE_GET 0x00
+#define HIDPP_FF_EFFECT_STATE_STOP 0x01
+#define HIDPP_FF_EFFECT_STATE_PLAY 0x02
+#define HIDPP_FF_EFFECT_STATE_PAUSE 0x03
+
+#define HIDPP_FF_EFFECT_CONSTANT 0x00
+#define HIDPP_FF_EFFECT_PERIODIC_SINE 0x01
+#define HIDPP_FF_EFFECT_PERIODIC_SQUARE 0x02
+#define HIDPP_FF_EFFECT_PERIODIC_TRIANGLE 0x03
+#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP 0x04
+#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN 0x05
+#define HIDPP_FF_EFFECT_SPRING 0x06
+#define HIDPP_FF_EFFECT_DAMPER 0x07
+#define HIDPP_FF_EFFECT_FRICTION 0x08
+#define HIDPP_FF_EFFECT_INERTIA 0x09
+#define HIDPP_FF_EFFECT_RAMP 0x0A
+
+#define HIDPP_FF_EFFECT_AUTOSTART 0x80
+
+#define HIDPP_FF_EFFECTID_NONE -1
+#define HIDPP_FF_EFFECTID_AUTOCENTER -2
+
+#define HIDPP_FF_MAX_PARAMS 20
+#define HIDPP_FF_RESERVED_SLOTS 1
+
+struct hidpp_ff_private_data {
+ struct hidpp_device *hidpp;
+ u8 feature_index;
+ u8 version;
+ u16 gain;
+ s16 range;
+ u8 slot_autocenter;
+ u8 num_effects;
+ int *effect_ids;
+ struct workqueue_struct *wq;
+ atomic_t workqueue_size;
+};
+
+struct hidpp_ff_work_data {
+ struct work_struct work;
+ struct hidpp_ff_private_data *data;
+ int effect_id;
+ u8 command;
+ u8 params[HIDPP_FF_MAX_PARAMS];
+ u8 size;
+};
+
+static const signed short hiddpp_ff_effects[] = {
+ FF_CONSTANT,
+ FF_PERIODIC,
+ FF_SINE,
+ FF_SQUARE,
+ FF_SAW_UP,
+ FF_SAW_DOWN,
+ FF_TRIANGLE,
+ FF_SPRING,
+ FF_DAMPER,
+ FF_AUTOCENTER,
+ FF_GAIN,
+ -1
+};
+
+static const signed short hiddpp_ff_effects_v2[] = {
+ FF_RAMP,
+ FF_FRICTION,
+ FF_INERTIA,
+ -1
+};
+
+static const u8 HIDPP_FF_CONDITION_CMDS[] = {
+ HIDPP_FF_EFFECT_SPRING,
+ HIDPP_FF_EFFECT_FRICTION,
+ HIDPP_FF_EFFECT_DAMPER,
+ HIDPP_FF_EFFECT_INERTIA
+};
+
+static const char *HIDPP_FF_CONDITION_NAMES[] = {
+ "spring",
+ "friction",
+ "damper",
+ "inertia"
+};
+
+
+static u8 hidpp_ff_find_effect(struct hidpp_ff_private_data *data, int effect_id)
+{
+ int i;
+
+ for (i = 0; i < data->num_effects; i++)
+ if (data->effect_ids[i] == effect_id)
+ return i+1;
+
+ return 0;
+}
+
+static void hidpp_ff_work_handler(struct work_struct *w)
+{
+ struct hidpp_ff_work_data *wd = container_of(w, struct hidpp_ff_work_data, work);
+ struct hidpp_ff_private_data *data = wd->data;
+ struct hidpp_report response;
+ u8 slot;
+ int ret;
+
+ /* add slot number if needed */
+ switch (wd->effect_id) {
+ case HIDPP_FF_EFFECTID_AUTOCENTER:
+ wd->params[0] = data->slot_autocenter;
+ break;
+ case HIDPP_FF_EFFECTID_NONE:
+ /* leave slot as zero */
+ break;
+ default:
+ /* find current slot for effect */
+ wd->params[0] = hidpp_ff_find_effect(data, wd->effect_id);
+ break;
+ }
+
+ /* send command and wait for reply */
+ ret = hidpp_send_fap_command_sync(data->hidpp, data->feature_index,
+ wd->command, wd->params, wd->size, &response);
+
+ if (ret) {
+ hid_err(data->hidpp->hid_dev, "Failed to send command to device!\n");
+ goto out;
+ }
+
+ /* parse return data */
+ switch (wd->command) {
+ case HIDPP_FF_DOWNLOAD_EFFECT:
+ slot = response.fap.params[0];
+ if (slot > 0 && slot <= data->num_effects) {
+ if (wd->effect_id >= 0)
+ /* regular effect uploaded */
+ data->effect_ids[slot-1] = wd->effect_id;
+ else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
+ /* autocenter spring uploaded */
+ data->slot_autocenter = slot;
+ }
+ break;
+ case HIDPP_FF_DESTROY_EFFECT:
+ if (wd->effect_id >= 0)
+ /* regular effect destroyed */
+ data->effect_ids[wd->params[0]-1] = -1;
+ else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
+ /* autocenter spring destoyed */
+ data->slot_autocenter = 0;
+ break;
+ case HIDPP_FF_SET_GLOBAL_GAINS:
+ data->gain = (wd->params[0] << 8) + wd->params[1];
+ break;
+ case HIDPP_FF_SET_APERTURE:
+ data->range = (wd->params[0] << 8) + wd->params[1];
+ break;
+ default:
+ /* no action needed */
+ break;
+ }
+
+out:
+ atomic_dec(&data->workqueue_size);
+ kfree(wd);
+}
+
+static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id, u8 command, u8 *params, u8 size)
+{
+ struct hidpp_ff_work_data *wd = kzalloc(sizeof(*wd), GFP_KERNEL);
+ int s;
+
+ if (!wd)
+ return -ENOMEM;
+
+ INIT_WORK(&wd->work, hidpp_ff_work_handler);
+
+ wd->data = data;
+ wd->effect_id = effect_id;
+ wd->command = command;
+ wd->size = size;
+ memcpy(wd->params, params, size);
+
+ atomic_inc(&data->workqueue_size);
+ queue_work(data->wq, &wd->work);
+
+ /* warn about excessive queue size */
+ s = atomic_read(&data->workqueue_size);
+ if (s >= 20 && s % 20 == 0)
+ hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
+
+ return 0;
+}
+
+static int hidpp_ff_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+ struct hidpp_ff_private_data *data = dev->ff->private;
+ u8 params[20];
+ u8 size;
+ int force;
+
+ /* set common parameters */
+ params[2] = effect->replay.length >> 8;
+ params[3] = effect->replay.length & 255;
+ params[4] = effect->replay.delay >> 8;
+ params[5] = effect->replay.delay & 255;
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+ force = (effect->u.constant.level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+ params[1] = HIDPP_FF_EFFECT_CONSTANT;
+ params[6] = force >> 8;
+ params[7] = force & 255;
+ params[8] = effect->u.constant.envelope.attack_level >> 7;
+ params[9] = effect->u.constant.envelope.attack_length >> 8;
+ params[10] = effect->u.constant.envelope.attack_length & 255;
+ params[11] = effect->u.constant.envelope.fade_level >> 7;
+ params[12] = effect->u.constant.envelope.fade_length >> 8;
+ params[13] = effect->u.constant.envelope.fade_length & 255;
+ size = 14;
+ dbg_hid("Uploading constant force level=%d in dir %d = %d\n",
+ effect->u.constant.level,
+ effect->direction, force);
+ dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+ effect->u.constant.envelope.attack_level,
+ effect->u.constant.envelope.attack_length,
+ effect->u.constant.envelope.fade_level,
+ effect->u.constant.envelope.fade_length);
+ break;
+ case FF_PERIODIC:
+ {
+ switch (effect->u.periodic.waveform) {
+ case FF_SINE:
+ params[1] = HIDPP_FF_EFFECT_PERIODIC_SINE;
+ break;
+ case FF_SQUARE:
+ params[1] = HIDPP_FF_EFFECT_PERIODIC_SQUARE;
+ break;
+ case FF_SAW_UP:
+ params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP;
+ break;
+ case FF_SAW_DOWN:
+ params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN;
+ break;
+ case FF_TRIANGLE:
+ params[1] = HIDPP_FF_EFFECT_PERIODIC_TRIANGLE;
+ break;
+ default:
+ hid_err(data->hidpp->hid_dev, "Unexpected periodic waveform type %i!\n", effect->u.periodic.waveform);
+ return -EINVAL;
+ }
+ force = (effect->u.periodic.magnitude * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+ params[6] = effect->u.periodic.magnitude >> 8;
+ params[7] = effect->u.periodic.magnitude & 255;
+ params[8] = effect->u.periodic.offset >> 8;
+ params[9] = effect->u.periodic.offset & 255;
+ params[10] = effect->u.periodic.period >> 8;
+ params[11] = effect->u.periodic.period & 255;
+ params[12] = effect->u.periodic.phase >> 8;
+ params[13] = effect->u.periodic.phase & 255;
+ params[14] = effect->u.periodic.envelope.attack_level >> 7;
+ params[15] = effect->u.periodic.envelope.attack_length >> 8;
+ params[16] = effect->u.periodic.envelope.attack_length & 255;
+ params[17] = effect->u.periodic.envelope.fade_level >> 7;
+ params[18] = effect->u.periodic.envelope.fade_length >> 8;
+ params[19] = effect->u.periodic.envelope.fade_length & 255;
+ size = 20;
+ dbg_hid("Uploading periodic force mag=%d/dir=%d, offset=%d, period=%d ms, phase=%d\n",
+ effect->u.periodic.magnitude, effect->direction,
+ effect->u.periodic.offset,
+ effect->u.periodic.period,
+ effect->u.periodic.phase);
+ dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+ effect->u.periodic.envelope.attack_level,
+ effect->u.periodic.envelope.attack_length,
+ effect->u.periodic.envelope.fade_level,
+ effect->u.periodic.envelope.fade_length);
+ break;
+ }
+ case FF_RAMP:
+ params[1] = HIDPP_FF_EFFECT_RAMP;
+ force = (effect->u.ramp.start_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+ params[6] = force >> 8;
+ params[7] = force & 255;
+ force = (effect->u.ramp.end_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+ params[8] = force >> 8;
+ params[9] = force & 255;
+ params[10] = effect->u.ramp.envelope.attack_level >> 7;
+ params[11] = effect->u.ramp.envelope.attack_length >> 8;
+ params[12] = effect->u.ramp.envelope.attack_length & 255;
+ params[13] = effect->u.ramp.envelope.fade_level >> 7;
+ params[14] = effect->u.ramp.envelope.fade_length >> 8;
+ params[15] = effect->u.ramp.envelope.fade_length & 255;
+ size = 16;
+ dbg_hid("Uploading ramp force level=%d -> %d in dir %d = %d\n",
+ effect->u.ramp.start_level,
+ effect->u.ramp.end_level,
+ effect->direction, force);
+ dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+ effect->u.ramp.envelope.attack_level,
+ effect->u.ramp.envelope.attack_length,
+ effect->u.ramp.envelope.fade_level,
+ effect->u.ramp.envelope.fade_length);
+ break;
+ case FF_FRICTION:
+ case FF_INERTIA:
+ case FF_SPRING:
+ case FF_DAMPER:
+ params[1] = HIDPP_FF_CONDITION_CMDS[effect->type - FF_SPRING];
+ params[6] = effect->u.condition[0].left_saturation >> 9;
+ params[7] = (effect->u.condition[0].left_saturation >> 1) & 255;
+ params[8] = effect->u.condition[0].left_coeff >> 8;
+ params[9] = effect->u.condition[0].left_coeff & 255;
+ params[10] = effect->u.condition[0].deadband >> 9;
+ params[11] = (effect->u.condition[0].deadband >> 1) & 255;
+ params[12] = effect->u.condition[0].center >> 8;
+ params[13] = effect->u.condition[0].center & 255;
+ params[14] = effect->u.condition[0].right_coeff >> 8;
+ params[15] = effect->u.condition[0].right_coeff & 255;
+ params[16] = effect->u.condition[0].right_saturation >> 9;
+ params[17] = (effect->u.condition[0].right_saturation >> 1) & 255;
+ size = 18;
+ dbg_hid("Uploading %s force left coeff=%d, left sat=%d, right coeff=%d, right sat=%d\n",
+ HIDPP_FF_CONDITION_NAMES[effect->type - FF_SPRING],
+ effect->u.condition[0].left_coeff,
+ effect->u.condition[0].left_saturation,
+ effect->u.condition[0].right_coeff,
+ effect->u.condition[0].right_saturation);
+ dbg_hid(" deadband=%d, center=%d\n",
+ effect->u.condition[0].deadband,
+ effect->u.condition[0].center);
+ break;
+ default:
+ hid_err(data->hidpp->hid_dev, "Unexpected force type %i!\n", effect->type);
+ return -EINVAL;
+ }
+
+ return hidpp_ff_queue_work(data, effect->id, HIDPP_FF_DOWNLOAD_EFFECT, params, size);
+}
+
+static int hidpp_ff_playback(struct input_dev *dev, int effect_id, int value)
+{
+ struct hidpp_ff_private_data *data = dev->ff->private;
+ u8 params[2];
+
+ params[1] = value ? HIDPP_FF_EFFECT_STATE_PLAY : HIDPP_FF_EFFECT_STATE_STOP;
+
+ dbg_hid("St%sing playback of effect %d.\n", value?"art":"opp", effect_id);
+
+ return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_SET_EFFECT_STATE, params, ARRAY_SIZE(params));
+}
+
+static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct hidpp_ff_private_data *data = dev->ff->private;
+ u8 slot = 0;
+
+ dbg_hid("Erasing effect %d.\n", effect_id);
+
+ return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_DESTROY_EFFECT, &slot, 1);
+}
+
+static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ struct hidpp_ff_private_data *data = dev->ff->private;
+ u8 params[18];
+
+ dbg_hid("Setting autocenter to %d.\n", magnitude);
+
+ /* start a standard spring effect */
+ params[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART;
+ /* zero delay and duration */
+ params[2] = params[3] = params[4] = params[5] = 0;
+ /* set coeff to 25% of saturation */
+ params[8] = params[14] = magnitude >> 11;
+ params[9] = params[15] = (magnitude >> 3) & 255;
+ params[6] = params[16] = magnitude >> 9;
+ params[7] = params[17] = (magnitude >> 1) & 255;
+ /* zero deadband and center */
+ params[10] = params[11] = params[12] = params[13] = 0;
+
+ hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_AUTOCENTER, HIDPP_FF_DOWNLOAD_EFFECT, params, ARRAY_SIZE(params));
+}
+
+static void hidpp_ff_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct hidpp_ff_private_data *data = dev->ff->private;
+ u8 params[4];
+
+ dbg_hid("Setting gain to %d.\n", gain);
+
+ params[0] = gain >> 8;
+ params[1] = gain & 255;
+ params[2] = 0; /* no boost */
+ params[3] = 0;
+
+ hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_NONE, HIDPP_FF_SET_GLOBAL_GAINS, params, ARRAY_SIZE(params));
+}
+
+static ssize_t hidpp_ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct hid_device *hid = to_hid_device(dev);
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct input_dev *idev = hidinput->input;
+ struct hidpp_ff_private_data *data = idev->ff->private;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", data->range);
+}
+
+static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct hid_device *hid = to_hid_device(dev);
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct input_dev *idev = hidinput->input;
+ struct hidpp_ff_private_data *data = idev->ff->private;
+ u8 params[2];
+ int range = simple_strtoul(buf, NULL, 10);
+
+ range = clamp(range, 180, 900);
+
+ params[0] = range >> 8;
+ params[1] = range & 0x00FF;
+
+ hidpp_ff_queue_work(data, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
+
+ return count;
+}
+
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp_ff_range_show, hidpp_ff_range_store);
+
+static void hidpp_ff_destroy(struct ff_device *ff)
+{
+ struct hidpp_ff_private_data *data = ff->private;
+
+ kfree(data->effect_ids);
+}
+
+static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
+{
+ struct hid_device *hid = hidpp->hid_dev;
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct input_dev *dev = hidinput->input;
+ const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
+ const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
+ struct ff_device *ff;
+ struct hidpp_report response;
+ struct hidpp_ff_private_data *data;
+ int error, j, num_slots;
+ u8 version;
+
+ if (!dev) {
+ hid_err(hid, "Struct input_dev not set!\n");
+ return -EINVAL;
+ }
+
+ /* Get firmware release */
+ version = bcdDevice & 255;
+
+ /* Set supported force feedback capabilities */
+ for (j = 0; hiddpp_ff_effects[j] >= 0; j++)
+ set_bit(hiddpp_ff_effects[j], dev->ffbit);
+ if (version > 1)
+ for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++)
+ set_bit(hiddpp_ff_effects_v2[j], dev->ffbit);
+
+ /* Read number of slots available in device */
+ error = hidpp_send_fap_command_sync(hidpp, feature_index,
+ HIDPP_FF_GET_INFO, NULL, 0, &response);
+ if (error) {
+ if (error < 0)
+ return error;
+ hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+ __func__, error);
+ return -EPROTO;
+ }
+
+ num_slots = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
+
+ error = input_ff_create(dev, num_slots);
+
+ if (error) {
+ hid_err(dev, "Failed to create FF device!\n");
+ return error;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
+ if (!data->effect_ids) {
+ kfree(data);
+ return -ENOMEM;
+ }
+ data->hidpp = hidpp;
+ data->feature_index = feature_index;
+ data->version = version;
+ data->slot_autocenter = 0;
+ data->num_effects = num_slots;
+ for (j = 0; j < num_slots; j++)
+ data->effect_ids[j] = -1;
+
+ ff = dev->ff;
+ ff->private = data;
+
+ ff->upload = hidpp_ff_upload_effect;
+ ff->erase = hidpp_ff_erase_effect;
+ ff->playback = hidpp_ff_playback;
+ ff->set_gain = hidpp_ff_set_gain;
+ ff->set_autocenter = hidpp_ff_set_autocenter;
+ ff->destroy = hidpp_ff_destroy;
+
+
+ /* reset all forces */
+ error = hidpp_send_fap_command_sync(hidpp, feature_index,
+ HIDPP_FF_RESET_ALL, NULL, 0, &response);
+
+ /* Read current Range */
+ error = hidpp_send_fap_command_sync(hidpp, feature_index,
+ HIDPP_FF_GET_APERTURE, NULL, 0, &response);
+ if (error)
+ hid_warn(hidpp->hid_dev, "Failed to read range from device!\n");
+ data->range = error ? 900 : get_unaligned_be16(&response.fap.params[0]);
+
+ /* Create sysfs interface */
+ error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
+ if (error)
+ hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
+
+ /* Read the current gain values */
+ error = hidpp_send_fap_command_sync(hidpp, feature_index,
+ HIDPP_FF_GET_GLOBAL_GAINS, NULL, 0, &response);
+ if (error)
+ hid_warn(hidpp->hid_dev, "Failed to read gain values from device!\n");
+ data->gain = error ? 0xffff : get_unaligned_be16(&response.fap.params[0]);
+ /* ignore boost value at response.fap.params[2] */
+
+ /* init the hardware command queue */
+ data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
+ atomic_set(&data->workqueue_size, 0);
+
+ /* initialize with zero autocenter to get wheel in usable state */
+ hidpp_ff_set_autocenter(dev, 0);
+
+ hid_info(hid, "Force feeback support loaded (firmware release %d).\n", version);
+
+ return 0;
+}
+
+static int hidpp_ff_deinit(struct hid_device *hid)
+{
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct input_dev *dev = hidinput->input;
+ struct hidpp_ff_private_data *data;
+
+ if (!dev) {
+ hid_err(hid, "Struct input_dev not found!\n");
+ return -EINVAL;
+ }
+
+ hid_info(hid, "Unloading HID++ force feedback.\n");
+ data = dev->ff->private;
+ if (!data) {
+ hid_err(hid, "Private data not found!\n");
+ return -EINVAL;
+ }
+
+ destroy_workqueue(data->wq);
+ device_remove_file(&hid->dev, &dev_attr_range);
+
+ return 0;
+}
+
+
/* ************************************************************************** */
/* */
/* Device Support */
@@ -1301,121 +1890,22 @@ static int k400_connect(struct hid_device *hdev, bool connected)
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
-/* Using session ID = 1 */
-#define CMD_G920_FORCE_GET_APERTURE 0x51
-#define CMD_G920_FORCE_SET_APERTURE 0x61
-
-struct g920_private_data {
- u8 force_feature;
- u16 range;
-};
-
-static ssize_t g920_range_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hid_device *hid = to_hid_device(dev);
- struct hidpp_device *hidpp = hid_get_drvdata(hid);
- struct g920_private_data *pdata;
-
- pdata = hidpp->private_data;
- if (!pdata) {
- hid_err(hid, "Private driver data not found!\n");
- return -EINVAL;
- }
-
- return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->range);
-}
-
-static ssize_t g920_range_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hid_device *hid = to_hid_device(dev);
- struct hidpp_device *hidpp = hid_get_drvdata(hid);
- struct g920_private_data *pdata;
- struct hidpp_report response;
- u8 params[2];
- int ret;
- u16 range = simple_strtoul(buf, NULL, 10);
-
- pdata = hidpp->private_data;
- if (!pdata) {
- hid_err(hid, "Private driver data not found!\n");
- return -EINVAL;
- }
-
- if (range < 180)
- range = 180;
- else if (range > 900)
- range = 900;
-
- params[0] = range >> 8;
- params[1] = range & 0x00FF;
-
- ret = hidpp_send_fap_command_sync(hidpp, pdata->force_feature,
- CMD_G920_FORCE_SET_APERTURE, params, 2, &response);
- if (ret)
- return ret;
-
- pdata->range = range;
- return count;
-}
-
-static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, g920_range_show, g920_range_store);
-
-static int g920_allocate(struct hid_device *hdev)
-{
- struct hidpp_device *hidpp = hid_get_drvdata(hdev);
- struct g920_private_data *pdata;
-
- pdata = devm_kzalloc(&hdev->dev, sizeof(struct g920_private_data),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- hidpp->private_data = pdata;
-
- return 0;
-}
-
static int g920_get_config(struct hidpp_device *hidpp)
{
- struct g920_private_data *pdata = hidpp->private_data;
- struct hidpp_report response;
u8 feature_type;
u8 feature_index;
int ret;
- pdata = hidpp->private_data;
- if (!pdata) {
- hid_err(hidpp->hid_dev, "Private driver data not found!\n");
- return -EINVAL;
- }
-
/* Find feature and store for later use */
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
&feature_index, &feature_type);
if (ret)
return ret;
- pdata->force_feature = feature_index;
-
- /* Read current Range */
- ret = hidpp_send_fap_command_sync(hidpp, feature_index,
- CMD_G920_FORCE_GET_APERTURE, NULL, 0, &response);
- if (ret > 0) {
- hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
- __func__, ret);
- return -EPROTO;
- }
- if (ret)
- return ret;
-
- pdata->range = get_unaligned_be16(&response.fap.params[0]);
-
- /* Create sysfs interface */
- ret = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
+ ret = hidpp_ff_init(hidpp, feature_index);
if (ret)
- hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d\n", ret);
+ hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n",
+ ret);
return 0;
}
@@ -1739,10 +2229,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = k400_allocate(hdev);
if (ret)
goto allocate_fail;
- } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- ret = g920_allocate(hdev);
- if (ret)
- goto allocate_fail;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1825,7 +2311,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_hw_open_failed:
hid_device_io_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- device_remove_file(&hdev->dev, &dev_attr_range);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
@@ -1843,7 +2328,7 @@ static void hidpp_remove(struct hid_device *hdev)
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- device_remove_file(&hdev->dev, &dev_attr_range);
+ hidpp_ff_deinit(hdev);
hid_hw_close(hdev);
}
hid_hw_stop(hdev);
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 77a2cf3e4..e924d5555 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -272,6 +272,12 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_PRESENTER },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
.driver_data = MS_ERGONOMY | MS_RDESC_3K },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K),
+ .driver_data = MS_ERGONOMY },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600),
+ .driver_data = MS_ERGONOMY },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1),
+ .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
.driver_data = MS_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
@@ -286,6 +292,8 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
.driver_data = MS_HIDINPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
+ .driver_data = MS_ERGONOMY},
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index a20fc604f..c741f5e50 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1138,6 +1138,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
+ if (ret)
+ dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n",
+ hdev->name);
mt_set_maxcontacts(hdev);
mt_set_input_mode(hdev);
@@ -1150,8 +1153,31 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
#ifdef CONFIG_PM
+static void mt_release_contacts(struct hid_device *hid)
+{
+ struct hid_input *hidinput;
+
+ list_for_each_entry(hidinput, &hid->inputs, list) {
+ struct input_dev *input_dev = hidinput->input;
+ struct input_mt *mt = input_dev->mt;
+ int i;
+
+ if (mt) {
+ for (i = 0; i < mt->num_slots; i++) {
+ input_mt_slot(input_dev, i);
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER,
+ false);
+ }
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ }
+ }
+}
+
static int mt_reset_resume(struct hid_device *hdev)
{
+ mt_release_contacts(hdev);
mt_set_maxcontacts(hdev);
mt_set_input_mode(hdev);
return 0;
diff --git a/drivers/hid/hid-penmount.c b/drivers/hid/hid-penmount.c
index c11dce85c..d90383f78 100644
--- a/drivers/hid/hid-penmount.c
+++ b/drivers/hid/hid-penmount.c
@@ -23,8 +23,12 @@ static int penmount_input_mapping(struct hid_device *hdev,
struct hid_usage *usage, unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
- hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
- return 1;
+ if (((usage->hid - 1) & HID_USAGE) == 0) {
+ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+ return 1;
+ } else {
+ return -1;
+ }
}
return 0;
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 67cd059a8..9cd2ca34a 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -594,6 +594,9 @@ static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
int ret;
u8 buf[RMI_F11_CTRL_REG_COUNT];
+ if (!(data->device_flags & RMI_DEVICE))
+ return 0;
+
ret = rmi_read_block(hdev, data->f11.control_base_addr, buf,
RMI_F11_CTRL_REG_COUNT);
if (ret)
@@ -613,6 +616,9 @@ static int rmi_post_reset(struct hid_device *hdev)
struct rmi_data *data = hid_get_drvdata(hdev);
int ret;
+ if (!(data->device_flags & RMI_DEVICE))
+ return 0;
+
ret = rmi_reset_attn_mode(hdev);
if (ret) {
hid_err(hdev, "can not set rmi mode\n");
@@ -640,6 +646,11 @@ static int rmi_post_reset(struct hid_device *hdev)
static int rmi_post_resume(struct hid_device *hdev)
{
+ struct rmi_data *data = hid_get_drvdata(hdev);
+
+ if (!(data->device_flags & RMI_DEVICE))
+ return 0;
+
return rmi_reset_attn_mode(hdev);
}
#endif /* CONFIG_PM */
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 9b8db0e0e..310436a54 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -50,6 +50,7 @@
#define MOTION_CONTROLLER_BT BIT(8)
#define NAVIGATION_CONTROLLER_USB BIT(9)
#define NAVIGATION_CONTROLLER_BT BIT(10)
+#define SINO_LITE_CONTROLLER BIT(11)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -74,7 +75,7 @@
* axis values. Additionally, the controller only has 20 actual, physical axes
* so there are several unused axes in between the used ones.
*/
-static __u8 sixaxis_rdesc[] = {
+static u8 sixaxis_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
@@ -152,7 +153,7 @@ static __u8 sixaxis_rdesc[] = {
};
/* PS/3 Motion controller */
-static __u8 motion_rdesc[] = {
+static u8 motion_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
@@ -249,9 +250,9 @@ static __u8 motion_rdesc[] = {
};
/* PS/3 Navigation controller */
-static __u8 navigation_rdesc[] = {
+static u8 navigation_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
- 0x09, 0x04, /* Usage (Joystik), */
+ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x85, 0x01, /* Report ID (1), */
@@ -809,7 +810,7 @@ static u8 dualshock4_bt_rdesc[] = {
0xC0 /* End Collection */
};
-static __u8 ps3remote_rdesc[] = {
+static u8 ps3remote_rdesc[] = {
0x05, 0x01, /* GUsagePage Generic Desktop */
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
@@ -817,14 +818,18 @@ static __u8 ps3remote_rdesc[] = {
/* Use collection 1 for joypad buttons */
0xA1, 0x02, /* MCollection Logical (interrelated data) */
- /* Ignore the 1st byte, maybe it is used for a controller
- * number but it's not needed for correct operation */
+ /*
+ * Ignore the 1st byte, maybe it is used for a controller
+ * number but it's not needed for correct operation
+ */
0x75, 0x08, /* GReportSize 0x08 [8] */
0x95, 0x01, /* GReportCount 0x01 [1] */
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
- /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
- * buttons multiple keypresses are allowed */
+ /*
+ * Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
+ * buttons multiple keypresses are allowed
+ */
0x05, 0x09, /* GUsagePage Button */
0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
@@ -849,8 +854,10 @@ static __u8 ps3remote_rdesc[] = {
0x95, 0x01, /* GReportCount 0x01 [1] */
0x80, /* MInput */
- /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
- * 0xff and 11th is for press indication */
+ /*
+ * Ignore bytes from 6th to 11th, 6th to 10th are always constant at
+ * 0xff and 11th is for press indication
+ */
0x75, 0x08, /* GReportSize 0x08 [8] */
0x95, 0x06, /* GReportCount 0x06 [6] */
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
@@ -929,7 +936,7 @@ static const unsigned int buzz_keymap[] = {
/*
* The controller has 4 remote buzzers, each with one LED and 5
* buttons.
- *
+ *
* We use the mapping chosen by the controller, which is:
*
* Key Offset
@@ -943,15 +950,15 @@ static const unsigned int buzz_keymap[] = {
* So, for example, the orange button on the third buzzer is mapped to
* BTN_TRIGGER_HAPPY14
*/
- [ 1] = BTN_TRIGGER_HAPPY1,
- [ 2] = BTN_TRIGGER_HAPPY2,
- [ 3] = BTN_TRIGGER_HAPPY3,
- [ 4] = BTN_TRIGGER_HAPPY4,
- [ 5] = BTN_TRIGGER_HAPPY5,
- [ 6] = BTN_TRIGGER_HAPPY6,
- [ 7] = BTN_TRIGGER_HAPPY7,
- [ 8] = BTN_TRIGGER_HAPPY8,
- [ 9] = BTN_TRIGGER_HAPPY9,
+ [1] = BTN_TRIGGER_HAPPY1,
+ [2] = BTN_TRIGGER_HAPPY2,
+ [3] = BTN_TRIGGER_HAPPY3,
+ [4] = BTN_TRIGGER_HAPPY4,
+ [5] = BTN_TRIGGER_HAPPY5,
+ [6] = BTN_TRIGGER_HAPPY6,
+ [7] = BTN_TRIGGER_HAPPY7,
+ [8] = BTN_TRIGGER_HAPPY8,
+ [9] = BTN_TRIGGER_HAPPY9,
[10] = BTN_TRIGGER_HAPPY10,
[11] = BTN_TRIGGER_HAPPY11,
[12] = BTN_TRIGGER_HAPPY12,
@@ -973,33 +980,33 @@ static enum power_supply_property sony_battery_props[] = {
};
struct sixaxis_led {
- __u8 time_enabled; /* the total time the led is active (0xff means forever) */
- __u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
- __u8 enabled;
- __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
- __u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
+ u8 time_enabled; /* the total time the led is active (0xff means forever) */
+ u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
+ u8 enabled;
+ u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+ u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
} __packed;
struct sixaxis_rumble {
- __u8 padding;
- __u8 right_duration; /* Right motor duration (0xff means forever) */
- __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
- __u8 left_duration; /* Left motor duration (0xff means forever) */
- __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+ u8 padding;
+ u8 right_duration; /* Right motor duration (0xff means forever) */
+ u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+ u8 left_duration; /* Left motor duration (0xff means forever) */
+ u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
} __packed;
struct sixaxis_output_report {
- __u8 report_id;
+ u8 report_id;
struct sixaxis_rumble rumble;
- __u8 padding[4];
- __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+ u8 padding[4];
+ u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
struct sixaxis_led _reserved; /* LED5, not actually soldered */
} __packed;
union sixaxis_output_report_01 {
struct sixaxis_output_report data;
- __u8 buf[36];
+ u8 buf[36];
};
struct motion_output_report_02 {
@@ -1028,30 +1035,30 @@ struct sony_sc {
struct led_classdev *leds[MAX_LEDS];
unsigned long quirks;
struct work_struct state_worker;
- void(*send_output_report)(struct sony_sc*);
+ void (*send_output_report)(struct sony_sc *);
struct power_supply *battery;
struct power_supply_desc battery_desc;
int device_id;
- __u8 *output_report_dmabuf;
+ u8 *output_report_dmabuf;
#ifdef CONFIG_SONY_FF
- __u8 left;
- __u8 right;
+ u8 left;
+ u8 right;
#endif
- __u8 mac_address[6];
- __u8 worker_initialized;
- __u8 cable_state;
- __u8 battery_charging;
- __u8 battery_capacity;
- __u8 led_state[MAX_LEDS];
- __u8 resume_led_state[MAX_LEDS];
- __u8 led_delay_on[MAX_LEDS];
- __u8 led_delay_off[MAX_LEDS];
- __u8 led_count;
+ u8 mac_address[6];
+ u8 worker_initialized;
+ u8 cable_state;
+ u8 battery_charging;
+ u8 battery_capacity;
+ u8 led_state[MAX_LEDS];
+ u8 resume_led_state[MAX_LEDS];
+ u8 led_delay_on[MAX_LEDS];
+ u8 led_delay_off[MAX_LEDS];
+ u8 led_count;
};
-static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
{
*rsize = sizeof(sixaxis_rdesc);
@@ -1072,7 +1079,7 @@ static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
return navigation_rdesc;
}
-static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
{
*rsize = sizeof(ps3remote_rdesc);
@@ -1113,11 +1120,14 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1;
}
-static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
+ if (sc->quirks & SINO_LITE_CONTROLLER)
+ return rdesc;
+
/*
* Some Sony RF receivers wrongly declare the mouse pointer as a
* a constant non-data variable.
@@ -1164,12 +1174,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc;
}
-static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
{
- static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
+ static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
unsigned long flags;
int offset;
- __u8 cable_state, battery_capacity, battery_charging;
+ u8 cable_state, battery_capacity, battery_charging;
/*
* The sixaxis is charging if the battery value is 0xee
@@ -1184,7 +1194,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
battery_charging = !(rd[offset] & 0x01);
cable_state = 1;
} else {
- __u8 index = rd[offset] <= 5 ? rd[offset] : 5;
+ u8 index = rd[offset] <= 5 ? rd[offset] : 5;
battery_capacity = sixaxis_battery_capacity[index];
battery_charging = 0;
cable_state = 0;
@@ -1197,14 +1207,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
spin_unlock_irqrestore(&sc->lock, flags);
}
-static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
{
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
unsigned long flags;
int n, offset;
- __u8 cable_state, battery_capacity, battery_charging;
+ u8 cable_state, battery_capacity, battery_charging;
/*
* Battery and touchpad data starts at byte 30 in the USB report and
@@ -1254,7 +1264,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
* follows the data for the first.
*/
for (n = 0; n < 2; n++) {
- __u16 x, y;
+ u16 x, y;
x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
@@ -1270,7 +1280,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
}
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
- __u8 *rd, int size)
+ u8 *rd, int size)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
@@ -1394,7 +1404,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
{
const int buf_size =
max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
- __u8 *buf;
+ u8 *buf;
int ret;
buf = kmalloc(buf_size, GFP_KERNEL);
@@ -1420,8 +1430,10 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
}
ret = hid_hw_output_report(hdev, buf, 1);
- if (ret < 0)
- hid_err(hdev, "can't set operational mode: step 3\n");
+ if (ret < 0) {
+ hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
+ ret = 0;
+ }
out:
kfree(buf);
@@ -1431,8 +1443,8 @@ out:
static int sixaxis_set_operational_bt(struct hid_device *hdev)
{
- static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
- __u8 *buf;
+ static const u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
+ u8 *buf;
int ret;
buf = kmemdup(report, sizeof(report), GFP_KERNEL);
@@ -1453,7 +1465,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
*/
static int dualshock4_set_operational_bt(struct hid_device *hdev)
{
- __u8 *buf;
+ u8 *buf;
int ret;
buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
@@ -1470,7 +1482,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{
- static const __u8 sixaxis_leds[10][4] = {
+ static const u8 sixaxis_leds[10][4] = {
{ 0x01, 0x00, 0x00, 0x00 },
{ 0x00, 0x01, 0x00, 0x00 },
{ 0x00, 0x00, 0x01, 0x00 },
@@ -1497,7 +1509,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc)
static void dualshock4_set_leds_from_id(struct sony_sc *sc)
{
/* The first 4 color/index entries match what the PS4 assigns */
- static const __u8 color_code[7][3] = {
+ static const u8 color_code[7][3] = {
/* Blue */ { 0x00, 0x00, 0x01 },
/* Red */ { 0x01, 0x00, 0x00 },
/* Green */ { 0x00, 0x01, 0x00 },
@@ -1525,7 +1537,7 @@ static void buzz_set_leds(struct sony_sc *sc)
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next,
struct hid_report, list);
- __s32 *value = report->field[0]->value;
+ s32 *value = report->field[0]->value;
BUILD_BUG_ON(MAX_LEDS < 4);
@@ -1619,7 +1631,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *drv_data = hid_get_drvdata(hdev);
int n;
- __u8 new_on, new_off;
+ u8 new_on, new_off;
if (!drv_data) {
hid_err(hdev, "No device data\n");
@@ -1690,8 +1702,8 @@ static int sony_leds_init(struct sony_sc *sc)
const char *name_fmt;
static const char * const ds4_name_str[] = { "red", "green", "blue",
"global" };
- __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
- __u8 use_hw_blink[MAX_LEDS] = { 0 };
+ u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
+ u8 use_hw_blink[MAX_LEDS] = { 0 };
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
@@ -1719,7 +1731,7 @@ static int sony_leds_init(struct sony_sc *sc)
name_len = 0;
name_fmt = "%s:%s";
} else if (sc->quirks & NAVIGATION_CONTROLLER) {
- static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
+ static const u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
sc->led_count = 1;
@@ -1796,7 +1808,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
static const union sixaxis_output_report_01 default_report = {
.buf = {
0x01,
- 0x00, 0xff, 0x00, 0xff, 0x00,
+ 0x01, 0xff, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
@@ -1842,7 +1854,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
}
}
- hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report,
+ hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
sizeof(struct sixaxis_output_report),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
@@ -1850,7 +1862,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
static void dualshock4_send_output_report(struct sony_sc *sc)
{
struct hid_device *hdev = sc->hdev;
- __u8 *buf = sc->output_report_dmabuf;
+ u8 *buf = sc->output_report_dmabuf;
int offset;
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
@@ -1910,7 +1922,7 @@ static void motion_send_output_report(struct sony_sc *sc)
report->rumble = max(sc->right, sc->left);
#endif
- hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
+ hid_hw_output_report(hdev, (u8 *)report, MOTION_REPORT_0x02_SIZE);
}
static inline void sony_send_output_report(struct sony_sc *sc)
@@ -1922,6 +1934,7 @@ static inline void sony_send_output_report(struct sony_sc *sc)
static void sony_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+
sc->send_output_report(sc);
}
@@ -2142,7 +2155,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
static int sony_check_add(struct sony_sc *sc)
{
- __u8 *buf = NULL;
+ u8 *buf = NULL;
int n, ret;
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
@@ -2253,7 +2266,7 @@ static void sony_release_device_id(struct sony_sc *sc)
}
static inline void sony_init_output_report(struct sony_sc *sc,
- void(*send_output_report)(struct sony_sc*))
+ void (*send_output_report)(struct sony_sc *))
{
sc->send_output_report = send_output_report;
@@ -2441,7 +2454,7 @@ static int sony_suspend(struct hid_device *hdev, pm_message_t message)
/*
* On suspend save the current LED state,
* stop running force-feedback and blank the LEDS.
- */
+ */
if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
struct sony_sc *sc = hid_get_drvdata(hdev);
@@ -2501,8 +2514,10 @@ static const struct hid_device_id sony_devices[] = {
.driver_data = VAIO_RDESC_CONSTANT },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
.driver_data = VAIO_RDESC_CONSTANT },
- /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
- * Logitech joystick from the device descriptor. */
+ /*
+ * Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
+ * Logitech joystick from the device descriptor.
+ */
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER),
.driver_data = BUZZ_CONTROLLER },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER),
@@ -2521,6 +2536,9 @@ static const struct hid_device_id sony_devices[] = {
.driver_data = DUALSHOCK4_CONTROLLER_USB },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
.driver_data = DUALSHOCK4_CONTROLLER_BT },
+ /* Nyko Core Controller for PS3 */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
+ .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
{ }
};
MODULE_DEVICE_TABLE(hid, sony_devices);
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c
index b95d3978c..847a497cd 100644
--- a/drivers/hid/hid-thingm.c
+++ b/drivers/hid/hid-thingm.c
@@ -14,7 +14,6 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/workqueue.h>
#include "hid-ids.h"
@@ -56,7 +55,6 @@ struct thingm_rgb {
struct thingm_led red;
struct thingm_led green;
struct thingm_led blue;
- struct work_struct work;
u8 num;
};
@@ -79,9 +77,13 @@ static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]);
+ mutex_lock(&tdev->lock);
+
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ mutex_unlock(&tdev->lock);
+
return ret < 0 ? ret : 0;
}
@@ -89,16 +91,31 @@ static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
{
int ret;
+ /*
+ * A read consists of two operations: sending the read command
+ * and the actual read from the device. Use the mutex to protect
+ * the full sequence of both operations.
+ */
+ mutex_lock(&tdev->lock);
+
+ ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0)
+ goto err;
+
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
- return ret;
+ goto err;
+
+ ret = 0;
hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]);
-
- return 0;
+err:
+ mutex_unlock(&tdev->lock);
+ return ret;
}
static int thingm_version(struct thingm_device *tdev)
@@ -106,10 +123,6 @@ static int thingm_version(struct thingm_device *tdev)
u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
int err;
- err = thingm_send(tdev, buf);
- if (err)
- return err;
-
err = thingm_recv(tdev, buf);
if (err)
return err;
@@ -131,25 +144,17 @@ static int thingm_write_color(struct thingm_rgb *rgb)
return thingm_send(rgb->tdev, buf);
}
-static void thingm_work(struct work_struct *work)
-{
- struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
-
- mutex_lock(&rgb->tdev->lock);
-
- if (thingm_write_color(rgb))
- hid_err(rgb->tdev->hdev, "failed to write color\n");
-
- mutex_unlock(&rgb->tdev->lock);
-}
-
-static void thingm_led_set(struct led_classdev *ldev,
- enum led_brightness brightness)
+static int thingm_led_set(struct led_classdev *ldev,
+ enum led_brightness brightness)
{
struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
+ int ret;
+
+ ret = thingm_write_color(led->rgb);
+ if (ret)
+ hid_err(led->rgb->tdev->hdev, "failed to write color\n");
- /* the ledclass has already stored the brightness value */
- schedule_work(&led->rgb->work);
+ return ret;
}
static int thingm_init_rgb(struct thingm_rgb *rgb)
@@ -162,10 +167,11 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
"thingm%d:red:led%d", minor, rgb->num);
rgb->red.ldev.name = rgb->red.name;
rgb->red.ldev.max_brightness = 255;
- rgb->red.ldev.brightness_set = thingm_led_set;
+ rgb->red.ldev.brightness_set_blocking = thingm_led_set;
rgb->red.rgb = rgb;
- err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
+ err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+ &rgb->red.ldev);
if (err)
return err;
@@ -174,46 +180,27 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
"thingm%d:green:led%d", minor, rgb->num);
rgb->green.ldev.name = rgb->green.name;
rgb->green.ldev.max_brightness = 255;
- rgb->green.ldev.brightness_set = thingm_led_set;
+ rgb->green.ldev.brightness_set_blocking = thingm_led_set;
rgb->green.rgb = rgb;
- err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
+ err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+ &rgb->green.ldev);
if (err)
- goto unregister_red;
+ return err;
/* Register the blue diode */
snprintf(rgb->blue.name, sizeof(rgb->blue.name),
"thingm%d:blue:led%d", minor, rgb->num);
rgb->blue.ldev.name = rgb->blue.name;
rgb->blue.ldev.max_brightness = 255;
- rgb->blue.ldev.brightness_set = thingm_led_set;
+ rgb->blue.ldev.brightness_set_blocking = thingm_led_set;
rgb->blue.rgb = rgb;
- err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
- if (err)
- goto unregister_green;
-
- INIT_WORK(&rgb->work, thingm_work);
-
- return 0;
-
-unregister_green:
- led_classdev_unregister(&rgb->green.ldev);
-
-unregister_red:
- led_classdev_unregister(&rgb->red.ldev);
-
+ err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+ &rgb->blue.ldev);
return err;
}
-static void thingm_remove_rgb(struct thingm_rgb *rgb)
-{
- led_classdev_unregister(&rgb->red.ldev);
- led_classdev_unregister(&rgb->green.ldev);
- led_classdev_unregister(&rgb->blue.ldev);
- flush_work(&rgb->work);
-}
-
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct thingm_device *tdev;
@@ -229,17 +216,13 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
err = hid_parse(hdev);
if (err)
- goto error;
-
- err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
- if (err)
- goto error;
+ return err;
mutex_init(&tdev->lock);
err = thingm_version(tdev);
if (err)
- goto stop;
+ return err;
hid_dbg(hdev, "firmware version: %c.%c\n",
tdev->version.major, tdev->version.minor);
@@ -250,17 +233,18 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!tdev->fwinfo) {
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
- err = -ENODEV;
- goto stop;
+ return -ENODEV;
}
tdev->rgb = devm_kzalloc(&hdev->dev,
sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
GFP_KERNEL);
- if (!tdev->rgb) {
- err = -ENOMEM;
- goto stop;
- }
+ if (!tdev->rgb)
+ return -ENOMEM;
+
+ err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (err)
+ return err;
for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
struct thingm_rgb *rgb = tdev->rgb + i;
@@ -269,28 +253,12 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
rgb->num = tdev->fwinfo->first + i;
err = thingm_init_rgb(rgb);
if (err) {
- while (--i >= 0)
- thingm_remove_rgb(tdev->rgb + i);
- goto stop;
+ hid_hw_stop(hdev);
+ return err;
}
}
return 0;
-stop:
- hid_hw_stop(hdev);
-error:
- return err;
-}
-
-static void thingm_remove(struct hid_device *hdev)
-{
- struct thingm_device *tdev = hid_get_drvdata(hdev);
- int i;
-
- hid_hw_stop(hdev);
-
- for (i = 0; i < tdev->fwinfo->numrgb; ++i)
- thingm_remove_rgb(tdev->rgb + i);
}
static const struct hid_device_id thingm_table[] = {
@@ -302,7 +270,6 @@ MODULE_DEVICE_TABLE(hid, thingm_table);
static struct hid_driver thingm_driver = {
.name = "thingm",
.probe = thingm_probe,
- .remove = thingm_remove,
.id_table = thingm_table,
};
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 4390eee2c..c830ed393 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -2049,9 +2049,11 @@ static void wiimod_mp_in_mp(struct wiimote_data *wdata, const __u8 *ext)
* -----+------------------------------+-----+-----+
* The single bits Yaw, Roll, Pitch in the lower right corner specify
* whether the wiimote is rotating fast (0) or slow (1). Speed for slow
- * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a
- * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast
- * and 9 for slow.
+ * roation is 8192/440 units / deg/s and for fast rotation 8192/2000
+ * units / deg/s. To get a linear scale for fast rotation we multiply
+ * by 2000/440 = ~4.5454 and scale both fast and slow by 9 to match the
+ * previous scale reported by this driver.
+ * This leaves a linear scale with 8192*9/440 (~167.564) units / deg/s.
* If the wiimote is not rotating the sensor reports 2^13 = 8192.
* Ext specifies whether an extension is connected to the motionp.
* which is parsed by wiimote-core.
@@ -2070,15 +2072,15 @@ static void wiimod_mp_in_mp(struct wiimote_data *wdata, const __u8 *ext)
z -= 8192;
if (!(ext[3] & 0x02))
- x *= 18;
+ x = (x * 2000 * 9) / 440;
else
x *= 9;
if (!(ext[4] & 0x02))
- y *= 18;
+ y = (y * 2000 * 9) / 440;
else
y *= 9;
if (!(ext[3] & 0x01))
- z *= 18;
+ z = (z * 2000 * 9) / 440;
else
z *= 9;
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index bb897497f..2e021ba8f 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -1112,13 +1112,30 @@ static int i2c_hid_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid = ihid->hid;
- int ret = 0;
+ int ret;
int wake_status;
- if (hid->driver && hid->driver->suspend)
+ if (hid->driver && hid->driver->suspend) {
+ /*
+ * Wake up the device so that IO issues in
+ * HID driver's suspend code can succeed.
+ */
+ ret = pm_runtime_resume(dev);
+ if (ret < 0)
+ return ret;
+
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!pm_runtime_suspended(dev)) {
+ /* Save some power */
+ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+
+ disable_irq(ihid->irq);
+ }
- disable_irq(ihid->irq);
if (device_may_wakeup(&client->dev)) {
wake_status = enable_irq_wake(ihid->irq);
if (!wake_status)
@@ -1128,10 +1145,7 @@ static int i2c_hid_suspend(struct device *dev)
wake_status);
}
- /* Save some power */
- i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-
- return ret;
+ return 0;
}
static int i2c_hid_resume(struct device *dev)
@@ -1142,11 +1156,6 @@ static int i2c_hid_resume(struct device *dev)
struct hid_device *hid = ihid->hid;
int wake_status;
- enable_irq(ihid->irq);
- ret = i2c_hid_hwreset(client);
- if (ret)
- return ret;
-
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
wake_status = disable_irq_wake(ihid->irq);
if (!wake_status)
@@ -1156,6 +1165,16 @@ static int i2c_hid_resume(struct device *dev)
wake_status);
}
+ /* We'll resume to full power */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ enable_irq(ihid->irq);
+ ret = i2c_hid_hwreset(client);
+ if (ret)
+ return ret;
+
if (hid->driver && hid->driver->reset_resume) {
ret = hid->driver->reset_resume(hid);
return ret;
@@ -1195,6 +1214,7 @@ static const struct dev_pm_ops i2c_hid_pm = {
static const struct i2c_device_id i2c_hid_id_table[] = {
{ "hid", 0 },
+ { "hid-over-i2c", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index e094c572b..16b6f11a0 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -384,7 +384,7 @@ struct uhid_create_req_compat {
static int uhid_event_from_user(const char __user *buffer, size_t len,
struct uhid_event *event)
{
- if (is_compat_task()) {
+ if (in_compat_syscall()) {
u32 type;
if (get_user(type, buffer))
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index dc8e6adf9..53fc856d6 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -55,6 +55,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
@@ -107,6 +108,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
@@ -141,6 +143,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
+ { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 5cb21dd91..ccf188331 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -152,6 +152,25 @@ static void wacom_feature_mapping(struct hid_device *hdev,
hid_data->inputmode = field->report->id;
hid_data->inputmode_index = usage->usage_index;
break;
+
+ case HID_UP_DIGITIZER:
+ if (field->report->id == 0x0B &&
+ (field->application == WACOM_G9_DIGITIZER ||
+ field->application == WACOM_G11_DIGITIZER)) {
+ wacom->wacom_wac.mode_report = field->report->id;
+ wacom->wacom_wac.mode_value = 0;
+ }
+ break;
+
+ case WACOM_G9_PAGE:
+ case WACOM_G11_PAGE:
+ if (field->report->id == 0x03 &&
+ (field->application == WACOM_G9_TOUCHSCREEN ||
+ field->application == WACOM_G11_TOUCHSCREEN)) {
+ wacom->wacom_wac.mode_report = field->report->id;
+ wacom->wacom_wac.mode_value = 0;
+ }
+ break;
}
}
@@ -322,26 +341,41 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev)
return 0;
}
-static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
- int length, int mode)
+static int wacom_set_device_mode(struct hid_device *hdev,
+ struct wacom_wac *wacom_wac)
{
- unsigned char *rep_data;
+ u8 *rep_data;
+ struct hid_report *r;
+ struct hid_report_enum *re;
+ int length;
int error = -ENOMEM, limit = 0;
- rep_data = kzalloc(length, GFP_KERNEL);
+ if (wacom_wac->mode_report < 0)
+ return 0;
+
+ re = &(hdev->report_enum[HID_FEATURE_REPORT]);
+ r = re->report_id_hash[wacom_wac->mode_report];
+ if (!r)
+ return -EINVAL;
+
+ rep_data = hid_alloc_report_buf(r, GFP_KERNEL);
if (!rep_data)
- return error;
+ return -ENOMEM;
+
+ length = hid_report_len(r);
do {
- rep_data[0] = report_id;
- rep_data[1] = mode;
+ rep_data[0] = wacom_wac->mode_report;
+ rep_data[1] = wacom_wac->mode_value;
error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data,
length, 1);
if (error >= 0)
error = wacom_get_report(hdev, HID_FEATURE_REPORT,
rep_data, length, 1);
- } while (error >= 0 && rep_data[1] != mode && limit++ < WAC_MSG_RETRIES);
+ } while (error >= 0 &&
+ rep_data[1] != wacom_wac->mode_report &&
+ limit++ < WAC_MSG_RETRIES);
kfree(rep_data);
@@ -411,32 +445,41 @@ static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
static int wacom_query_tablet_data(struct hid_device *hdev,
struct wacom_features *features)
{
+ struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
if (hdev->bus == BUS_BLUETOOTH)
return wacom_bt_query_tablet_data(hdev, 1, features);
- if (features->type == HID_GENERIC)
- return wacom_hid_set_device_mode(hdev);
-
- if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
- if (features->type > TABLETPC) {
- /* MT Tablet PC touch */
- return wacom_set_device_mode(hdev, 3, 4, 4);
- }
- else if (features->type == WACOM_24HDT) {
- return wacom_set_device_mode(hdev, 18, 3, 2);
- }
- else if (features->type == WACOM_27QHDT) {
- return wacom_set_device_mode(hdev, 131, 3, 2);
- }
- else if (features->type == BAMBOO_PAD) {
- return wacom_set_device_mode(hdev, 2, 2, 2);
- }
- } else if (features->device_type & WACOM_DEVICETYPE_PEN) {
- if (features->type <= BAMBOO_PT) {
- return wacom_set_device_mode(hdev, 2, 2, 2);
+ if (features->type != HID_GENERIC) {
+ if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
+ if (features->type > TABLETPC) {
+ /* MT Tablet PC touch */
+ wacom_wac->mode_report = 3;
+ wacom_wac->mode_value = 4;
+ } else if (features->type == WACOM_24HDT) {
+ wacom_wac->mode_report = 18;
+ wacom_wac->mode_value = 2;
+ } else if (features->type == WACOM_27QHDT) {
+ wacom_wac->mode_report = 131;
+ wacom_wac->mode_value = 2;
+ } else if (features->type == BAMBOO_PAD) {
+ wacom_wac->mode_report = 2;
+ wacom_wac->mode_value = 2;
+ }
+ } else if (features->device_type & WACOM_DEVICETYPE_PEN) {
+ if (features->type <= BAMBOO_PT) {
+ wacom_wac->mode_report = 2;
+ wacom_wac->mode_value = 2;
+ }
}
}
+ wacom_set_device_mode(hdev, wacom_wac);
+
+ if (features->type == HID_GENERIC)
+ return wacom_hid_set_device_mode(hdev);
+
return 0;
}
@@ -1357,6 +1400,9 @@ static void wacom_clean_inputs(struct wacom *wacom)
wacom->wacom_wac.pen_input = NULL;
wacom->wacom_wac.touch_input = NULL;
wacom->wacom_wac.pad_input = NULL;
+ wacom->wacom_wac.pen_registered = false;
+ wacom->wacom_wac.touch_registered = false;
+ wacom->wacom_wac.pad_registered = false;
wacom_destroy_leds(wacom);
}
@@ -1494,123 +1540,6 @@ static void wacom_calculate_res(struct wacom_features *features)
features->unitExpo);
}
-static void wacom_wireless_work(struct work_struct *work)
-{
- struct wacom *wacom = container_of(work, struct wacom, work);
- struct usb_device *usbdev = wacom->usbdev;
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
- struct hid_device *hdev1, *hdev2;
- struct wacom *wacom1, *wacom2;
- struct wacom_wac *wacom_wac1, *wacom_wac2;
- int error;
-
- /*
- * Regardless if this is a disconnect or a new tablet,
- * remove any existing input and battery devices.
- */
-
- wacom_destroy_battery(wacom);
-
- /* Stylus interface */
- hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
- wacom1 = hid_get_drvdata(hdev1);
- wacom_wac1 = &(wacom1->wacom_wac);
- wacom_clean_inputs(wacom1);
-
- /* Touch interface */
- hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
- wacom2 = hid_get_drvdata(hdev2);
- wacom_wac2 = &(wacom2->wacom_wac);
- wacom_clean_inputs(wacom2);
-
- if (wacom_wac->pid == 0) {
- hid_info(wacom->hdev, "wireless tablet disconnected\n");
- wacom_wac1->shared->type = 0;
- } else {
- const struct hid_device_id *id = wacom_ids;
-
- hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
- wacom_wac->pid);
-
- while (id->bus) {
- if (id->vendor == USB_VENDOR_ID_WACOM &&
- id->product == wacom_wac->pid)
- break;
- id++;
- }
-
- if (!id->bus) {
- hid_info(wacom->hdev, "ignoring unknown PID.\n");
- return;
- }
-
- /* Stylus interface */
- wacom_wac1->features =
- *((struct wacom_features *)id->driver_data);
- wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
- wacom_set_default_phy(&wacom_wac1->features);
- wacom_calculate_res(&wacom_wac1->features);
- snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
- wacom_wac1->features.name);
- if (wacom_wac1->features.type < BAMBOO_PEN ||
- wacom_wac1->features.type > BAMBOO_PT) {
- snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
- wacom_wac1->features.name);
- wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
- }
- wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
- wacom_wac1->shared->type = wacom_wac1->features.type;
- wacom_wac1->pid = wacom_wac->pid;
- error = wacom_allocate_inputs(wacom1) ||
- wacom_register_inputs(wacom1);
- if (error)
- goto fail;
-
- /* Touch interface */
- if (wacom_wac1->features.touch_max ||
- (wacom_wac1->features.type >= INTUOSHT &&
- wacom_wac1->features.type <= BAMBOO_PT)) {
- wacom_wac2->features =
- *((struct wacom_features *)id->driver_data);
- wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
- wacom_set_default_phy(&wacom_wac2->features);
- wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
- wacom_calculate_res(&wacom_wac2->features);
- snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
- "%s (WL) Finger",wacom_wac2->features.name);
- if (wacom_wac1->features.touch_max)
- wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
- if (wacom_wac1->features.type >= INTUOSHT &&
- wacom_wac1->features.type <= BAMBOO_PT) {
- snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
- "%s (WL) Pad",wacom_wac2->features.name);
- wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
- }
- wacom_wac2->pid = wacom_wac->pid;
- error = wacom_allocate_inputs(wacom2) ||
- wacom_register_inputs(wacom2);
- if (error)
- goto fail;
-
- if ((wacom_wac1->features.type == INTUOSHT ||
- wacom_wac1->features.type == INTUOSHT2) &&
- wacom_wac1->features.touch_max)
- wacom_wac->shared->touch_input = wacom_wac2->touch_input;
- }
-
- error = wacom_initialize_battery(wacom);
- if (error)
- goto fail;
- }
-
- return;
-
-fail:
- wacom_clean_inputs(wacom1);
- wacom_clean_inputs(wacom2);
- return;
-}
-
void wacom_battery_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
@@ -1642,7 +1571,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
return size;
}
-static void wacom_update_name(struct wacom *wacom)
+static void wacom_update_name(struct wacom *wacom, const char *suffix)
{
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
@@ -1678,68 +1607,28 @@ static void wacom_update_name(struct wacom *wacom)
/* Append the device type to the name */
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
- "%s Pen", name);
+ "%s%s Pen", name, suffix);
snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
- "%s Finger", name);
+ "%s%s Finger", name, suffix);
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
- "%s Pad", name);
+ "%s%s Pad", name, suffix);
}
-static int wacom_probe(struct hid_device *hdev,
- const struct hid_device_id *id)
+static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
- struct usb_device *dev = interface_to_usbdev(intf);
- struct wacom *wacom;
- struct wacom_wac *wacom_wac;
- struct wacom_features *features;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *features = &wacom_wac->features;
+ struct hid_device *hdev = wacom->hdev;
int error;
unsigned int connect_mask = HID_CONNECT_HIDRAW;
- if (!id->driver_data)
- return -EINVAL;
-
- hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
-
- /* hid-core sets this quirk for the boot interface */
- hdev->quirks &= ~HID_QUIRK_NOGET;
-
- wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
- if (!wacom)
- return -ENOMEM;
-
- hid_set_drvdata(hdev, wacom);
- wacom->hdev = hdev;
-
- /* ask for the report descriptor to be loaded by HID */
- error = hid_parse(hdev);
- if (error) {
- hid_err(hdev, "parse failed\n");
- goto fail_parse;
- }
-
- wacom_wac = &wacom->wacom_wac;
- wacom_wac->features = *((struct wacom_features *)id->driver_data);
- features = &wacom_wac->features;
features->pktlen = wacom_compute_pktlen(hdev);
- if (features->pktlen > WACOM_PKGLEN_MAX) {
- error = -EINVAL;
- goto fail_pktlen;
- }
-
- if (features->check_for_hid_type && features->hid_type != hdev->type) {
- error = -ENODEV;
- goto fail_type;
- }
-
- wacom->usbdev = dev;
- wacom->intf = intf;
- mutex_init(&wacom->lock);
- INIT_WORK(&wacom->work, wacom_wireless_work);
+ if (features->pktlen > WACOM_PKGLEN_MAX)
+ return -EINVAL;
error = wacom_allocate_inputs(wacom);
if (error)
- goto fail_allocate_inputs;
+ return error;
/*
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
@@ -1752,7 +1641,7 @@ static int wacom_probe(struct hid_device *hdev,
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
error = -ENODEV;
- goto fail_shared_data;
+ goto fail_allocate_inputs;
}
}
@@ -1772,14 +1661,14 @@ static int wacom_probe(struct hid_device *hdev,
error ? "Ignoring" : "Assuming pen");
if (error)
- goto fail_shared_data;
+ goto fail_parsed;
features->device_type |= WACOM_DEVICETYPE_PEN;
}
wacom_calculate_res(features);
- wacom_update_name(wacom);
+ wacom_update_name(wacom, wireless ? " (WL)" : "");
error = wacom_add_shared_data(hdev);
if (error)
@@ -1796,14 +1685,6 @@ static int wacom_probe(struct hid_device *hdev,
if (error)
goto fail_register_inputs;
- if (hdev->bus == BUS_BLUETOOTH) {
- error = device_create_file(&hdev->dev, &dev_attr_speed);
- if (error)
- hid_warn(hdev,
- "can't create sysfs speed attribute err: %d\n",
- error);
- }
-
if (features->type == HID_GENERIC)
connect_mask |= HID_CONNECT_DRIVER;
@@ -1814,8 +1695,10 @@ static int wacom_probe(struct hid_device *hdev,
goto fail_hw_start;
}
- /* Note that if query fails it is not a hard failure */
- wacom_query_tablet_data(hdev, features);
+ if (!wireless) {
+ /* Note that if query fails it is not a hard failure */
+ wacom_query_tablet_data(hdev, features);
+ }
/* touch only Bamboo doesn't support pen */
if ((features->type == BAMBOO_TOUCH) &&
@@ -1844,18 +1727,169 @@ static int wacom_probe(struct hid_device *hdev,
return 0;
fail_hw_start:
- if (hdev->bus == BUS_BLUETOOTH)
- device_remove_file(&hdev->dev, &dev_attr_speed);
+ hid_hw_stop(hdev);
fail_register_inputs:
wacom_clean_inputs(wacom);
wacom_destroy_battery(wacom);
fail_battery:
wacom_remove_shared_data(wacom);
fail_shared_data:
- wacom_clean_inputs(wacom);
+fail_parsed:
fail_allocate_inputs:
+ wacom_clean_inputs(wacom);
+ return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+ struct wacom *wacom = container_of(work, struct wacom, work);
+ struct usb_device *usbdev = wacom->usbdev;
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct hid_device *hdev1, *hdev2;
+ struct wacom *wacom1, *wacom2;
+ struct wacom_wac *wacom_wac1, *wacom_wac2;
+ int error;
+
+ /*
+ * Regardless if this is a disconnect or a new tablet,
+ * remove any existing input and battery devices.
+ */
+
+ wacom_destroy_battery(wacom);
+
+ /* Stylus interface */
+ hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
+ wacom1 = hid_get_drvdata(hdev1);
+ wacom_wac1 = &(wacom1->wacom_wac);
+ wacom_clean_inputs(wacom1);
+
+ /* Touch interface */
+ hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
+ wacom2 = hid_get_drvdata(hdev2);
+ wacom_wac2 = &(wacom2->wacom_wac);
+ wacom_clean_inputs(wacom2);
+
+ if (wacom_wac->pid == 0) {
+ hid_info(wacom->hdev, "wireless tablet disconnected\n");
+ wacom_wac1->shared->type = 0;
+ } else {
+ const struct hid_device_id *id = wacom_ids;
+
+ hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
+ wacom_wac->pid);
+
+ while (id->bus) {
+ if (id->vendor == USB_VENDOR_ID_WACOM &&
+ id->product == wacom_wac->pid)
+ break;
+ id++;
+ }
+
+ if (!id->bus) {
+ hid_info(wacom->hdev, "ignoring unknown PID.\n");
+ return;
+ }
+
+ /* Stylus interface */
+ wacom_wac1->features =
+ *((struct wacom_features *)id->driver_data);
+
+ wacom_wac1->pid = wacom_wac->pid;
+ hid_hw_stop(hdev1);
+ error = wacom_parse_and_register(wacom1, true);
+ if (error)
+ goto fail;
+
+ /* Touch interface */
+ if (wacom_wac1->features.touch_max ||
+ (wacom_wac1->features.type >= INTUOSHT &&
+ wacom_wac1->features.type <= BAMBOO_PT)) {
+ wacom_wac2->features =
+ *((struct wacom_features *)id->driver_data);
+ wacom_wac2->pid = wacom_wac->pid;
+ hid_hw_stop(hdev2);
+ error = wacom_parse_and_register(wacom2, true);
+ if (error)
+ goto fail;
+ }
+
+ error = wacom_initialize_battery(wacom);
+ if (error)
+ goto fail;
+ }
+
+ return;
+
+fail:
+ wacom_clean_inputs(wacom1);
+ wacom_clean_inputs(wacom2);
+ return;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct wacom *wacom;
+ struct wacom_wac *wacom_wac;
+ struct wacom_features *features;
+ int error;
+
+ if (!id->driver_data)
+ return -EINVAL;
+
+ hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+ /* hid-core sets this quirk for the boot interface */
+ hdev->quirks &= ~HID_QUIRK_NOGET;
+
+ wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+ if (!wacom)
+ return -ENOMEM;
+
+ hid_set_drvdata(hdev, wacom);
+ wacom->hdev = hdev;
+
+ wacom_wac = &wacom->wacom_wac;
+ wacom_wac->features = *((struct wacom_features *)id->driver_data);
+ features = &wacom_wac->features;
+
+ if (features->check_for_hid_type && features->hid_type != hdev->type) {
+ error = -ENODEV;
+ goto fail_type;
+ }
+
+ wacom_wac->hid_data.inputmode = -1;
+ wacom_wac->mode_report = -1;
+
+ wacom->usbdev = dev;
+ wacom->intf = intf;
+ mutex_init(&wacom->lock);
+ INIT_WORK(&wacom->work, wacom_wireless_work);
+
+ /* ask for the report descriptor to be loaded by HID */
+ error = hid_parse(hdev);
+ if (error) {
+ hid_err(hdev, "parse failed\n");
+ goto fail_parse;
+ }
+
+ error = wacom_parse_and_register(wacom, false);
+ if (error)
+ goto fail_parse;
+
+ if (hdev->bus == BUS_BLUETOOTH) {
+ error = device_create_file(&hdev->dev, &dev_attr_speed);
+ if (error)
+ hid_warn(hdev,
+ "can't create sysfs speed attribute err: %d\n",
+ error);
+ }
+
+ return 0;
+
fail_type:
-fail_pktlen:
fail_parse:
kfree(wacom);
hid_set_drvdata(hdev, NULL);
@@ -1865,6 +1899,11 @@ fail_parse:
static void wacom_remove(struct hid_device *hdev)
{
struct wacom *wacom = hid_get_drvdata(hdev);
+ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_features *features = &wacom_wac->features;
+
+ if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
+ hid_hw_close(hdev);
hid_hw_stop(hdev);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index f71187aad..cf2ba4345 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -575,16 +575,102 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
return 1;
}
+static int wacom_intuos_get_tool_type(int tool_id)
+{
+ int tool_type;
+
+ switch (tool_id) {
+ case 0x812: /* Inking pen */
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x120802: /* Intuos4/5 Inking Pen */
+ case 0x012:
+ tool_type = BTN_TOOL_PENCIL;
+ break;
+
+ case 0x822: /* Pen */
+ case 0x842:
+ case 0x852:
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x885: /* Intuos3 Marker Pen */
+ case 0x802: /* Intuos4/5 13HD/24HD General Pen */
+ case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
+ case 0x8e2: /* IntuosHT2 pen */
+ case 0x022:
+ case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
+ case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
+ case 0x160802: /* Cintiq 13HD Pro Pen */
+ case 0x180802: /* DTH2242 Pen */
+ case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
+ tool_type = BTN_TOOL_PEN;
+ break;
+
+ case 0x832: /* Stroke pen */
+ case 0x032:
+ tool_type = BTN_TOOL_BRUSH;
+ break;
+
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x09c:
+ case 0x094:
+ case 0x017: /* Intuos3 2D Mouse */
+ case 0x806: /* Intuos4 Mouse */
+ tool_type = BTN_TOOL_MOUSE;
+ break;
+
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ case 0x006: /* Intuos4 Lens cursor */
+ tool_type = BTN_TOOL_LENS;
+ break;
+
+ case 0x82a: /* Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0xd1a:
+ case 0x0fa:
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
+ case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+ case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+ case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
+ case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+ case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
+ case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
+ case 0x18080a: /* DTH2242 Eraser */
+ case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+ tool_type = BTN_TOOL_RUBBER;
+ break;
+
+ case 0xd12:
+ case 0x912:
+ case 0x112:
+ case 0x913: /* Intuos3 Airbrush */
+ case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
+ case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
+ tool_type = BTN_TOOL_AIRBRUSH;
+ break;
+
+ default: /* Unknown tool */
+ tool_type = BTN_TOOL_PEN;
+ break;
+ }
+ return tool_type;
+}
+
static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
struct input_dev *input = wacom->pen_input;
- int idx = 0;
+ int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
- /* tool number */
- if (features->type == INTUOS)
- idx = data[1] & 0x01;
+ if (!(((data[1] & 0xfc) == 0xc0) || /* in prox */
+ ((data[1] & 0xfe) == 0x20) || /* in range */
+ ((data[1] & 0xfe) == 0x80))) /* out prox */
+ return 0;
/* Enter report */
if ((data[1] & 0xfc) == 0xc0) {
@@ -596,116 +682,25 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
- switch (wacom->id[idx]) {
- case 0x812: /* Inking pen */
- case 0x801: /* Intuos3 Inking pen */
- case 0x120802: /* Intuos4/5 Inking Pen */
- case 0x012:
- wacom->tool[idx] = BTN_TOOL_PENCIL;
- break;
-
- case 0x822: /* Pen */
- case 0x842:
- case 0x852:
- case 0x823: /* Intuos3 Grip Pen */
- case 0x813: /* Intuos3 Classic Pen */
- case 0x885: /* Intuos3 Marker Pen */
- case 0x802: /* Intuos4/5 13HD/24HD General Pen */
- case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
- case 0x022:
- case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
- case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
- case 0x160802: /* Cintiq 13HD Pro Pen */
- case 0x180802: /* DTH2242 Pen */
- case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
- wacom->tool[idx] = BTN_TOOL_PEN;
- break;
-
- case 0x832: /* Stroke pen */
- case 0x032:
- wacom->tool[idx] = BTN_TOOL_BRUSH;
- break;
-
- case 0x007: /* Mouse 4D and 2D */
- case 0x09c:
- case 0x094:
- case 0x017: /* Intuos3 2D Mouse */
- case 0x806: /* Intuos4 Mouse */
- wacom->tool[idx] = BTN_TOOL_MOUSE;
- break;
-
- case 0x096: /* Lens cursor */
- case 0x097: /* Intuos3 Lens cursor */
- case 0x006: /* Intuos4 Lens cursor */
- wacom->tool[idx] = BTN_TOOL_LENS;
- break;
-
- case 0x82a: /* Eraser */
- case 0x85a:
- case 0x91a:
- case 0xd1a:
- case 0x0fa:
- case 0x82b: /* Intuos3 Grip Pen Eraser */
- case 0x81b: /* Intuos3 Classic Pen Eraser */
- case 0x91b: /* Intuos3 Airbrush Eraser */
- case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
- case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
- case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
- case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
- case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
- case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
- case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
- case 0x18080a: /* DTH2242 Eraser */
- case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
- wacom->tool[idx] = BTN_TOOL_RUBBER;
- break;
-
- case 0xd12:
- case 0x912:
- case 0x112:
- case 0x913: /* Intuos3 Airbrush */
- case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
- case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
- wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
- break;
-
- default: /* Unknown tool */
- wacom->tool[idx] = BTN_TOOL_PEN;
- break;
- }
+ wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]);
+
+ wacom->shared->stylus_in_proximity = true;
return 1;
}
- /*
- * don't report events for invalid data
- */
- /* older I4 styli don't work with new Cintiqs */
- if ((!((wacom->id[idx] >> 20) & 0x01) &&
- (features->type == WACOM_21UX2)) ||
- /* Only large Intuos support Lense Cursor */
- (wacom->tool[idx] == BTN_TOOL_LENS &&
- (features->type == INTUOS3 ||
- features->type == INTUOS3S ||
- features->type == INTUOS4 ||
- features->type == INTUOS4S ||
- features->type == INTUOS5 ||
- features->type == INTUOS5S ||
- features->type == INTUOSPM ||
- features->type == INTUOSPS)) ||
- /* Cintiq doesn't send data when RDY bit isn't set */
- (features->type == CINTIQ && !(data[1] & 0x40)))
- return 1;
+ /* in Range */
+ if ((data[1] & 0xfe) == 0x20) {
+ if (features->type != INTUOSHT2)
+ wacom->shared->stylus_in_proximity = true;
- wacom->shared->stylus_in_proximity = true;
- if (wacom->shared->touch_down)
+ /* in Range while exiting */
+ if (wacom->reporting_data) {
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
+ return 2;
+ }
return 1;
-
- /* in Range while exiting */
- if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
- return 2;
}
/* Exit report */
@@ -750,13 +745,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
return 2;
}
- /* don't report other events if we don't know the ID */
- if (!wacom->id[idx]) {
- /* but reschedule a read of the current tool */
- wacom_intuos_schedule_prox_event(wacom);
- return 1;
- }
-
return 0;
}
@@ -897,6 +885,36 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
data[0] != WACOM_REPORT_INTUOS_PEN)
return 0;
+ if (wacom->shared->touch_down)
+ return 1;
+
+ /* don't report events if we don't know the tool ID */
+ if (!wacom->id[idx]) {
+ /* but reschedule a read of the current tool */
+ wacom_intuos_schedule_prox_event(wacom);
+ return 1;
+ }
+
+ /*
+ * don't report events for invalid data
+ */
+ /* older I4 styli don't work with new Cintiqs */
+ if ((!((wacom->id[idx] >> 20) & 0x01) &&
+ (features->type == WACOM_21UX2)) ||
+ /* Only large Intuos support Lense Cursor */
+ (wacom->tool[idx] == BTN_TOOL_LENS &&
+ (features->type == INTUOS3 ||
+ features->type == INTUOS3S ||
+ features->type == INTUOS4 ||
+ features->type == INTUOS4S ||
+ features->type == INTUOS5 ||
+ features->type == INTUOS5S ||
+ features->type == INTUOSPM ||
+ features->type == INTUOSPS)) ||
+ /* Cintiq doesn't send data when RDY bit isn't set */
+ (features->type == CINTIQ && !(data[1] & 0x40)))
+ return 1;
+
x = (be16_to_cpup((__be16 *)&data[2]) << 1) | ((data[9] >> 1) & 1);
y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1);
distance = data[9] >> 2;
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 25baa7f29..e2084d914 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -84,6 +84,12 @@
#define WACOM_DEVICETYPE_WL_MONITOR 0x0008
#define WACOM_VENDORDEFINED_PEN 0xff0d0001
+#define WACOM_G9_PAGE 0xff090000
+#define WACOM_G9_DIGITIZER (WACOM_G9_PAGE | 0x02)
+#define WACOM_G9_TOUCHSCREEN (WACOM_G9_PAGE | 0x11)
+#define WACOM_G11_PAGE 0xff110000
+#define WACOM_G11_DIGITIZER (WACOM_G11_PAGE | 0x02)
+#define WACOM_G11_TOUCHSCREEN (WACOM_G11_PAGE | 0x11)
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_STYLUS) || \
@@ -238,6 +244,8 @@ struct wacom_wac {
int ps_connected;
u8 bt_features;
u8 bt_high_speed;
+ int mode_report;
+ int mode_value;
struct hid_data hid_data;
};